pidgin/pidgin

67e558b6711b
Merged in kill-oscar-prpl (pull request #609)

Kill oscar prpl

Approved-by: Elliott Sales de Andrade
Approved-by: Gary Kramlich
Approved-by: Eion Robb
  • +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
  • +0 -1
    libpurple/protocols/meson.build
  • +0 -49
    libpurple/protocols/oscar/AUTHORS
  • +0 -504
    libpurple/protocols/oscar/COPYING
  • +0 -84
    libpurple/protocols/oscar/aim.c
  • +0 -56
    libpurple/protocols/oscar/aim.h
  • +0 -120
    libpurple/protocols/oscar/authorization.c
  • +0 -291
    libpurple/protocols/oscar/bstream.c
  • +0 -634
    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 -647
    libpurple/protocols/oscar/family_auth.c
  • +0 -190
    libpurple/protocols/oscar/family_bart.c
  • +0 -92
    libpurple/protocols/oscar/family_bos.c
  • +0 -153
    libpurple/protocols/oscar/family_buddy.c
  • +0 -403
    libpurple/protocols/oscar/family_chat.c
  • +0 -446
    libpurple/protocols/oscar/family_chatnav.c
  • +0 -2065
    libpurple/protocols/oscar/family_feedbag.c
  • +0 -2149
    libpurple/protocols/oscar/family_icbm.c
  • +0 -792
    libpurple/protocols/oscar/family_icq.c
  • +0 -1546
    libpurple/protocols/oscar/family_locate.c
  • +0 -994
    libpurple/protocols/oscar/family_oservice.c
  • +0 -84
    libpurple/protocols/oscar/family_popup.c
  • +0 -64
    libpurple/protocols/oscar/family_stats.c
  • +0 -156
    libpurple/protocols/oscar/family_userlookup.c
  • +0 -1117
    libpurple/protocols/oscar/flap_connection.c
  • +0 -91
    libpurple/protocols/oscar/icq.c
  • +0 -56
    libpurple/protocols/oscar/icq.h
  • +0 -425
    libpurple/protocols/oscar/kerberos.c
  • +0 -63
    libpurple/protocols/oscar/meson.build
  • +0 -133
    libpurple/protocols/oscar/misc.c
  • +0 -179
    libpurple/protocols/oscar/msgcookie.c
  • +0 -626
    libpurple/protocols/oscar/odc.c
  • +0 -823
    libpurple/protocols/oscar/oft.c
  • +0 -5839
    libpurple/protocols/oscar/oscar.c
  • +0 -1379
    libpurple/protocols/oscar/oscar.h
  • +0 -167
    libpurple/protocols/oscar/oscar_data.c
  • +0 -131
    libpurple/protocols/oscar/oscarcommon.h
  • +0 -1212
    libpurple/protocols/oscar/peer.c
  • +0 -295
    libpurple/protocols/oscar/peer.h
  • +0 -354
    libpurple/protocols/oscar/peer_proxy.c
  • +0 -95
    libpurple/protocols/oscar/rxhandlers.c
  • +0 -163
    libpurple/protocols/oscar/snac.c
  • +0 -288
    libpurple/protocols/oscar/snactypes.h
  • +0 -8
    libpurple/protocols/oscar/tests/meson.build
  • +0 -42
    libpurple/protocols/oscar/tests/test_oscar_util.c
  • +0 -815
    libpurple/protocols/oscar/tlv.c
  • +0 -553
    libpurple/protocols/oscar/userinfo.c
  • +0 -322
    libpurple/protocols/oscar/util.c
  • +0 -139
    libpurple/protocols/oscar/visibility.c
  • +0 -32
    libpurple/protocols/oscar/visibility.h
  • +1 -2
    meson.build
  • +2 -43
    po/POTFILES.in
  • 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/libpurple/protocols/meson.build Sun Oct 20 00:24:28 2019 +0300
    +++ b/libpurple/protocols/meson.build Fri Oct 25 07:52:44 2019 +0000
    @@ -5,7 +5,6 @@
    subdir('jabber')
    subdir('novell')
    subdir('null')
    -subdir('oscar')
    subdir('sametime')
    subdir('silc')
    subdir('simple')
    --- a/libpurple/protocols/oscar/AUTHORS Sun Oct 20 00:24:28 2019 +0300
    +++ /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 Sun Oct 20 00:24:28 2019 +0300
    +++ /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/aim.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,84 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "aim.h"
    -
    -#include "core.h"
    -#include "plugins.h"
    -#include "signals.h"
    -
    -#include "oscarcommon.h"
    -
    -static void
    -aim_protocol_init(AIMProtocol *self)
    -{
    - PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    -
    - protocol->id = "prpl-aim";
    - protocol->name = "AIM";
    -
    - oscar_init_account_options(protocol, FALSE);
    -}
    -
    -static void
    -aim_protocol_class_init(AIMProtocolClass *klass)
    -{
    - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    -
    - protocol_class->list_icon = oscar_list_icon_aim;
    -}
    -
    -static void
    -aim_protocol_class_finalize(G_GNUC_UNUSED AIMProtocolClass *klass)
    -{
    -}
    -
    -static void
    -aim_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    -{
    - client_iface->get_max_message_size = oscar_get_max_message_size;
    -}
    -
    -static void
    -aim_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    -{
    - privacy_iface->add_permit = oscar_add_permit;
    - privacy_iface->rem_permit = oscar_rem_permit;
    - privacy_iface->set_permit_deny = oscar_set_aim_permdeny;
    -}
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - AIMProtocol, aim_protocol, OSCAR_TYPE_PROTOCOL, 0,
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    - aim_protocol_client_iface_init)
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    - aim_protocol_privacy_iface_init));
    -
    -/* This exists solely because the above macro makes aim_protocol_register_type
    - * static. */
    -void
    -aim_protocol_register(PurplePlugin *plugin)
    -{
    - aim_protocol_register_type(G_TYPE_MODULE(plugin));
    -}
    --- a/libpurple/protocols/oscar/aim.h Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,56 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_OSCAR_AIM_H
    -#define PURPLE_OSCAR_AIM_H
    -
    -#include "oscar.h"
    -
    -#define AIM_TYPE_PROTOCOL (aim_protocol_get_type())
    -#define AIM_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), AIM_TYPE_PROTOCOL, AIMProtocol))
    -#define AIM_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), AIM_TYPE_PROTOCOL, AIMProtocolClass))
    -#define AIM_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), AIM_TYPE_PROTOCOL))
    -#define AIM_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), AIM_TYPE_PROTOCOL))
    -#define AIM_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), AIM_TYPE_PROTOCOL, AIMProtocolClass))
    -
    -typedef struct
    -{
    - OscarProtocol parent;
    -} AIMProtocol;
    -
    -typedef struct
    -{
    - OscarProtocolClass parent_class;
    -} AIMProtocolClass;
    -
    -/**
    - * Registers the AIMProtocol type in the type system.
    - */
    -G_GNUC_INTERNAL
    -void aim_protocol_register(PurplePlugin *plugin);
    -
    -/**
    - * Returns the GType for the AIMProtocol object.
    - */
    -G_MODULE_EXPORT GType aim_protocol_get_type(void);
    -
    -#endif /* PURPLE_OSCAR_AIM_H */
    --- a/libpurple/protocols/oscar/authorization.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,120 +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_blist_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_protocol_got_user_status(account,
    - purple_buddy_get_name(buddy),
    - OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_protocol_got_user_status(account,
    - purple_buddy_get_name(buddy),
    - OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    - }
    - }
    -}
    -
    -static void
    -oscar_auth_grant(const char *message, 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(const char *msg, 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, 0x00, msg ? msg : _("No reason given."));
    -
    - oscar_free_name_data(data);
    -}
    -
    -void
    -oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    - 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_blist_find_buddy(account, data->name) != NULL,
    - oscar_auth_grant, oscar_auth_dontgrant, data);
    -}
    --- a/libpurple/protocols/oscar/bstream.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,291 +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)
    -{
    - g_return_val_if_fail(bs != NULL, 0);
    - g_return_val_if_fail(bs->len >= bs->offset, 0);
    -
    - 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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,634 +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 "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(PurpleXmlNode *resp, const char *url)
    -{
    - PurpleXmlNode *text;
    - PurpleXmlNode *status_code_node;
    - gboolean have_error_code = TRUE;
    - gchar *err = NULL;
    - gchar *details = NULL;
    -
    - status_code_node = purple_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 = purple_xmlnode_get_data_unescaped(status_code_node);
    - if (purple_strequal(status_code, "200")) {
    - have_error_code = FALSE;
    - }
    - }
    - if (have_error_code && resp && (text = purple_xmlnode_get_child(resp, "statusText"))) {
    - details = purple_xmlnode_get_data(text);
    - }
    -
    - if (details && *details) {
    - /* Translators: The first %s is a URL. The second is a brief error
    - message. */
    - err = g_strdup_printf(_("Received unexpected response from %s: %s"), url, details);
    - } else {
    - /* Translators: %s in this string is a URL */
    - 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)
    -{
    - GHmac *hmac;
    - guchar digest[32];
    - gsize digest_len = 32;
    -
    - hmac = g_hmac_new(G_CHECKSUM_SHA256, (guchar *)key, strlen(key));
    - g_hmac_update(hmac, (guchar *)message, -1);
    - g_hmac_get_digest(hmac, digest, &digest_len);
    - g_hmac_unref(hmac);
    -
    - return g_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);
    - PurpleXmlNode *response_node, *tmp_node, *data_node;
    - PurpleXmlNode *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 = purple_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);
    - msg = generate_error_message(response_node,
    - get_start_oscar_session_url(od));
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - return FALSE;
    - }
    -
    - /* Grab the necessary XML nodes */
    - tmp_node = purple_xmlnode_get_child(response_node, "statusCode");
    - data_node = purple_xmlnode_get_child(response_node, "data");
    - if (data_node != NULL) {
    - host_node = purple_xmlnode_get_child(data_node, "host");
    - port_node = purple_xmlnode_get_child(data_node, "port");
    - cookie_node = purple_xmlnode_get_child(data_node, "cookie");
    - }
    -
    - /* Make sure we have a status code */
    - if (tmp_node == NULL || (tmp = purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - purple_xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - /* Make sure the status code was 200 */
    - code = atoi(tmp);
    - if (code != 200)
    - {
    - PurpleXmlNode *status_detail_node;
    - guint status_detail = 0;
    -
    - status_detail_node = purple_xmlnode_get_child(response_node,
    - "statusDetailCode");
    - if (status_detail_node) {
    - gchar *data = purple_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(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(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
    - g_free(msg);
    - }
    -
    - g_free(tmp);
    - purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - purple_xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - if (!purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION)) {
    - tls_node = purple_xmlnode_get_child(data_node, "tlsCertName");
    - if (tls_node != NULL) {
    - *tls_certname = purple_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(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required encryption in your account settings, but one of the servers doesn't support it."));
    - purple_xmlnode_free(response_node);
    - return FALSE;
    - }
    - }
    - }
    -
    - /* Extract data from the XML */
    - *host = purple_xmlnode_get_data_unescaped(host_node);
    - tmp = purple_xmlnode_get_data_unescaped(port_node);
    - *cookie = purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - g_free(*host);
    - g_free(tmp);
    - g_free(*cookie);
    - purple_xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - *port = atoi(tmp);
    - g_free(tmp);
    -
    - return TRUE;
    -}
    -
    -static void
    -start_oscar_session_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
    - gpointer user_data)
    -{
    - OscarData *od = user_data;
    - PurpleConnection *gc;
    - char *host, *cookie;
    - char *tls_certname = NULL;
    - unsigned short port;
    - guint8 *cookiedata;
    - gsize cookiedata_len = 0;
    -
    - gc = od->gc;
    -
    - g_clear_object(&od->http_conns);
    -
    - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
    - gchar *tmp;
    - /* Translators: The first %s is a URL, the second is a brief error
    - message. */
    - tmp = g_strdup_printf(_("Error requesting %s: %d %s"),
    - get_start_oscar_session_url(od), msg->status_code,
    - msg->reason_phrase);
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - if (!parse_start_oscar_session_response(gc, msg->response_body->data,
    - msg->response_body->length, &host,
    - &port, &cookie, &tls_certname)) {
    - return;
    - }
    -
    - cookiedata = g_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)
    -{
    - gchar *query_string, *signature, *uri;
    - SoupMessage *msg;
    - 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=%" G_GINT64_FORMAT
    - "&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),
    - (gint64)hosttime,
    - !purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION));
    - signature = generate_signature("GET", get_start_oscar_session_url(od),
    - query_string, session_key);
    -
    - uri = g_strdup_printf("%s?%s&sig_sha256=%s",
    - get_start_oscar_session_url(od), query_string,
    - signature);
    -
    - msg = soup_message_new("GET", uri);
    - soup_session_queue_message(od->http_conns, msg, start_oscar_session_cb, od);
    -
    - g_free(query_string);
    - g_free(signature);
    - g_free(uri);
    -}
    -
    -/**
    - * 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()
    - * 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);
    - PurpleXmlNode *response_node, *tmp_node, *data_node;
    - PurpleXmlNode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL;
    - char *tmp;
    -
    - /* Parse the response as XML */
    - response_node = purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - return FALSE;
    - }
    -
    - /* Grab the necessary XML nodes */
    - tmp_node = purple_xmlnode_get_child(response_node, "statusCode");
    - data_node = purple_xmlnode_get_child(response_node, "data");
    - if (data_node != NULL) {
    - secret_node = purple_xmlnode_get_child(data_node, "sessionSecret");
    - hosttime_node = purple_xmlnode_get_child(data_node, "hostTime");
    - token_node = purple_xmlnode_get_child(data_node, "token");
    - if (token_node != NULL)
    - tokena_node = purple_xmlnode_get_child(token_node, "a");
    - }
    -
    - /* Make sure we have a status code */
    - if (tmp_node == NULL || (tmp = purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - purple_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 = purple_xmlnode_get_child(response_node, "statusDetailCode");
    - if (tmp_node != NULL && (tmp = purple_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, NULL, NULL);
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Incorrect password"));
    - } else if (status_code == 330 && status_detail_code == 3015) {
    - purple_connection_error(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(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(gc,
    - PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
    - g_free(msg);
    - }
    -
    - purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - purple_xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - /* Extract data from the XML */
    - *token = purple_xmlnode_get_data_unescaped(tokena_node);
    - *secret = purple_xmlnode_get_data_unescaped(secret_node);
    - tmp = purple_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(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - g_free(*token);
    - g_free(*secret);
    - g_free(tmp);
    - purple_xmlnode_free(response_node);
    - return FALSE;
    - }
    -
    - *hosttime = strtol(tmp, NULL, 10);
    - g_free(tmp);
    -
    - purple_xmlnode_free(response_node);
    -
    - return TRUE;
    -}
    -
    -static void
    -client_login_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
    - gpointer user_data)
    -{
    - OscarData *od = user_data;
    - PurpleConnection *gc;
    - char *token, *secret, *session_key;
    - time_t hosttime;
    - int password_len;
    - char *password;
    -
    - gc = od->gc;
    -
    - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
    - gchar *tmp;
    - tmp = g_strdup_printf(_("Error requesting %s: %d %s"),
    - get_client_login_url(od), msg->status_code,
    - msg->reason_phrase);
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - if (!parse_client_login_response(gc, msg->response_body->data,
    - msg->response_body->length, &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;
    - SoupMessage *msg;
    - 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);
    -
    - msg = soup_form_request_new("POST", get_client_login_url(od), "devId",
    - get_client_key(od), "f", "xml", "pwd", password,
    - "s", username, NULL);
    - soup_session_queue_message(od->http_conns, msg, client_login_cb, od);
    -
    - g_free(password);
    -}
    --- a/libpurple/protocols/oscar/encoding.c Sun Oct 20 00:24:28 2019 +0300
    +++ /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 Sun Oct 20 00:24:28 2019 +0300
    +++ /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 PURPLE_OSCAR_ENCODING_H
    -#define PURPLE_OSCAR_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 /* PURPLE_OSCAR_ENCODING_H */
    --- a/libpurple/protocols/oscar/family_admin.c Sun Oct 20 00:24:28 2019 +0300
    +++ /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 Sun Oct 20 00:24:28 2019 +0300
    +++ /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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,647 +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 "oscarcommon.h"
    -
    -#include <ctype.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)
    -{
    - GChecksum *hash;
    - gsize digest_len = 16;
    -
    - hash = g_checksum_new(G_CHECKSUM_MD5);
    - g_checksum_update(hash, (const guchar *)key, -1);
    - g_checksum_update(hash, (const guchar *)password, password_len);
    - g_checksum_update(hash, (const guchar *)AIM_MD5_STRING, -1);
    - g_checksum_get_digest(hash, digest, &digest_len);
    - g_checksum_free(hash);
    -
    - return 0;
    -}
    -#else
    -static int
    -aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
    -{
    - GChecksum *hash;
    - guchar passdigest[16];
    - gsize digest_len = 16;
    -
    - hash = g_checksum_new(G_CHECKSUM_MD5);
    - g_checksum_update(hash, (const guchar *)password, password_len);
    - g_checksum_get_digest(hash, passdigest, &digest_len);
    - g_checksum_reset(hash);
    -
    - g_checksum_update(hash, (const guchar *)key, -1);
    - g_checksum_update(hash, passdigest, digest_len);
    - g_checksum_update(hash, (const guchar *)AIM_MD5_STRING, -1);
    - g_checksum_get_digest(hash, digest, &digest_len);
    - g_checksum_free(hash);
    -
    - return 0;
    -}
    -#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;
    - char *keystr;
    - GSList *tlvlist;
    - gboolean truncate_pass;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
    - ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - keylen = byte_stream_get16(bs);
    - keystr = byte_stream_getstr(bs, keylen);
    - if (!g_utf8_validate(keystr, -1, NULL)) {
    - purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with "
    - "invalid UTF-8 keystr.\n", snac->family, snac->subtype);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    - _("Received unexpected response from server"));
    - g_free(keystr);
    - return 1;
    - }
    -
    - 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 */
    -
    - aim_send_login(od, conn, purple_account_get_username(account),
    - purple_connection_get_password(gc), truncate_pass,
    - od->icq ? &icqinfo : &aiminfo, keystr,
    - purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
    -
    - purple_connection_update_progress(gc,
    - _("Password sent"), 2, OSCAR_CONNECT_STEPS);
    -
    - g_free(keystr);
    - aim_tlvlist_free(tlvlist);
    -
    - return 1;
    -}
    -
    -/**
    - * 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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,190 +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 || *bn == '\0' || !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));
    - if (!g_utf8_validate(bn, -1, NULL)) {
    - purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with "
    - "invalid UTF-8 buddy name.\n", snac->family, snac->subtype);
    - g_free(bn);
    - return 1;
    - }
    - 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 Sun Oct 20 00:24:28 2019 +0300
    +++ /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 Sun Oct 20 00:24:28 2019 +0300
    +++ /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_blist_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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,403 +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 (!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 += 4) {
    - gint32 rnd = g_random_int();
    - ckstr[i] = (guint8)((rnd & 0xFF000000) >> 24);
    - ckstr[i+1] = (guint8)((rnd & 0xFF0000) >> 16);
    - ckstr[i+2] = (guint8)((rnd & 0xFF00) >> 8);
    - ckstr[i+3] = (guint8)(rnd & 0xFF);
    - }
    -
    - 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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,446 +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"),
    - purple_request_cpar_from_connection(od->gc));
    -
    - 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);
    -
    - 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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2065 +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.
    - *
    - * Deals with storing certain types of information, such as a user's buddy
    - * list, permit/deny list, and permit/deny preferences, 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 "oscarcommon.h"
    -#include "debug.h"
    -
    -static int aim_ssi_addmoddel(OscarData *od);
    -
    -static void aim_ssi_item_free(struct aim_ssi_item *item)
    -{
    - g_free(item->name);
    - aim_tlvlist_free(item->data);
    - g_free(item);
    -}
    -
    -static void aim_ssi_item_set_name(struct aim_ssi_itemlist *list, struct aim_ssi_item *item, const char *name)
    -{
    - gchar key[3000];
    -
    - if (item->name) {
    - /* Remove old name from hash table */
    - snprintf(key, sizeof(key), "%hx%s", item->type, oscar_normalize(NULL, item->name));
    - g_hash_table_remove(list->idx_all_named_items, key);
    - }
    -
    - g_free(item->name);
    - item->name = g_strdup(name);
    -
    - if (name) {
    - /* Add new name to hash table */
    - snprintf(key, sizeof(key), "%hx%s", item->type, oscar_normalize(NULL, item->name));
    - g_hash_table_insert(list->idx_all_named_items, g_strdup(key), item);
    - }
    -}
    -
    -/**
    - * 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_itemlist *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->data; cur; cur=cur->next)
    - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
    - newlen += 2;
    - } else {
    - for (cur=list->data; 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 = g_new(guint8, newlen);
    - newlen = 0;
    - if (group->gid == 0x0000) {
    - for (cur=list->data; 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->data; 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_itemlist *list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
    -{
    - gboolean exists;
    - struct aim_ssi_item *cur, *new;
    -
    - new = g_new0(struct aim_ssi_item, 1);
    -
    - /* 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->data; 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->data; 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->data; 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;
    -
    - /* Add it to the gid+bid hashtable */
    - g_hash_table_insert(list->idx_gid_bid, GINT_TO_POINTER((new->gid << 16) + new->bid), new);
    -
    - /* Set the name - do this *AFTER* setting the type because type is used for the key */
    - aim_ssi_item_set_name(list, new, name);
    -
    - /* 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->data) {
    - if ((new->gid < list->data->gid) || ((new->gid == list->data->gid) && (new->bid < list->data->bid))) {
    - new->next = list->data;
    - list->data = new;
    - } else {
    - struct aim_ssi_item *prev;
    - for ((prev=list->data, cur=list->data->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->data;
    - list->data = 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_itemlist *list, struct aim_ssi_item *del)
    -{
    - gchar key[3000];
    -
    - if (!(list->data) || !del)
    - return -EINVAL;
    -
    - /* Remove the item from the list */
    - if (list->data == del) {
    - list->data = list->data->next;
    - } else {
    - struct aim_ssi_item *cur;
    - for (cur=list->data; (cur->next && (cur->next!=del)); cur=cur->next);
    - if (cur->next)
    - cur->next = del->next;
    - }
    -
    - /* Remove from the hashtables */
    - g_hash_table_remove(list->idx_gid_bid, GINT_TO_POINTER((del->gid << 16) + del->bid));
    -
    - snprintf(key, sizeof(key), "%hx%s", del->type, oscar_normalize(NULL, del->name));
    - g_hash_table_remove(list->idx_all_named_items, key);
    -
    - /* Free the removed item */
    - aim_ssi_item_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_itemlist *list, struct aim_ssi_item *item)
    -{
    - struct aim_ssi_item *cur;
    - for (cur=list->data; 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_itemlist *list, guint16 gid, guint16 bid)
    -{
    - guint32 id_key = (gid << 16) + bid;
    - return g_hash_table_lookup(list->idx_gid_bid, GINT_TO_POINTER(id_key));
    -}
    -
    -/**
    - * 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_itemlist *list, const char *gn, const char *bn, guint16 type)
    -{
    - struct aim_ssi_item *cur;
    - gchar key[3000];
    -
    - if (!list->data)
    - return NULL;
    -
    - if (gn && bn) { /* For finding buddies in groups */
    - g_return_val_if_fail(type == AIM_SSI_TYPE_BUDDY, NULL);
    - for (cur=list->data; 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->data; 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 || bn) { /* For finding groups, permits, denies and ignores */
    - snprintf(key, sizeof(key), "%hx%s", type, oscar_normalize(NULL, gn ? gn : bn));
    - return g_hash_table_lookup(list->idx_all_named_items, key);
    -
    - /* For stuff without names--permit deny setting, visibility mask, etc. */
    - } else for (cur=list->data; 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_itemlist *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_itemlist *list, const char *bn)
    -{
    - struct aim_ssi_item *cur, *curg;
    - if (!list->data || !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_itemlist *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_itemlist *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_itemlist *list, const char *gn, const char *bn)
    -{
    - struct aim_ssi_item *item = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
    - if (item) {
    - return aim_ssi_getalias_from_item(item);
    - }
    - return NULL;
    -}
    -
    -char *aim_ssi_getalias_from_item(struct aim_ssi_item *item)
    -{
    - aim_tlv_t *tlv = aim_tlv_gettlv(item->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_itemlist *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_itemlist *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;
    -
    - /*
    - * 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.
    - */
    - debugstr = g_string_new("");
    -
    - /* Deletions */
    - if (!od->ssi.pending) {
    - for (cur1=od->ssi.official.data; 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.data; 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.data; 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()) {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - g_string_truncate(debugstr, 0);
    - for (cur1 = od->ssi.local.data; 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_account_get_username(account), 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.data;
    - while (cur) {
    - del = cur;
    - cur = cur->next;
    - aim_ssi_item_free(del);
    - }
    -
    - cur = od->ssi.local.data;
    - while (cur) {
    - del = cur;
    - cur = cur->next;
    - aim_ssi_item_free(del);
    - }
    -
    - curtmp = od->ssi.pending;
    - while (curtmp) {
    - deltmp = curtmp;
    - curtmp = curtmp->next;
    - g_free(deltmp);
    - }
    -
    - od->ssi.numitems = 0;
    - od->ssi.official.data = NULL;
    - od->ssi.local.data = NULL;
    - od->ssi.pending = NULL;
    - od->ssi.timestamp = (time_t)0;
    -}
    -
    -/**
    - * Look up the given TLV type in the item's data. If the value of
    - * the TLV is not a valid UTF-8 string then use purple_utf8_salvage()
    - * to replace invalid bytes with question marks.
    - */
    -static void cleanlist_ensure_utf8_data(struct aim_ssi_item *item, guint16 tlvtype)
    -{
    - aim_tlv_t *tlv;
    - gchar *value, *salvaged;
    -
    - tlv = aim_tlv_gettlv(item->data, tlvtype, 1);
    - if (tlv && tlv->length && !g_utf8_validate((const gchar *)tlv->value, tlv->length, NULL)) {
    - purple_debug_warning("oscar", "cleanlist found invalid UTF-8 "
    - "for 0x%04hx field of 0x%04hx item with name %s. "
    - "Attempting to repair.\n",
    - tlvtype, item->type, item->name ? item->name : "(null)");
    - value = g_strndup((const gchar *)tlv->value, tlv->length);
    - salvaged = purple_utf8_salvage(value);
    - g_free(value);
    - if (*salvaged)
    - aim_tlvlist_replace_str(&item->data, tlvtype, salvaged);
    - else
    - aim_tlvlist_remove(&item->data, tlvtype);
    - g_free(salvaged);
    - }
    -}
    -
    -/**
    - * This "cleans" the ssi list. It does things like:
    - * - Makes sure all buddies, permits, and denies have names
    - * - Makes sure all buddies are in a group that exist
    - * - Makes sure strings are valid UTF-8
    - *
    - * @param od The oscar odion.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -static 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.data;
    - 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, PURPLE_BLIST_DEFAULT_GROUP_NAME,
    - NULL, alias, NULL, NULL, FALSE);
    - aim_ssi_delbuddy(od, cur->name, NULL);
    - g_free(alias);
    - }
    - cur = next;
    - }
    -
    - cur = od->ssi.local.data;
    - 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;
    -
    - /* Make sure there aren't any duplicate permits or denies, or
    - duplicate buddies within a group */
    - cur2 = cur->next;
    - while (cur2) {
    - next2 = cur2->next;
    - if (cur->type == cur2->type
    - && cur->gid == cur2->gid
    - && cur->name
    - && cur2->name
    - && !oscar_util_name_compare(cur->name, cur2->name))
    - {
    - aim_ssi_itemlist_del(&od->ssi.local, cur2);
    - }
    - cur2 = next2;
    - }
    -
    - /* Make sure alias is valid UTF-8 */
    - cleanlist_ensure_utf8_data(cur, 0x0131);
    -
    - /* Make sure comment is valid UTF-8 */
    - cleanlist_ensure_utf8_data(cur, 0x013c);
    - }
    - 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, NULL);
    -
    - /* 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 && *alias)
    - 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 && *comment)
    - 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;
    -
    - aim_ssi_item_set_name(&od->ssi.local, group, 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_account_get_username(purple_connection_get_account(od->gc)), 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.data; cur; cur=cur->next)
    - aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
    -
    - /* Clean the buddy list */
    - aim_ssi_cleanlist(od);
    -
    - 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;
    - aim_ssi_item_set_name(&od->ssi.local, item, 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;
    - aim_ssi_item_set_name(&od->ssi.official, item, 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 */
    - /* TODO: "Still valid memory"? That's bad form. */
    - 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))) {
    - aim_ssi_item_set_name(&od->ssi.official, cur->item, 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))) {
    - aim_ssi_item_set_name(&od->ssi.official, cur1, 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);
    - return 0;
    - }
    -
    - /* 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 (!tmp)
    - purple_debug_warning("oscar", "unknown field missing");
    -
    - 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);
    - return 0;
    - }
    -
    - /* 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 (!tmp)
    - purple_debug_warning("oscar", "unknown field missing");
    -
    - 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);
    - return 0;
    - }
    -
    - /* 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 (!tmp)
    - purple_debug_warning("oscar", "unknown field missing");
    -
    - 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);
    - return 0;
    - }
    -
    - 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 Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2149 +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"
    -
    -#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)g_random_int() % 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_conversation_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, reason_str, _(errcodereason[errcode]));
    - } else {
    - buf = g_strdup_printf(_("Unable to send message to %s: %s"), bn,
    - reason_str);
    - }
    - purple_notify_error(od->gc, NULL, buf, reason_str,
    - purple_request_cpar_from_connection(od->gc));
    - }
    - g_free(buf);
    -
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return 1;
    -}
    -
    -/**
    - * Subtype 0x0002 - Set ICBM parameters.
    - *
    - * I definitely recommend sending this. If you don't, you'll be stuck
    - * with the rather unreasonable defaults.
    - *
    - */
    -int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!params)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 16);
    -
    - /* This is read-only (see Parameter Reply). Must be set to zero here. */
    - byte_stream_put16(&bs, 0x0000);
    -
    - /* These are all read-write */
    - byte_stream_put32(&bs, params->flags);
    - byte_stream_put16(&bs, params->maxmsglen);
    - byte_stream_put16(&bs, params->maxsenderwarn);
    - byte_stream_put16(&bs, params->maxrecverwarn);
    - byte_stream_put32(&bs, params->minmsginterval);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0004 - Request ICBM parameter information.
    - *
    - */
    -int aim_im_reqparams(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0005 - Receive parameter information.
    - *
    - */
    -static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - struct aim_icbmparameters params;
    -
    - params.maxchan = byte_stream_get16(bs);
    - params.flags = byte_stream_get32(bs);
    - params.maxmsglen = byte_stream_get16(bs);
    - params.maxsenderwarn = byte_stream_get16(bs);
    - params.maxrecverwarn = byte_stream_get16(bs);
    - params.minmsginterval = byte_stream_get32(bs);
    -
    - params.flags = AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED
    - | AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED
    - | AIM_IMPARAM_FLAG_EVENTS_ALLOWED
    - | AIM_IMPARAM_FLAG_SMS_SUPPORTED
    - | AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED
    - | AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ;
    - params.maxmsglen = 8000;
    - params.minmsginterval = 0;
    -
    - aim_im_setparams(od, &params);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0006 - Send an ICBM (instant message).
    - *
    - *
    - * Possible flags:
    - * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse
    - * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
    - * online (probably ICQ only).
    - *
    - * Implementation note: Since this is one of the most-used functions
    - * in all of libfaim, it is written with performance in mind. As such,
    - * it is not as clear as it could be in respect to how this message is
    - * supposed to be layed out. Most obviously, tlvlists should be used
    - * instead of writing out the bytes manually.
    - */
    -int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
    -{
    - FlapConnection *conn;
    - aim_snacid_t snacid;
    - ByteStream data;
    - guchar cookie[8];
    - int msgtlvlen;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!args)
    - return -EINVAL;
    -
    - if (!args->msg || (args->msglen <= 0))
    - return -EINVAL;
    -
    - if (args->msglen > MAXMSGLEN)
    - return -E2BIG;
    -
    - /* Painfully calculate the size of the message TLV */
    - msgtlvlen = 1 + 1; /* 0501 */
    - msgtlvlen += 2 + args->featureslen;
    - msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
    - msgtlvlen += 4 /* charset */ + args->msglen;
    -
    - byte_stream_new(&data, msgtlvlen + 128);
    -
    - /* Generate an ICBM cookie */
    - aim_icbm_makecookie(cookie);
    -
    - /* ICBM header */
    - aim_im_puticbm(&data, cookie, 0x0001, args->destbn);
    -
    - /* Message TLV (type 0x0002) */
    - byte_stream_put16(&data, 0x0002);
    - byte_stream_put16(&data, msgtlvlen);
    -
    - /* Features TLV (type 0x0501) */
    - byte_stream_put16(&data, 0x0501);
    - byte_stream_put16(&data, args->featureslen);
    - byte_stream_putraw(&data, args->features, args->featureslen);
    -
    - /* Insert message text in a TLV (type 0x0101) */
    - byte_stream_put16(&data, 0x0101);
    -
    - /* Message block length */
    - byte_stream_put16(&data, args->msglen + 0x04);
    -
    - /* Character set */
    - byte_stream_put16(&data, args->charset);
    - /* Character subset -- we always use 0 here */
    - byte_stream_put16(&data, 0x0);
    -
    - /* Message. Not terminated */
    - byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
    -
    - /* Set the Autoresponse flag */
    - if (args->flags & AIM_IMFLAGS_AWAY) {
    - byte_stream_put16(&data, 0x0004);
    - byte_stream_put16(&data, 0x0000);
    - } else {
    - /* Set the Request Acknowledge flag */
    - byte_stream_put16(&data, 0x0003);
    - byte_stream_put16(&data, 0x0000);
    -
    - if (args->flags & AIM_IMFLAGS_OFFLINE) {
    - /* Allow this message to be queued as an offline message */
    - byte_stream_put16(&data, 0x0006);
    - byte_stream_put16(&data, 0x0000);
    - }
    - }
    -
    - /*
    - * Set the I HAVE A REALLY PURTY ICON flag.
    - * XXX - This should really only be sent on initial
    - * IMs and when you change your icon.
    - */
    - if (args->flags & AIM_IMFLAGS_HASICON) {
    - byte_stream_put16(&data, 0x0008);
    - byte_stream_put16(&data, 0x000c);
    - byte_stream_put32(&data, args->iconlen);
    - byte_stream_put16(&data, 0x0001);
    - byte_stream_put16(&data, args->iconsum);
    - byte_stream_put32(&data, args->iconstamp);
    - }
    -
    - /*
    - * Set the Buddy Icon Requested flag.
    - * XXX - Every time? Surely not...
    - */
    - if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
    - byte_stream_put16(&data, 0x0009);
    - byte_stream_put16(&data, 0x0000);
    - }
    -
    - /* XXX - should be optional */
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &data);
    - byte_stream_destroy(&data);
    -
    - /* clean out SNACs over 60sec old */
    - aim_cleansnacs(od, 60);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0006 - Send a chat invitation.
    - */
    -int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - IcbmCookie *msgcookie;
    - struct aim_invite_priv *priv;
    - guchar cookie[8];
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!bn || !msg || !roomname)
    - return -EINVAL;
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_new(&bs, 1142+strlen(bn)+strlen(roomname)+strlen(msg));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1);
    -
    - /* XXX should be uncached by an unwritten 'invite accept' handler */
    - priv = g_new0(struct aim_invite_priv, 1);
    - priv->bn = g_strdup(bn);
    - priv->roomname = g_strdup(roomname);
    - priv->exchange = exchange;
    - priv->instance = instance;
    -
    - if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv)))
    - aim_cachecookie(od, msgcookie);
    - else
    - g_free(priv);
    -
    - /* ICBM Header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - /*
    - * TLV t(0005)
    - *
    - * Everything else is inside this TLV.
    - *
    - * Sigh. AOL was rather inconsistent right here. So we have
    - * to play some minor tricks. Right inside the type 5 is some
    - * raw data, followed by a series of TLVs.
    - *
    - */
    - byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2);
    -
    - byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */
    - byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
    -
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_add_str(&inner_tlvlist, 0x000c, msg);
    - aim_tlvlist_add_chatroom(&inner_tlvlist, 0x2711, exchange, roomname, instance);
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Subtype 0x0006 - Send your icon to a given user.
    - *
    - * This is also performance sensitive. (If you can believe it...)
    - *
    - */
    -int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - guchar cookie[8];
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
    - return -EINVAL;
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_new(&bs, 8+2+1+strlen(bn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - /*
    - * TLV t(0005)
    - *
    - * Encompasses everything below.
    - */
    - byte_stream_put16(&bs, 0x0005);
    - byte_stream_put16(&bs, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
    -
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_putraw(&bs, cookie, 8);
    - byte_stream_putcaps(&bs, OSCAR_CAPABILITY_BUDDYICON);
    -
    - /* TLV t(000a) */
    - byte_stream_put16(&bs, 0x000a);
    - byte_stream_put16(&bs, 0x0002);
    - byte_stream_put16(&bs, 0x0001);
    -
    - /* TLV t(000f) */
    - byte_stream_put16(&bs, 0x000f);
    - byte_stream_put16(&bs, 0x0000);
    -
    - /* TLV t(2711) */
    - byte_stream_put16(&bs, 0x2711);
    - byte_stream_put16(&bs, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, iconsum);
    - byte_stream_put32(&bs, iconlen);
    - byte_stream_put32(&bs, stamp);
    - byte_stream_putraw(&bs, icon, iconlen);
    - byte_stream_putstr(&bs, AIM_ICONIDENT);
    -
    - /* TLV t(0003) */
    - byte_stream_put16(&bs, 0x0003);
    - byte_stream_put16(&bs, 0x0000);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Cancel a rendezvous invitation. It could be an invitation to
    - * establish a direct connection, or a file-send, or a chat invite.
    - */
    -void
    -aim_im_sendch2_cancel(PeerConnection *peer_conn)
    -{
    - OscarData *od;
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - od = peer_conn->od;
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 118+strlen(peer_conn->bn));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 64);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_CANCEL);
    - byte_stream_putraw(&hdrbs, peer_conn->cookie, 8);
    - byte_stream_putcaps(&hdrbs, peer_conn->type);
    -
    - /* This TLV means "cancel!" */
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000b, 0x0001);
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send an "I accept and I've connected to
    - * you" message.
    - */
    -void
    -aim_im_sendch2_connected(PeerConnection *peer_conn)
    -{
    - OscarData *od;
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - od = peer_conn->od;
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
    -
    - byte_stream_put16(&bs, 0x0005);
    - byte_stream_put16(&bs, 0x001a);
    - byte_stream_put16(&bs, AIM_RENDEZVOUS_CONNECTED);
    - byte_stream_putraw(&bs, peer_conn->cookie, 8);
    - byte_stream_putcaps(&bs, peer_conn->type);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send a direct connect rendezvous ICBM. This
    - * could have a number of meanings, depending on the content:
    - * "I want you to connect to me"
    - * "I want to connect to you"
    - * "I want to connect through a proxy server"
    - */
    -void
    -aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - g_return_if_fail(bn != NULL);
    - g_return_if_fail(ip != NULL);
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 246+strlen(bn));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 128);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send a direct connect rendezvous ICBM asking the
    - * remote user to connect to us via a proxy server.
    - */
    -void
    -aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    - guint8 ip_comp[4];
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 246+strlen(bn));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 128);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
    -
    - /* Send the bitwise complement of the port and ip. As a check? */
    - ip_comp[0] = ~ip[0];
    - ip_comp[1] = ~ip[1];
    - ip_comp[2] = ~ip[2];
    - ip_comp[3] = ~ip[3];
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
    -
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send an "I want to send you this file" message
    - *
    - */
    -void
    -aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    -
    - g_return_if_fail(bn != NULL);
    - g_return_if_fail(ip != NULL);
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 1014);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 512);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - /* TODO: Send 0x0016 and 0x0017 */
    -
    - if (filename != NULL)
    - {
    - ByteStream inner_bs;
    -
    - /* Begin TLV t(2711) */
    - byte_stream_new(&inner_bs, 2+2+4+strlen(filename)+1);
    - byte_stream_put16(&inner_bs, (numfiles > 1) ? 0x0002 : 0x0001);
    - byte_stream_put16(&inner_bs, numfiles);
    - byte_stream_put32(&inner_bs, size);
    -
    - /* Filename - NULL terminated, for some odd reason */
    - byte_stream_putstr(&inner_bs, filename);
    - byte_stream_put8(&inner_bs, 0x00);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, inner_bs.len, inner_bs.data);
    - byte_stream_destroy(&inner_bs);
    - /* End TLV t(2711) */
    - }
    -
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Subtype 0x0006 - Send a sendfile connect rendezvous ICBM asking the
    - * remote user to connect to us via a proxy server.
    - */
    -void
    -aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream hdrbs;
    - guint8 ip_comp[4];
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (conn == NULL)
    - return;
    -
    - byte_stream_new(&bs, 1014);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
    -
    - /* ICBM header */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    -
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
    -
    - byte_stream_new(&hdrbs, 512);
    -
    - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
    - byte_stream_putraw(&hdrbs, cookie, 8);
    - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
    -
    - /* Send the bitwise complement of the port and ip. As a check? */
    - ip_comp[0] = ~ip[0];
    - ip_comp[1] = ~ip[1];
    - ip_comp[2] = ~ip[2];
    - ip_comp[3] = ~ip[3];
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
    - aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
    -
    - if (filename != NULL)
    - {
    - ByteStream filename_bs;
    -
    - /* Begin TLV t(2711) */
    - byte_stream_new(&filename_bs, 2+2+4+strlen(filename)+1);
    - byte_stream_put16(&filename_bs, (numfiles > 1) ? 0x0002 : 0x0001);
    - byte_stream_put16(&filename_bs, numfiles);
    - byte_stream_put32(&filename_bs, size);
    -
    - /* Filename - NULL terminated, for some odd reason */
    - byte_stream_putstr(&filename_bs, filename);
    - byte_stream_put8(&filename_bs, 0x00);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, filename_bs.len, filename_bs.data);
    - byte_stream_destroy(&filename_bs);
    - /* End TLV t(2711) */
    - }
    -
    - aim_tlvlist_write(&hdrbs, &inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
    - byte_stream_destroy(&hdrbs);
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - aim_tlvlist_free(inner_tlvlist);
    - aim_tlvlist_free(outer_tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -static void
    -incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - /*
    - * We're interested in the inner TLV 0x101, which contains precious, precious message.
    - */
    - while (byte_stream_bytes_left(message) >= 4) {
    - guint16 type = byte_stream_get16(message);
    - guint16 length = byte_stream_get16(message);
    - if (type == 0x101) {
    - gchar *msg;
    - guint16 msglen = length - 4; /* charset + charsubset */
    - guint16 charset = byte_stream_get16(message);
    - byte_stream_advance(message, 2); /* charsubset */
    -
    - msg = byte_stream_getstr(message, msglen);
    - args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen);
    - g_free(msg);
    - } else {
    - byte_stream_advance(message, length);
    - }
    - }
    -}
    -
    -static int
    -incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
    -{
    - guint16 type, length;
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - struct aim_incomingim_ch1_args args;
    - unsigned int endpos;
    -
    - memset(&args, 0, sizeof(args));
    -
    - /*
    - * This used to be done using tlvchains. For performance reasons,
    - * I've changed it to process the TLVs in-place. This avoids lots
    - * of per-IM memory allocations.
    - */
    - while (byte_stream_bytes_left(bs) >= 4)
    - {
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    -
    - if (length > byte_stream_bytes_left(bs))
    - {
    - purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn);
    - break;
    - }
    -
    - endpos = byte_stream_curpos(bs) + length;
    -
    - if (type == 0x0002) { /* Message Block */
    - ByteStream tlv02;
    - byte_stream_init(&tlv02, bs->data + bs->offset, length);
    - incomingim_ch1_parsemsg(od, userinfo, &tlv02, &args);
    - } else if (type == 0x0003) { /* Server Ack Requested */
    - args.icbmflags |= AIM_IMFLAGS_ACK;
    - } else if (type == 0x0004) { /* Message is Auto Response */
    - args.icbmflags |= AIM_IMFLAGS_AWAY;
    - } else if (type == 0x0006) { /* Message was received offline. */
    - /*
    - * This flag is set on incoming offline messages for both
    - * AIM and ICQ accounts.
    - */
    - args.icbmflags |= AIM_IMFLAGS_OFFLINE;
    - } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
    - args.iconlen = byte_stream_get32(bs);
    - byte_stream_get16(bs); /* 0x0001 */
    - args.iconsum = byte_stream_get16(bs);
    - args.iconstamp = byte_stream_get32(bs);
    -
    - /*
    - * This looks to be a client bug. MacAIM 4.3 will
    - * send this tag, but with all zero values, in the
    - * first message of a conversation. This makes no
    - * sense whatsoever, so I'm going to say its a bug.
    - *
    - * You really shouldn't advertise a zero-length icon
    - * anyway.
    - *
    - */
    - if (args.iconlen)
    - args.icbmflags |= AIM_IMFLAGS_HASICON;
    - } else if (type == 0x0009) {
    - args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
    - } else if (type == 0x000b) { /* Non-direct connect typing notification */
    - args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
    - } else if (type == 0x0016) {
    - /*
    - * UTC timestamp for when the message was sent. Only
    - * provided for offline messages.
    - */
    - args.timestamp = byte_stream_get32(bs);
    - }
    -
    - /*
    - * This is here to protect ourselves from ourselves. That
    - * is, if something above doesn't completely parse its value
    - * section, or, worse, overparses it, this will set the
    - * stream where it needs to be in order to land on the next
    - * TLV when the loop continues.
    - *
    - */
    - byte_stream_setpos(bs, endpos);
    - }
    -
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, userinfo, &args);
    -
    - g_free(args.msg);
    - return ret;
    -}
    -
    -static void
    -incomingim_ch2_buddylist(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - /*
    - * This goes like this...
    - *
    - * group name length
    - * group name
    - * num of buddies in group
    - * buddy name length
    - * buddy name
    - * buddy name length
    - * buddy name
    - * ...
    - * group name length
    - * group name
    - * num of buddies in group
    - * buddy name length
    - * buddy name
    - * ...
    - * ...
    - */
    - while (byte_stream_bytes_left(servdata))
    - {
    - guint16 gnlen, numb;
    - int i;
    - char *gn;
    -
    - gnlen = byte_stream_get16(servdata);
    - gn = byte_stream_getstr(servdata, gnlen);
    - numb = byte_stream_get16(servdata);
    -
    - for (i = 0; i < numb; i++) {
    - guint16 bnlen;
    - char *bn;
    -
    - bnlen = byte_stream_get16(servdata);
    - bn = byte_stream_getstr(servdata, bnlen);
    -
    - purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->bn, gn, bn);
    -
    - g_free(bn);
    - }
    -
    - g_free(gn);
    - }
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_buddyicon_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - g_free(args->info.icon.icon);
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_buddyicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - args->info.icon.checksum = byte_stream_get32(servdata);
    - args->info.icon.length = byte_stream_get32(servdata);
    - args->info.icon.timestamp = byte_stream_get32(servdata);
    - args->info.icon.icon = byte_stream_getraw(servdata, args->info.icon.length);
    -
    - args->destructor = (void *)incomingim_ch2_buddyicon_free;
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - /* XXX - aim_chat_roominfo_free() */
    - g_free(args->info.chat.roominfo.name);
    -
    - return;
    -}
    -
    -static void
    -incomingim_ch2_chat(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - /*
    - * Chat room info.
    - */
    - aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
    -
    - args->destructor = (void *)incomingim_ch2_chat_free;
    -}
    -
    -static void
    -incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - g_free((char *)args->info.rtfmsg.msg);
    -}
    -
    -/*
    - * The relationship between OSCAR_CAPABILITY_ICQSERVERRELAY and OSCAR_CAPABILITY_ICQRTF is
    - * kind of odd. This sends the client ICQRTF since that is all that I've seen
    - * SERVERRELAY used for.
    - *
    - * Note that this is all little-endian. Cringe.
    - *
    - */
    -static void
    -incomingim_ch2_icqserverrelay(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - guint16 hdrlen, msglen;
    -
    - args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
    -
    -#define SKIP_HEADER(expected_hdrlen) \
    - hdrlen = byte_stream_getle16(servdata); \
    - if (hdrlen != expected_hdrlen) { \
    - purple_debug_warning("oscar", "Expected to find a header with length " #expected_hdrlen "; ignoring message"); \
    - return; \
    - } \
    - byte_stream_advance(servdata, hdrlen);
    -
    - SKIP_HEADER(0x001b);
    - SKIP_HEADER(0x000e);
    -
    - args->info.rtfmsg.msgtype = byte_stream_get8(servdata);
    - /*
    - * Copied from http://iserverd.khstu.ru/oscar/message.html:
    - * xx byte message flags
    - * xx xx word (LE) status code
    - * xx xx word (LE) priority code
    - *
    - * We don't need any of these, so just skip them.
    - */
    - byte_stream_advance(servdata, 1 + 2 + 2);
    -
    - msglen = byte_stream_getle16(servdata);
    - args->info.rtfmsg.msg = byte_stream_getstr(servdata, msglen);
    -}
    -
    -static void
    -incomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args)
    -{
    - g_free(args->info.sendfile.filename);
    -}
    -
    -/* Someone is sending us a file */
    -static void
    -incomingim_ch2_sendfile(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
    -{
    - int flen;
    -
    - args->destructor = (void *)incomingim_ch2_sendfile_free;
    -
    - /* Maybe there is a better way to tell what kind of sendfile
    - * this is? Maybe TLV t(000a)? */
    -
    - /* subtype is one of AIM_OFT_SUBTYPE_* */
    - args->info.sendfile.subtype = byte_stream_get16(servdata);
    - args->info.sendfile.totfiles = byte_stream_get16(servdata);
    - args->info.sendfile.totsize = byte_stream_get32(servdata);
    -
    - /*
    - * I hope to God I'm right when I guess that there is a
    - * 32 char max filename length for single files. I think
    - * OFT tends to do that. Gotta love inconsistency. I saw
    - * a 26 byte filename?
    - */
    - /* AAA - create an byte_stream_getnullstr function (don't anymore)(maybe) */
    - /* Use an inelegant way of getting the null-terminated filename,
    - * since there's no easy bstream routine. */
    - for (flen = 0; byte_stream_get8(servdata); flen++);
    - byte_stream_advance(servdata, -flen -1);
    - args->info.sendfile.filename = byte_stream_getstr(servdata, flen);
    -
    - /* There is sometimes more after the null-terminated filename,
    - * but I'm unsure of its format. */
    - /* I don't believe him. */
    - /* There is sometimes a null byte inside a unicode filename,
    - * but as far as I can tell the filename is the last
    - * piece of data that will be in this message. --Jonathan */
    -}
    -
    -typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args);
    -
    -static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
    -{
    - aim_rxcallback_t userfunc;
    - aim_tlv_t *block1, *servdatatlv;
    - GSList *list2;
    - aim_tlv_t *tlv;
    - IcbmArgsCh2 args;
    - ByteStream bbs, sdbs, *sdbsptr = NULL;
    - guint8 *cookie2;
    - int ret = 0;
    -
    - char proxyip[30] = {""};
    - char clientip[30] = {""};
    - char verifiedip[30] = {""};
    -
    - memset(&args, 0, sizeof(args));
    -
    - /*
    - * There's another block of TLVs embedded in the type 5 here.
    - */
    - block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1);
    - if (block1 == NULL)
    - {
    - /* The server sent us ch2 ICBM without ch2 info? Weird. */
    - return 1;
    - }
    - byte_stream_init(&bbs, block1->value, block1->length);
    -
    - /*
    - * First two bytes represent the status of the connection.
    - * One of the AIM_RENDEZVOUS_ defines.
    - *
    - * 0 is a request, 1 is a cancel, 2 is an accept
    - */
    - args.status = byte_stream_get16(&bbs);
    -
    - /*
    - * Next comes the cookie. Should match the ICBM cookie.
    - */
    - cookie2 = byte_stream_getraw(&bbs, 8);
    - if (memcmp(cookie, cookie2, 8) != 0)
    - {
    - purple_debug_warning("oscar",
    - "Cookies don't match in rendezvous ICBM, bailing out.\n");
    - g_free(cookie2);
    - return 1;
    - }
    - memcpy(args.cookie, cookie2, 8);
    - g_free(cookie2);
    -
    - /*
    - * The next 16bytes are a capability block so we can
    - * identify what type of rendezvous this is.
    - */
    - args.type = aim_locate_getcaps(od, &bbs, 0x10);
    -
    - /*
    - * What follows may be TLVs or nothing, depending on the
    - * purpose of the message.
    - *
    - * Ack packets for instance have nothing more to them.
    - */
    - list2 = aim_tlvlist_read(&bbs);
    -
    - /*
    - * IP address to proxy the file transfer through.
    - *
    - * TODO: I don't like this. Maybe just read in an int? Or inet_ntoa...
    - */
    - tlv = aim_tlv_gettlv(list2, 0x0002, 1);
    - if ((tlv != NULL) && (tlv->length == 4))
    - snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu",
    - tlv->value[0], tlv->value[1],
    - tlv->value[2], tlv->value[3]);
    -
    - /*
    - * IP address from the perspective of the client.
    - */
    - tlv = aim_tlv_gettlv(list2, 0x0003, 1);
    - if ((tlv != NULL) && (tlv->length == 4))
    - snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu",
    - tlv->value[0], tlv->value[1],
    - tlv->value[2], tlv->value[3]);
    -
    - /*
    - * Verified IP address (from the perspective of Oscar).
    - *
    - * This is added by the server.
    - */
    - tlv = aim_tlv_gettlv(list2, 0x0004, 1);
    - if ((tlv != NULL) && (tlv->length == 4))
    - snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu",
    - tlv->value[0], tlv->value[1],
    - tlv->value[2], tlv->value[3]);
    -
    - /*
    - * Port number for something.
    - */
    - if (aim_tlv_gettlv(list2, 0x0005, 1))
    - args.port = aim_tlv_get16(list2, 0x0005, 1);
    -
    - /*
    - * File transfer "request number":
    - * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
    - * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy)
    - * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
    - */
    - if (aim_tlv_gettlv(list2, 0x000a, 1))
    - args.requestnumber = aim_tlv_get16(list2, 0x000a, 1);
    -
    - /*
    - * Terminate connection/error code. 0x0001 means the other user
    - * cancelled the connection.
    - */
    - if (aim_tlv_gettlv(list2, 0x000b, 1))
    - args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
    -
    - /*
    - * Invitation message / chat description.
    - */
    - if (aim_tlv_gettlv(list2, 0x000c, 1)) {
    - args.msg = aim_tlv_getstr(list2, 0x000c, 1);
    - args.msglen = aim_tlv_getlength(list2, 0x000c, 1);
    - }
    -
    - /*
    - * Character set.
    - */
    - if (aim_tlv_gettlv(list2, 0x000d, 1))
    - args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
    -
    - /*
    - * Language.
    - */
    - if (aim_tlv_gettlv(list2, 0x000e, 1))
    - args.language = aim_tlv_getstr(list2, 0x000e, 1);
    -
    - /*
    - * Flag meaning we should proxy the file transfer through an AIM server
    - */
    - if (aim_tlv_gettlv(list2, 0x0010, 1))
    - args.use_proxy = TRUE;
    -
    - if (*proxyip != '\0')
    - args.proxyip = (char *)proxyip;
    - if (*clientip != '\0')
    - args.clientip = (char *)clientip;
    - if (*verifiedip != '\0')
    - args.verifiedip = (char *)verifiedip;
    -
    - /*
    - * This must be present in PROPOSALs, but will probably not
    - * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite
    - * Beta 4.0 URLs (OSCAR_CAPABILITY_ICQSERVERRELAY).
    - *
    - * Service Data blocks are module-specific in format.
    - */
    - if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) {
    -
    - byte_stream_init(&sdbs, servdatatlv->value, servdatatlv->length);
    - sdbsptr = &sdbs;
    -
    - /*
    - * The rest of the handling depends on what type it is.
    - *
    - * Not all of them have special handling (yet).
    - */
    - if (args.type & OSCAR_CAPABILITY_BUDDYICON)
    - incomingim_ch2_buddyicon(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_SENDBUDDYLIST)
    - incomingim_ch2_buddylist(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_CHAT)
    - incomingim_ch2_chat(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_ICQSERVERRELAY)
    - incomingim_ch2_icqserverrelay(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - else if (args.type & OSCAR_CAPABILITY_SENDFILE)
    - incomingim_ch2_sendfile(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, userinfo, &args);
    -
    -
    - if (args.destructor)
    - ((ch2_args_destructor_t)args.destructor)(od, &args);
    -
    - g_free((char *)args.msg);
    - g_free((char *)args.encoding);
    - g_free((char *)args.language);
    -
    - aim_tlvlist_free(list2);
    -
    - return ret;
    -}
    -
    -static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
    -{
    - ByteStream meat;
    - aim_rxcallback_t userfunc;
    - aim_tlv_t *block;
    - struct aim_incomingim_ch4_args args;
    - int ret = 0;
    -
    - /*
    - * Make a bstream for the meaty part. Yum. Meat.
    - */
    - if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1)))
    - return -1;
    - byte_stream_init(&meat, block->value, block->length);
    -
    - args.uin = byte_stream_getle32(&meat);
    - args.type = byte_stream_getle8(&meat);
    - args.flags = byte_stream_getle8(&meat);
    - if (args.type == 0x1a)
    - /* There seems to be a problem with the length in SMS msgs from server, this fixed it */
    - args.msglen = block->length - 6;
    - else
    - args.msglen = byte_stream_getle16(&meat);
    - args.msg = (gchar *)byte_stream_getraw(&meat, args.msglen);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, userinfo, &args);
    -
    - g_free(args.msg);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0007
    - *
    - * It can easily be said that parsing ICBMs is THE single
    - * most difficult thing to do in the in AIM protocol. In
    - * fact, I think I just did say that.
    - *
    - * Below is the best damned solution I've come up with
    - * over the past sixteen months of battling with it. This
    - * can parse both away and normal messages from every client
    - * I have access to. Its not fast, its not clean. But it works.
    - *
    - */
    -static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - guchar *cookie;
    - guint16 channel;
    - aim_userinfo_t userinfo;
    -
    - memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
    -
    - /*
    - * Read ICBM Cookie.
    - */
    - cookie = byte_stream_getraw(bs, 8);
    -
    - /*
    - * Channel ID.
    - *
    - * Channel 0x0001 is the message channel. It is
    - * used to send basic ICBMs.
    - *
    - * Channel 0x0002 is the Rendezvous channel, which
    - * is where Chat Invitiations and various client-client
    - * connection negotiations come from.
    - *
    - * Channel 0x0003 is used for chat messages.
    - *
    - * Channel 0x0004 is used for ICQ authorization, or
    - * possibly any system notice.
    - *
    - */
    - channel = byte_stream_get16(bs);
    -
    - /*
    - * Extract the standard user info block.
    - *
    - * Note that although this contains TLVs that appear contiguous
    - * with the TLVs read below, they are two different pieces. The
    - * userinfo block contains the number of TLVs that contain user
    - * information, the rest are not even though there is no separation.
    - * You can start reading the message TLVs after aim_info_extract()
    - * parses out the standard userinfo block.
    - *
    - * That also means that TLV types can be duplicated between the
    - * userinfo block and the rest of the message, however there should
    - * never be two TLVs of the same type in one block.
    - *
    - */
    - aim_info_extract(od, bs, &userinfo);
    -
    - /*
    - * From here on, its depends on what channel we're on.
    - *
    - * Technically all channels have a TLV list have this, however,
    - * for the common channel 1 case, in-place parsing is used for
    - * performance reasons (less memory allocation).
    - */
    - if (channel == 1) {
    -
    - ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie);
    -
    - } else if (channel == 2) {
    - GSList *tlvlist;
    -
    - /*
    - * Read block of TLVs (not including the userinfo data). All
    - * further data is derived from what is parsed here.
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - ret = incomingim_ch2(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - } else if (channel == 4) {
    - GSList *tlvlist;
    -
    - tlvlist = aim_tlvlist_read(bs);
    - ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
    - aim_tlvlist_free(tlvlist);
    -
    - } else {
    - purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel);
    - }
    -
    - aim_info_free(&userinfo);
    - g_free(cookie);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x000a */
    -static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 channel, nummissed, reason;
    - aim_userinfo_t userinfo;
    -
    - while (byte_stream_bytes_left(bs)) {
    -
    - channel = byte_stream_get16(bs);
    - aim_info_extract(od, bs, &userinfo);
    - nummissed = byte_stream_get16(bs);
    - reason = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, &userinfo, nummissed, reason);
    -
    - aim_info_free(&userinfo);
    - }
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000b
    - *
    - * Possible codes:
    - * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
    - *
    - */
    -int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 8+2+1+strlen(bn)+6);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
    -
    - byte_stream_putraw(&bs, cookie, 8);
    -
    - byte_stream_put16(&bs, 0x0002); /* channel */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - aim_tlvlist_add_16(&tlvlist, 0x0003, code);
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x000b.
    - * Send confirmation for a channel 2 message (Miranda wants it by default).
    - */
    -void
    -aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - guint32 header_size, data_size;
    - guint16 cookie2 = (guint16)g_random_int();
    -
    - purple_debug_misc("oscar", "Sending message ack to %s\n", bn);
    -
    - header_size = 8 + 2 + 1 + strlen(bn) + 2;
    - data_size = 2 + 1 + 16 + 4*2 + 2*3 + 4*3 + 1*2 + 2*3 + 1;
    - byte_stream_new(&bs, header_size + data_size);
    -
    - /* The message header. */
    - aim_im_puticbm(&bs, cookie, 0x0002, bn);
    - byte_stream_put16(&bs, 0x0003); /* reason */
    -
    - /* The actual message. */
    - byte_stream_putle16(&bs, 0x1b); /* subheader #1 length */
    - byte_stream_put8(&bs, 0x08); /* protocol version */
    - byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY);
    - byte_stream_put32(&bs, 0x3); /* client features */
    - byte_stream_put32(&bs, 0x0004); /* DC type */
    - byte_stream_put16(&bs, cookie2); /* a cookie, chosen by fair dice roll */
    - byte_stream_putle16(&bs, 0x0e); /* header #2 len? */
    - byte_stream_put16(&bs, cookie2); /* the same cookie again */
    - byte_stream_put32(&bs, 0); /* unknown */
    - byte_stream_put32(&bs, 0); /* unknown */
    - byte_stream_put32(&bs, 0); /* unknown */
    - byte_stream_put8(&bs, 0x01); /* plain text message */
    - byte_stream_put8(&bs, 0x00); /* no message flags */
    - byte_stream_put16(&bs, 0x0000); /* no icq status */
    - byte_stream_put16(&bs, 0x0100); /* priority */
    - byte_stream_putle16(&bs, 1); /* query message len */
    - byte_stream_put8(&bs, 0x00); /* empty query message */
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - g_warn_if_fail(conn);
    - if (conn) {
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b,
    - snacid, &bs);
    - }
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Subtype 0x000b - Receive the response from an ICQ status message
    - * request (in which case this contains the ICQ status message) or
    - * a file transfer or direct IM request was declined.
    - */
    -static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 channel, reason;
    - char *bn;
    - guchar *cookie;
    - guint8 bnlen;
    - char *xml = NULL;
    - guint16 hdrlen;
    - int curpos;
    - guint16 num1, num2;
    - PurpleAccount *account;
    - PurpleBuddy *buddy;
    - PurplePresence *presence;
    - PurpleStatus *status;
    -
    - cookie = byte_stream_getraw(bs, 8);
    - channel = byte_stream_get16(bs);
    - bnlen = byte_stream_get8(bs);
    - bn = byte_stream_getstr(bs, bnlen);
    - reason = byte_stream_get16(bs);
    -
    - if (channel == 0x0002)
    - {
    - hdrlen = byte_stream_getle16(bs);
    - if (hdrlen == 27 && bs->len > (27 + 51)) {
    - byte_stream_advance(bs, 51);
    - num1 = byte_stream_getle16(bs);
    - num2 = byte_stream_getle16(bs);
    - purple_debug_misc("oscar", "X-Status: num1 %hu, num2 %hu\n", num1, num2);
    -
    - if (num1 == 0x4f00 && num2 == 0x3b00) {
    - byte_stream_advance(bs, 86);
    - curpos = byte_stream_curpos(bs);
    - xml = byte_stream_getstr(bs, bs->len - curpos);
    - purple_debug_misc("oscar", "X-Status: Received XML reply\n");
    - if (xml) {
    - GString *xstatus;
    - char *tmp1, *tmp2, *unescaped_xstatus;
    -
    - /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
    -
    - xstatus = g_string_new(NULL);
    -
    - tmp1 = strstr(xml, "&lt;title&gt;");
    - if (tmp1 != NULL) {
    - tmp1 += 13;
    - tmp2 = strstr(tmp1, "&lt;/title&gt;");
    - if (tmp2 != NULL)
    - g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
    - }
    - tmp1 = strstr(xml, "&lt;desc&gt;");
    - if (tmp1 != NULL) {
    - tmp1 += 12;
    - tmp2 = strstr(tmp1, "&lt;/desc&gt;");
    - if (tmp2 != NULL) {
    - if (xstatus->len > 0 && tmp2 > tmp1)
    - g_string_append(xstatus, " - ");
    - g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
    - }
    - }
    - unescaped_xstatus = purple_unescape_text(xstatus->str);
    - g_string_free(xstatus, TRUE);
    - if (*unescaped_xstatus) {
    - purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus);
    - account = purple_connection_get_account(od->gc);
    - buddy = purple_blist_find_buddy(account, bn);
    - presence = purple_buddy_get_presence(buddy);
    - status = purple_presence_get_status(presence, "mood");
    - if (status) {
    - purple_protocol_got_user_status(account, bn,
    - "mood",
    - PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME),
    - PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL);
    - }
    - }
    - g_free(unescaped_xstatus);
    - } else {
    - purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
    - }
    - } else {
    - purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
    - }
    -
    - }
    -
    - } else if (channel == 0x0004) { /* ICQ message */
    - switch (reason) {
    - case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
    - guint8 statusmsgtype, *msg;
    - guint16 len;
    - guint32 state;
    -
    - len = byte_stream_getle16(bs); /* Should be 0x001b */
    - byte_stream_advance(bs, len); /* Unknown */
    -
    - len = byte_stream_getle16(bs); /* Should be 0x000e */
    - byte_stream_advance(bs, len); /* Unknown */
    -
    - statusmsgtype = byte_stream_getle8(bs);
    - switch (statusmsgtype) {
    - case 0xe8:
    - state = AIM_ICQ_STATE_AWAY;
    - break;
    - case 0xe9:
    - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
    - break;
    - case 0xea:
    - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
    - break;
    - case 0xeb:
    - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
    - break;
    - case 0xec:
    - state = AIM_ICQ_STATE_CHAT;
    - break;
    - default:
    - state = 0;
    - break;
    - }
    -
    - byte_stream_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
    - byte_stream_getle16(bs); /* Unknown - 0x0000 */
    - byte_stream_getle16(bs); /* Unknown - 0x0000 */
    -
    - len = byte_stream_getle16(bs);
    - msg = byte_stream_getraw(bs, len);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, bn, reason, state, msg);
    -
    - g_free(msg);
    - } break;
    -
    - default: {
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, bn, reason);
    - } break;
    - } /* end switch */
    - }
    -
    - g_free(cookie);
    - g_free(bn);
    - g_free(xml);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
    - */
    -static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guchar *cookie;
    - char *bn;
    - int ret = 0;
    -
    - cookie = byte_stream_getraw(bs, 8);
    - byte_stream_get16(bs); /* ch */
    - bn = byte_stream_getstr(bs, byte_stream_get8(bs));
    -
    - purple_debug_info("oscar", "Sent message to %s.\n", bn);
    -
    - g_free(bn);
    - g_free(cookie);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0010 - Request any offline messages that are waiting for
    - * us. This is the "new" way of handling offline messages which is
    - * used for both AIM and ICQ. The old way is to use the ugly
    - * aim_icq_reqofflinemsgs() function, but that is no longer necessary.
    - *
    - * We set the 0x00000100 flag on the ICBM message parameters, which
    - * tells the oscar servers that we support offline messages. When we
    - * set that flag the servers do not automatically send us offline
    - * messages. Instead we must request them using this function. This
    - * should happen after sending the 0x0001/0x0002 "client online" SNAC.
    - */
    -int aim_im_reqofflinemsgs(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
    - return -EINVAL;
    -
    - aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
    - *
    - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
    - * and Purple 0.60 and newer.
    - *
    - */
    -int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
    - return -EINVAL;
    -
    - if (!bn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 11 + strlen(bn) + 2);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
    -
    - /* ICBM cookie */
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    -
    - /*
    - * Channel (should be 0x0001 for mtn)
    - */
    - byte_stream_put16(&bs, channel);
    -
    - /*
    - * Dest buddy name
    - */
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - /*
    - * Event (should be 0x0000, 0x0001, or 0x0002 for mtn)
    - */
    - byte_stream_put16(&bs, event);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0006 - Send eXtra Status request
    - */
    -int icq_im_xstatus_request(OscarData *od, const char *sn)
    -{
    - FlapConnection *conn;
    - aim_snacid_t snacid;
    - guchar cookie[8];
    - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
    - ByteStream bs, header, plugindata;
    - PurpleAccount *account;
    - const char *fmt;
    - char *statxml;
    - int xmllen;
    -
    - static const guint8 pluginid[] = {
    - 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
    - };
    -
    - static const guint8 c_plugindata[] = {
    - 0x1B, 0x00, 0x0A,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
    - 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
    - 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
    - 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
    - 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
    - 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
    - };
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
    - return -EINVAL;
    -
    - if (!sn)
    - return -EINVAL;
    -
    - fmt = "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";
    -
    - account = purple_connection_get_account(od->gc);
    -
    - statxml = g_strdup_printf(fmt, purple_account_get_username(account));
    - xmllen = strlen(statxml);
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2
    - + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
    - + 2 + 2 + sizeof(c_plugindata) + xmllen
    - + 2 + 2);
    -
    - snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
    - aim_im_puticbm(&bs, cookie, 0x0002, sn);
    -
    - byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */
    - byte_stream_put16(&header, 0x0000); /* Message Type: Request */
    - byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */
    - byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */
    -
    - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
    - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
    -
    - /* Add Plugin Specific Data */
    - byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
    - byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */
    - byte_stream_putraw(&plugindata, (const guint8*)statxml, xmllen);
    -
    - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
    -
    - aim_tlvlist_write(&header, &inner_tlvlist);
    - aim_tlvlist_free(inner_tlvlist);
    -
    - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data);
    - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */
    -
    - aim_tlvlist_write(&bs, &outer_tlvlist);
    -
    - purple_debug_misc("oscar", "X-Status Request\n");
    - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, TRUE);
    -
    - aim_tlvlist_free(outer_tlvlist);
    - byte_stream_destroy(&header);
    - byte_stream_destroy(&plugindata);
    - byte_stream_destroy(&bs);
    - g_free(statxml);
    -
    - return 0;
    -}
    -
    -int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - PurpleAccount *account;
    - PurpleStatus *status;
    - const char *fmt;
    - const char *formatted_msg;
    - char *msg;
    - char *statxml;
    - const char *title;
    - int len;
    -
    - static const guint8 plugindata[] = {
    - 0x1B, 0x00,
    - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F,
    - 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0,
    - 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00,
    - 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75,
    - 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
    - 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
    - 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
    - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
    - };
    -
    - fmt = "<NR><RES>&lt;ret event='OnRemoteNotification'&gt;&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;val srv_id='cAwaySrv'&gt;&lt;Root&gt;&lt;CASXtraSetAwayMessage&gt;&lt;/CASXtraSetAwayMessage&gt;&l t;uin&gt;%s&lt;/uin&gt;&lt;index&gt;1&lt;/index&gt;&lt;title&gt;%s&lt;/title&gt;&lt;desc&gt;%s&lt;/desc&gt;&lt;/Root&gt;&lt;/val&gt;&lt;/srv&gt;&lt;srv&gt;&lt;id&gt;cRandomizerSrv&lt;/id&gt;&lt;val srv_id='cRandomizerSrv'&gt;undefined&lt;/val&gt;&lt;/srv&gt;&lt;/ret&gt;</RES></NR>\r\n";
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
    - return -EINVAL;
    -
    - if (!sn)
    - return -EINVAL;
    -
    - account = purple_connection_get_account(od->gc);
    - if (!account)
    - return -EINVAL;
    -
    - /* if (purple_strequal(account->username, sn))
    - icq_im_xstatus_request(od, sn); */
    -
    - status = purple_presence_get_active_status(purple_account_get_presence(account));
    - if (!status)
    - return -EINVAL;
    -
    - title = purple_status_get_name(status);
    - if (!title)
    - return -EINVAL;
    -
    - formatted_msg = purple_status_get_attr_string(status, "message");
    - if (!formatted_msg)
    - return -EINVAL;
    -
    - msg = purple_markup_strip_html(formatted_msg);
    - if (!msg)
    - return -EINVAL;
    -
    - statxml = g_strdup_printf(fmt, purple_account_get_username(account), title, msg);
    - len = strlen(statxml);
    -
    - purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
    -
    - byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + len); /* 16 extra */
    -
    - snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
    - aim_im_puticbm(&bs, cookie, 0x0002, sn);
    - byte_stream_put16(&bs, 0x0003);
    - byte_stream_putraw(&bs, plugindata, sizeof(plugindata));
    - byte_stream_putraw(&bs, (const guint8*)statxml, len);
    -
    - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, snacid, &bs, TRUE);
    -
    - g_free(statxml);
    - g_free(msg);
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
    - *
    - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
    - * and Purple 0.60 and newer.
    - *
    - */
    -static int mtn_receive(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - char *bn;
    - guint8 bnlen;
    - guint16 channel, event;
    -
    - byte_stream_advance(bs, 8); /* ICBM cookie */
    - channel = byte_stream_get16(bs);
    - bnlen = byte_stream_get8(bs);
    - bn = byte_stream_getstr(bs, bnlen);
    - if (!g_utf8_validate(bn, -1, NULL)) {
    - purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with "
    - "invalid UTF-8 buddy name.\n", snac->family, snac->subtype);
    - g_free(bn);
    - return 1;
    - }
    - event = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, channel, bn, event);
    -
    - g_free(bn);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0005)
    - return aim_im_paraminfo(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0007)
    - return incomingim(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000a)
    - return missedcall(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000b)
    - return clientautoresp(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000c)
    - return msgack(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0014)
    - return mtn_receive(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -msg_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_ICBM;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "messaging", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_icq.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,792 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0015 - Encapsulated ICQ.
    - *
    - */
    -
    -#include "glibcompat.h"
    -
    -#include "encoding.h"
    -#include "oscar.h"
    -
    -#define AIM_ICQ_INFO_REQUEST 0x04b2
    -#define AIM_ICQ_ALIAS_REQUEST 0x04ba
    -
    -static
    -int compare_icq_infos(gconstpointer a, gconstpointer b)
    -{
    - const struct aim_icq_info* aa = a;
    - const guint16* bb = b;
    - return aa->reqid - *bb;
    -}
    -
    -static void aim_icq_freeinfo(struct aim_icq_info *info) {
    - int i;
    -
    - if (!info)
    - return;
    - g_free(info->nick);
    - g_free(info->first);
    - g_free(info->last);
    - g_free(info->email);
    - g_free(info->homecity);
    - g_free(info->homestate);
    - g_free(info->homephone);
    - g_free(info->homefax);
    - g_free(info->homeaddr);
    - g_free(info->mobile);
    - g_free(info->homezip);
    - g_free(info->personalwebpage);
    - if (info->email2)
    - for (i = 0; i < info->numaddresses; i++)
    - g_free(info->email2[i]);
    - g_free(info->email2);
    - g_free(info->workcity);
    - g_free(info->workstate);
    - g_free(info->workphone);
    - g_free(info->workfax);
    - g_free(info->workaddr);
    - g_free(info->workzip);
    - g_free(info->workcompany);
    - g_free(info->workdivision);
    - g_free(info->workposition);
    - g_free(info->workwebpage);
    - g_free(info->info);
    - g_free(info->status_note_title);
    - g_free(info->auth_request_reason);
    -}
    -
    -static
    -int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs)
    -{
    - aim_snac_t *original_snac = aim_remsnac(od, error_snac->id);
    - guint16 *request_type;
    - GSList *original_info_ptr;
    - struct aim_icq_info *original_info;
    - guint16 reason;
    - gchar *uin;
    -
    - if (!original_snac || (original_snac->family != SNAC_FAMILY_ICQ) || !original_snac->data) {
    - purple_debug_misc("oscar", "icq: the original snac for the error packet was not found");
    - g_free(original_snac);
    - return 0;
    - }
    -
    - request_type = original_snac->data;
    - original_info_ptr = g_slist_find_custom(od->icq_info, &original_snac->id, compare_icq_infos);
    -
    - if (!original_info_ptr) {
    - purple_debug_misc("oscar", "icq: the request info for the error packet was not found");
    - g_free(original_snac);
    - return 0;
    - }
    -
    - original_info = original_info_ptr->data;
    -
    - reason = byte_stream_get16(bs);
    - uin = g_strdup_printf("%u", original_info->uin);
    - switch (*request_type) {
    - case AIM_ICQ_INFO_REQUEST:
    - oscar_user_info_display_error(od, reason, uin);
    - break;
    - case AIM_ICQ_ALIAS_REQUEST:
    - /* Couldn't retrieve an alias for the buddy requesting authorization; have to make do with UIN only. */
    - if (original_info->for_auth_request)
    - oscar_auth_recvrequest(od->gc, uin, NULL, original_info->auth_request_reason);
    - break;
    - default:
    - purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type);
    - break;
    - }
    -
    - aim_icq_freeinfo(original_info);
    - od->icq_info = g_slist_remove(od->icq_info, original_info_ptr);
    - g_free(original_snac->data);
    - g_free(original_snac);
    - return 1;
    -}
    -
    -int
    -aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, 0x0c3a); /* shrug. */
    - byte_stream_putle16(&bs, 0x030c);
    - byte_stream_putle16(&bs, 0x0001);
    - byte_stream_putle8(&bs, webaware);
    - byte_stream_putle8(&bs, 0xf8);
    - byte_stream_putle8(&bs, 0x02);
    - byte_stream_putle8(&bs, 0x01);
    - byte_stream_putle8(&bs, 0x00);
    - byte_stream_putle8(&bs, !auth_required);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/**
    - * Change your ICQ password.
    - *
    - * @param od The oscar session
    - * @param passwd The new password. If this is longer than 8 characters it
    - * will be truncated.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int aim_icq_changepasswd(OscarData *od, const char *passwd)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen, passwdlen;
    -
    - if (!passwd)
    - return -EINVAL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - passwdlen = strlen(passwd);
    - if (passwdlen > MAXICQPASSLEN)
    - passwdlen = MAXICQPASSLEN;
    - bslen = 2+4+2+2+2+2+passwdlen+1;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, 0x042e); /* shrug. */
    - byte_stream_putle16(&bs, passwdlen+1);
    - byte_stream_putraw(&bs, (const guint8 *)passwd, passwdlen);
    - byte_stream_putle8(&bs, '\0');
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -int aim_icq_getallinfo(OscarData *od, const char *uin)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    - struct aim_icq_info *info;
    - guint16 request_type = AIM_ICQ_INFO_REQUEST;
    -
    - if (!uin || uin[0] < '0' || uin[0] > '9')
    - return -EINVAL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - bslen = 2 + 4 + 2 + 2 + 2 + 4;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, request_type); /* shrug. */
    - byte_stream_putle32(&bs, atoi(uin));
    -
    - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    -
    - /* Keep track of this request and the ICQ number and request ID */
    - info = g_new0(struct aim_icq_info, 1);
    - info->reqid = snacid;
    - info->uin = atoi(uin);
    - od->icq_info = g_slist_prepend(od->icq_info, info);
    -
    - return 0;
    -}
    -
    -int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen;
    - struct aim_icq_info *info;
    - guint16 request_type = AIM_ICQ_ALIAS_REQUEST;
    -
    - if (!uin || uin[0] < '0' || uin[0] > '9')
    - return -EINVAL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - purple_debug_info("oscar", "Requesting ICQ alias for %s\n", uin);
    -
    - bslen = 2 + 4 + 2 + 2 + 2 + 4;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    - byte_stream_putle16(&bs, request_type); /* shrug. */
    - byte_stream_putle32(&bs, atoi(uin));
    -
    - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    -
    - /* Keep track of this request and the ICQ number and request ID */
    - info = g_new0(struct aim_icq_info, 1);
    - info->reqid = snacid;
    - info->uin = atoi(uin);
    - info->for_auth_request = for_auth_request;
    - info->auth_request_reason = g_strdup(auth_request_reason);
    - od->icq_info = g_slist_prepend(od->icq_info, info);
    -
    - return 0;
    -}
    -
    -/*
    - * Send an SMS message. This is the non-US way. The US-way is to IM
    - * their cell phone number (+19195551234).
    - *
    - * We basically construct and send an XML message. The format is:
    - * <icq_sms_message>
    - * <destination>full_phone_without_leading_+</destination>
    - * <text>message</text>
    - * <codepage>1252</codepage>
    - * <senders_UIN>self_uin</senders_UIN>
    - * <senders_name>self_name</senders_name>
    - * <delivery_receipt>Yes|No</delivery_receipt>
    - * <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time>
    - * </icq_sms_message>
    - *
    - * Yeah hi Peter, whaaaat's happening. If there's any way to use
    - * a codepage other than 1252 that would be great. Thaaaanks.
    - */
    -int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias)
    -{
    - FlapConnection *conn;
    - PurpleAccount *account;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - int bslen, xmllen;
    - char *xml;
    - const char *timestr, *username;
    - time_t t;
    - struct tm *tm;
    - gchar *stripped;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
    - return -EINVAL;
    -
    - if (!name || !msg || !alias)
    - return -EINVAL;
    -
    - account = purple_connection_get_account(od->gc);
    - username = purple_account_get_username(account);
    -
    - time(&t);
    - tm = gmtime(&t);
    - timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm);
    -
    - stripped = purple_markup_strip_html(msg);
    -
    - /* The length of xml included the null terminating character */
    - xmllen = 209 + strlen(name) + strlen(stripped) + strlen(username) + strlen(alias) + strlen(timestr) + 1;
    -
    - xml = g_new(char, xmllen);
    - snprintf(xml, xmllen, "<icq_sms_message>"
    - "<destination>%s</destination>"
    - "<text>%s</text>"
    - "<codepage>1252</codepage>"
    - "<senders_UIN>%s</senders_UIN>"
    - "<senders_name>%s</senders_name>"
    - "<delivery_receipt>Yes</delivery_receipt>"
    - "<time>%s</time>"
    - "</icq_sms_message>",
    - name, stripped, username, alias, timestr);
    -
    - bslen = 36 + xmllen;
    -
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
    -
    - /* For simplicity, don't bother using a tlvlist */
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, bslen);
    -
    - byte_stream_putle16(&bs, bslen - 2);
    - byte_stream_putuid(&bs, od);
    - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
    - byte_stream_putle16(&bs, snacid); /* eh. */
    -
    - /* From libicq200-0.3.2/src/SNAC-SRV.cpp */
    - byte_stream_putle16(&bs, 0x1482);
    - byte_stream_put16(&bs, 0x0001);
    - byte_stream_put16(&bs, 0x0016);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    -
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, xmllen);
    - byte_stream_putstr(&bs, xml);
    - byte_stream_put8(&bs, 0x00);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - g_free(xml);
    - g_free(stripped);
    -
    - return 0;
    -}
    -
    -static void
    -gotalias(OscarData *od, struct aim_icq_info *info)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleBuddy *b;
    - gchar *utf8 = oscar_utf8_try_convert(account, od, info->nick);
    -
    - if (info->for_auth_request) {
    - oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason);
    - } else {
    - if (utf8 && *utf8) {
    - gchar who[16];
    - g_snprintf(who, sizeof(who), "%u", info->uin);
    - purple_serv_got_alias(gc, who, utf8);
    - if ((b = purple_blist_find_buddy(account, who))) {
    - purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8);
    - }
    - }
    - g_free(utf8);
    - }
    -}
    -
    -/**
    - * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
    - */
    -static int
    -icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - GSList *tlvlist;
    - aim_tlv_t *datatlv;
    - ByteStream qbs;
    - guint32 ouruin;
    - guint16 cmdlen, cmd, reqid;
    -
    - if (!(tlvlist = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tlvlist, 0x0001, 1))) {
    - aim_tlvlist_free(tlvlist);
    - purple_debug_misc("oscar", "corrupt ICQ response\n");
    - return 0;
    - }
    -
    - byte_stream_init(&qbs, datatlv->value, datatlv->length);
    -
    - cmdlen = byte_stream_getle16(&qbs);
    - ouruin = byte_stream_getle32(&qbs);
    - cmd = byte_stream_getle16(&qbs);
    - reqid = byte_stream_getle16(&qbs);
    -
    - purple_debug_misc("oscar", "icq response: %d bytes, %u, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid);
    -
    - if (cmd == 0x07da) { /* information */
    - guint16 subtype;
    - GSList *info_ptr;
    - struct aim_icq_info *info;
    -
    - subtype = byte_stream_getle16(&qbs);
    - byte_stream_advance(&qbs, 1); /* 0x0a */
    -
    - /* find other data from the same request */
    - info_ptr = g_slist_find_custom(od->icq_info, &reqid, compare_icq_infos);
    - if (!info_ptr) {
    - struct aim_icq_info *new_info = g_new0(struct aim_icq_info, 1);
    - new_info->reqid = reqid;
    - info_ptr = od->icq_info = g_slist_prepend(od->icq_info, new_info);
    - }
    -
    - info = info_ptr->data;
    - switch (subtype) {
    - case 0x00a0: { /* hide ip status */
    - /* nothing */
    - } break;
    -
    - case 0x00aa: { /* password change status */
    - /* nothing */
    - } break;
    -
    - case 0x00c8: { /* general and "home" information */
    - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homecity = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homestate = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homephone = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homefax = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homeaddr = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->mobile = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homezip = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->homecountry = byte_stream_getle16(&qbs);
    - /* 0x0a 00 02 00 */
    - /* 1 byte timezone? */
    - /* 1 byte hide email flag? */
    - } break;
    -
    - case 0x00dc: { /* personal information */
    - info->age = byte_stream_getle8(&qbs);
    - info->unknown = byte_stream_getle8(&qbs);
    - info->gender = byte_stream_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */
    - info->personalwebpage = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->birthyear = byte_stream_getle16(&qbs);
    - info->birthmonth = byte_stream_getle8(&qbs);
    - info->birthday = byte_stream_getle8(&qbs);
    - info->language1 = byte_stream_getle8(&qbs);
    - info->language2 = byte_stream_getle8(&qbs);
    - info->language3 = byte_stream_getle8(&qbs);
    - /* 0x00 00 01 00 00 01 00 00 00 00 00 */
    - } break;
    -
    - case 0x00d2: { /* work information */
    - info->workcity = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workstate = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workphone = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workfax = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workaddr = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workzip = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workcountry = byte_stream_getle16(&qbs);
    - info->workcompany = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workdivision = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->workposition = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - byte_stream_advance(&qbs, 2); /* 0x01 00 */
    - info->workwebpage = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - } break;
    -
    - case 0x00e6: { /* additional personal information */
    - info->info = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)-1);
    - } break;
    -
    - case 0x00eb: { /* email address(es) */
    - int i;
    - info->numaddresses = byte_stream_getle16(&qbs);
    - info->email2 = g_new0(char *, info->numaddresses);
    - for (i = 0; i < info->numaddresses; i++) {
    - info->email2[i] = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - if (i+1 != info->numaddresses)
    - byte_stream_advance(&qbs, 1); /* 0x00 */
    - }
    - } break;
    -
    - case 0x00f0: { /* personal interests */
    - } break;
    -
    - case 0x00fa: { /* past background and current organizations */
    - } break;
    -
    - case 0x0104: { /* alias info */
    - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - byte_stream_advance(&qbs, byte_stream_getle16(&qbs)); /* email address? */
    - /* Then 0x00 02 00 */
    - } break;
    -
    - case 0x010e: { /* unknown */
    - /* 0x00 00 */
    - } break;
    -
    - case 0x019a: { /* simple info */
    - byte_stream_advance(&qbs, 2);
    - info->uin = byte_stream_getle32(&qbs);
    - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
    - /* Then 0x00 02 00 00 00 00 00 */
    - } break;
    -
    - /* status note title and send request for status note text */
    - case 0x0fb4: {
    - GSList *tlvlist;
    - aim_tlv_t *tlv;
    - FlapConnection *conn;
    - char *uin = NULL;
    - char *status_note_title = NULL;
    -
    - conn = flap_connection_findbygroup(od, 0x0004);
    - if (conn == NULL)
    - {
    - purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n");
    - break;
    - }
    -
    - byte_stream_advance(&qbs, 0x02); /* length */
    - byte_stream_advance(&qbs, 0x2f); /* unknown stuff */
    -
    - tlvlist = aim_tlvlist_read(&qbs);
    -
    - tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1);
    - if (tlv != NULL)
    - /* Get user number */
    - uin = aim_tlv_getvalue_as_string(tlv);
    -
    - tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1);
    - if (tlv != NULL)
    - /* Get status note title */
    - status_note_title = aim_tlv_getvalue_as_string(tlv);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - if (uin == NULL || status_note_title == NULL)
    - {
    - purple_debug_misc("oscar", "icq/0x0fb4: uin or "
    - "status_note_title was not found\n");
    - g_free(uin);
    - g_free(status_note_title);
    - break;
    - }
    -
    - if (status_note_title[0] == '\0')
    - {
    - PurpleAccount *account;
    - PurpleBuddy *buddy;
    - PurplePresence *presence;
    - PurpleStatus *status;
    -
    - account = purple_connection_get_account(od->gc);
    - buddy = purple_blist_find_buddy(account, uin);
    - presence = purple_buddy_get_presence(buddy);
    - status = purple_presence_get_active_status(presence);
    -
    - purple_protocol_got_user_status(account, uin,
    - purple_status_get_id(status),
    - "message", NULL, NULL);
    -
    - g_free(status_note_title);
    - }
    - else
    - {
    - struct aim_icq_info *info;
    - ByteStream bs;
    - guint32 bslen;
    - aim_snacid_t snacid;
    - guchar cookie[8];
    -
    - info = g_new0(struct aim_icq_info, 1);
    -
    - bslen = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4;
    - byte_stream_new(&bs, 4 + bslen);
    -
    - snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
    -
    - aim_icbm_makecookie(cookie);
    -
    - byte_stream_putraw(&bs, cookie, 8); /* ICBM cookie */
    - byte_stream_put16(&bs, 0x0002); /* message channel */
    - byte_stream_put8(&bs, strlen(uin)); /* uin */
    - byte_stream_putstr(&bs, uin);
    -
    - byte_stream_put16(&bs, 0x0005); /* rendez vous data */
    - byte_stream_put16(&bs, 0x00b2);
    - byte_stream_put16(&bs, 0x0000); /* request */
    - byte_stream_putraw(&bs, cookie, 8); /* ICBM cookie */
    - byte_stream_put32(&bs, 0x09461349); /* ICQ server relaying */
    - byte_stream_put16(&bs, 0x4c7f);
    - byte_stream_put16(&bs, 0x11d1);
    - byte_stream_put32(&bs, 0x82224445);
    - byte_stream_put32(&bs, 0x53540000);
    -
    - byte_stream_put16(&bs, 0x000a); /* unknown TLV */
    - byte_stream_put16(&bs, 0x0002);
    - byte_stream_put16(&bs, 0x0001);
    -
    - byte_stream_put16(&bs, 0x000f); /* unknown TLV */
    - byte_stream_put16(&bs, 0x0000);
    -
    - byte_stream_put16(&bs, 0x2711); /* extended data */
    - byte_stream_put16(&bs, 0x008a);
    - byte_stream_putle16(&bs, 0x001b); /* length */
    - byte_stream_putle16(&bs, 0x0009); /* version */
    - byte_stream_putle32(&bs, 0x00000000); /* plugin: none */
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle16(&bs, 0x0000); /* unknown */
    - byte_stream_putle32(&bs, 0x00000000); /* client capabilities flags */
    - byte_stream_put8(&bs, 0x00); /* unknown */
    - byte_stream_putle16(&bs, 0x0064); /* downcounter? */
    - byte_stream_putle16(&bs, 0x000e); /* length */
    - byte_stream_putle16(&bs, 0x0064); /* downcounter? */
    - byte_stream_putle32(&bs, 0x00000000); /* unknown */
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_putle32(&bs, 0x00000000);
    - byte_stream_put8(&bs, 0x1a); /* message type: plugin message descibed by text string */
    - byte_stream_put8(&bs, 0x00); /* message flags */
    - byte_stream_putle16(&bs, 0x0000); /* status code */
    - byte_stream_putle16(&bs, 0x0001); /* priority code */
    - byte_stream_putle16(&bs, 0x0000); /* text length */
    -
    - byte_stream_put8(&bs, 0x3a); /* message dump */
    - byte_stream_put32(&bs, 0x00811a18);
    - byte_stream_put32(&bs, 0xbc0e6c18);
    - byte_stream_put32(&bs, 0x47a5916f);
    - byte_stream_put32(&bs, 0x18dcc76f);
    - byte_stream_put32(&bs, 0x1a010013);
    - byte_stream_put32(&bs, 0x00000041);
    - byte_stream_put32(&bs, 0x77617920);
    - byte_stream_put32(&bs, 0x53746174);
    - byte_stream_put32(&bs, 0x7573204d);
    - byte_stream_put32(&bs, 0x65737361);
    - byte_stream_put32(&bs, 0x67650100);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x00000015);
    - byte_stream_put32(&bs, 0x00000000);
    - byte_stream_put32(&bs, 0x0000000d);
    - byte_stream_put32(&bs, 0x00000074);
    - byte_stream_put32(&bs, 0x6578742f);
    - byte_stream_put32(&bs, 0x782d616f);
    - byte_stream_put32(&bs, 0x6c727466);
    -
    - byte_stream_put16(&bs, 0x0003); /* server ACK requested */
    - byte_stream_put16(&bs, 0x0000);
    -
    - info->uin = atoi(uin);
    - info->status_note_title = status_note_title;
    -
    - memcpy(&info->icbm_cookie, cookie, 8);
    -
    - od->icq_info = g_slist_prepend(od->icq_info, info);
    -
    - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    - }
    -
    - g_free(uin);
    -
    - } break;
    -
    - } /* End switch statement */
    -
    - if (!(snac->flags & 0x0001)) {
    - if (subtype != 0x0104)
    - oscar_user_info_display_icq(od, info);
    -
    - if (info->uin && info->nick)
    - gotalias(od, info);
    -
    - aim_icq_freeinfo(info);
    - od->icq_info = g_slist_remove(od->icq_info, info);
    - }
    - }
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return 1;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, snac, bs);
    - else if (snac->subtype == 0x0003)
    - return icqresponse(od, snac, bs);
    -
    - return 0;
    -}
    -
    -static void
    -icq_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - g_slist_free_full(od->icq_info, (GDestroyNotify)aim_icq_freeinfo);
    -}
    -
    -int
    -icq_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_ICQ;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x047c;
    - mod->flags = 0;
    - strncpy(mod->name, "icq", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = icq_shutdown;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_locate.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1546 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0002 - Locate.
    - *
    - * The functions here are responsible for requesting and parsing information-
    - * gathering SNACs. Or something like that. This family contains the SNACs
    - * for getting and setting info, away messages, directory profile thingy, etc.
    - */
    -
    -#include "oscar.h"
    -
    -/* Define to log unknown TLVs */
    -/* #define LOG_UNKNOWN_TLV */
    -
    -/*
    - * Capability blocks.
    - *
    - * These are CLSIDs. They should actually be of the form:
    - *
    - * {0x0946134b, 0x4c7f, 0x11d1,
    - * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
    - *
    - * But, eh.
    - */
    -static const struct {
    - guint64 flag;
    - guint8 data[16];
    -} aim_caps[] = {
    -
    - /*
    - * These are in ascending numerical order.
    - */
    -
    - /* Client understands short caps, a UUID of the form
    - * 0946XXYY-4C7F-11D1-8222-444553540000 where XXYY is the short cap. */
    - {OSCAR_CAPABILITY_SHORTCAPS,
    - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_SECUREIM,
    - {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* OSCAR_CAPABILITY_XHTML_IM */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_VIDEO,
    - {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */
    - {OSCAR_CAPABILITY_LIVEVIDEO,
    - {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* "Camera" support in Windows AIM 5.5.3501 and newer */
    - {OSCAR_CAPABILITY_CAMERA,
    - {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* "Microphone" support in Windows AIM 5.5.3501 and newer */
    - /* OSCAR_CAPABILITY_MICROPHONE */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Supports RTC Audio */
    - /* OSCAR_CAPABILITY_RTCAUDIO */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* In iChatAV (version numbers...?) */
    - {OSCAR_CAPABILITY_ICHATAV,
    - {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}},
    -
    - /* Supports "new status message features" (Who advertises this one?) */
    - /* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x0a, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Supports "see as I type" (Who advertises this one?) */
    - /* OSCAR_CAPABILITY_SEE_AS_I_TYPE */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0x0b, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Client only asserts caps for services in which it is participating */
    - /* OSCAR_CAPABILITY_SMARTCAPS */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_HIPTOP,
    - {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_TALK,
    - {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_SENDFILE,
    - {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ICQ_DIRECT,
    - {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_DIRECTIM,
    - {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_BUDDYICON,
    - {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ADDINS,
    - {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_GETFILE,
    - {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ICQSERVERRELAY,
    - {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /*
    - * Indeed, there are two of these. The former appears to be correct,
    - * but in some versions of winaim, the second one is set. Either they
    - * forgot to fix endianness, or they made a typo. It really doesn't
    - * matter which.
    - */
    - {OSCAR_CAPABILITY_GAMES,
    - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    - {OSCAR_CAPABILITY_GAMES2,
    - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* New format of caps (xtraz icons) */
    - {OSCAR_CAPABILITY_NEWCAPS,
    - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* Support xtraz statuses */
    - {OSCAR_CAPABILITY_XTRAZ,
    - {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5,
    - 0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}},
    -
    - {OSCAR_CAPABILITY_SENDBUDDYLIST,
    - {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /*
    - * Setting this lets AIM users receive messages from ICQ users, and ICQ
    - * users receive messages from AIM users. It also lets ICQ users show
    - * up in buddy lists for AIM users, and AIM users show up in buddy lists
    - * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy,
    - * in that if you add a user to your deny list, you will not be able to
    - * see them as online (previous you could still see them, but they
    - * couldn't see you.
    - */
    - {OSCAR_CAPABILITY_INTEROPERATE,
    - {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_UNICODE,
    - {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_ICHAT_SCREENSHARE,
    - {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_UNICODEOLD,
    - {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
    - 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
    -
    - {OSCAR_CAPABILITY_TYPING,
    - {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
    - 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
    -
    - /*
    - * Chat is oddball.
    - */
    - {OSCAR_CAPABILITY_CHAT,
    - {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
    - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
    -
    - /* This is added by the servers and it only shows up for ourselves... */
    - {OSCAR_CAPABILITY_GENERICUNKNOWN,
    - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
    - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}},
    -
    - {OSCAR_CAPABILITY_ICQRTF,
    - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
    - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
    -
    - {OSCAR_CAPABILITY_APINFO,
    - {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
    - 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}},
    -
    - {OSCAR_CAPABILITY_TRILLIANCRYPT,
    - {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
    - 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_EMPTY,
    - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
    -
    - {OSCAR_CAPABILITY_HTML_MSGS,
    - {0x01, 0x38, 0xca, 0x7b, 0x76, 0x9a, 0x49, 0x15,
    - 0x88, 0xf2, 0x13, 0xfc, 0x00, 0x97, 0x9e, 0xa8}},
    -
    - {OSCAR_CAPABILITY_LAST,
    - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
    -};
    -
    -/* Keep this array synchronized with icq_purple_moods. */
    -static const struct {
    - const char *mood;
    - guint8 data[16];
    -} icq_custom_icons[] = {
    -
    - {"thinking",
    - {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60,
    - 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}},
    -
    - {"busy",
    - {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08,
    - 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}},
    -
    - {"shopping",
    - {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff,
    - 0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}},
    -
    - /* This was in the original patch, but isn't what the official client
    - * (ICQ 6) sets when you choose its typewriter icon. */
    - {"typing",
    - {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1,
    - 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}},
    -
    - {"question",
    - {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0,
    - 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}},
    -
    - {"angry",
    - {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a,
    - 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}},
    -
    - {"plate",
    - {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42,
    - 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}},
    -
    - {"cinema",
    - {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4,
    - 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}},
    -
    - {"sick",
    - {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60,
    - 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}},
    -
    - {"typing",
    - {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70,
    - 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}},
    -
    - {"suit",
    - {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27,
    - 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}},
    -
    - {"bathing",
    - {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c,
    - 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}},
    -
    - {"tv",
    - {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76,
    - 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}},
    -
    - {"excited",
    - {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff,
    - 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}},
    -
    - {"sleeping",
    - {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65,
    - 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}},
    -
    - {"hiptop",
    - {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9,
    - 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}},
    -
    - {"in_love",
    - {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48,
    - 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}},
    -
    - {"sleepy",
    - {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78,
    - 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}},
    -
    - {"meeting",
    - {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d,
    - 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}},
    -
    - {"phone",
    - {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66,
    - 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}},
    -
    - {"surfing",
    - {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4,
    - 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}},
    -
    - {"mobile",
    - {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3,
    - 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}},
    -
    - {"search",
    - {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5,
    - 0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}},
    -
    - {"party",
    - {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1,
    - 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}},
    -
    - {"coffee",
    - {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38,
    - 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}},
    -
    - {"console",
    - {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0,
    - 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}},
    -
    - {"internet",
    - {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e,
    - 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}},
    -
    - {"cigarette",
    - {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17,
    - 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}},
    -
    - {"writing",
    - {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd,
    - 0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}},
    -
    - {"beer",
    - {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86,
    - 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}},
    -
    - {"music",
    - {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d,
    - 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}},
    -
    - {"studying",
    - {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6,
    - 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}},
    -
    - {"working",
    - {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b,
    - 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}},
    -
    - {"restroom",
    - {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35,
    - 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}},
    -
    - {NULL,
    - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
    -};
    -
    -/* Keep this array synchronized with icq_custom_icons. */
    -static PurpleMood icq_purple_moods[] = {
    - {"thinking", N_("Thinking"), NULL},
    - {"busy", N_("Busy"), NULL},
    - {"shopping", N_("Shopping"), NULL},
    - /* This was in the original patch, but isn't what the official client
    - * (ICQ 6) sets when you choose its typewriter icon. */
    - {"typing", NULL, NULL},
    - {"question", N_("Questioning"), NULL},
    - {"angry", N_("Angry"), NULL},
    - {"plate", N_("Eating"), NULL},
    - {"cinema", N_("Watching a movie"), NULL},
    - {"sick", N_("Sick"), NULL},
    - {"typing", N_("Typing"), NULL},
    - {"suit", N_("At the office"), NULL},
    - {"bathing", N_("Taking a bath"), NULL},
    - {"tv", N_("Watching TV"), NULL},
    - {"excited", N_("Having fun"), NULL},
    - {"sleeping", N_("Sleeping"), NULL},
    - {"hiptop", N_("Using a PDA"), NULL},
    - {"in_love", N_("In love"), NULL},
    - /* Sleepy / Tired */
    - {"sleepy", N_("Sleepy"), NULL},
    - {"meeting", N_("Meeting friends"), NULL},
    - {"phone", N_("On the phone"), NULL},
    - {"surfing", N_("Surfing"), NULL},
    - /* "I am mobile." / "John is mobile." */
    - {"mobile", N_("Mobile"), NULL},
    - {"search", N_("Searching the web"), NULL},
    - {"party", N_("At a party"), NULL},
    - {"coffee", N_("Having Coffee"), NULL},
    - /* Playing video games */
    - {"console", N_("Gaming"), NULL},
    - {"internet", N_("Browsing the web"), NULL},
    - {"cigarette", N_("Smoking"), NULL},
    - {"writing", N_("Writing"), NULL},
    - /* Drinking [Alcohol] */
    - {"beer", N_("Drinking"), NULL},
    - {"music", N_("Listening to music"), NULL},
    - {"studying", N_("Studying"), NULL},
    - {"working", N_("Working"), NULL},
    - {"restroom", N_("In the restroom"), NULL},
    - /* Mark the last record. */
    - {NULL, NULL, NULL},
    -};
    -
    -
    -/*
    - * Add the userinfo to our linked list. If we already have userinfo
    - * for this buddy, then just overwrite parts of the old data.
    - *
    - * @param userinfo Contains the new information for the buddy.
    - */
    -static void
    -aim_locate_adduserinfo(OscarData *od, aim_userinfo_t *userinfo)
    -{
    - aim_userinfo_t *cur;
    -
    - cur = aim_locate_finduserinfo(od, userinfo->bn);
    -
    - if (cur == NULL) {
    - cur = (aim_userinfo_t *)g_new0(aim_userinfo_t, 1);
    - cur->bn = g_strdup(userinfo->bn);
    - cur->next = od->locate.userinfo;
    - od->locate.userinfo = cur;
    - }
    -
    - cur->warnlevel = userinfo->warnlevel;
    - cur->idletime = userinfo->idletime;
    - if (userinfo->flags != 0)
    - cur->flags = userinfo->flags;
    - if (userinfo->createtime != 0)
    - cur->createtime = userinfo->createtime;
    - if (userinfo->membersince != 0)
    - cur->membersince = userinfo->membersince;
    - if (userinfo->onlinesince != 0)
    - cur->onlinesince = userinfo->onlinesince;
    - if (userinfo->sessionlen != 0)
    - cur->sessionlen = userinfo->sessionlen;
    - if (userinfo->capabilities != 0)
    - cur->capabilities = userinfo->capabilities;
    -
    - cur->present |= userinfo->present;
    -
    - if (userinfo->iconcsumlen > 0) {
    - g_free(cur->iconcsum);
    - cur->iconcsum = (guint8 *)g_malloc(userinfo->iconcsumlen);
    - memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen);
    - cur->iconcsumlen = userinfo->iconcsumlen;
    - }
    -
    - if (userinfo->info != NULL) {
    - g_free(cur->info);
    - g_free(cur->info_encoding);
    - if (userinfo->info_len > 0) {
    - cur->info = (char *)g_malloc(userinfo->info_len);
    - memcpy(cur->info, userinfo->info, userinfo->info_len);
    - } else
    - cur->info = NULL;
    - cur->info_encoding = g_strdup(userinfo->info_encoding);
    - cur->info_len = userinfo->info_len;
    - }
    -
    - if (userinfo->status != NULL) {
    - g_free(cur->status);
    - g_free(cur->status_encoding);
    - if (userinfo->status_len > 0) {
    - cur->status = (char *)g_malloc(userinfo->status_len);
    - memcpy(cur->status, userinfo->status, userinfo->status_len);
    - } else
    - cur->status = NULL;
    - if (userinfo->status_encoding != NULL)
    - cur->status_encoding = g_strdup(userinfo->status_encoding);
    - else
    - cur->status_encoding = NULL;
    - cur->status_len = userinfo->status_len;
    - }
    -
    - if (userinfo->itmsurl != NULL) {
    - g_free(cur->itmsurl);
    - g_free(cur->itmsurl_encoding);
    - if (userinfo->itmsurl_len > 0) {
    - cur->itmsurl = (char *)g_malloc(userinfo->itmsurl_len);
    - memcpy(cur->itmsurl, userinfo->itmsurl, userinfo->itmsurl_len);
    - } else
    - cur->itmsurl = NULL;
    - if (userinfo->itmsurl_encoding != NULL)
    - cur->itmsurl_encoding = g_strdup(userinfo->itmsurl_encoding);
    - else
    - cur->itmsurl_encoding = NULL;
    - cur->itmsurl_len = userinfo->itmsurl_len;
    - }
    -
    - if (userinfo->away != NULL) {
    - g_free(cur->away);
    - g_free(cur->away_encoding);
    - if (userinfo->away_len > 0) {
    - cur->away = (char *)g_malloc(userinfo->away_len);
    - memcpy(cur->away, userinfo->away, userinfo->away_len);
    - } else
    - cur->away = NULL;
    - cur->away_encoding = g_strdup(userinfo->away_encoding);
    - cur->away_len = userinfo->away_len;
    -
    - } else {
    - /*
    - * We don't have an away message specified in this user_info
    - * block, so clear any cached away message now.
    - */
    - g_free(cur->away);
    - cur->away = NULL;
    - g_free(cur->away_encoding);
    - cur->away_encoding = NULL;
    - cur->away_len = 0;
    - }
    -}
    -
    -aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn) {
    - aim_userinfo_t *cur = NULL;
    -
    - if (bn == NULL)
    - return NULL;
    -
    - cur = od->locate.userinfo;
    -
    - while (cur != NULL) {
    - if (oscar_util_name_compare(cur->bn, bn) == 0)
    - return cur;
    - cur = cur->next;
    - }
    -
    - return NULL;
    -}
    -
    -guint64
    -aim_locate_getcaps(OscarData *od, ByteStream *bs, int len)
    -{
    - guint64 flags = 0;
    - int offset;
    -
    - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
    - guint8 *cap;
    - int i, identified;
    -
    - cap = byte_stream_getraw(bs, 0x10);
    -
    - for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) {
    - if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
    - flags |= aim_caps[i].flag;
    - identified++;
    - break; /* should only match once... */
    - }
    - }
    -
    - if (!identified)
    - purple_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
    - cap[0], cap[1], cap[2], cap[3],
    - cap[4], cap[5],
    - cap[6], cap[7],
    - cap[8], cap[9],
    - cap[10], cap[11], cap[12], cap[13],
    - cap[14], cap[15]);
    - g_free(cap);
    - }
    -
    - return flags;
    -}
    -
    -static const char *
    -aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
    -{
    - int offset;
    - const char *result = NULL;
    -
    - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
    - /* check wheather this capability is a custom user icon */
    - guint8 *cap;
    - int i;
    -
    - cap = byte_stream_getraw(bs, 0x10);
    -
    - for (i = 0; icq_custom_icons[i].mood; i++) {
    - if (memcmp(&icq_custom_icons[i].data, cap, 0x10) == 0) {
    - purple_debug_misc("oscar", "Custom status icon: %s\n", icq_purple_moods[i].description);
    - result = icq_custom_icons[i].mood;
    - break; /* should only match once... */
    - }
    - }
    - g_free(cap);
    - }
    -
    - return result;
    -}
    -
    -guint64
    -aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
    -{
    - guint64 flags = 0;
    - int offset;
    -
    - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x02) {
    - guint8 *cap;
    - int i, identified;
    -
    - cap = byte_stream_getraw(bs, 0x02);
    -
    - for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) {
    - if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) {
    - flags |= aim_caps[i].flag;
    - identified++;
    - break; /* should only match once... */
    - }
    - }
    -
    - if (!identified)
    - purple_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]);
    -
    - g_free(cap);
    - }
    -
    - return flags;
    -}
    -
    -int
    -byte_stream_putcaps(ByteStream *bs, guint64 caps)
    -{
    - int i;
    -
    - if (!bs)
    - return -EINVAL;
    -
    - for (i = 0; byte_stream_bytes_left(bs); i++) {
    - if (aim_caps[i].flag == OSCAR_CAPABILITY_LAST)
    - break;
    -
    - if (caps & aim_caps[i].flag)
    - byte_stream_putraw(bs, aim_caps[i].data, 0x10);
    - }
    - return 0;
    -}
    -
    -#ifdef LOG_UNKNOWN_TLV
    -static void
    -dumptlv(OscarData *od, guint16 type, ByteStream *bs, guint8 len)
    -{
    - int i;
    -
    - if (!od || !bs || !len)
    - return;
    -
    - purple_debug_misc("oscar", "userinfo: type =0x%04x\n", type);
    - purple_debug_misc("oscar", "userinfo: length=0x%04x\n", len);
    - purple_debug_misc("oscar", "userinfo: value:\n");
    -
    - for (i = 0; i < len; i++) {
    - if ((i % 8) == 0)
    - purple_debug_misc("oscar", "\nuserinfo: ");
    - purple_debug_misc("oscar", "0x%2x ", byte_stream_get8(bs));
    - }
    -
    - purple_debug_misc("oscar", "\n");
    -
    - return;
    -}
    -#endif
    -
    -void
    -aim_info_free(aim_userinfo_t *info)
    -{
    - g_free(info->bn);
    - g_free(info->iconcsum);
    - g_free(info->info);
    - g_free(info->info_encoding);
    - g_free(info->status);
    - g_free(info->status_encoding);
    - g_free(info->itmsurl);
    - g_free(info->itmsurl_encoding);
    - g_free(info->away);
    - g_free(info->away_encoding);
    -}
    -
    -static const struct {
    - char *icqmood;
    - const char *mood;
    -} icqmoods[] = {
    - {"icqmood0", "shopping"},
    - {"icqmood1", "bathing"},
    - {"icqmood2", "sleepy"},
    - {"icqmood3", "party"},
    - {"icqmood4", "beer"},
    - {"icqmood5", "thinking"},
    - {"icqmood6", "plate"},
    - {"icqmood7", "tv"},
    - {"icqmood8", "meeting"},
    - {"icqmood9", "coffee"},
    - {"icqmood10", "music"},
    - {"icqmood11", "suit"},
    - {"icqmood12", "cinema"},
    - {"icqmood13", "smile-big"},
    - {"icqmood14", "phone"},
    - {"icqmood15", "console"},
    - {"icqmood16", "studying"},
    - {"icqmood17", "sick"},
    - {"icqmood18", "sleeping"},
    - {"icqmood19", "surfing"},
    - {"icqmood20", "internet"},
    - {"icqmood21", "working"},
    - {"icqmood22", "typing"},
    - {"icqmood23", "angry"},
    - {NULL, 0}
    -
    -};
    -
    -/*
    - * AIM is fairly regular about providing user info. This is a generic
    - * routine to extract it in its standard form.
    - */
    -int
    -aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
    -{
    - int curtlv, tlvcnt;
    - guint8 bnlen;
    -
    - if (!bs || !outinfo)
    - return -EINVAL;
    -
    - /* Clear out old data first */
    - memset(outinfo, 0x00, sizeof(aim_userinfo_t));
    -
    - /*
    - * Username. Stored as an unterminated string prepended with a
    - * byte containing its length.
    - */
    - bnlen = byte_stream_get8(bs);
    - outinfo->bn = byte_stream_getstr(bs, bnlen);
    -
    - /*
    - * Warning Level. Stored as an unsigned short.
    - */
    - outinfo->warnlevel = byte_stream_get16(bs);
    -
    - /*
    - * TLV Count. Unsigned short representing the number of
    - * Type-Length-Value triples that follow.
    - */
    - tlvcnt = byte_stream_get16(bs);
    -
    - /*
    - * Parse out the Type-Length-Value triples as they're found.
    - */
    - for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
    - guint16 type, length;
    - int endpos;
    - int curpos;
    -
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    - curpos = byte_stream_curpos(bs);
    - endpos = curpos + MIN(length, byte_stream_bytes_left(bs));
    -
    - if (type == 0x0001) {
    - /*
    - * User flags
    - *
    - * Specified as any of the following ORed together:
    - * 0x0001 Unconfirmed account
    - * 0x0002 Unknown bit 2
    - * 0x0004 AOL Main Service user
    - * 0x0008 Unknown bit 4
    - * 0x0010 Free (AIM) user
    - * 0x0020 Away
    - * 0x0040 ICQ user (AIM bit also set)
    - * 0x0080 Mobile device
    - * 0x0400 Bot (like ActiveBuddy)
    - */
    - outinfo->flags = byte_stream_get16(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
    -
    - } else if (type == 0x0002) {
    - /*
    - * Account creation time
    - *
    - * The time/date that the user originally registered for
    - * the service, stored in time_t format.
    - *
    - * I'm not sure how this differs from type 5 ("member
    - * since").
    - *
    - * Note: This is the field formerly known as "member
    - * since". All these years and I finally found out
    - * that I got the name wrong.
    - */
    - outinfo->createtime = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME;
    -
    - } else if (type == 0x0003) {
    - /*
    - * On-Since date
    - *
    - * The time/date that the user started their current
    - * session, stored in time_t format.
    - */
    - outinfo->onlinesince = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
    -
    - } else if (type == 0x0004) {
    - /*
    - * Idle time
    - *
    - * Number of minutes since the user actively used the
    - * service.
    - *
    - * Note that the client tells the server when to start
    - * counting idle times, so this may or may not be
    - * related to reality.
    - */
    - outinfo->idletime = byte_stream_get16(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
    -
    - } else if (type == 0x0005) {
    - /*
    - * Member since date
    - *
    - * The time/date that the user originally registered for
    - * the service, stored in time_t format.
    - *
    - * This is sometimes sent instead of type 2 ("account
    - * creation time"), particularly in the self-info.
    - * And particularly for ICQ?
    - */
    - outinfo->membersince = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
    -
    - } else if (type == 0x0006) {
    - /*
    - * ICQ Online Status
    - *
    - * ICQ's Away/DND/etc "enriched" status. Some decoding
    - * of values done by Scott <darkagl@pcnet.com>
    - */
    - byte_stream_get16(bs);
    - outinfo->icqinfo.status = byte_stream_get16(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
    -
    - } else if (type == 0x0008) {
    - /*
    - * Client type, or some such.
    - */
    -
    - } else if (type == 0x000a) {
    - /*
    - * ICQ User IP Address
    - *
    - * Ahh, the joy of ICQ security.
    - */
    - outinfo->icqinfo.ipaddr = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
    -
    - } else if (type == 0x000c) {
    - /*
    - * Random crap containing the IP address,
    - * apparently a port number, and some Other Stuff.
    - *
    - * Format is:
    - * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
    - */
    - byte_stream_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
    - outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
    -
    - } else if (type == 0x000d) {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const char *mood;
    -
    - /*
    - * OSCAR Capability information
    - */
    - outinfo->capabilities |= aim_locate_getcaps(od, bs, length);
    - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
    - byte_stream_setpos(bs, curpos);
    -
    - mood = aim_receive_custom_icon(od, bs, length);
    - if (mood)
    - purple_protocol_got_user_status(account, outinfo->bn, "mood",
    - PURPLE_MOOD_NAME, mood,
    - NULL);
    - else
    - purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood");
    -
    - } else if (type == 0x000e) {
    - /*
    - * AOL capability information
    - */
    -
    - } else if ((type == 0x000f) || (type == 0x0010)) {
    - /*
    - * Type = 0x000f: Session Length. (AIM)
    - * Type = 0x0010: Session Length. (AOL)
    - *
    - * The duration, in seconds, of the user's current
    - * session.
    - *
    - * Which TLV type this comes in depends on the
    - * service the user is using (AIM or AOL).
    - */
    - outinfo->sessionlen = byte_stream_get32(bs);
    - outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
    -
    - } else if (type == 0x0014) {
    - /*
    - * My instance number.
    - */
    - byte_stream_get8(bs);
    -
    - } else if (type == 0x0019) {
    - /*
    - * OSCAR short capability information. A shortened
    - * form of the normal capabilities.
    - */
    - outinfo->capabilities |= aim_locate_getcaps_short(od, bs, length);
    - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
    -
    - } else if (type == 0x001a) {
    - /*
    - * Type = 0x001a
    - *
    - * AOL short capability information. A shortened
    - * form of the normal capabilities.
    - */
    -
    - } else if (type == 0x001b) {
    - /*
    - * Encryption certification MD5 checksum.
    - */
    -
    - } else if (type == 0x001d) {
    - /*
    - * Buddy icon information and status/available messages.
    - *
    - * This almost seems like the AIM protocol guys gave
    - * the iChat guys a Type, and the iChat guys tried to
    - * cram as much cool shit into it as possible. Then
    - * the Windows AIM guys were like, "hey, that's
    - * pretty neat, let's copy those prawns."
    - *
    - * In that spirit, this can contain a custom message,
    - * kind of like an away message, but you're not away
    - * (it's called an "available" message). Or it can
    - * contain information about the buddy icon the user
    - * has stored on the server.
    - */
    - guint16 type2;
    - guint8 number2, length2;
    - int endpos2;
    -
    - /*
    - * Continue looping as long as we're able to read type2,
    - * number2, and length2.
    - */
    - while (byte_stream_curpos(bs) + 4 <= endpos) {
    - type2 = byte_stream_get16(bs);
    - number2 = byte_stream_get8(bs);
    - length2 = byte_stream_get8(bs);
    -
    - endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_bytes_left(bs));
    -
    - switch (type2) {
    - case 0x0000: { /* This is an official buddy icon? */
    - /* This is always 5 bytes of "0x02 01 d2 04 72"? */
    - } break;
    -
    - case 0x0001: { /* A buddy icon checksum */
    - if ((length2 > 0) && ((number2 == 0x00) || (number2 == 0x01))) {
    - g_free(outinfo->iconcsum);
    - outinfo->iconcsumtype = number2;
    - outinfo->iconcsum = byte_stream_getraw(bs, length2);
    - outinfo->iconcsumlen = length2;
    - }
    - } break;
    -
    - case 0x0002: { /* A status/available message */
    - g_free(outinfo->status);
    - g_free(outinfo->status_encoding);
    - if (length2 >= 4) {
    - outinfo->status_len = byte_stream_get16(bs);
    - outinfo->status = byte_stream_getstr(bs, outinfo->status_len);
    - if (byte_stream_get16(bs) == 0x0001) { /* We have an encoding */
    - byte_stream_get16(bs);
    - outinfo->status_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
    - } else {
    - /* No explicit encoding, client should use UTF-8 */
    - outinfo->status_encoding = NULL;
    - }
    - } else {
    - byte_stream_advance(bs, length2);
    - outinfo->status_len = 0;
    - outinfo->status = g_strdup("");
    - outinfo->status_encoding = NULL;
    - }
    - } break;
    -
    - case 0x0009: { /* An iTunes Music Store link */
    - g_free(outinfo->itmsurl);
    - g_free(outinfo->itmsurl_encoding);
    - if (length2 >= 4) {
    - outinfo->itmsurl_len = byte_stream_get16(bs);
    - outinfo->itmsurl = byte_stream_getstr(bs, outinfo->itmsurl_len);
    - if (byte_stream_get16(bs) == 0x0001) {
    - /* We have an encoding */
    - byte_stream_get16(bs);
    - outinfo->itmsurl_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
    - } else {
    - /* No explicit encoding, client should use UTF-8 */
    - outinfo->itmsurl_encoding = NULL;
    - }
    - } else {
    - byte_stream_advance(bs, length2);
    - outinfo->itmsurl_len = 0;
    - outinfo->itmsurl = g_strdup("");
    - outinfo->itmsurl_encoding = NULL;
    - }
    - } break;
    -
    - case 0x000e: { /* ICQ mood */
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - char *icqmood;
    - gint32 i;
    - const char *mood = NULL;
    -
    - icqmood = byte_stream_getstr(bs, length2);
    -
    - /* icqmood = "" means X-Status
    - * with no mood icon. */
    - if (*icqmood) {
    - for (i = 0; icqmoods[i].icqmood; i++) {
    - if (purple_strequal(icqmood, icqmoods[i].icqmood)) {
    - mood = icqmoods[i].mood;
    - break; /* should only match once... */
    - }
    - }
    -
    - if (!mood)
    - purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood);
    - }
    - g_free(icqmood);
    -
    - if (mood)
    - purple_protocol_got_user_status(account, outinfo->bn, "mood",
    - PURPLE_MOOD_NAME, mood,
    - NULL);
    - else
    - purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood");
    - } break;
    - }
    -
    - /* Save ourselves. */
    - byte_stream_setpos(bs, endpos2);
    - }
    -
    - } else if (type == 0x001e) {
    - /*
    - * Always four bytes, but it doesn't look like an int.
    - */
    -
    - } else if (type == 0x001f) {
    - /*
    - * Upper bytes of user flags. Can be any size
    - *
    - * Seen on a buddy using DeadAIM. Data was 4 bytes:
    - * 0x00 00 00 10
    - */
    -
    - } else if (type == 0x0023) {
    - /*
    - * Last Buddy Feed update time, in seconds since the epoch.
    - */
    -
    - } else if (type == 0x0026) {
    - /*
    - * Time that the profile was set, in seconds since the epoch.
    - */
    -
    - } else if (type == 0x0027) {
    - /*
    - * Time that the away message was set, in seconds since the epoch.
    - */
    -
    - } else if (type == 0x002a) {
    - /*
    - * Country code based on GeoIP data.
    - */
    -
    - } else {
    -
    - /*
    - * Reaching here indicates that either AOL has
    - * added yet another TLV for us to deal with,
    - * or the parsing has gone Terribly Wrong.
    - *
    - * Either way, inform the owner and attempt
    - * recovery.
    - *
    - */
    -#ifdef LOG_UNKNOWN_TLV
    - purple_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
    - purple_debug_misc("oscar", "userinfo: bn =%s\n", outinfo->bn);
    - dumptlv(od, type, bs, length);
    -#endif
    - }
    -
    - /* Save ourselves. */
    - byte_stream_setpos(bs, endpos);
    - }
    -
    - aim_locate_adduserinfo(od, outinfo);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0001
    - */
    -static int
    -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_snac_t *snac2;
    - guint16 reason;
    - char *bn;
    -
    - snac2 = aim_remsnac(od, snac->id);
    - if (!snac2) {
    - purple_debug_misc("oscar", "locate error: received response from unknown request!\n");
    - return 0;
    - }
    -
    - if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
    - purple_debug_misc("oscar", "locate error: received response from invalid request! %d\n", snac2->family);
    - g_free(snac2->data);
    - g_free(snac2);
    - return 0;
    - }
    -
    - bn = snac2->data;
    - if (!bn) {
    - purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n");
    - g_free(snac2);
    - return 0;
    - }
    -
    - reason = byte_stream_get16(bs);
    -
    - oscar_user_info_display_error(od, reason, bn);
    -
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return 1;
    -}
    -
    -/*
    - * Subtype 0x0002
    - *
    - * Request Location services rights.
    - *
    - */
    -int
    -aim_locate_reqrights(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
    - return -EINVAL;
    -
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_REQRIGHTS);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0003
    - *
    - * Normally contains:
    - * t(0001) - short containing max profile length (value = 1024)
    - * t(0002) - short - unknown (value = 16) [max MIME type length?]
    - * t(0003) - short - unknown (value = 10)
    - * t(0004) - short - unknown (value = 2048) [ICQ only?]
    - */
    -static int
    -rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - GSList *tlvlist;
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - guint16 maxsiglen = 0;
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
    - maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, maxsiglen);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0004
    - *
    - * Gives BOS your profile.
    - *
    - * profile_encoding and awaymsg_encoding MUST be set if profile or
    - * away are set, respectively, and their value may or may not be
    - * restricted to a few choices. I am currently aware of:
    - *
    - * us-ascii Just that
    - * unicode-2-0 UTF-16BE
    - *
    - * profile_len and awaymsg_len MUST be set similarly, and they MUST
    - * be the length of their respective strings in bytes.
    - *
    - * To get the previous behavior of awaymsg == "" un-setting the away
    - * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
    - * obvious equivalent).
    - *
    - */
    -int
    -aim_locate_setprofile(OscarData *od,
    - const char *profile_encoding, const gchar *profile, const int profile_len,
    - const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    - char *encoding;
    - static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
    - return -EINVAL;
    -
    - if (!profile && !awaymsg)
    - return -EINVAL;
    -
    - if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
    - return -EINVAL;
    - }
    -
    - /* Build the packet first to get real length */
    - if (profile) {
    - encoding = g_strdup_printf(defencoding, profile_encoding);
    - aim_tlvlist_add_str(&tlvlist, 0x0001, encoding);
    - aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile);
    - g_free(encoding);
    - }
    -
    - /*
    - * So here's how this works:
    - * - You are away when you have a non-zero-length type 4 TLV stored.
    - * - You become unaway when you clear the TLV with a zero-length
    - * type 4 TLV.
    - * - If you do not send the type 4 TLV, your status does not change
    - * (that is, if you were away, you'll remain away).
    - */
    - if (awaymsg) {
    - if (awaymsg_len) {
    - encoding = g_strdup_printf(defencoding, awaymsg_encoding);
    - aim_tlvlist_add_str(&tlvlist, 0x0003, encoding);
    - aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg);
    - g_free(encoding);
    - } else
    - aim_tlvlist_add_noval(&tlvlist, 0x0004);
    - }
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0004 - Set your client's capabilities.
    - */
    -int
    -aim_locate_setcaps(OscarData *od, guint64 caps)
    -{
    - FlapConnection *conn;
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurplePresence *presence = purple_account_get_presence(account);
    - PurpleStatus *status = purple_presence_get_status(presence, "mood");
    - const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
    - return -EINVAL;
    -
    - aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood);
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/* Subtype 0x0006 */
    -static int
    -userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_userinfo_t *userinfo, *userinfo2;
    - GSList *tlvlist;
    - aim_tlv_t *tlv = NULL;
    -
    - userinfo = g_new0(aim_userinfo_t, 1);
    - aim_info_extract(od, bs, userinfo);
    - tlvlist = aim_tlvlist_read(bs);
    -
    - /* Profile will be 1 and 2 */
    - userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1);
    - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
    - userinfo->info = (char *)g_malloc(tlv->length);
    - memcpy(userinfo->info, tlv->value, tlv->length);
    - userinfo->info_len = tlv->length;
    - }
    -
    - /* Away message will be 3 and 4 */
    - userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1);
    - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
    - userinfo->away = (char *)g_malloc(tlv->length);
    - memcpy(userinfo->away, tlv->value, tlv->length);
    - userinfo->away_len = tlv->length;
    - }
    -
    - /* Caps will be 5 */
    - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
    - ByteStream cbs;
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const char *mood;
    -
    - byte_stream_init(&cbs, tlv->value, tlv->length);
    - userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length);
    - byte_stream_rewind(&cbs);
    - userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
    -
    - mood = aim_receive_custom_icon(od, &cbs, tlv->length);
    - if (mood)
    - purple_protocol_got_user_status(account, userinfo->bn, "mood",
    - PURPLE_MOOD_NAME, mood,
    - NULL);
    - else
    - purple_protocol_got_user_status_deactive(account, userinfo->bn, "mood");
    - }
    - aim_tlvlist_free(tlvlist);
    -
    - aim_locate_adduserinfo(od, userinfo);
    - userinfo2 = aim_locate_finduserinfo(od, userinfo->bn);
    - aim_info_free(userinfo);
    - g_free(userinfo);
    -
    - /* Show the info to the user */
    - oscar_user_info_display_aim(od, userinfo2);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0015 - Request the info of a user using the short method. This is
    - * what iChat uses. It normally is VERY leniently rate limited.
    - *
    - * @param bn The buddy name whose info you wish to request.
    - * @param flags The bitmask which specifies the type of info you wish to request.
    - * 0x00000001 - Info/profile.
    - * 0x00000002 - Away message.
    - * 0x00000004 - Capabilities.
    - * 0x00000008 - Certification.
    - * @return Return 0 if no errors, otherwise return the error number.
    - */
    -int
    -aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 4 + 1 + strlen(bn));
    - byte_stream_put32(&bs, flags);
    - byte_stream_put8(&bs, strlen(bn));
    - byte_stream_putstr(&bs, bn);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1);
    - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, snacid, &bs, FALSE);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0003)
    - return rights(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0006)
    - return userinfo(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -static void
    -locate_shutdown(OscarData *od, aim_module_t *mod)
    -{
    - aim_userinfo_t *del;
    -
    - while (od->locate.userinfo) {
    - del = od->locate.userinfo;
    - od->locate.userinfo = od->locate.userinfo->next;
    - aim_info_free(del);
    - g_free(del);
    - }
    -}
    -
    -int
    -locate_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_LOCATE;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "locate", sizeof(mod->name));
    - mod->snachandler = snachandler;
    - mod->shutdown = locate_shutdown;
    -
    - return 0;
    -}
    -
    -const char*
    -icq_get_custom_icon_description(const char *mood)
    -{
    - int i;
    -
    - if (!(mood && *mood))
    - return NULL;
    -
    - for (i = 0; icq_custom_icons[i].mood; i++) {
    - /* We check that description is not NULL to exclude
    - * duplicates, like the typing duplicate. */
    - if (icq_purple_moods[i].description &&
    - purple_strequal(mood, icq_custom_icons[i].mood)) {
    - return icq_purple_moods[i].description;
    - }
    - }
    -
    - return NULL;
    -}
    -
    -guint8*
    -icq_get_custom_icon_data(const char *mood)
    -{
    - int i;
    -
    - if (!(mood && *mood))
    - return NULL;
    -
    - for (i = 0; icq_custom_icons[i].mood; i++) {
    - /* We check that description is not NULL to exclude
    - * duplicates, like the typing duplicate. */
    - if (icq_purple_moods[i].description &&
    - purple_strequal(mood, icq_custom_icons[i].mood)) {
    - return (guint8 *)icq_custom_icons[i].data;
    - }
    - }
    - return NULL;
    -}
    -
    -PurpleMood*
    -icq_get_purple_moods(PurpleAccount *account)
    -{
    - return icq_purple_moods;
    -}
    --- a/libpurple/protocols/oscar/family_oservice.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,994 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0001 - This is a very special group. All connections support
    - * this group, as it does some particularly good things (like rate limiting).
    - */
    -
    -#include "oscar.h"
    -
    -/*
    - * Each time we make a FLAP connection to an oscar server the server gives
    - * us a list of rate classes. Each rate class has different properties for
    - * how frequently we can send SNACs in that rate class before we become
    - * throttled or disconnected.
    - *
    - * The server also gives us a list of every available SNAC and tells us which
    - * rate class it's in. There are a lot of different SNACs, so this list can be
    - * fairly large. One important characteristic of these rate classes is that
    - * currently (and since at least 2004) most SNACs are in the same rate class.
    - *
    - * One optimization we can do to save memory is to only keep track of SNACs
    - * that are in classes other than this default rate class. So if we try to
    - * look up a SNAC and it's not in our hash table then we can assume that it's
    - * in the default rate class.
    - */
    -#define OSCAR_DEFAULT_RATECLASS 1
    -
    -/* Subtype 0x0002 - Client Online */
    -void
    -aim_srv_clientready(OscarData *od, FlapConnection *conn)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *cur;
    -
    - byte_stream_new(&bs, 1142);
    -
    - /*
    - * Send only the tool versions that the server cares about (that it
    - * marked as supporting in the server ready SNAC).
    - */
    - for (cur = conn->groups; cur != NULL; cur = cur->next)
    - {
    - aim_module_t *mod;
    -
    - if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
    - {
    - byte_stream_put16(&bs, mod->family);
    - byte_stream_put16(&bs, mod->version);
    - byte_stream_put16(&bs, mod->toolid);
    - byte_stream_put16(&bs, mod->toolversion);
    - }
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Subtype 0x0003 - Host Online
    - *
    - * See comments in conn.c about how the group associations are supposed
    - * to work, and how they really work.
    - *
    - * This info probably doesn't even need to make it to the client.
    - *
    - * We don't actually call the client here. This starts off the connection
    - * initialization routine required by all AIM connections. The next time
    - * the client is called is the CONNINITDONE callback, which should be
    - * shortly after the rate information is acknowledged.
    - *
    - */
    -static int
    -hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int group;
    -
    - while (byte_stream_bytes_left(bs))
    - {
    - group = byte_stream_get16(bs);
    - conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group));
    - }
    -
    - /*
    - * Next step is in the Host Versions handler.
    - *
    - * Note that we must send this before we request rates, since
    - * the format of the rate information depends on the versions we
    - * give it.
    - *
    - */
    - aim_srv_setversions(od, conn);
    -
    - return 1;
    -}
    -
    -/* Subtype 0x0004 - Service request */
    -void
    -aim_srv_requestnew(OscarData *od, guint16 serviceid)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
    - if(!conn)
    - return;
    -
    - byte_stream_new(&bs, 6);
    -
    - byte_stream_put16(&bs, serviceid);
    -
    - if (od->use_ssl)
    - /* Request SSL Connection */
    - aim_tlvlist_add_noval(&tlvlist, 0x008c);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Join a room of name roomname. This is the first step to joining an
    - * already created room. It's basically a Service Request for
    - * family 0x000e, with a little added on to specify the exchange and room
    - * name.
    - */
    -int
    -aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    - struct chatsnacinfo csi;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
    - if (!conn || !roomname || roomname[0] == '\0')
    - return -EINVAL;
    -
    - byte_stream_new(&bs, 506);
    -
    - memset(&csi, 0, sizeof(csi));
    - csi.exchange = exchange;
    - g_strlcpy(csi.name, roomname, sizeof(csi.name));
    - csi.instance = instance;
    -
    - /*
    - * Requesting service chat (0x000e)
    - */
    - byte_stream_put16(&bs, 0x000e);
    -
    - aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
    -
    - if (od->use_ssl)
    - /* Request SSL Connection */
    - aim_tlvlist_add_noval(&tlvlist, 0x008c);
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/* Subtype 0x0005 - Redirect */
    -static int
    -redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - struct aim_redirect_data redir;
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - aim_snac_t *origsnac = NULL;
    - int ret = 0;
    -
    - memset(&redir, 0, sizeof(redir));
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
    - !aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
    - !aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
    - aim_tlvlist_free(tlvlist);
    - return 0;
    - }
    -
    - redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
    - redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    - redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
    - redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
    - redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1);
    - redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1);
    -
    - /* Fetch original SNAC so we can get csi if needed */
    - origsnac = aim_remsnac(od, snac->id);
    -
    - if ((redir.group == SNAC_FAMILY_CHAT) && origsnac) {
    - struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
    -
    - redir.chat.exchange = csi->exchange;
    - redir.chat.room = csi->name;
    - redir.chat.instance = csi->instance;
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, &redir);
    -
    - g_free((void *)redir.ip);
    - g_free((void *)redir.cookie);
    - g_free((void *)redir.ssl_cert_cn);
    -
    - if (origsnac)
    - g_free(origsnac->data);
    - g_free(origsnac);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0006 - Request Rate Information. */
    -void
    -aim_srv_reqrates(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
    -}
    -
    -/*
    - * OSCAR defines several 'rate classes'. Each class has separate
    - * rate limiting properties (limit level, alert level, disconnect
    - * level, etc), and a set of SNAC family/type pairs associated with
    - * it. The rate classes, their limiting properties, and the definitions
    - * of which SNACs belong to which class are defined in the
    - * Rate Response packet at login to each host.
    - *
    - * Logically, all rate offenses within one class count against further
    - * offenses for other SNACs in the same class (ie, sending messages
    - * too fast will limit the number of user info requests you can send,
    - * since those two SNACs are in the same rate class).
    - *
    - * Since the rate classes are defined dynamically at login, the values
    - * below may change. But they seem to be fairly constant.
    - *
    - * Currently, BOS defines five rate classes, with the commonly used
    - * members as follows...
    - *
    - * Rate class 0x0001:
    - * - Everything thats not in any of the other classes
    - *
    - * Rate class 0x0002:
    - * - Buddy list add/remove
    - * - Permit list add/remove
    - * - Deny list add/remove
    - *
    - * Rate class 0x0003:
    - * - User information requests
    - * - Outgoing ICBMs
    - *
    - * Rate class 0x0004:
    - * - A few unknowns: 2/9, 2/b, and f/2
    - *
    - * Rate class 0x0005:
    - * - Chat room create
    - * - Outgoing chat ICBMs
    - *
    - * The only other thing of note is that class 5 (chat) has slightly looser
    - * limiting properties than class 3 (normal messages). But thats just a
    - * small bit of trivia for you.
    - *
    - * The last thing that needs to be learned about the rate limiting
    - * system is how the actual numbers relate to the passing of time. This
    - * seems to be a big mystery.
    - *
    - * See joscar's javadoc for the RateClassInfo class for a great
    - * explanation. You might be able to find it at
    - * http://dscoder.com/RateClassInfo.html
    - */
    -
    -static struct rateclass *
    -rateclass_find(GSList *rateclasses, guint16 id)
    -{
    - GSList *tmp;
    -
    - for (tmp = rateclasses; tmp != NULL; tmp = tmp->next)
    - {
    - struct rateclass *rateclass;
    - rateclass = tmp->data;
    - if (rateclass->classid == id)
    - return rateclass;
    - }
    -
    - return NULL;
    -}
    -
    -/* Subtype 0x0007 - Rate Parameters */
    -static int
    -rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guint16 numclasses, i;
    - aim_rxcallback_t userfunc;
    -
    - /*
    - * First are the parameters for each rate class.
    - */
    - numclasses = byte_stream_get16(bs);
    - for (i = 0; i < numclasses; i++)
    - {
    - struct rateclass *rateclass;
    - guint32 delta;
    - struct timeval now;
    -
    - gettimeofday(&now, NULL);
    - rateclass = g_new(struct rateclass, 1);
    -
    - rateclass->classid = byte_stream_get16(bs);
    - rateclass->windowsize = byte_stream_get32(bs);
    - rateclass->clear = byte_stream_get32(bs);
    - rateclass->alert = byte_stream_get32(bs);
    - rateclass->limit = byte_stream_get32(bs);
    - rateclass->disconnect = byte_stream_get32(bs);
    - rateclass->current = byte_stream_get32(bs);
    - rateclass->max = byte_stream_get32(bs);
    - if (mod->version >= 3) {
    - delta = byte_stream_get32(bs);
    - rateclass->dropping_snacs = byte_stream_get8(bs);
    - } else {
    - delta = 0;
    - rateclass->dropping_snacs = 0;
    - }
    -
    - rateclass->last.tv_sec = now.tv_sec - delta / 1000;
    - rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
    -
    - conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass);
    -
    - if (rateclass->classid == OSCAR_DEFAULT_RATECLASS)
    - conn->default_rateclass = rateclass;
    - }
    - conn->rateclasses = g_slist_reverse(conn->rateclasses);
    -
    - /*
    - * Then the members of each class.
    - */
    - for (i = 0; i < numclasses; i++)
    - {
    - guint16 classid, count;
    - struct rateclass *rateclass;
    - int j;
    -
    - classid = byte_stream_get16(bs);
    - count = byte_stream_get16(bs);
    -
    - if (classid == OSCAR_DEFAULT_RATECLASS) {
    - /*
    - * Don't bother adding these SNACs to the hash table. See the
    - * comment for OSCAR_DEFAULT_RATECLASS at the top of this file.
    - */
    - byte_stream_advance(bs, 4 * count);
    - continue;
    - }
    -
    - rateclass = rateclass_find(conn->rateclasses, classid);
    -
    - for (j = 0; j < count; j++)
    - {
    - guint16 group, subtype;
    -
    - group = byte_stream_get16(bs);
    - subtype = byte_stream_get16(bs);
    -
    - if (rateclass != NULL)
    - g_hash_table_insert(conn->rateclass_members,
    - GUINT_TO_POINTER((group << 16) + subtype),
    - rateclass);
    - }
    - }
    -
    - /*
    - * We don't pass the rate information up to the client, as it really
    - * doesn't care. The information is stored in the connection, however
    - * so that we can do rate limiting management when sending SNACs.
    - */
    -
    - /*
    - * Subscribe to rate change information for all rate classes.
    - */
    - aim_srv_rates_addparam(od, conn);
    -
    - /*
    - * Finally, tell the client it's ready to go...
    - */
    - if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
    - userfunc(od, conn, frame);
    -
    - return 1;
    -}
    -
    -/* Subtype 0x0008 - Add Rate Parameter */
    -void
    -aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tmp;
    -
    - byte_stream_new(&bs, 502);
    -
    - for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
    - {
    - struct rateclass *rateclass;
    - rateclass = tmp->data;
    - byte_stream_put16(&bs, rateclass->classid);
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/* Subtype 0x000a - Rate Change */
    -static int
    -ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guint16 code, classid;
    - struct rateclass *rateclass;
    - guint32 delta;
    - struct timeval now;
    - static const char *codes[5] = {
    - "invalid",
    - "change",
    - "warning",
    - "limit",
    - "limit cleared",
    - };
    -
    - gettimeofday(&now, NULL);
    - code = byte_stream_get16(bs);
    - classid = byte_stream_get16(bs);
    -
    - rateclass = rateclass_find(conn->rateclasses, classid);
    - if (rateclass == NULL)
    - /* This should never really happen */
    - return 0;
    -
    - rateclass->windowsize = byte_stream_get32(bs);
    - rateclass->clear = byte_stream_get32(bs);
    - rateclass->alert = byte_stream_get32(bs);
    - rateclass->limit = byte_stream_get32(bs);
    - rateclass->disconnect = byte_stream_get32(bs);
    - rateclass->current = byte_stream_get32(bs);
    - rateclass->max = byte_stream_get32(bs);
    - if (mod->version >= 3) {
    - delta = byte_stream_get32(bs);
    - rateclass->dropping_snacs = byte_stream_get8(bs);
    - } else {
    - delta = 0;
    - rateclass->dropping_snacs = 0;
    - }
    -
    - rateclass->last.tv_sec = now.tv_sec - delta / 1000;
    - rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
    -
    - purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, "
    - "maxavg = %u, alert at %u, clear warning at %u, limit at %u, "
    - "disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
    - (code < 5) ? codes[code] : codes[0], rateclass->classid,
    - rateclass->current, rateclass->max, rateclass->alert,
    - rateclass->clear, rateclass->limit, rateclass->disconnect,
    - delta, rateclass->dropping_snacs, rateclass->windowsize);
    -
    - if (code == AIM_RATE_CODE_LIMIT) {
    - purple_debug_warning("oscar", "The last action you attempted "
    - "could not be performed because you are over the rate "
    - "limit. Please wait 10 seconds and try again.\n");
    - }
    -
    - return 1;
    -}
    -
    -/*
    - * How Migrations work.
    - *
    - * The server sends a Server Pause message, which the client should respond to
    - * with a Server Pause Ack, which contains the families it needs on this
    - * connection. The server will send a Migration Notice with an IP address, and
    - * then disconnect. Next the client should open the connection and send the
    - * cookie. Repeat the normal login process and pretend this never happened.
    - *
    - * The Server Pause contains no data.
    - *
    - */
    -
    -/* Subtype 0x000b - Service Pause */
    -static int
    -serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x000d - Service Resume */
    -static int
    -serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x000e - Request self-info */
    -void
    -aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
    -{
    - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
    -}
    -
    -/* Subtype 0x000f - Self User Info */
    -static int
    -selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - aim_userinfo_t userinfo;
    -
    - aim_info_extract(od, bs, &userinfo);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, &userinfo);
    -
    - aim_info_free(&userinfo);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0010 - Evil Notification */
    -static int
    -evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 newevil;
    - aim_userinfo_t userinfo;
    -
    - memset(&userinfo, 0, sizeof(aim_userinfo_t));
    -
    - newevil = byte_stream_get16(bs);
    -
    - if (byte_stream_bytes_left(bs))
    - aim_info_extract(od, bs, &userinfo);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, newevil, &userinfo);
    -
    - aim_info_free(&userinfo);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0011 - Idle Notification
    - *
    - * Should set your current idle time in seconds. Note that this should
    - * never be called consecutively with a non-zero idle time. That makes
    - * OSCAR do funny things. Instead, just set it once you go idle, and then
    - * call it again with zero when you're back.
    - *
    - */
    -void
    -aim_srv_setidle(OscarData *od, guint32 idletime)
    -{
    - FlapConnection *conn;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
    - if(!conn)
    - return;
    -
    - aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
    -}
    -
    -/*
    - * Subtype 0x0012 - Service Migrate
    - *
    - * This is the final SNAC sent on the original connection during a migration.
    - * It contains the IP and cookie used to connect to the new server, and
    - * optionally a list of the SNAC groups being migrated.
    - *
    - */
    -static int
    -migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - int ret = 0;
    - guint16 groupcount, i;
    - GSList *tlvlist;
    - char *ip = NULL;
    - aim_tlv_t *cktlv;
    -
    - /*
    - * Apparently there's some fun stuff that can happen right here. The
    - * migration can actually be quite selective about what groups it
    - * moves to the new server. When not all the groups for a connection
    - * are migrated, or they are all migrated but some groups are moved
    - * to a different server than others, it is called a bifurcated
    - * migration.
    - *
    - * Let's play dumb and not support that.
    - *
    - */
    - groupcount = byte_stream_get16(bs);
    - for (i = 0; i < groupcount; i++) {
    - guint16 group;
    -
    - group = byte_stream_get16(bs);
    -
    - purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
    - }
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    -
    - cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL);
    -
    - aim_tlvlist_free(tlvlist);
    - g_free(ip);
    -
    - return ret;
    -}
    -
    -/* Subtype 0x0013 - Message of the Day */
    -static int
    -motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - char *msg = NULL;
    - int ret = 0;
    - GSList *tlvlist;
    - guint16 id;
    -
    - /*
    - * Code.
    - *
    - * Valid values:
    - * 1 Mandatory upgrade
    - * 2 Advisory upgrade
    - * 3 System bulletin
    - * 4 Nothing's wrong ("top o the world" -- normal)
    - * 5 Lets-break-something.
    - *
    - */
    - id = byte_stream_get16(bs);
    -
    - /*
    - * TLVs follow
    - */
    - tlvlist = aim_tlvlist_read(bs);
    -
    - msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, id, msg);
    -
    - g_free(msg);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0017 - Set client versions
    - *
    - * If you've seen the clientonline/clientready SNAC you're probably
    - * wondering what the point of this one is. And that point seems to be
    - * that the versions in the client online SNAC are sent too late for the
    - * server to be able to use them to change the protocol for the earlier
    - * login packets (client versions are sent right after Host Online is
    - * received, but client online versions aren't sent until quite a bit later).
    - * We can see them already making use of this by changing the format of
    - * the rate information based on what version of group 1 we advertise here.
    - *
    - */
    -void
    -aim_srv_setversions(OscarData *od, FlapConnection *conn)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *cur;
    -
    - byte_stream_new(&bs, 1142);
    -
    - /*
    - * Send only the versions that the server cares about (that it
    - * marked as supporting in the server ready SNAC).
    - */
    - for (cur = conn->groups; cur != NULL; cur = cur->next)
    - {
    - aim_module_t *mod;
    -
    - if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
    - {
    - byte_stream_put16(&bs, mod->family);
    - byte_stream_put16(&bs, mod->version);
    - }
    - }
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/* Subtype 0x0018 - Host versions */
    -static int
    -hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int vercount;
    - guint8 *versions;
    -
    - /* This is frivolous. (Thank you SmarterChild.) */
    - vercount = byte_stream_bytes_left(bs)/4;
    -
    - /* XXX: vercount probably should be used for reading versions. */
    - (void)vercount;
    - versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
    - g_free(versions);
    -
    - /*
    - * Now request rates.
    - */
    - aim_srv_reqrates(od, conn);
    -
    - return 1;
    -}
    -
    -/**
    - * Subtype 0x001e - Extended Status/Extra Info.
    - *
    - * These settings are transient, not server-stored (i.e. they only
    - * apply to this session, and must be re-set the next time you sign
    - * on).
    - *
    - * You can set your ICQ status (available, away, do not disturb,
    - * etc.), or whether your IP address should be hidden or not, or
    - * if your status is visible on ICQ web sites, and you can set
    - * your IP address info and what not.
    - *
    - * You can also set your "available" message. This is currently
    - * only supported by iChat, Purple and other 3rd party clients.
    - *
    - * These are the same TLVs seen in user info. You can
    - * also set 0x0008 and 0x000c.
    - */
    -int
    -aim_srv_setextrainfo(OscarData *od,
    - gboolean seticqstatus, guint32 icqstatus,
    - gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
    - return -EINVAL;
    -
    - if (seticqstatus)
    - {
    - aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
    - AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
    - }
    -
    - if (setstatusmsg)
    - {
    - size_t statusmsglen, itmsurllen;
    - ByteStream tmpbs;
    -
    - statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
    - itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
    -
    - byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
    - byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
    - byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
    -
    - aim_tlvlist_add_raw(&tlvlist, 0x001d,
    - byte_stream_curpos(&tmpbs), tmpbs.data);
    - byte_stream_destroy(&tmpbs);
    - }
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    -
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/* Send dummy DC (direct connect) information to the server.
    - * Direct connect is ICQ's counterpart for AIM's DirectIM,
    - * as far as I can tell. Anyway, we don't support it;
    - * the reason to send this packet is that some clients
    - * (Miranda, QIP) won't send us channel 2 ICBM messages
    - * unless we specify DC version >= 8.
    - *
    - * See #12044 for more information.
    - */
    -void
    -aim_srv_set_dc_info(OscarData *od)
    -{
    - FlapConnection *conn;
    -
    - ByteStream bs, tlv0c;
    - aim_snacid_t snacid;
    - GSList *tlvlist = NULL;
    -
    - /* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv.
    - * Kopete sends a dummy DC info, too, so I just copied the values from them.
    - */
    - byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */
    - byte_stream_put16(&tlv0c, 8); /* DC version */
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x50);
    - byte_stream_put32(&tlv0c, 0x3);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put32(&tlv0c, 0x0);
    - byte_stream_put16(&tlv0c, 0x0);
    - aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data);
    - byte_stream_destroy(&tlv0c);
    -
    - byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
    - aim_tlvlist_write(&bs, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - g_warn_if_fail(conn != NULL);
    - if (conn) {
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE,
    - 0x001e, snacid, &bs);
    - }
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Subtype 0x0021 - Receive our extended status
    - *
    - * This is used for iChat's "available" messages, and maybe ICQ extended
    - * status messages? It's also used to tell the client whether or not it
    - * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
    - */
    -static int
    -aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - guint16 type = byte_stream_get16(bs);
    - if (type == 0x0000 || type == 0x0001) {
    - /* buddy icon checksum */
    - /* not sure what the difference between 1 and 0 is */
    - guint8 flags = byte_stream_get8(bs);
    - guint8 length = byte_stream_get8(bs);
    - guint8 *md5 = byte_stream_getraw(bs, length);
    -
    - if ((flags == 0x00) || (flags == 0x41)) {
    - if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
    - od->iconconnecting = TRUE;
    - od->set_icon = TRUE;
    - aim_srv_requestnew(od, SNAC_FAMILY_BART);
    - } else {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurpleImage *img = purple_buddy_icons_find_account_icon(account);
    - if (img == NULL) {
    - aim_ssi_delicon(od);
    - } else {
    -
    - purple_debug_info("oscar",
    - "Uploading icon to icon server\n");
    - aim_bart_upload(od,
    - purple_image_get_data(img),
    - purple_image_get_data_size(img));
    - g_object_unref(img);
    - }
    - }
    - } else if (flags == 0x81) {
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - PurpleImage *img = purple_buddy_icons_find_account_icon(account);
    - if (img == NULL)
    - aim_ssi_delicon(od);
    - else {
    - aim_ssi_seticon(od, md5, length);
    - g_object_unref(img);
    - }
    - }
    -
    - g_free(md5);
    - }
    -
    - return 0;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0003)
    - return hostonline(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0005)
    - return redirect(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0007)
    - return rateresp(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000a)
    - return ratechange(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000b)
    - return serverpause(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000d)
    - return serverresume(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x000f)
    - return selfinfo(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0010)
    - return evilnotify(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0012)
    - return migrate(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0013)
    - return motd(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0018)
    - return hostversions(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0021)
    - return aim_parse_extstatus(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int service_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_OSERVICE;
    - mod->version = 0x0003;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "oservice", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_popup.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,84 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x0008 - Popups.
    - *
    - * Popups are just what it sounds like. They're a way for the server to
    - * open up an informative box on the client's screen.
    - */
    -
    -#include <oscar.h>
    -
    -/*
    - * This is all there is to it.
    - *
    - * The message is probably HTML.
    - *
    - */
    -static int
    -parsepopup(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - aim_rxcallback_t userfunc;
    - GSList *tlvlist;
    - int ret = 0;
    - char *msg, *url;
    - guint16 width, height, delay;
    -
    - tlvlist = aim_tlvlist_read(bs);
    -
    - msg = aim_tlv_getstr(tlvlist, 0x0001, 1);
    - url = aim_tlv_getstr(tlvlist, 0x0002, 1);
    - width = aim_tlv_get16(tlvlist, 0x0003, 1);
    - height = aim_tlv_get16(tlvlist, 0x0004, 1);
    - delay = aim_tlv_get16(tlvlist, 0x0005, 1);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, msg, url, width, height, delay);
    -
    - aim_tlvlist_free(tlvlist);
    - g_free(msg);
    - g_free(url);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0002)
    - return parsepopup(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -popups_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_POPUP;
    - mod->version = 0x0001;
    - mod->toolid = 0x0104;
    - mod->toolversion = 0x0001;
    - mod->flags = 0;
    - strncpy(mod->name, "popup", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_stats.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,64 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x000b - Statistics.
    - *
    - */
    -
    -#include <oscar.h>
    -
    -static int
    -reportinterval(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - guint16 interval;
    -
    - interval = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, interval);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0002)
    - return reportinterval(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -stats_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_STATS;
    - mod->version = 0x0001;
    - mod->toolid = 0x0104;
    - mod->toolversion = 0x0001;
    - mod->flags = 0;
    - strncpy(mod->name, "stats", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/family_userlookup.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,156 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Family 0x000a - User Search.
    - *
    - * TODO: Add aim_usersearch_name()
    - *
    - */
    -
    -#include "oscar.h"
    -
    -/*
    - * Subtype 0x0001
    - *
    - * XXX can this be integrated with the rest of the error handling?
    - */
    -static int error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - aim_rxcallback_t userfunc;
    - aim_snac_t *snac2;
    -
    - /* XXX the modules interface should have already retrieved this for us */
    - if (!(snac2 = aim_remsnac(od, snac->id))) {
    - purple_debug_misc("oscar", "search error: couldn't get a snac for 0x%08x\n", snac->id);
    - return 0;
    - }
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, snac2->data /* address */);
    -
    - /* XXX freesnac()? */
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - return ret;
    -}
    -
    -/*
    - * Subtype 0x0002
    - *
    - */
    -int aim_search_address(OscarData *od, const char *address)
    -{
    - FlapConnection *conn;
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - conn = flap_connection_findbygroup(od, SNAC_FAMILY_USERLOOKUP);
    -
    - if (!conn || !address)
    - return -EINVAL;
    -
    - byte_stream_new(&bs, strlen(address));
    -
    - byte_stream_putstr(&bs, address);
    -
    - snacid = aim_cachesnac(od, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, address, strlen(address)+1);
    - flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -
    - return 0;
    -}
    -
    -/*
    - * Subtype 0x0003
    - *
    - */
    -static int reply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int j = 0, m, ret = 0;
    - GSList *tlvlist;
    - char *cur = NULL, *buf = NULL;
    - aim_rxcallback_t userfunc;
    - aim_snac_t *snac2;
    - const char *searchaddr = NULL;
    -
    - if ((snac2 = aim_remsnac(od, snac->id)))
    - searchaddr = (const char *)snac2->data;
    -
    - tlvlist = aim_tlvlist_read(bs);
    - m = aim_tlvlist_count(tlvlist);
    -
    - /* XXX uhm.
    - * This is the only place that uses something other than 1 for the 3rd
    - * parameter to aim_tlv_gettlv_whatever().
    - */
    - while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m)
    - {
    - buf = g_realloc(buf, (j+1) * (MAXSNLEN+1));
    -
    - strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
    - g_free(cur);
    -
    - j++;
    - }
    - g_free(cur);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, searchaddr, j, buf);
    -
    - /* XXX freesnac()? */
    - if (snac2)
    - g_free(snac2->data);
    - g_free(snac2);
    -
    - g_free(buf);
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return error(od, conn, mod, frame, snac, bs);
    - else if (snac->subtype == 0x0003)
    - return reply(od, conn, mod, frame, snac, bs);
    -
    - return 0;
    -}
    -
    -int
    -search_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = SNAC_FAMILY_USERLOOKUP;
    - mod->version = 0x0001;
    - mod->toolid = 0x0110;
    - mod->toolversion = 0x0629;
    - mod->flags = 0;
    - strncpy(mod->name, "userlookup", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/flap_connection.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1117 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -
    -#include "eventloop.h"
    -#include "proxy.h"
    -
    -#ifndef _WIN32
    -#include <netdb.h>
    -#include <sys/socket.h>
    -#include <netinet/in.h>
    -#endif
    -
    -/**
    - * This sends a channel 1 SNAC containing the FLAP version.
    - * The FLAP version is sent by itself at the beginning of every
    - * connection to a FLAP server. It is always the very first
    - * packet sent by both the server and the client after the SYN,
    - * SYN/ACK, ACK handshake.
    - */
    -void
    -flap_connection_send_version(OscarData *od, FlapConnection *conn)
    -{
    - FlapFrame *frame;
    -
    - frame = flap_frame_new(od, 0x01, 4);
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - flap_connection_send(conn, frame);
    -}
    -
    -/**
    - * This sends a channel 1 FLAP containing the FLAP version and
    - * the authentication cookie. This is sent when connecting to
    - * any FLAP server after the initial connection to the auth
    - * server. It is always the very first packet sent by both the
    - * server and the client after the SYN, SYN/ACK, ACK handshake.
    - */
    -void
    -flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
    -{
    - FlapFrame *frame;
    - GSList *tlvlist = NULL;
    -
    - frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length);
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
    - aim_tlvlist_write(&frame->data, &tlvlist);
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -}
    -
    -void
    -flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_logins)
    -{
    - FlapFrame *frame;
    - GSList *tlvlist = NULL;
    -
    - frame = flap_frame_new(od, 0x01, 1152 + length);
    -
    - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
    - aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
    -
    - if (ci->clientstring != NULL)
    - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
    - else {
    - gchar *clientstring = oscar_get_clientstring();
    - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring);
    - g_free(clientstring);
    - }
    - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
    - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
    - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
    - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
    - aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x03));
    -
    - aim_tlvlist_write(&frame->data, &tlvlist);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - flap_connection_send(conn, frame);
    -}
    -
    -static struct rateclass *
    -flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype)
    -{
    - gconstpointer key;
    - gpointer rateclass;
    -
    - key = GUINT_TO_POINTER((family << 16) + subtype);
    - rateclass = g_hash_table_lookup(conn->rateclass_members, key);
    - if (rateclass != NULL)
    - return rateclass;
    -
    - return conn->default_rateclass;
    -}
    -
    -/*
    - * Attempt to calculate what our new current average would be if we
    - * were to send a SNAC in this rateclass at the given time.
    - */
    -static guint32
    -rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now)
    -{
    - unsigned long timediff; /* In milliseconds */
    - guint32 current;
    -
    - /* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */
    - timediff = (now->tv_sec - rateclass->last.tv_sec) * 1000 + (now->tv_usec - rateclass->last.tv_usec) / 1000;
    - current = ((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize;
    -
    - return MIN(current, rateclass->max);
    -}
    -
    -/*
    - * Attempt to send the contents of a given queue
    - *
    - * @return TRUE if the queue was completely emptied or was initially
    - * empty; FALSE if rate limiting prevented it from being
    - * emptied.
    - */
    -static gboolean flap_connection_send_snac_queue(FlapConnection *conn, struct timeval now, GQueue *queue)
    -{
    - while (!g_queue_is_empty(queue))
    - {
    - QueuedSnac *queued_snac;
    - struct rateclass *rateclass;
    -
    - queued_snac = g_queue_peek_head(queue);
    -
    - rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype);
    - if (rateclass != NULL)
    - {
    - guint32 new_current;
    -
    - new_current = rateclass_get_new_current(conn, rateclass, &now);
    -
    - if (rateclass->dropping_snacs || new_current <= rateclass->alert)
    - /* Not ready to send this SNAC yet--keep waiting. */
    - return FALSE;
    -
    - rateclass->current = new_current;
    - rateclass->last.tv_sec = now.tv_sec;
    - rateclass->last.tv_usec = now.tv_usec;
    - }
    -
    - flap_connection_send(conn, queued_snac->frame);
    - g_free(queued_snac);
    - g_queue_pop_head(queue);
    - }
    -
    - /* We emptied the queue */
    - return TRUE;
    -}
    -
    -static gboolean flap_connection_send_queued(gpointer data)
    -{
    - FlapConnection *conn;
    - struct timeval now;
    -
    - conn = data;
    - gettimeofday(&now, NULL);
    -
    - purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n",
    - (conn->queued_snacs ? conn->queued_snacs->length : 0),
    - (conn->queued_lowpriority_snacs ? conn->queued_lowpriority_snacs->length : 0),
    - conn);
    - if (!conn->queued_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_snacs)) {
    - if (!conn->queued_lowpriority_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_lowpriority_snacs)) {
    - /* Both queues emptied. */
    - conn->queued_timeout = 0;
    - return FALSE;
    - }
    - }
    -
    - /* We couldn't send all our SNACs. Keep trying */
    - return TRUE;
    -}
    -
    -/**
    - * This sends a channel 2 FLAP containing a SNAC. The SNAC family and
    - * subtype are looked up in the rate info for this connection, and if
    - * sending this SNAC will induce rate limiting then we delay sending
    - * of the SNAC by putting it into an outgoing holding queue.
    - *
    - * @param data The optional bytestream that makes up the data portion
    - * of this SNAC. For empty SNACs this should be NULL.
    - * @param high_priority If TRUE, the SNAC will be queued normally if
    - * needed. If FALSE, it will be queued separately, to be sent
    - * only if all high priority SNACs have been sent.
    - */
    -void
    -flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority)
    -{
    - FlapFrame *frame;
    - guint32 length;
    - gboolean enqueue = FALSE;
    - struct rateclass *rateclass;
    -
    - length = data != NULL ? data->offset : 0;
    -
    - frame = flap_frame_new(od, 0x02, 10 + length);
    - aim_putsnac(&frame->data, family, subtype, snacid);
    -
    - if (length > 0)
    - {
    - byte_stream_rewind(data);
    - byte_stream_putbs(&frame->data, data, length);
    - }
    -
    - if (conn->queued_timeout != 0)
    - enqueue = TRUE;
    - else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL)
    - {
    - struct timeval now;
    - guint32 new_current;
    -
    - gettimeofday(&now, NULL);
    - new_current = rateclass_get_new_current(conn, rateclass, &now);
    -
    - if (rateclass->dropping_snacs || new_current <= rateclass->alert)
    - {
    - purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn, new_current, rateclass->alert);
    -
    - enqueue = TRUE;
    - }
    - else
    - {
    - rateclass->current = new_current;
    - rateclass->last.tv_sec = now.tv_sec;
    - rateclass->last.tv_usec = now.tv_usec;
    - }
    - }
    -
    - if (enqueue)
    - {
    - /* We've been sending too fast, so delay this message */
    - QueuedSnac *queued_snac;
    -
    - queued_snac = g_new(QueuedSnac, 1);
    - queued_snac->family = family;
    - queued_snac->subtype = subtype;
    - queued_snac->frame = frame;
    -
    - if (high_priority) {
    - if (!conn->queued_snacs)
    - conn->queued_snacs = g_queue_new();
    - g_queue_push_tail(conn->queued_snacs, queued_snac);
    - } else {
    - if (!conn->queued_lowpriority_snacs)
    - conn->queued_lowpriority_snacs = g_queue_new();
    - g_queue_push_tail(conn->queued_lowpriority_snacs, queued_snac);
    - }
    -
    - if (conn->queued_timeout == 0)
    - conn->queued_timeout = g_timeout_add(500, flap_connection_send_queued, conn);
    -
    - return;
    - }
    -
    - flap_connection_send(conn, frame);
    -}
    -
    -void
    -flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data)
    -{
    - flap_connection_send_snac_with_priority(od, conn, family, subtype, snacid, data, TRUE);
    -}
    -
    -/**
    - * This sends an empty channel 4 FLAP. This is sent to signify
    - * that we're logging off. This shouldn't really be necessary--
    - * usually the AIM server will detect that the TCP connection has
    - * been destroyed--but it's good practice.
    - */
    -static void
    -flap_connection_send_close(OscarData *od, FlapConnection *conn)
    -{
    - FlapFrame *frame;
    -
    - frame = flap_frame_new(od, 0x04, 0);
    - flap_connection_send(conn, frame);
    -}
    -
    -/**
    - * This sends an empty channel 5 FLAP. This is used as a keepalive
    - * packet in FLAP connections. WinAIM 4.x and higher send these
    - * _every minute_ to keep the connection alive.
    - */
    -void
    -flap_connection_send_keepalive(OscarData *od, FlapConnection *conn)
    -{
    - FlapFrame *frame;
    -
    - frame = flap_frame_new(od, 0x05, 0);
    - flap_connection_send(conn, frame);
    -
    - /* clean out SNACs over 60sec old */
    - aim_cleansnacs(od, 60);
    -}
    -
    -/**
    - * Allocate a new empty connection structure.
    - *
    - * @param od The oscar session associated with this connection.
    - * @param type Type of connection to create
    - *
    - * @return Returns the new connection structure.
    - */
    -FlapConnection *
    -flap_connection_new(OscarData *od, int type)
    -{
    - FlapConnection *conn;
    -
    - conn = g_new0(FlapConnection, 1);
    - conn->od = od;
    - conn->buffer_outgoing = purple_circular_buffer_new(0);
    - conn->fd = -1;
    - conn->subtype = -1;
    - conn->type = type;
    - conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal);
    -
    - od->oscar_connections = g_slist_prepend(od->oscar_connections, conn);
    -
    - return conn;
    -}
    -
    -/**
    - * Close (but not free) a connection.
    - *
    - * This cancels any currently pending connection attempt,
    - * closes any open fd and frees the auth cookie.
    - *
    - * @param conn The connection to close.
    - */
    -void
    -flap_connection_close(OscarData *od, FlapConnection *conn)
    -{
    - if (conn->connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->connect_data);
    - conn->connect_data = NULL;
    - }
    -
    - if (conn->gsc != NULL && conn->gsc->connect_data != NULL)
    - {
    - purple_ssl_close(conn->gsc);
    - conn->gsc = NULL;
    - }
    -
    - if (conn->new_conn_data != NULL)
    - {
    - if (conn->type == SNAC_FAMILY_CHAT)
    - {
    - oscar_chat_destroy(conn->new_conn_data);
    - conn->new_conn_data = NULL;
    - }
    - }
    -
    - if ((conn->fd >= 0 || conn->gsc != NULL)
    - && conn->type == SNAC_FAMILY_LOCATE)
    - flap_connection_send_close(od, conn);
    -
    - if (conn->watcher_incoming != 0)
    - {
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - }
    -
    - if (conn->watcher_outgoing != 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - }
    -
    - if (conn->fd >= 0)
    - {
    - close(conn->fd);
    - conn->fd = -1;
    - }
    -
    - if (conn->gsc != NULL)
    - {
    - purple_ssl_close(conn->gsc);
    - conn->gsc = NULL;
    - }
    -
    - g_free(conn->buffer_incoming.data.data);
    - conn->buffer_incoming.data.data = NULL;
    -
    - g_object_unref(G_OBJECT(conn->buffer_outgoing));
    - conn->buffer_outgoing = NULL;
    -}
    -
    -/**
    - * Free a FlapFrame
    - *
    - * @param frame The frame to free.
    - */
    -static void
    -flap_frame_destroy(FlapFrame *frame)
    -{
    - g_free(frame->data.data);
    - g_free(frame);
    -}
    -
    -static gboolean
    -flap_connection_destroy_cb(gpointer data)
    -{
    - FlapConnection *conn;
    - OscarData *od;
    - PurpleAccount *account;
    - aim_rxcallback_t userfunc;
    -
    - conn = data;
    - /* Explicitly added for debugging #5927. Don't re-order this, only
    - * consider removing it.
    - */
    - purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn);
    -
    - od = conn->od;
    - account = purple_connection_get_account(od->gc);
    -
    - purple_debug_info("oscar", "Destroying oscar connection (%p) of "
    - "type 0x%04hx. Disconnect reason is %d\n", conn,
    - conn->type, conn->disconnect_reason);
    -
    - od->oscar_connections = g_slist_remove(od->oscar_connections, conn);
    -
    - if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
    - userfunc(od, conn, NULL, conn->disconnect_code, conn->error_message);
    -
    - /*
    - * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
    - * we should try to request one instead of disconnecting.
    - */
    - if (!purple_account_is_disconnecting(account) && ((od->oscar_connections == NULL)
    - || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE))))
    - {
    - /* No more FLAP connections! Sign off this PurpleConnection! */
    - gchar *tmp;
    - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
    -
    - if (conn->disconnect_code == 0x0001) {
    - reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
    - tmp = g_strdup(_("You have signed on from another location"));
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL, NULL, NULL);
    - } else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
    - tmp = g_strdup(_("Server closed the connection"));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
    - tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - conn->error_message);
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
    - tmp = g_strdup(_("Received invalid data on connection with server"));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
    - tmp = g_strdup_printf(_("Unable to connect: %s"),
    - conn->error_message);
    - else
    - /*
    - * We shouldn't print a message for some disconnect_reasons.
    - * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
    - */
    - tmp = NULL;
    -
    - if (tmp != NULL)
    - {
    - purple_connection_error(od->gc, reason, tmp);
    - g_free(tmp);
    - }
    - }
    -
    - flap_connection_close(od, conn);
    -
    - g_free(conn->error_message);
    - g_free(conn->cookie);
    -
    - /*
    - * Free conn->internal, if necessary
    - */
    - if (conn->type == SNAC_FAMILY_CHAT)
    - flap_connection_destroy_chat(od, conn);
    -
    - g_slist_free(conn->groups);
    - g_slist_free_full(conn->rateclasses, g_free);
    -
    - g_hash_table_destroy(conn->rateclass_members);
    -
    - if (conn->queued_snacs) {
    - while (!g_queue_is_empty(conn->queued_snacs))
    - {
    - QueuedSnac *queued_snac;
    - queued_snac = g_queue_pop_head(conn->queued_snacs);
    - flap_frame_destroy(queued_snac->frame);
    - g_free(queued_snac);
    - }
    - g_queue_free(conn->queued_snacs);
    - }
    -
    - if (conn->queued_lowpriority_snacs) {
    - while (!g_queue_is_empty(conn->queued_lowpriority_snacs))
    - {
    - QueuedSnac *queued_snac;
    - queued_snac = g_queue_pop_head(conn->queued_lowpriority_snacs);
    - flap_frame_destroy(queued_snac->frame);
    - g_free(queued_snac);
    - }
    - g_queue_free(conn->queued_lowpriority_snacs);
    - }
    -
    - if (conn->queued_timeout > 0)
    - g_source_remove(conn->queued_timeout);
    -
    - g_free(conn);
    -
    - return FALSE;
    -}
    -
    -/**
    - * See the comments for the parameters of
    - * flap_connection_schedule_destroy().
    - */
    -void
    -flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - g_source_remove(conn->destroy_timeout);
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - flap_connection_destroy_cb(conn);
    -}
    -
    -/**
    - * Schedule Purple to destroy the given FlapConnection as soon as we
    - * return control back to the program's main loop. We must do this
    - * if we want to destroy the connection but we are still using it
    - * for some reason.
    - *
    - * @param reason The reason for the disconnection.
    - * @param error_message A brief error message that gives more detail
    - * regarding the reason for the disconnecting. This should
    - * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION,
    - * in which case it should contain the value of g_strerror(errno),
    - * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it
    - * should contain the error_message passed back from the call
    - * to purple_proxy_connect().
    - */
    -void
    -flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - /* Already taken care of */
    - return;
    -
    - purple_debug_info("oscar", "Scheduling destruction of FLAP "
    - "connection %p of type 0x%04hx\n", conn, conn->type);
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - conn->destroy_timeout = g_timeout_add(0, flap_connection_destroy_cb, conn);
    -}
    -
    -/**
    - * In OSCAR, every connection has a set of SNAC groups associated
    - * with it. These are the groups that you can send over this connection
    - * without being guaranteed a "Not supported" SNAC error.
    - *
    - * The grand theory of things says that these associations transcend
    - * what libfaim calls "connection types" (conn->type). You can probably
    - * see the elegance here, but since I want to revel in it for a bit, you
    - * get to hear it all spelled out.
    - *
    - * So let us say that you have your core BOS connection running. One
    - * of your modules has just given you a SNAC of the group 0x0004 to send
    - * you. Maybe an IM destined for some twit in Greenland. So you start
    - * at the top of your connection list, looking for a connection that
    - * claims to support group 0x0004. You find one. Why, that neat BOS
    - * connection of yours can do that. So you send it on its way.
    - *
    - * Now, say, that fellow from Greenland has friends and they all want to
    - * meet up with you in a lame chat room. This has landed you a SNAC
    - * in the family 0x000e and you have to admit you're a bit lost. You've
    - * searched your connection list for someone who wants to make your life
    - * easy and deliver this SNAC for you, but there isn't one there.
    - *
    - * Here comes the good bit. Without even letting anyone know, particularly
    - * the module that decided to send this SNAC, and definitely not that twit
    - * in Greenland, you send out a service request. In this request, you have
    - * marked the need for a connection supporting group 0x000e. A few seconds
    - * later, you receive a service redirect with an IP address and a cookie in
    - * it. Great, you say. Now I have something to do. Off you go, making
    - * that connection. One of the first things you get from this new server
    - * is a message saying that indeed it does support the group you were looking
    - * for. So you continue and send rate confirmation and all that.
    - *
    - * Then you remember you had that SNAC to send, and now you have a means to
    - * do it, and you do, and everyone is happy. Except the Greenlander, who is
    - * still stuck in the bitter cold.
    - *
    - * Oh, and this is useful for building the Migration SNACs, too. In the
    - * future, this may help convince me to implement rate limit mitigation
    - * for real. We'll see.
    - *
    - * Just to make me look better, I'll say that I've known about this great
    - * scheme for quite some time now. But I still haven't convinced myself
    - * to make libfaim work that way. It would take a fair amount of effort,
    - * and probably some client API changes as well. (Whenever I don't want
    - * to do something, I just say it would change the client API. Then I
    - * instantly have a couple of supporters of not doing it.)
    - *
    - * Generally, addgroup is only called by the internal handling of the
    - * server ready SNAC. So if you want to do something before that, you'll
    - * have to be more creative. That is done rather early, though, so I don't
    - * think you have to worry about it. Unless you're me. I care deeply
    - * about such inane things.
    - *
    - */
    -
    -/**
    - * Find a FlapConnection that supports the given oscar
    - * family.
    - */
    -FlapConnection *
    -flap_connection_findbygroup(OscarData *od, guint16 group)
    -{
    - GSList *cur;
    -
    - for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
    - {
    - FlapConnection *conn;
    -
    - conn = cur->data;
    -
    - if (g_slist_find(conn->groups, GUINT_TO_POINTER(group)) != NULL) {
    - return conn;
    - }
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Locates a connection of the specified type in the
    - * specified session.
    - *
    - * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
    - *
    - * @param od The session to search.
    - * @param type The type of connection to look for.
    - *
    - * @return Returns the first connection found of the given target type,
    - * or NULL if none could be found.
    - */
    -FlapConnection *
    -flap_connection_getbytype(OscarData *od, int type)
    -{
    - GSList *cur;
    -
    - for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
    - {
    - FlapConnection *conn;
    - conn = cur->data;
    - if ((conn->type == type) && (conn->connected))
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -FlapConnection *
    -flap_connection_getbytype_all(OscarData *od, int type)
    -{
    - GSList *cur;
    -
    - for (cur = od->oscar_connections; cur; cur = cur->next)
    - {
    - FlapConnection *conn;
    - conn = cur->data;
    - if (conn->type == type)
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Allocate a new FLAP frame.
    - *
    - * @param channel The FLAP channel. This is almost always 2.
    - */
    -FlapFrame *
    -flap_frame_new(OscarData *od, guint16 channel, int datalen)
    -{
    - FlapFrame *frame;
    -
    - frame = g_new0(FlapFrame, 1);
    - frame->channel = channel;
    -
    - if (datalen > 0)
    - byte_stream_new(&frame->data, datalen);
    -
    - return frame;
    -}
    -
    -static void
    -parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
    -{
    - aim_module_t *cur;
    - aim_modsnac_t snac;
    -
    - if (byte_stream_bytes_left(&frame->data) < 10)
    - return;
    -
    - snac.family = byte_stream_get16(&frame->data);
    - snac.subtype = byte_stream_get16(&frame->data);
    - snac.flags = byte_stream_get16(&frame->data);
    - snac.id = byte_stream_get32(&frame->data);
    -
    - /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
    - if (snac.flags & 0x0001) {
    - /*
    - * This means the SNAC will be followed by another SNAC with
    - * related information. We don't need to do anything about
    - * this here.
    - */
    - }
    - if (snac.flags & 0x8000) {
    - /*
    - * This packet contains the version of the family that this SNAC is
    - * in. You get this when your SSI module is version 2 or higher.
    - * For now we have no need for this, but you could always save
    - * it as a part of aim_modnsac_t, or something. The format is...
    - * 2 byte length of total mini-header (which is 6 bytes), then TLV
    - * of type 0x0001, length 0x0002, value is the 2 byte version
    - * number
    - */
    - byte_stream_advance(&frame->data, byte_stream_get16(&frame->data));
    - }
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    -
    - if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
    - (cur->family != snac.family))
    - continue;
    -
    - if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
    - return;
    - }
    -}
    -
    -static void
    -parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype)
    -{
    - aim_module_t *cur;
    - aim_modsnac_t snac;
    -
    - snac.family = family;
    - snac.subtype = subtype;
    - snac.flags = snac.id = 0;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    -
    - if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
    - (cur->family != snac.family))
    - continue;
    -
    - if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
    - return;
    - }
    -}
    -
    -static void
    -parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
    -{
    - GSList *tlvlist;
    - char *msg = NULL;
    -
    - if (byte_stream_bytes_left(&frame->data) == 0) {
    - /* XXX should do something with this */
    - return;
    - }
    -
    - /* An ICQ account is logging in */
    - if (conn->type == SNAC_FAMILY_AUTH)
    - {
    - parse_fakesnac(od, conn, frame, 0x0017, 0x0003);
    - return;
    - }
    -
    - tlvlist = aim_tlvlist_read(&frame->data);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x0009, 1))
    - conn->disconnect_code = aim_tlv_get16(tlvlist, 0x0009, 1);
    -
    - if (aim_tlv_gettlv(tlvlist, 0x000b, 1))
    - msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
    -
    - /*
    - * The server ended this FLAP connnection, so let's be nice and
    - * close the physical TCP connection
    - */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_REMOTE_CLOSED, msg);
    -
    - aim_tlvlist_free(tlvlist);
    -
    - g_free(msg);
    -}
    -
    -/**
    - * Takes a new incoming FLAP frame and sends it to the appropriate
    - * handler function to be parsed.
    - */
    -static void
    -parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame)
    -{
    - if (frame->channel == 0x01) {
    - guint32 flap_version = byte_stream_get32(&frame->data);
    - if (flap_version != 0x00000001)
    - {
    - /* Error! */
    - purple_debug_warning("oscar", "Expecting FLAP version "
    - "0x00000001 but received FLAP version %08x. Closing connection.\n",
    - flap_version);
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - }
    - else
    - conn->connected = TRUE;
    -
    - } else if (frame->channel == 0x02) {
    - parse_snac(od, conn, frame);
    -
    - } else if (frame->channel == 0x04) {
    - parse_flap_ch4(od, conn, frame);
    -
    - } else if (frame->channel == 0x05) {
    - /* TODO: Reset our keepalive watchdog? */
    -
    - }
    -}
    -
    -/**
    - * Read in all available data on the socket for a given connection.
    - * All complete FLAPs handled immedate after they're received.
    - * Incomplete FLAP data is stored locally and appended to the next
    - * time this callback is triggered.
    - *
    - * This is called by flap_connection_recv_cb and
    - * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
    - */
    -static void
    -flap_connection_recv(FlapConnection *conn)
    -{
    - gpointer buf;
    - gsize buflen;
    - gssize read;
    -
    - /* Read data until we run out of data and break out of the loop */
    - while (TRUE)
    - {
    - /* Start reading a new FLAP */
    - if (conn->buffer_incoming.data.data == NULL)
    - {
    - buf = conn->header + conn->header_received;
    - buflen = 6 - conn->header_received;
    -
    - /* Read the first 6 bytes (the FLAP header) */
    - if (conn->gsc)
    - read = purple_ssl_read(conn->gsc, buf, buflen);
    - else
    - read = recv(conn->fd, buf, buflen, 0);
    -
    - /* Check if the FLAP server closed the connection */
    - if (read == 0)
    - {
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - break;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - break;
    -
    - /* Error! */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - break;
    - }
    - purple_connection_update_last_received(conn->od->gc);
    -
    - /* If we don't even have a complete FLAP header then do nothing */
    - conn->header_received += read;
    - if (conn->header_received < 6)
    - break;
    -
    - /* All FLAP frames must start with the byte 0x2a */
    - if (aimutil_get8(&conn->header[0]) != 0x2a)
    - {
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - break;
    - }
    -
    - /* Initialize a new temporary FlapFrame for incoming data */
    - conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]);
    - conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]);
    - conn->buffer_incoming.data.len = aimutil_get16(&conn->header[4]);
    - conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len);
    - conn->buffer_incoming.data.offset = 0;
    - }
    -
    - buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset;
    - if (buflen)
    - {
    - buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
    - /* Read data into the temporary FlapFrame until it is complete */
    - if (conn->gsc)
    - read = purple_ssl_read(conn->gsc, buf, buflen);
    - else
    - read = recv(conn->fd, buf, buflen, 0);
    -
    - /* Check if the FLAP server closed the connection */
    - if (read == 0)
    - {
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - break;
    - }
    -
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - break;
    -
    - /* Error! */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - break;
    - }
    -
    - conn->buffer_incoming.data.offset += read;
    - if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len)
    - /* Waiting for more data to arrive */
    - break;
    - }
    -
    - /* We have a complete FLAP! Handle it and continue reading */
    - byte_stream_rewind(&conn->buffer_incoming.data);
    - parse_flap(conn->od, conn, &conn->buffer_incoming);
    - conn->lastactivity = time(NULL);
    -
    - g_free(conn->buffer_incoming.data.data);
    - conn->buffer_incoming.data.data = NULL;
    -
    - conn->header_received = 0;
    - }
    -}
    -
    -void
    -flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - FlapConnection *conn = data;
    -
    - flap_connection_recv(conn);
    -}
    -
    -void
    -flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
    -{
    - FlapConnection *conn = data;
    -
    - flap_connection_recv(conn);
    -}
    -
    -/**
    - * @param source When this function is called as a callback source is
    - * set to the fd that triggered the callback. But this function
    - * is also called directly from flap_connection_send_byte_stream(),
    - * in which case source will be -1. So don't use source--use
    - * conn->gsc or conn->fd instead.
    - */
    -static void
    -send_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - FlapConnection *conn;
    - int writelen, ret;
    - const gchar *output = NULL;
    -
    - conn = data;
    - writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing);
    - output = purple_circular_buffer_get_output(conn->buffer_outgoing);
    -
    - if (writelen == 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - return;
    - }
    -
    - if (conn->gsc)
    - ret = purple_ssl_write(conn->gsc, output, writelen);
    - else
    - ret = send(conn->fd, output, writelen, 0);
    - if (ret <= 0)
    - {
    - if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - /* Error! */
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - if (conn->gsc) {
    - purple_ssl_close(conn->gsc);
    - conn->gsc = NULL;
    - } else {
    - close(conn->fd);
    - conn->fd = -1;
    - }
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - purple_circular_buffer_mark_read(conn->buffer_outgoing, ret);
    -}
    -
    -static void
    -flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count)
    -{
    - if (conn == NULL)
    - return;
    -
    - /* Make sure we don't send past the end of the bs */
    - if (count > byte_stream_bytes_left(bs))
    - count = byte_stream_bytes_left(bs); /* truncate to remaining space */
    -
    - if (count == 0)
    - return;
    -
    - /* Add everything to our outgoing buffer */
    - purple_circular_buffer_append(conn->buffer_outgoing, bs->data, count);
    -
    - /* If we haven't already started writing stuff, then start the cycle */
    - if (conn->watcher_outgoing == 0)
    - {
    - if (conn->gsc) {
    - conn->watcher_outgoing = purple_input_add(conn->gsc->fd,
    - PURPLE_INPUT_WRITE, send_cb, conn);
    - send_cb(conn, -1, 0);
    - } else if (conn->fd >= 0) {
    - conn->watcher_outgoing = purple_input_add(conn->fd,
    - PURPLE_INPUT_WRITE, send_cb, conn);
    - send_cb(conn, -1, 0);
    - }
    - }
    -}
    -
    -static void
    -sendframe_flap(FlapConnection *conn, FlapFrame *frame)
    -{
    - ByteStream bs;
    - int payloadlen, bslen;
    -
    - payloadlen = byte_stream_curpos(&frame->data);
    -
    - byte_stream_new(&bs, 6 + payloadlen);
    -
    - /* FLAP header */
    - byte_stream_put8(&bs, 0x2a);
    - byte_stream_put8(&bs, frame->channel);
    - byte_stream_put16(&bs, frame->seqnum);
    - byte_stream_put16(&bs, payloadlen);
    -
    - /* Payload */
    - byte_stream_rewind(&frame->data);
    - byte_stream_putbs(&bs, &frame->data, payloadlen);
    -
    - bslen = byte_stream_curpos(&bs);
    - byte_stream_rewind(&bs);
    - flap_connection_send_byte_stream(&bs, conn, bslen);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -void
    -flap_connection_send(FlapConnection *conn, FlapFrame *frame)
    -{
    - frame->seqnum = ++(conn->seqnum_out);
    - sendframe_flap(conn, frame);
    - flap_frame_destroy(frame);
    -}
    --- a/libpurple/protocols/oscar/icq.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,91 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "icq.h"
    -
    -#include "core.h"
    -#include "plugins.h"
    -#include "signals.h"
    -
    -#include "oscarcommon.h"
    -
    -static GHashTable *
    -icq_get_account_text_table(PurpleAccount *account)
    -{
    - GHashTable *table;
    - table = g_hash_table_new(g_str_hash, g_str_equal);
    - g_hash_table_insert(table, "login_label", (gpointer)_("ICQ UIN..."));
    - return table;
    -}
    -
    -static gssize
    -icq_get_max_message_size(PurpleConversation *conv)
    -{
    - /* XXX: got from pidgin-otr - verify and document it */
    - return 2346;
    -}
    -
    -static void
    -icq_protocol_init(ICQProtocol *self)
    -{
    - PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    -
    - protocol->id = "prpl-icq";
    - protocol->name = "ICQ";
    -
    - oscar_init_account_options(protocol, TRUE);
    -}
    -
    -static void
    -icq_protocol_class_init(ICQProtocolClass *klass)
    -{
    - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    -
    - protocol_class->list_icon = oscar_list_icon_icq;
    -}
    -
    -static void
    -icq_protocol_class_finalize(G_GNUC_UNUSED ICQProtocolClass *klass)
    -{
    -}
    -
    -static void
    -icq_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    -{
    - client_iface->get_account_text_table = icq_get_account_text_table;
    - client_iface->get_moods = oscar_get_purple_moods;
    - client_iface->get_max_message_size = icq_get_max_message_size;
    -}
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - ICQProtocol, icq_protocol, OSCAR_TYPE_PROTOCOL, 0,
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    - icq_protocol_client_iface_init));
    -
    -/* This exists solely because the above macro makes icq_protocol_register_type
    - * static. */
    -void
    -icq_protocol_register(PurplePlugin *plugin)
    -{
    - icq_protocol_register_type(G_TYPE_MODULE(plugin));
    -}
    --- a/libpurple/protocols/oscar/icq.h Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,56 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef PURPLE_OSCAR_ICQ_H
    -#define PURPLE_OSCAR_ICQ_H
    -
    -#include "oscar.h"
    -
    -#define ICQ_TYPE_PROTOCOL (icq_protocol_get_type())
    -#define ICQ_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ICQ_TYPE_PROTOCOL, ICQProtocol))
    -#define ICQ_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ICQ_TYPE_PROTOCOL, ICQProtocolClass))
    -#define ICQ_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ICQ_TYPE_PROTOCOL))
    -#define ICQ_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ICQ_TYPE_PROTOCOL))
    -#define ICQ_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ICQ_TYPE_PROTOCOL, ICQProtocolClass))
    -
    -typedef struct
    -{
    - OscarProtocol parent;
    -} ICQProtocol;
    -
    -typedef struct
    -{
    - OscarProtocolClass parent_class;
    -} ICQProtocolClass;
    -
    -/**
    - * Registers the ICQProtocol type in the type system.
    - */
    -G_GNUC_INTERNAL
    -void icq_protocol_register(PurplePlugin *plugin);
    -
    -/**
    - * Returns the GType for the ICQProtocol object.
    - */
    -G_MODULE_EXPORT GType icq_protocol_get_type(void);
    -
    -#endif /* PURPLE_OSCAR_ICQ_H */
    --- a/libpurple/protocols/oscar/kerberos.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,425 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/**
    - * This file implements AIM's kerberos procedure for authenticating
    - * users. This replaces the older MD5-based and XOR-based
    - * authentication methods that use SNAC family 0x0017.
    - *
    - * This doesn't use SNACs or FLAPs at all. It makes https
    - * POSTs to AOL KDC server to validate the user based on the password they
    - * provided to us. Upon successful authentication we receive two tokens
    - * in the response. One is assumed to be the kerberos ticket for authentication
    - * on the various AOL websites, while the other contains BOSS information, such
    - * as the hostname and port number to use, the TLS certificate name as well as
    - * the cookie to use to authenticate to the BOS server.
    - * And then everything else is the same as with BUCP.
    - *
    - */
    -
    -#include "oscar.h"
    -#include "oscarcommon.h"
    -#include "core.h"
    -
    -#define MAXAIMPASSLEN 16
    -
    -/*
    - * Incomplete X-SNAC format taken from reverse engineering doen by digsby:
    - * https://github.com/ifwe/digsby/blob/master/digsby/src/oscar/login2.py
    - */
    -typedef struct {
    - aim_tlv_t *main_tlv;
    - gchar *principal1;
    - gchar *service;
    - gchar *principal1_again;
    - gchar *principal2;
    - gchar unknown;
    - guint8 *footer;
    - struct {
    - guint32 unknown1;
    - guint32 unknown2;
    - guint32 epoch_now;
    - guint32 epoch_valid;
    - guint32 epoch_renew;
    - guint32 epoch_expire;
    - guint32 unknown3;
    - guint32 unknown4;
    - guint32 unknown5;
    - } dates;
    - GSList *tlvlist;
    -} aim_xsnac_token_t;
    -
    -typedef struct {
    - guint16 family;
    - guint16 subtype;
    - guint8 flags[8];
    - guint16 request_id;
    - guint32 epoch;
    - guint32 unknown;
    - gchar *principal1;
    - gchar *principal2;
    - guint16 num_tokens;
    - aim_xsnac_token_t *tokens;
    - GSList *tlvlist;
    -} aim_xsnac_t;
    -
    -static gchar *get_kdc_url(OscarData *od)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - const gchar *server;
    - gchar *url;
    - gchar *port_str = NULL;
    - gint port;
    -
    - server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
    - port = purple_account_get_int(account, "port", AIM_DEFAULT_KDC_PORT);
    - if (port != 443)
    - port_str = g_strdup_printf(":%d", port);
    - url = g_strdup_printf("https://%s%s/", server, port_str ? port_str : "");
    - g_free(port_str);
    -
    - return url;
    -}
    -
    -static const char *get_client_key(OscarData *od)
    -{
    - return oscar_get_ui_info_string(
    - od->icq ? "prpl-icq-clientkey" : "prpl-aim-clientkey",
    - od->icq ? ICQ_DEFAULT_CLIENT_KEY : AIM_DEFAULT_CLIENT_KEY);
    -}
    -
    -static void
    -aim_encode_password(const char *password, gchar *encoded)
    -{
    - guint8 encoding_table[] = {
    - 0x76, 0x91, 0xc5, 0xe7,
    - 0xd0, 0xd9, 0x95, 0xdd,
    - 0x9e, 0x2F, 0xea, 0xd8,
    - 0x6B, 0x21, 0xc2, 0xbc,
    -
    - };
    - guint i;
    -
    - /*
    - * We truncate AIM passwords to 16 characters since that's what
    - * the official client does as well.
    - */
    - for (i = 0; i < strlen(password) && i < MAXAIMPASSLEN; i++)
    - encoded[i] = (password[i] ^ encoding_table[i]);
    -}
    -
    -static void
    -aim_xsnac_free(aim_xsnac_t *xsnac)
    -{
    - gint i;
    -
    - g_free(xsnac->principal1);
    - g_free(xsnac->principal2);
    - aim_tlvlist_free(xsnac->tlvlist);
    -
    - for (i = 0; i < xsnac->num_tokens; i++) {
    - g_free(xsnac->tokens[i].main_tlv->value);
    - g_free(xsnac->tokens[i].main_tlv);
    - g_free(xsnac->tokens[i].principal1);
    - g_free(xsnac->tokens[i].service);
    - g_free(xsnac->tokens[i].principal1_again);
    - g_free(xsnac->tokens[i].principal2);
    - g_free(xsnac->tokens[i].footer);
    - aim_tlvlist_free(xsnac->tokens[i].tlvlist);
    - }
    - g_free(xsnac->tokens);
    -}
    -
    -static void
    -kerberos_login_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
    - gpointer user_data)
    -{
    - OscarData *od = user_data;
    - PurpleConnection *gc;
    - ByteStream bs;
    - aim_xsnac_t xsnac = {0};
    - guint16 len;
    - gchar *bosip = NULL;
    - gchar *tlsCertName = NULL;
    - guint8 *cookie = NULL;
    - guint32 cookie_len = 0;
    - char *host; int port;
    - gsize i;
    -
    - gc = od->gc;
    -
    - g_clear_object(&od->http_conns);
    -
    - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
    - gchar *tmp;
    - gchar *url;
    -
    - url = get_kdc_url(od);
    - tmp = g_strdup_printf(_("Error requesting %s: %d %s"), url,
    - msg->status_code, msg->reason_phrase);
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - g_free(url);
    - return;
    - }
    -
    - purple_debug_info("oscar",
    - "Received kerberos login HTTP response %" G_GOFFSET_FORMAT
    - " : ",
    - msg->response_body->length);
    -
    - byte_stream_init(&bs, (guint8 *)msg->response_body->data,
    - msg->response_body->length);
    -
    - xsnac.family = byte_stream_get16(&bs);
    - xsnac.subtype = byte_stream_get16(&bs);
    - byte_stream_getrawbuf(&bs, (guint8 *) xsnac.flags, 8);
    -
    - if (xsnac.family == 0x50C && xsnac.subtype == 0x0005) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("Incorrect password"));
    - return;
    - }
    - if (xsnac.family != 0x50C || xsnac.subtype != 0x0003) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Error parsing response from authentication server"));
    - return;
    - }
    - xsnac.request_id = byte_stream_get16(&bs);
    - xsnac.epoch = byte_stream_get32(&bs);
    - xsnac.unknown = byte_stream_get32(&bs);
    - len = byte_stream_get16(&bs);
    - xsnac.principal1 = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.principal2 = byte_stream_getstr(&bs, len);
    - xsnac.num_tokens = byte_stream_get16(&bs);
    -
    - purple_debug_info("oscar", "KDC: %d tokens between '%s' and '%s'\n",
    - xsnac.num_tokens, xsnac.principal1, xsnac.principal2);
    - xsnac.tokens = g_new0(aim_xsnac_token_t, xsnac.num_tokens);
    - for (i = 0; i < xsnac.num_tokens; i++) {
    - GSList *tlv;
    -
    - tlv = aim_tlvlist_readnum(&bs, 1);
    - if (tlv)
    - xsnac.tokens[i].main_tlv = tlv->data;
    - g_slist_free(tlv);
    -
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].principal1 = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].service = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].principal1_again = byte_stream_getstr(&bs, len);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].principal2 = byte_stream_getstr(&bs, len);
    - xsnac.tokens[i].unknown = byte_stream_get8(&bs);
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].footer = byte_stream_getraw(&bs, len);
    -
    - xsnac.tokens[i].dates.unknown1 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown2 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_now = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_valid = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_renew = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.epoch_expire = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown3 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown4 = byte_stream_get32(&bs);
    - xsnac.tokens[i].dates.unknown5 = byte_stream_get32(&bs);
    -
    - len = byte_stream_get16(&bs);
    - xsnac.tokens[i].tlvlist = aim_tlvlist_readnum(&bs, len);
    -
    - purple_debug_info("oscar",
    - "Token %" G_GSIZE_FORMAT
    - " has %d TLVs for service '%s'\n",
    - i, len, xsnac.tokens[i].service);
    - }
    - len = byte_stream_get16(&bs);
    - xsnac.tlvlist = aim_tlvlist_readnum(&bs, len);
    -
    - for (i = 0; i < xsnac.num_tokens; i++) {
    - if (purple_strequal(xsnac.tokens[i].service, "im/boss")) {
    - aim_tlv_t *tlv;
    - GSList *tlvlist;
    - ByteStream tbs;
    -
    - tlv = aim_tlv_gettlv(xsnac.tokens[i].tlvlist, 0x0003, 1);
    - if (tlv != NULL) {
    - byte_stream_init(&tbs, tlv->value, tlv->length);
    - byte_stream_get32(&tbs);
    - tlvlist = aim_tlvlist_read(&tbs);
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - bosip = aim_tlv_getstr(tlvlist, 0x0005, 1);
    - if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
    - tlsCertName = aim_tlv_getstr(tlvlist, 0x008D, 1);
    - tlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
    - if (tlv) {
    - cookie_len = tlv->length;
    - cookie = tlv->value;
    - }
    - }
    - break;
    - }
    - }
    - if (bosip && cookie) {
    - port = AIM_DEFAULT_KDC_PORT;
    - for (i = 0; i < strlen(bosip); i++) {
    - if (bosip[i] == ':') {
    - port = atoi(&(bosip[i+1]));
    - break;
    - }
    - }
    - host = g_strndup(bosip, i);
    - oscar_connect_to_bos(gc, od, host, port, cookie, cookie_len, tlsCertName);
    - g_free(host);
    - } else {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unknown error during authentication"));
    - }
    - aim_xsnac_free(&xsnac);
    - g_free(tlsCertName);
    - g_free(bosip);
    -}
    -
    -/**
    - * This function sends a binary blob request to the Kerberos KDC server
    - * https://kdc.uas.aol.com with the user's username and password and
    - * receives the IM cookie, which is used to request a connection to the
    - * BOSS server.
    - * The binary data below is what AIM 8.0.8.1 sends in order to authenticate
    - * to the KDC server. It is an 'X-SNAC' packet, which is relatively similar
    - * to SNAC packets but somehow different.
    - * The header starts with the 0x50C family follow by 0x0002 subtype, then
    - * some fixed length data and TLVs. The string "COOL" appears in there for
    - * some reason followed by the 'US' and 'en' strings.
    - * Then the 'imApp key=<client key>' comes after that, and then the username
    - * and the string "im/boss" which seems to represent the service we are
    - * requesting the authentication for. Changing that will lead to a
    - * 'unknown service' error. The client key is then added again (without the
    - * 'imApp key' string prepended to it) then a XOR-ed version of the password.
    - * The meaning of the header/footer/in-between bytes is not known but never
    - * seems to change so there is no need to reverse engineer their meaning at
    - * this point.
    - */
    -void send_kerberos_login(OscarData *od, const char *username)
    -{
    - PurpleConnection *gc;
    - SoupMessage *msg;
    - gchar *url;
    - const gchar *password;
    - gchar password_xored[MAXAIMPASSLEN];
    - const gchar *client_key;
    - gchar *imapp_key;
    - GString *body;
    - guint16 len_be;
    - guint16 reqid;
    - const gchar header[] = {
    - 0x05, 0x0C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
    - 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05,
    - 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
    - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x18, 0x99,
    - 0x00, 0x05, 0x00, 0x04, 0x43, 0x4F, 0x4F, 0x4C,
    - 0x00, 0x0A, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0B,
    - 0x00, 0x04, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00,
    - 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
    - 0x55, 0x53, 0x00, 0x02, 0x65, 0x6E, 0x00, 0x04,
    - 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D,
    - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04,
    - 0x00, 0x05};
    - const gchar pre_username[] = {
    - 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x01, 0x8B,
    - 0x01, 0x00, 0x00, 0x00, 0x00};
    - const gchar post_username[] = {
    - 0x00, 0x07, 0x69, 0x6D, 0x2F, 0x62, 0x6F, 0x73,
    - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    - 0x04, 0x00, 0x02};
    - const gchar pre_password[] = {
    - 0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01,
    - 0x00, 0x00};
    - const gchar post_password[] = {0x00, 0x00, 0x00, 0x1D};
    - const gchar footer[] = {
    - 0x00, 0x21, 0x00, 0x32, 0x00, 0x01, 0x10, 0x03,
    - 0x00, 0x2C, 0x00, 0x07, 0x00, 0x14, 0x00, 0x04,
    - 0x00, 0x00, 0x01, 0x8B, 0x00, 0x16, 0x00, 0x02,
    - 0x00, 0x26, 0x00, 0x17, 0x00, 0x02, 0x00, 0x07,
    - 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19,
    - 0x00, 0x02, 0x00, 0x0D, 0x00, 0x1A, 0x00, 0x02,
    - 0x00, 0x04, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x28,
    - 0x00, 0x00};
    -
    - gc = od->gc;
    -
    - password = purple_connection_get_password(gc);
    - aim_encode_password(password, password_xored);
    -
    - client_key = get_client_key(od);
    - imapp_key = g_strdup_printf("imApp key=%s", client_key);
    -
    - /* Construct the body of the HTTP POST request */
    - body = g_string_new(NULL);
    - g_string_append_len(body, header, sizeof(header));
    - reqid = (guint16) g_random_int();
    - g_string_overwrite_len(body, 0xC, (void *)&reqid, sizeof(guint16));
    -
    - len_be = GUINT16_TO_BE(strlen(imapp_key));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append(body, imapp_key);
    -
    - len_be = GUINT16_TO_BE(strlen(username));
    - g_string_append_len(body, pre_username, sizeof(pre_username));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append(body, username);
    - g_string_append_len(body, post_username, sizeof(post_username));
    -
    - len_be = GUINT16_TO_BE(strlen(password) + 0x10);
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append_len(body, pre_password, sizeof(pre_password));
    - len_be = GUINT16_TO_BE(strlen(password) + 4);
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - len_be = GUINT16_TO_BE(strlen(password));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append_len(body, password_xored, strlen(password));
    - g_string_append_len(body, post_password, sizeof(post_password));
    -
    - len_be = GUINT16_TO_BE(strlen(client_key));
    - g_string_append_len(body, (void *)&len_be, sizeof(guint16));
    - g_string_append(body, client_key);
    - g_string_append_len(body, footer, sizeof(footer));
    -
    - g_free(imapp_key);
    -
    - url = get_kdc_url(od);
    - msg = soup_message_new("POST", url);
    - soup_message_set_request(msg, "application/x-snac", SOUP_MEMORY_TAKE,
    - body->str, body->len);
    - soup_message_headers_replace(msg->request_headers, "Accept",
    - "application/x-snac");
    - soup_session_queue_message(od->http_conns, msg, kerberos_login_cb, od);
    -
    - g_string_free(body, FALSE);
    - g_free(url);
    -}
    --- a/libpurple/protocols/oscar/meson.build Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,63 +0,0 @@
    -OSCARSOURCES = [
    - 'authorization.c',
    - 'aim.c',
    - 'aim.h',
    - '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',
    - 'icq.c',
    - 'icq.h',
    - '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'
    -]
    -
    -if IS_WIN32
    - oscar_link_args = ['-Wl,--export-all-symbols']
    -else
    - oscar_link_args = []
    -endif
    -
    -if DYNAMIC_OSCAR
    - oscar_prpl = shared_library('oscar', OSCARSOURCES,
    - link_args : oscar_link_args,
    - dependencies : [libpurple_dep, glib, libsoup, ws2_32],
    - install : true, install_dir : PURPLE_PLUGINDIR)
    -
    - subdir('tests')
    -endif
    --- a/libpurple/protocols/oscar/misc.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,133 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Random stuff. Basically just a few functions for sending
    - * simple SNACs, and then the generic error handler.
    - */
    -
    -#include "oscar.h"
    -
    -/*
    - * Generic routine for sending commands.
    - *
    - * I know I can do this in a smarter way...but I'm not thinking straight
    - * right now...
    - *
    - * I had one big function that handled all three cases, but then it broke
    - * and I split it up into three. But then I fixed it. I just never went
    - * back to the single. I don't see any advantage to doing it either way.
    - *
    - */
    -void
    -aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
    -{
    - aim_snacid_t snacid = 0x00000000;
    -
    - flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
    -}
    -
    -void
    -aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
    -{
    - aim_snacid_t snacid;
    -
    - snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
    -
    - flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
    -}
    -
    -void
    -aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *longdata)
    -{
    - ByteStream bs;
    - aim_snacid_t snacid;
    -
    - if (!longdata)
    - {
    - aim_genericreq_n(od, conn, family, subtype);
    - return;
    - }
    -
    - byte_stream_new(&bs, 4);
    -
    - snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
    -
    - byte_stream_put32(&bs, *longdata);
    -
    - flap_connection_send_snac(od, conn, family, subtype, snacid, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/*
    - * Should be generic enough to handle the errors for all groups.
    - *
    - */
    -static int
    -generror(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - int ret = 0;
    - int error = 0;
    - aim_rxcallback_t userfunc;
    - aim_snac_t *snac2;
    -
    - snac2 = aim_remsnac(od, snac->id);
    -
    - if (byte_stream_bytes_left(bs))
    - error = byte_stream_get16(bs);
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - ret = userfunc(od, conn, frame, error, snac2 ? snac2->data : NULL);
    -
    - if (snac2) {
    - g_free(snac2->data);
    - g_free(snac2);
    - }
    -
    - return ret;
    -}
    -
    -static int
    -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
    -{
    - if (snac->subtype == 0x0001)
    - return generror(od, conn, mod, frame, snac, bs);
    - else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) {
    - aim_rxcallback_t userfunc;
    -
    - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
    - return userfunc(od, conn, frame);
    - }
    -
    - return 0;
    -}
    -
    -int
    -misc_modfirst(OscarData *od, aim_module_t *mod)
    -{
    - mod->family = 0xffff;
    - mod->version = 0x0000;
    - mod->flags = AIM_MODFLAG_MULTIFAMILY;
    - strncpy(mod->name, "misc", sizeof(mod->name));
    - mod->snachandler = snachandler;
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/msgcookie.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,179 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Cookie Caching stuff. Adam wrote this, apparently just some
    - * derivatives of n's SNAC work. I cleaned it up, added comments.
    - *
    - */
    -
    -/*
    - * I'm assuming that cookies are type-specific. that is, we can have
    - * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we
    - * lose some error checking. if we assume cookies are not type-specific and are
    - * wrong, we get quirky behavior when cookies step on each others' toes.
    - */
    -
    -#include "oscar.h"
    -
    -/**
    - * aim_cachecookie - appends a cookie to the cookie list
    - *
    - * if cookie->cookie for type cookie->type is found, updates the
    - * ->addtime of the found structure; otherwise adds the given cookie
    - * to the cache
    - *
    - * @param od session to add to
    - * @param cookie pointer to struct to append
    - * @return returns -1 on error, 0 on append, 1 on update. the cookie you pass
    - * in may be free'd, so don't count on its value after calling this!
    - */
    -int aim_cachecookie(OscarData *od, IcbmCookie *cookie)
    -{
    - IcbmCookie *newcook;
    -
    - if (!od || !cookie)
    - return -EINVAL;
    -
    - newcook = aim_checkcookie(od, cookie->cookie, cookie->type);
    -
    - if (newcook == cookie) {
    - newcook->addtime = time(NULL);
    - return 1;
    - } else if (newcook)
    - aim_cookie_free(od, newcook);
    -
    - cookie->addtime = time(NULL);
    -
    - cookie->next = od->msgcookies;
    - od->msgcookies = cookie;
    -
    - return 0;
    -}
    -
    -/**
    - * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list)
    - *
    - * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process.
    - *
    - * @param od session to grab cookie from
    - * @param cookie cookie string to look for
    - * @param type cookie type to look for
    - * @return if found, returns the struct; if none found (or on error), returns NULL:
    - */
    -IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type)
    -{
    - IcbmCookie *cur, **prev;
    -
    - if (!cookie || !od->msgcookies)
    - return NULL;
    -
    - for (prev = &od->msgcookies; (cur = *prev); ) {
    - if ((cur->type == type) &&
    - (memcmp(cur->cookie, cookie, 8) == 0)) {
    - *prev = cur->next;
    - return cur;
    - }
    - prev = &cur->next;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * aim_mkcookie - generate an IcbmCookie *struct from a cookie string, a type, and a data pointer.
    - *
    - * @param c pointer to the cookie string array
    - * @param type cookie type to use
    - * @param data data to be cached with the cookie
    - * @return returns NULL on error, a pointer to the newly-allocated
    - * cookie on success.
    - */
    -IcbmCookie *aim_mkcookie(guint8 *c, int type, void *data)
    -{
    - IcbmCookie *cookie;
    -
    - if (!c)
    - return NULL;
    -
    - cookie = g_new0(IcbmCookie, 1);
    -
    - cookie->data = data;
    - cookie->type = type;
    - memcpy(cookie->cookie, c, 8);
    -
    - return cookie;
    -}
    -
    -/**
    - * aim_checkcookie - check to see if a cookietuple has been cached
    - *
    - * @param od session to check for the cookie in
    - * @param cookie pointer to the cookie string array
    - * @param type type of the cookie to look for
    - * @return returns a pointer to the cookie struct (still in the list)
    - * on success; returns NULL on error/not found
    - */
    -
    -IcbmCookie *aim_checkcookie(OscarData *od, const guint8 *cookie, const int type)
    -{
    - IcbmCookie *cur;
    -
    - for (cur = od->msgcookies; cur; cur = cur->next) {
    - if ((cur->type == type) &&
    - (memcmp(cur->cookie, cookie, 8) == 0))
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * aim_cookie_free - free an IcbmCookie struct
    - *
    - * this function removes the cookie *cookie from the list of cookies
    - * in od, and then frees all memory associated with it. including
    - * its data! if you want to use the private data after calling this,
    - * make sure you copy it first.
    - *
    - * @param od session to remove the cookie from
    - * @param cookie the address of a pointer to the cookie struct to remove
    - * @return returns -1 on error, 0 on success.
    - *
    - */
    -int aim_cookie_free(OscarData *od, IcbmCookie *cookie)
    -{
    - IcbmCookie *cur, **prev;
    -
    - if (!od || !cookie)
    - return -EINVAL;
    -
    - for (prev = &od->msgcookies; (cur = *prev); ) {
    - if (cur == cookie)
    - *prev = cur->next;
    - else
    - prev = &cur->next;
    - }
    -
    - g_free(cookie->data);
    - g_free(cookie);
    -
    - return 0;
    -}
    --- a/libpurple/protocols/oscar/odc.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,626 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/* From the oscar PRPL */
    -#include "encoding.h"
    -#include "oscar.h"
    -#include "peer.h"
    -
    -/* From Purple */
    -#include "conversation.h"
    -#include "image-store.h"
    -#include "util.h"
    -
    -#define DIRECTIM_MAX_FILESIZE 52428800
    -
    -/**
    - * Free any ODC related data and print a message to the conversation
    - * window based on conn->disconnect_reason.
    - */
    -void
    -peer_odc_close(PeerConnection *conn)
    -{
    - gchar *tmp;
    -
    - if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
    - tmp = g_strdup(_("The remote user has closed the connection."));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED)
    - tmp = g_strdup(_("The remote user has declined your request."));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
    - tmp = g_strdup_printf(_("Lost connection with the remote user:<br>%s"),
    - conn->error_message);
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
    - tmp = g_strdup(_("Received invalid data on connection with remote user."));
    - else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
    - tmp = g_strdup(_("Unable to establish a connection with the remote user."));
    - else
    - /*
    - * We shouldn't print a message for some disconnect_reasons.
    - * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
    - */
    - tmp = NULL;
    -
    - if (tmp != NULL)
    - {
    - PurpleAccount *account;
    - PurpleIMConversation *im;
    -
    - account = purple_connection_get_account(conn->od->gc);
    - im = purple_im_conversation_new(account, conn->bn);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), tmp, 0);
    - g_free(tmp);
    - }
    -
    - if (conn->frame != NULL)
    - {
    - OdcFrame *frame;
    - frame = conn->frame;
    - g_free(frame->payload.data);
    - g_free(frame);
    - }
    -}
    -
    -/**
    - * Write the given OdcFrame to a ByteStream and send it out
    - * on the established PeerConnection.
    - */
    -static void
    -peer_odc_send(PeerConnection *conn, OdcFrame *frame)
    -{
    - PurpleAccount *account;
    - const char *username;
    - size_t length;
    - ByteStream bs;
    -
    - purple_debug_info("oscar", "Outgoing ODC frame to %s with "
    - "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
    - conn->bn, frame->type, frame->flags, frame->payload.len);
    -
    - account = purple_connection_get_account(conn->od->gc);
    - username = purple_account_get_username(account);
    - memcpy(frame->bn, username, strlen(username));
    - memcpy(frame->cookie, conn->cookie, 8);
    -
    - length = 76;
    - byte_stream_new(&bs, length + frame->payload.len);
    - byte_stream_putraw(&bs, conn->magic, 4);
    - byte_stream_put16(&bs, length);
    - byte_stream_put16(&bs, frame->type);
    - byte_stream_put16(&bs, frame->subtype);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_putraw(&bs, frame->cookie, 8);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put32(&bs, frame->payload.len);
    - byte_stream_put16(&bs, frame->encoding);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, frame->flags);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_put16(&bs, 0x0000);
    - byte_stream_putraw(&bs, frame->bn, 32);
    - byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
    -
    - peer_connection_send(conn, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Send a very basic ODC frame (which contains the cookie) so that the
    - * remote user can verify that we are the person they were expecting.
    - * If we made an outgoing connection to then remote user, then we send
    - * this immediately. If the remote user connected to us, then we wait
    - * for the other person to send this to us, then we send one to them.
    - */
    -void
    -peer_odc_send_cookie(PeerConnection *conn)
    -{
    - OdcFrame frame;
    -
    - memset(&frame, 0, sizeof(OdcFrame));
    - frame.type = 0x0001;
    - frame.subtype = 0x0006;
    - frame.flags = 0x0060; /* Maybe this means "we're sending the cookie"? */
    -
    - peer_odc_send(conn, &frame);
    -}
    -
    -/**
    - * Send client-to-client typing notification over an established direct connection.
    - */
    -void
    -peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing)
    -{
    - OdcFrame frame;
    -
    - memset(&frame, 0, sizeof(OdcFrame));
    - frame.type = 0x0001;
    - frame.subtype = 0x0006;
    - if (typing == PURPLE_IM_TYPING)
    - frame.flags = 0x0002 | 0x0008;
    - else if (typing == PURPLE_IM_TYPED)
    - frame.flags = 0x0002 | 0x0004;
    - else
    - frame.flags = 0x0002;
    -
    - peer_odc_send(conn, &frame);
    -}
    -
    -/**
    - * Send client-to-client IM over an established direct connection.
    - * To send a direct IM, call this just like you would aim_send_im.
    - *
    - * @param conn The already-connected ODC connection.
    - * @param msg Null-terminated string to send.
    - * @param len The length of the message to send, including binary data.
    - * @param encoding See the AIM_CHARSET_* defines in oscar.h
    - * @param autoreply TRUE if this is any auto-reply.
    - */
    -void
    -peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply)
    -{
    - OdcFrame frame;
    -
    - g_return_if_fail(msg != NULL);
    - g_return_if_fail(len > 0);
    -
    - memset(&frame, 0, sizeof(OdcFrame));
    - frame.type = 0x0001;
    - frame.subtype = 0x0006;
    - frame.payload.len = len;
    - frame.encoding = encoding;
    - frame.flags = autoreply;
    - byte_stream_new(&frame.payload, len);
    - byte_stream_putraw(&frame.payload, (guint8 *)msg, len);
    -
    - peer_odc_send(conn, &frame);
    -
    - g_free(frame.payload.data);
    -}
    -
    -struct embedded_data
    -{
    - size_t size;
    - const guint8 *data;
    -};
    -
    -/**
    - * This is called after a direct IM has been received in its entirety. This
    - * function is passed a long chunk of data which contains the IM with any
    - * data chunks (images) appended to it.
    - *
    - * This function rips out all the data chunks and creates a PurpleImage (?) for
    - * each one. In order to do this, it first goes through the IM and takes
    - * out all the IMG tags. When doing so, it rewrites the original IMG tag
    - * with one compatible with the PurpleImage (?) code. For each one, we
    - * then read in chunks of data from the end of the message and actually
    - * create the img store using the given data.
    - *
    - * For somewhat easy reference, here's a sample message
    - * (with added whitespace):
    - *
    - * <HTML><BODY BGCOLOR="#ffffff">
    - * <FONT LANG="0">
    - * This is a really stupid picture:<BR>
    - * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
    - * Yeah it is<BR>
    - * Here is another one:<BR>
    - * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">
    - * </FONT>
    - * </BODY></HTML>
    - * <BINARY>
    - * <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
    - * <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
    - * </BINARY>
    - */
    -static void
    -peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - const char *msgend, *binary_start, *dataend;
    - const char *tmp, *start, *end, *idstr, *src, *sizestr;
    - GData *attributes;
    - GHashTable *embedded_datas;
    - struct embedded_data *embedded_data;
    - gboolean any_images = FALSE;
    - gchar *utf8;
    - GString *newmsg;
    - PurpleMessageFlags imflags;
    -
    - gc = conn->od->gc;
    - account = purple_connection_get_account(gc);
    -
    - dataend = msg + len;
    -
    - /*
    - * Create a hash table containing references to each embedded
    - * data chunk. The key is the "ID" and the value is an
    - * embedded_data struct.
    - */
    - embedded_datas = g_hash_table_new_full(g_direct_hash,
    - g_direct_equal, NULL, g_free);
    -
    - /*
    - * Create an index of any binary chunks. If we run into any
    - * problems while parsing the binary data section then we stop
    - * parsing it, and the local user will see broken image icons.
    - */
    - binary_start = purple_strcasestr(msg, "<binary>");
    - if (binary_start == NULL)
    - msgend = dataend;
    - else
    - {
    - msgend = binary_start;
    -
    - /* Move our pointer to immediately after the <binary> tag */
    - tmp = binary_start + 8;
    -
    - /* The embedded binary markup has a mimimum length of 29 bytes */
    - while ((tmp + 29 <= dataend) &&
    - purple_markup_find_tag("data", tmp, &start, &tmp, &attributes))
    - {
    - unsigned int id;
    - size_t size;
    -
    - /* Move the binary pointer from ">" to the start of the data */
    - tmp++;
    -
    - /* Get the ID */
    - idstr = g_datalist_get_data(&attributes, "id");
    - if (idstr == NULL)
    - {
    - g_datalist_clear(&attributes);
    - break;
    - }
    - id = atoi(idstr);
    -
    - /* Get the size */
    - sizestr = g_datalist_get_data(&attributes, "size");
    - if (sizestr == NULL)
    - {
    - g_datalist_clear(&attributes);
    - break;
    - }
    - size = atol(sizestr);
    -
    - g_datalist_clear(&attributes);
    -
    - if ((size > 0) && (tmp + size > dataend))
    - break;
    -
    - embedded_data = g_new(struct embedded_data, 1);
    - embedded_data->size = size;
    - embedded_data->data = (const guint8 *)tmp;
    - tmp += size;
    -
    - /* Skip past the closing </data> tag */
    - if (g_ascii_strncasecmp(tmp, "</data>", 7))
    - {
    - g_free(embedded_data);
    - break;
    - }
    - tmp += 7;
    -
    - g_hash_table_insert(embedded_datas,
    - GINT_TO_POINTER(id), embedded_data);
    - }
    - }
    -
    - /*
    - * Loop through the message, replacing OSCAR img tags with the
    - * equivalent Purple img tag.
    - */
    - newmsg = g_string_new("");
    - tmp = msg;
    - while (purple_markup_find_tag("img", tmp, &start, &end, &attributes))
    - {
    - PurpleImage *image = NULL;
    -
    - idstr = g_datalist_get_data(&attributes, "id");
    - src = g_datalist_get_data(&attributes, "src");
    - sizestr = g_datalist_get_data(&attributes, "datasize");
    -
    - if ((idstr != NULL) && (src != NULL) && (sizestr!= NULL))
    - {
    - unsigned int id;
    - size_t size;
    -
    - id = atoi(idstr);
    - size = atol(sizestr);
    - embedded_data = g_hash_table_lookup(embedded_datas,
    - GINT_TO_POINTER(id));
    -
    - if ((embedded_data != NULL) && (embedded_data->size == size))
    - {
    - image = purple_image_new_from_data(
    - embedded_data->data,
    - size);
    - purple_image_set_friendly_filename(image, src);
    - }
    - }
    -
    - /* Delete the attribute list */
    - g_datalist_clear(&attributes);
    -
    - /* Append the message up to the tag */
    - utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, start - tmp);
    - if (utf8 != NULL) {
    - g_string_append(newmsg, utf8);
    - g_free(utf8);
    - }
    -
    - if (image)
    - {
    - guint img_id;
    -
    - img_id = purple_image_store_add_temporary(image);
    - g_object_unref(image);
    - any_images = TRUE;
    -
    - /* Write the new image tag */
    - g_string_append_printf(newmsg, "<img src=\""
    - PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id);
    - }
    -
    - /* Continue from the end of the tag */
    - tmp = end + 1;
    - }
    -
    - /* Append any remaining message data */
    - if (tmp <= msgend)
    - {
    - utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp);
    - if (utf8 != NULL) {
    - g_string_append(newmsg, utf8);
    - g_free(utf8);
    - }
    - }
    -
    - /* Display the message we received */
    - imflags = 0;
    - if (any_images)
    - imflags |= PURPLE_MESSAGE_IMAGES;
    - if (autoreply)
    - imflags |= PURPLE_MESSAGE_AUTO_RESP;
    - purple_serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL));
    - g_string_free(newmsg, TRUE);
    -
    - /* Delete our list of pointers to embedded images */
    - g_hash_table_destroy(embedded_datas);
    -}
    -
    -/**
    - * This is a purple_input_add() watcher callback function for reading
    - * direct IM payload data. "Payload data" is always an IM and
    - * maybe some embedded images or files or something. The actual
    - * ODC frame is read using peer_connection_recv_cb(). We temporarily
    - * switch to this watcher callback ONLY to read the payload, and we
    - * switch back once we're done.
    - */
    -static void
    -peer_odc_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - OdcFrame *frame;
    - ByteStream *bs;
    - gssize read;
    -
    - conn = data;
    - frame = conn->frame;
    - bs = &frame->payload;
    -
    - /* Read data into the temporary buffer until it is complete */
    - read = recv(conn->fd,
    - &bs->data[bs->offset],
    - bs->len - bs->offset,
    - 0);
    -
    - /* Check if the remote user closed the connection */
    - if (read == 0)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - return;
    - }
    -
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - bs->offset += read;
    - if (bs->offset < bs->len)
    - /* Waiting for more data to arrive */
    - return;
    - /* TODO: Instead of null-terminating this, it would be better if we just
    - respected the length of the buffer when parsing it. But it doesn't
    - really matter and this is easy. */
    - bs->data[bs->len] = '\0';
    -
    - /* We have a complete ODC/OFT frame! Handle it and continue reading */
    - byte_stream_rewind(bs);
    - peer_odc_handle_payload(conn, (const char *)bs->data,
    - bs->len, frame->encoding, frame->flags & 0x0001);
    - g_free(bs->data);
    - bs->data = NULL;
    - g_free(frame);
    - conn->frame = NULL;
    -
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
    -}
    -
    -/**
    - * Handle an incoming OdcFrame. If there is a payload associated
    - * with this frame, then we remove the old watcher and add the
    - * ODC watcher to read in the payload.
    - */
    -void
    -peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
    -{
    - PurpleConnection *gc;
    - OdcFrame *frame;
    -
    - gc = conn->od->gc;
    -
    - frame = g_new0(OdcFrame, 1);
    - frame->type = byte_stream_get16(bs);
    - frame->subtype = byte_stream_get16(bs);
    - byte_stream_advance(bs, 2);
    - byte_stream_getrawbuf(bs, frame->cookie, 8);
    - byte_stream_advance(bs, 8);
    - frame->payload.len = byte_stream_get32(bs);
    - frame->encoding = byte_stream_get16(bs);
    - byte_stream_advance(bs, 4);
    - frame->flags = byte_stream_get16(bs);
    - byte_stream_advance(bs, 4);
    - byte_stream_getrawbuf(bs, frame->bn, 32);
    -
    - purple_debug_info("oscar", "Incoming ODC frame from %s with "
    - "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n",
    - frame->bn, frame->type, frame->flags, frame->payload.len);
    -
    - if (!conn->ready)
    - {
    - /*
    - * We need to verify the cookie so that we know we are
    - * connected to our friend and not a malicious middle man.
    - */
    -
    - PurpleAccount *account;
    - PurpleIMConversation *im;
    -
    - if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)
    - {
    - if (memcmp(conn->cookie, frame->cookie, 8))
    - {
    - /*
    - * Oh no! The user that connected to us did not send
    - * the correct cookie! They are not our friend. Go try
    - * to accept another connection?
    - */
    - purple_debug_info("oscar", "Received an incorrect cookie. "
    - "Closing connection.\n");
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - g_free(frame);
    - return;
    - }
    -
    - /*
    - * Ok, we know they are legit. Now be courteous and
    - * send them our cookie. Note: This doesn't seem
    - * to be necessary, but it also doesn't seem to hurt.
    - */
    - peer_odc_send_cookie(conn);
    - }
    -
    - conn->ready = TRUE;
    -
    - /*
    - * If they connected to us then close the listener socket
    - * and send them our cookie.
    - */
    - if (conn->listenerfd != -1)
    - {
    - close(conn->listenerfd);
    - conn->listenerfd = -1;
    - }
    -
    - /* Tell the local user that we are connected */
    - account = purple_connection_get_account(gc);
    - im = purple_im_conversation_new(account, conn->bn);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), _("Direct IM established"), 0);
    - }
    -
    - if ((frame->type != 0x0001) && (frame->subtype != 0x0006))
    - {
    - purple_debug_info("oscar", "Unknown ODC frame type 0x%04hx, "
    - "subtype 0x%04hx.\n", frame->type, frame->subtype);
    - g_free(frame);
    - return;
    - }
    -
    - if (frame->flags & 0x0008)
    - {
    - /* I had to leave this. It's just too funny. It reminds me of my sister. */
    - purple_debug_info("oscar", "ohmigod! %s has started typing "
    - "(DirectIM). He's going to send you a message! "
    - "*squeal*\n", conn->bn);
    - purple_serv_got_typing(gc, conn->bn, 0, PURPLE_IM_TYPING);
    - }
    - else if (frame->flags & 0x0004)
    - {
    - purple_serv_got_typing(gc, conn->bn, 0, PURPLE_IM_TYPED);
    - }
    - else
    - {
    - purple_serv_got_typing_stopped(gc, conn->bn);
    - }
    -
    - if (frame->payload.len > 0)
    - {
    - if (frame->payload.len > DIRECTIM_MAX_FILESIZE)
    - {
    - gchar *tmp, *size1, *size2;
    - PurpleAccount *account;
    - PurpleIMConversation *im;
    -
    - size1 = g_format_size(frame->payload.len);
    - size2 = g_format_size(DIRECTIM_MAX_FILESIZE);
    - tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM. Try using file transfer instead.\n"), conn->bn, size1, size2);
    - g_free(size1);
    - g_free(size2);
    -
    - account = purple_connection_get_account(conn->od->gc);
    - im = purple_im_conversation_new(account, conn->bn);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), tmp, 0);
    - g_free(tmp);
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    - g_free(frame);
    - return;
    - }
    -
    - /* We have payload data! Switch to the ODC watcher to read it. */
    - frame->payload.data = g_new(guint8, frame->payload.len + 1);
    - frame->payload.offset = 0;
    - conn->frame = frame;
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_odc_recv_cb, conn);
    - return;
    - }
    -
    - g_free(frame);
    -}
    --- a/libpurple/protocols/oscar/oft.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,823 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * I feel like this is a good place to explain OFT, so I'm going to
    - * do just that. Each OFT packet has a header type. I guess this
    - * is pretty similar to the subtype of a SNAC packet. The type
    - * basically tells the other client the meaning of the OFT packet.
    - * There are two distinct types of file transfer, which I usually
    - * call "sendfile" and "getfile." Sendfile is when you send a file
    - * to another AIM user. Getfile is when you share a group of files,
    - * and other users request that you send them the files.
    - *
    - * A typical sendfile file transfer goes like this:
    - * 1) Sender sends a channel 2 ICBM telling the other user that
    - * we want to send them a file. At the same time, we open a
    - * listener socket (this should be done before sending the
    - * ICBM) on some port, and wait for them to connect to us.
    - * The ICBM we sent should contain our IP address and the port
    - * number that we're listening on.
    - * 2) The receiver connects to the sender on the given IP address
    - * and port. After the connection is established, the receiver
    - * sends an ICBM signifying that we are ready and waiting.
    - * 3) The sender sends an OFT PROMPT message over the OFT
    - * connection.
    - * 4) The receiver of the file sends back an exact copy of this
    - * OFT packet, except the cookie is filled in with the cookie
    - * from the ICBM. I think this might be an attempt to verify
    - * that the user that is connected is actually the guy that
    - * we sent the ICBM to. Oh, I've been calling this the ACK.
    - * 5) The sender starts sending raw data across the connection
    - * until the entire file has been sent.
    - * 6) The receiver knows the file is finished because the sender
    - * sent the file size in an earlier OFT packet. So then the
    - * receiver sends the DONE thingy (after filling in the
    - * "received" checksum and size) and closes the connection.
    - */
    -
    -#include "oscar.h"
    -#include "peer.h"
    -
    -#include "util.h"
    -
    -#define CHECKSUM_BUFFER_SIZE 256 * 1024
    -
    -struct _ChecksumData
    -{
    - PeerConnection *conn;
    - PurpleXfer *xfer;
    - GSourceFunc callback;
    - size_t size;
    - guint32 checksum;
    - size_t total;
    - FILE *file;
    - guint8 buffer[CHECKSUM_BUFFER_SIZE];
    - guint timer;
    -};
    -
    -void
    -peer_oft_checksum_destroy(ChecksumData *checksum_data)
    -{
    - checksum_data->conn->checksum_data = NULL;
    - fclose(checksum_data->file);
    - if (checksum_data->timer > 0)
    - g_source_remove(checksum_data->timer);
    - g_free(checksum_data);
    -}
    -
    -/**
    - * Calculate oft checksum of buffer
    - *
    - * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The
    - * checksum is kind of a rolling checksum thing, so each time you get bytes
    - * of a file you just call this puppy and it updates the checksum. You can
    - * calculate the checksum of an entire file by calling this in a while or a
    - * for loop, or something.
    - *
    - * Thanks to Graham Booker for providing this improved checksum routine,
    - * which is simpler and should be more accurate than Josh Myer's original
    - * code. -- wtm
    - *
    - * This algorithm works every time I have tried it. The other fails
    - * sometimes. So, AOL who thought this up? It has got to be the weirdest
    - * checksum I have ever seen.
    - *
    - * @param buffer Buffer of data to checksum. Man I'd like to buff her...
    - * @param bufsize Size of buffer.
    - * @param prevchecksum Previous checksum.
    - * @param odd Whether an odd number of bytes have been processed before this call
    - */
    -static guint32
    -peer_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevchecksum, int odd)
    -{
    - guint32 checksum, oldchecksum;
    - int i = 0;
    - unsigned short val;
    -
    - checksum = (prevchecksum >> 16) & 0xffff;
    - if (odd)
    - {
    - /*
    - * This is one hell of a hack, but it should always work.
    - * Essentially, I am reindexing the array so that index 1
    - * is the first element. Since the odd and even bytes are
    - * detected by the index number.
    - */
    - i = 1;
    - bufferlen++;
    - buffer--;
    - }
    - for (; i < bufferlen; i++)
    - {
    - oldchecksum = checksum;
    - if (i & 1)
    - val = buffer[i];
    - else
    - val = buffer[i] << 8;
    - checksum -= val;
    - /*
    - * The following appears to be necessary.... It happens
    - * every once in a while and the checksum doesn't fail.
    - */
    - if (checksum > oldchecksum)
    - checksum--;
    - }
    - checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
    - checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
    - return checksum << 16;
    -}
    -
    -static gboolean
    -peer_oft_checksum_file_piece(gpointer data)
    -{
    - ChecksumData *checksum_data;
    - gboolean repeat;
    -
    - checksum_data = data;
    - repeat = FALSE;
    -
    - if (checksum_data->total < checksum_data->size)
    - {
    - size_t bytes = MIN(CHECKSUM_BUFFER_SIZE,
    - checksum_data->size - checksum_data->total);
    -
    - bytes = fread(checksum_data->buffer, 1, bytes, checksum_data->file);
    - if (bytes != 0)
    - {
    - checksum_data->checksum = peer_oft_checksum_chunk(checksum_data->buffer, bytes, checksum_data->checksum, checksum_data->total & 1);
    - checksum_data->total += bytes;
    - repeat = TRUE;
    - }
    - }
    -
    - if (!repeat)
    - {
    - purple_debug_info("oscar", "Checksum of %s calculated\n",
    - purple_xfer_get_local_filename(checksum_data->xfer));
    - if (checksum_data->callback != NULL)
    - checksum_data->callback(checksum_data);
    - peer_oft_checksum_destroy(checksum_data);
    - }
    -
    - return repeat;
    -}
    -
    -/**
    - * Calculate oft checksum of a file in a series of calls to
    - * peer_oft_checksum_file_piece(). We do it this way because
    - * calculating the checksum on large files can take a long time,
    - * and we want to return control to the UI so that the application
    - * doesn't appear completely frozen.
    - *
    - * @param conn The connection used for this file transfer.
    - * @param xfer The file transfer needing this checksum.
    - * @param callback The function to call upon calculation of the checksum.
    - * @param size The maximum size to check.
    - */
    -
    -static void
    -peer_oft_checksum_file(PeerConnection *conn, PurpleXfer *xfer, GSourceFunc callback, size_t size)
    -{
    - ChecksumData *checksum_data;
    -
    - purple_debug_info("oscar", "Calculating checksum of %s\n",
    - purple_xfer_get_local_filename(xfer));
    -
    - checksum_data = g_malloc0(sizeof(ChecksumData));
    - checksum_data->conn = conn;
    - checksum_data->xfer = xfer;
    - checksum_data->callback = callback;
    - checksum_data->size = size;
    - checksum_data->checksum = 0xffff0000;
    - checksum_data->file = g_fopen(purple_xfer_get_local_filename(xfer), "rb");
    -
    - if (checksum_data->file == NULL)
    - {
    - purple_debug_error("oscar", "Unable to open %s for checksumming: %s\n",
    - purple_xfer_get_local_filename(xfer), g_strerror(errno));
    - callback(checksum_data);
    - g_free(checksum_data);
    - }
    - else
    - {
    - checksum_data->timer = g_timeout_add(10,
    - peer_oft_checksum_file_piece, checksum_data);
    - conn->checksum_data = checksum_data;
    - }
    -}
    -
    -static void
    -peer_oft_copy_xfer_data(PeerConnection *conn, OftFrame *frame)
    -{
    - g_free(conn->xferdata.name);
    -
    - memcpy(&(conn->xferdata), frame, sizeof(OftFrame));
    - conn->xferdata.name = g_memdup(frame->name, frame->name_length);
    -}
    -
    -/**
    - * Free any OFT related data.
    - */
    -void
    -peer_oft_close(PeerConnection *conn)
    -{
    - /*
    - * If cancelled by local user, and we're receiving a file, and
    - * we're not connected/ready then send an ICBM cancel message.
    - */
    - if ((purple_xfer_get_status(conn->xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
    - !conn->ready)
    - {
    - aim_im_sendch2_cancel(conn);
    - }
    -
    - if (conn->sending_data_timer != 0)
    - {
    - g_source_remove(conn->sending_data_timer);
    - conn->sending_data_timer = 0;
    - }
    -}
    -
    -/**
    - * Write the given OftFrame to a ByteStream and send it out
    - * on the established PeerConnection.
    - */
    -static void
    -peer_oft_send(PeerConnection *conn, OftFrame *frame)
    -{
    - size_t length;
    - ByteStream bs;
    -
    - length = 192 + frame->name_length;
    - byte_stream_new(&bs, length);
    - byte_stream_putraw(&bs, conn->magic, 4);
    - byte_stream_put16(&bs, length);
    - byte_stream_put16(&bs, frame->type);
    - byte_stream_putraw(&bs, frame->cookie, 8);
    - byte_stream_put16(&bs, frame->encrypt);
    - byte_stream_put16(&bs, frame->compress);
    - byte_stream_put16(&bs, frame->totfiles);
    - byte_stream_put16(&bs, frame->filesleft);
    - byte_stream_put16(&bs, frame->totparts);
    - byte_stream_put16(&bs, frame->partsleft);
    - byte_stream_put32(&bs, frame->totsize);
    - byte_stream_put32(&bs, frame->size);
    - byte_stream_put32(&bs, frame->modtime);
    - byte_stream_put32(&bs, frame->checksum);
    - byte_stream_put32(&bs, frame->rfrcsum);
    - byte_stream_put32(&bs, frame->rfsize);
    - byte_stream_put32(&bs, frame->cretime);
    - byte_stream_put32(&bs, frame->rfcsum);
    - byte_stream_put32(&bs, frame->nrecvd);
    - byte_stream_put32(&bs, frame->recvcsum);
    - byte_stream_putraw(&bs, frame->idstring, 32);
    - byte_stream_put8(&bs, frame->flags);
    - byte_stream_put8(&bs, frame->lnameoffset);
    - byte_stream_put8(&bs, frame->lsizeoffset);
    - byte_stream_putraw(&bs, frame->dummy, 69);
    - byte_stream_putraw(&bs, frame->macfileinfo, 16);
    - byte_stream_put16(&bs, frame->nencode);
    - byte_stream_put16(&bs, frame->nlanguage);
    - /*
    - * The name can be more than 64 characters, but if it is less than
    - * 64 characters it is padded with NULLs.
    - */
    - byte_stream_putraw(&bs, frame->name, frame->name_length);
    -
    - peer_connection_send(conn, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -void
    -peer_oft_send_prompt(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_PROMPT;
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -static void
    -peer_oft_send_ack(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_ACK;
    -
    - /* Fill in the cookie */
    - memcpy(conn->xferdata.cookie, conn->cookie, 8);
    -
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -static void
    -peer_oft_send_resume_accept(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_RESUMEACCEPT;
    -
    - /* Fill in the cookie */
    - memcpy(conn->xferdata.cookie, conn->cookie, 8);
    -
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -static void
    -peer_oft_send_done(PeerConnection *conn)
    -{
    - conn->xferdata.type = PEER_TYPE_DONE;
    - conn->xferdata.rfrcsum = 0xffff0000;
    - conn->xferdata.nrecvd = purple_xfer_get_bytes_sent(conn->xfer);
    - peer_oft_send(conn, &conn->xferdata);
    -}
    -
    -/**
    - * This function exists so that we don't remove the outgoing
    - * data watcher while we're still sending data. In most cases
    - * any data we're sending will be instantly wisked away to a TCP
    - * buffer maintained by our operating system... but we want to
    - * make sure the core doesn't start sending file data while
    - * we're still sending OFT frame data. That would be bad.
    - */
    -static gboolean
    -start_transfer_when_done_sending_data(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0)
    - {
    - int fd = conn->fd;
    - conn->sending_data_timer = 0;
    - conn->fd = -1;
    - purple_xfer_start(conn->xfer, fd, NULL, 0);
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * This function is similar to the above function, except instead
    - * of starting the xfer it will destroy the connection. This is
    - * used when you want to send one final message across the peer
    - * connection, and then close everything.
    - */
    -static gboolean
    -destroy_connection_when_done_sending_data(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0)
    - {
    - conn->sending_data_timer = 0;
    - peer_connection_destroy(conn, conn->disconnect_reason, NULL);
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/*
    - * This is called when a buddy sends us some file info. This happens when they
    - * are sending a file to you, and you have just established a connection to them.
    - * You should send them the exact same info except use the real cookie. We also
    - * get like totally ready to like, receive the file, kay?
    - */
    -static void
    -peer_oft_recv_frame_prompt(PeerConnection *conn, OftFrame *frame)
    -{
    - /* Record the file information and send an ack */
    - peer_oft_copy_xfer_data(conn, frame);
    - peer_oft_send_ack(conn);
    -
    - /* Remove our watchers and use the file transfer watchers in the core */
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - conn->sending_data_timer = g_timeout_add(100,
    - start_transfer_when_done_sending_data, conn);
    -}
    -
    -/**
    - * We are sending a file to someone else. They have just acknowledged our
    - * prompt, so we want to start sending data like there's no tomorrow.
    - */
    -static void
    -peer_oft_recv_frame_ack(PeerConnection *conn, OftFrame *frame)
    -{
    - if (memcmp(conn->cookie, frame->cookie, 8) != 0)
    - {
    - purple_debug_info("oscar", "Received an incorrect cookie. "
    - "Closing connection.\n");
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - return;
    - }
    -
    - /* Remove our watchers and use the file transfer watchers in the core */
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - conn->sending_data_timer = g_timeout_add(100,
    - start_transfer_when_done_sending_data, conn);
    -}
    -
    -static gboolean
    -peer_oft_recv_frame_resume_checksum_calculated_cb(gpointer data)
    -{
    - ChecksumData *checksum_data;
    - PeerConnection *conn;
    -
    - checksum_data = data;
    - conn = checksum_data->conn;
    -
    - /* Check the checksums here. If not match, don't allow resume */
    - if (checksum_data->checksum != conn->xferdata.recvcsum || checksum_data->total != conn->xferdata.nrecvd)
    - {
    - /* Reset internal structure */
    - conn->xferdata.recvcsum = 0xffff0000;
    - conn->xferdata.rfrcsum = 0xffff0000;
    - conn->xferdata.nrecvd = 0;
    - }
    - else
    - /* Accept the change */
    - purple_xfer_set_bytes_sent(checksum_data->xfer, conn->xferdata.nrecvd);
    -
    - peer_oft_send_resume_accept(conn);
    -
    - return FALSE;
    -}
    -
    -/**
    - * We are sending a file to someone else. They have just acknowledged our
    - * prompt and are asking to resume, so we accept their resume and await
    - * a resume ack.
    - */
    -static void
    -peer_oft_recv_frame_resume(PeerConnection *conn, OftFrame *frame)
    -{
    - if (memcmp(conn->cookie, frame->cookie, 8) != 0)
    - {
    - purple_debug_info("oscar", "Received an incorrect cookie. "
    - "Closing connection.\n");
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - return;
    - }
    -
    - /* Copy resume data into internal structure */
    - conn->xferdata.recvcsum = frame->recvcsum;
    - conn->xferdata.rfrcsum = frame->rfrcsum;
    - conn->xferdata.nrecvd = frame->nrecvd;
    -
    - peer_oft_checksum_file(conn, conn->xfer,
    - peer_oft_recv_frame_resume_checksum_calculated_cb,
    - frame->nrecvd);
    -}
    -
    -/*
    - * We just sent a file to someone. They said they got it and everything,
    - * so we can close our direct connection and what not.
    - */
    -static void
    -peer_oft_recv_frame_done(PeerConnection *conn, OftFrame *frame)
    -{
    - /*
    - * The core ft code sets the xfer to completed automatically if we've
    - * sent all bytes to the other user. But this function can be called
    - * even if we haven't sent all bytes to the other user (in the case
    - * where the user already has this file on their computer and the
    - * checksum matches).
    - */
    - if (!purple_xfer_is_completed(conn->xfer))
    - purple_xfer_set_completed(conn->xfer, TRUE);
    -
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - purple_xfer_set_fd(conn->xfer, conn->fd);
    - conn->fd = -1;
    - conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
    - peer_connection_schedule_destroy(conn, conn->disconnect_reason, NULL);
    -}
    -
    -/**
    - * Handle an incoming OftFrame. If there is a payload associated
    - * with this frame, then we remove the old watcher and add the
    - * OFT watcher to read in the payload.
    - */
    -void
    -peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs)
    -{
    - OftFrame frame;
    -
    - frame.type = byte_stream_get16(bs);
    - byte_stream_getrawbuf(bs, frame.cookie, 8);
    - frame.encrypt = byte_stream_get16(bs);
    - frame.compress = byte_stream_get16(bs);
    - frame.totfiles = byte_stream_get16(bs);
    - frame.filesleft = byte_stream_get16(bs);
    - frame.totparts = byte_stream_get16(bs);
    - frame.partsleft = byte_stream_get16(bs);
    - frame.totsize = byte_stream_get32(bs);
    - frame.size = byte_stream_get32(bs);
    - frame.modtime = byte_stream_get32(bs);
    - frame.checksum = byte_stream_get32(bs);
    - frame.rfrcsum = byte_stream_get32(bs);
    - frame.rfsize = byte_stream_get32(bs);
    - frame.cretime = byte_stream_get32(bs);
    - frame.rfcsum = byte_stream_get32(bs);
    - frame.nrecvd = byte_stream_get32(bs);
    - frame.recvcsum = byte_stream_get32(bs);
    - byte_stream_getrawbuf(bs, frame.idstring, 32);
    - frame.flags = byte_stream_get8(bs);
    - frame.lnameoffset = byte_stream_get8(bs);
    - frame.lsizeoffset = byte_stream_get8(bs);
    - byte_stream_getrawbuf(bs, frame.dummy, 69);
    - byte_stream_getrawbuf(bs, frame.macfileinfo, 16);
    - frame.nencode = byte_stream_get16(bs);
    - frame.nlanguage = byte_stream_get16(bs);
    - frame.name_length = bs->len - 186;
    - frame.name = byte_stream_getraw(bs, frame.name_length);
    -
    - purple_debug_info("oscar", "Incoming OFT frame from %s with "
    - "type=0x%04x\n", conn->bn, frame.type);
    -
    - /* TODOFT: peer_oft_dirconvert_fromstupid(frame->name); */
    -
    - switch(frame.type)
    - {
    - case PEER_TYPE_PROMPT:
    - peer_oft_recv_frame_prompt(conn, &frame);
    - break;
    - case PEER_TYPE_ACK:
    - case PEER_TYPE_RESUMEACK:
    - peer_oft_recv_frame_ack(conn, &frame);
    - break;
    - case PEER_TYPE_RESUME:
    - peer_oft_recv_frame_resume(conn, &frame);
    - break;
    - case PEER_TYPE_DONE:
    - peer_oft_recv_frame_done(conn, &frame);
    - break;
    - default:
    - break;
    - }
    -
    - g_free(frame.name);
    -}
    -
    -/*******************************************************************/
    -/* Begin PurpleXfer callbacks for use when receiving a file */
    -/*******************************************************************/
    -
    -void
    -peer_oft_recvcb_init(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    -
    - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - peer_connection_trynext(conn);
    -}
    -
    -void
    -peer_oft_recvcb_end(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    -
    - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    -
    - /* Tell the other person that we've received everything */
    - conn->fd = purple_xfer_get_fd(conn->xfer);
    - purple_xfer_set_fd(conn->xfer, -1);
    - peer_oft_send_done(conn);
    -
    - conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
    - conn->sending_data_timer = g_timeout_add(100,
    - destroy_connection_when_done_sending_data, conn);
    -}
    -
    -void
    -peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size)
    -{
    - PeerConnection *conn;
    -
    - /* Update our rolling checksum. Like Walmart, yo. */
    - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    - conn->xferdata.recvcsum = peer_oft_checksum_chunk(buffer,
    - size, conn->xferdata.recvcsum, purple_xfer_get_bytes_sent(xfer) & 1);
    -}
    -
    -/*******************************************************************/
    -/* End PurpleXfer callbacks for use when receiving a file */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin PurpleXfer callbacks for use when sending a file */
    -/*******************************************************************/
    -
    -static gboolean
    -peer_oft_checksum_calculated_cb(gpointer data)
    -{
    - ChecksumData *checksum_data;
    - PeerConnection *conn;
    -
    - checksum_data = data;
    - conn = checksum_data->conn;
    -
    - conn->xferdata.checksum = checksum_data->checksum;
    -
    - /* Start the connection process */
    - peer_connection_trynext(checksum_data->conn);
    -
    - return FALSE;
    -}
    -
    -void
    -peer_oft_sendcb_init(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    - goffset size;
    -
    - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    -
    - /* Make sure the file size can be represented in 32 bits */
    - size = purple_xfer_get_size(xfer);
    - if (size > G_MAXUINT32)
    - {
    - gchar *tmp, *size1, *size2;
    - size1 = g_format_size(size);
    - size2 = g_format_size(G_MAXUINT32);
    - tmp = g_strdup_printf(_("File %s is %s, which is larger than "
    - "the maximum size of %s."),
    - purple_xfer_get_local_filename(xfer), size1, size2);
    - purple_xfer_error(purple_xfer_get_xfer_type(xfer),
    - purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer), tmp);
    - g_free(size1);
    - g_free(size2);
    - g_free(tmp);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    - return;
    - }
    -
    - /* Keep track of file transfer info */
    - conn->xferdata.totfiles = 1;
    - conn->xferdata.filesleft = 1;
    - conn->xferdata.totparts = 1;
    - conn->xferdata.partsleft = 1;
    - conn->xferdata.totsize = size;
    - conn->xferdata.size = size;
    - conn->xferdata.checksum = 0xffff0000;
    - conn->xferdata.rfrcsum = 0xffff0000;
    - conn->xferdata.rfcsum = 0xffff0000;
    - conn->xferdata.recvcsum = 0xffff0000;
    - strncpy((gchar *)conn->xferdata.idstring, "Cool FileXfer", 31);
    - conn->xferdata.modtime = 0;
    - conn->xferdata.cretime = 0;
    - purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer)));
    - conn->xferdata.name_length = MAX(64, strlen(purple_xfer_get_filename(xfer)) + 1);
    - conn->xferdata.name = (guchar *)g_strndup(purple_xfer_get_filename(xfer), conn->xferdata.name_length - 1);
    -
    - peer_oft_checksum_file(conn, xfer,
    - peer_oft_checksum_calculated_cb, G_MAXUINT32);
    -}
    -
    -/*
    - * AIM file transfers aren't really meant to be thought
    - * of as a transferring just a single file. The rendezvous
    - * establishes a connection between two computers, and then
    - * those computers can use the same connection for transferring
    - * multiple files. So we don't want the Purple core up and closing
    - * the socket all willy-nilly. We want to do that in the oscar
    - * protocol, whenever one side or the other says they're finished
    - * using the connection. There might be a better way to intercept
    - * the socket from the core...
    - */
    -void
    -peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size)
    -{
    - PeerConnection *conn;
    -
    - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    -
    - /*
    - * If we're done sending, intercept the socket from the core ft code
    - * and wait for the other guy to send the "done" OFT packet.
    - */
    - if (purple_xfer_get_bytes_remaining(xfer) <= 0)
    - {
    - purple_input_remove(purple_xfer_get_watcher(xfer));
    - conn->fd = purple_xfer_get_fd(xfer);
    - purple_xfer_set_fd(xfer, -1);
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
    - }
    -}
    -
    -/*******************************************************************/
    -/* End PurpleXfer callbacks for use when sending a file */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin PurpleXfer callbacks for use when sending and receiving */
    -/*******************************************************************/
    -
    -void
    -peer_oft_cb_generic_cancel(PurpleXfer *xfer)
    -{
    - PeerConnection *conn;
    -
    - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    -
    - if (conn == NULL)
    - return;
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -}
    -
    -/*******************************************************************/
    -/* End PurpleXfer callbacks for use when sending and receiving */
    -/*******************************************************************/
    -
    -#ifdef TODOFT
    -/*
    - * This little area in oscar.c is the nexus of file transfer code,
    - * so I wrote a little explanation of what happens. I am such a
    - * ninja.
    - *
    - * The series of events for a file send is:
    - * -Create xfer and call purple_xfer_request (this happens in oscar_ask_sendfile)
    - * -User chooses a file and oscar_xfer_init is called. It establishes a
    - * listening socket, then asks the remote user to connect to us (and
    - * gives them the file name, port, IP, etc.)
    - * -They connect to us and we send them an PEER_TYPE_PROMPT (this happens
    - * in peer_oft_recv_frame_established)
    - * -They send us an PEER_TYPE_ACK and then we start sending data
    - * -When we finish, they send us an PEER_TYPE_DONE and they close the
    - * connection.
    - * -We get drunk because file transfer kicks ass.
    - *
    - * The series of events for a file receive is:
    - * -Create xfer and call purple_xfer request (this happens in incomingim_chan2)
    - * -Purple user selects file to name and location to save file to and
    - * oscar_xfer_init is called
    - * -It connects to the remote user using the IP they gave us earlier
    - * -After connecting, they send us an PEER_TYPE_PROMPT. In reply, we send
    - * them an PEER_TYPE_ACK.
    - * -They begin to send us lots of raw data.
    - * -When they finish sending data we send an PEER_TYPE_DONE and then close
    - * the connection.
    - *
    - * Update August 2005:
    - * The series of events for transfers has been seriously complicated by the addition
    - * of transfer redirects and proxied connections. I could throw a whole lot of words
    - * at trying to explain things here, but it probably wouldn't do much good. To get
    - * a better idea of what happens, take a look at the diagrams and documentation
    - * from my Summer of Code project. -- Jonathan Clark
    - */
    -
    -/**
    - * Convert the directory separator from / (0x2f) to ^A (0x01)
    - *
    - * @param name The filename to convert.
    - */
    -static void
    -peer_oft_dirconvert_tostupid(char *name)
    -{
    - while (name[0]) {
    - if (name[0] == 0x01)
    - name[0] = G_DIR_SEPARATOR;
    - name++;
    - }
    -}
    -
    -/**
    - * Convert the directory separator from ^A (0x01) to / (0x2f)
    - *
    - * @param name The filename to convert.
    - */
    -static void
    -peer_oft_dirconvert_fromstupid(char *name)
    -{
    - while (name[0]) {
    - if (name[0] == G_DIR_SEPARATOR)
    - name[0] = 0x01;
    - name++;
    - }
    -}
    -#endif
    --- a/libpurple/protocols/oscar/oscar.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,5839 +0,0 @@
    -/*
    - * purple
    - *
    - * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
    - * Some code copyright (C) 1999-2001, Eric Warmenhoven
    - * Some code copyright (C) 2001-2003, Sean Egan
    - * Some code copyright (C) 2001-2007, Mark Doliner <thekingant@users.sourceforge.net>
    - * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net>
    - * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
    - * Some code copyright (C) 2008, Aman Gupta
    - *
    - * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
    - * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "internal.h"
    -
    -#include "account.h"
    -#include "buddyicon.h"
    -#include "conversation.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "encoding.h"
    -#include "image-store.h"
    -#include "network.h"
    -#include "notify.h"
    -#include "protocol.h"
    -#include "proxy.h"
    -#include "purpleaccountoption.h"
    -#include "request.h"
    -#include "util.h"
    -#include "version.h"
    -#include "visibility.h"
    -
    -#include "aim.h"
    -#include "icq.h"
    -#include "oscarcommon.h"
    -#include "oscar.h"
    -#include "peer.h"
    -
    -static PurpleProtocol *aim_protocol = NULL;
    -static PurpleProtocol *icq_protocol = NULL;
    -
    -static guint64 purple_caps =
    - OSCAR_CAPABILITY_CHAT
    - | OSCAR_CAPABILITY_BUDDYICON
    - | OSCAR_CAPABILITY_DIRECTIM
    - | OSCAR_CAPABILITY_SENDFILE
    - | OSCAR_CAPABILITY_UNICODE
    - | OSCAR_CAPABILITY_INTEROPERATE
    - | OSCAR_CAPABILITY_SHORTCAPS
    - | OSCAR_CAPABILITY_TYPING
    - | OSCAR_CAPABILITY_ICQSERVERRELAY
    - | OSCAR_CAPABILITY_NEWCAPS
    - | OSCAR_CAPABILITY_XTRAZ
    - | OSCAR_CAPABILITY_HTML_MSGS;
    -
    -static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
    -static guint8 features_icq[] = {0x01};
    -
    -struct create_room {
    - char *name;
    - int exchange;
    -};
    -
    -struct oscar_ask_directim_data
    -{
    - OscarData *od;
    - char *who;
    -};
    -
    -/* All the libfaim->purple callback functions */
    -
    -/* Only used when connecting with the old-style BUCP login */
    -static int purple_parse_auth_resp (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...);
    -
    -static int purple_handle_redirect (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_info_change (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_account_confirm (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_oncoming (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_offgoing (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_incoming_im(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_misses (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_motd (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_chatnav_info (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_chat_conversation_join (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_chat_conversation_left (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_chat_conversation_info_update (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_chat_conversation_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_bosrights (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_connerr (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_mtn (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_popup (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parseerr (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parserights (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parselist (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parseack (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_parseaddmod (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_authgiven (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_authrequest (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_authreply (OscarData *, FlapConnection *, FlapFrame *, ...);
    -static int purple_ssi_gotadded (OscarData *, FlapConnection *, FlapFrame *, ...);
    -
    -static void purple_icons_fetch(PurpleConnection *gc);
    -
    -void oscar_set_info(PurpleConnection *gc, const char *info);
    -static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status);
    -static void oscar_set_extended_status(PurpleConnection *gc);
    -static gboolean purple_ssi_rerequestdata(gpointer data);
    -
    -void oscar_free_name_data(struct name_data *data) {
    - g_free(data->name);
    - g_free(data->nick);
    - g_free(data);
    -}
    -
    -#ifdef _WIN32
    -const char *oscar_get_locale_charset(void) {
    - static const char *charset = NULL;
    - if (charset == NULL)
    - g_get_charset(&charset);
    - return charset;
    -}
    -#endif
    -
    -static char *oscar_icqstatus(int state) {
    - /* Make a cute little string that shows the status of the dude or dudet */
    - if (state & AIM_ICQ_STATE_CHAT)
    - return g_strdup(_("Free For Chat"));
    - else if (state & AIM_ICQ_STATE_DND)
    - return g_strdup(_("Do Not Disturb"));
    - else if (state & AIM_ICQ_STATE_OUT)
    - return g_strdup(_("Not Available"));
    - else if (state & AIM_ICQ_STATE_BUSY)
    - return g_strdup(_("Occupied"));
    - else if (state & AIM_ICQ_STATE_AWAY)
    - return g_strdup(_("Away"));
    - else if (state & AIM_ICQ_STATE_WEBAWARE)
    - return g_strdup(_("Web Aware"));
    - else if (state & AIM_ICQ_STATE_INVISIBLE)
    - return g_strdup(_("Invisible"));
    - else if (state & AIM_ICQ_STATE_EVIL)
    - return g_strdup(_("Evil"));
    - else if (state & AIM_ICQ_STATE_DEPRESSION)
    - return g_strdup(_("Depression"));
    - else if (state & AIM_ICQ_STATE_ATHOME)
    - return g_strdup(_("At home"));
    - else if (state & AIM_ICQ_STATE_ATWORK)
    - return g_strdup(_("At work"));
    - else if (state & AIM_ICQ_STATE_LUNCH)
    - return g_strdup(_("At lunch"));
    - else
    - return g_strdup(_("Online"));
    -}
    -
    -static char *extract_name(const char *name) {
    - char *tmp, *x;
    - int i, j;
    -
    - if (!name)
    - return NULL;
    -
    - x = strchr(name, '-');
    - if (!x)
    - return NULL;
    -
    - x = strchr(x + 1, '-');
    - if (!x)
    - return NULL;
    -
    - tmp = g_strdup(++x);
    -
    - for (i = 0, j = 0; x[i]; i++) {
    - char hex[3];
    - if (x[i] != '%') {
    - tmp[j++] = x[i];
    - continue;
    - }
    - strncpy(hex, x + ++i, 2);
    - hex[2] = 0;
    - i++;
    - tmp[j++] = strtol(hex, NULL, 16);
    - }
    -
    - tmp[j] = 0;
    - return tmp;
    -}
    -
    -static struct chat_connection *
    -find_oscar_chat(PurpleConnection *gc, int id)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GSList *cur;
    - struct chat_connection *cc;
    -
    - for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
    - {
    - cc = (struct chat_connection *)cur->data;
    - if (cc->id == id)
    - return cc;
    - }
    -
    - return NULL;
    -}
    -
    -static struct chat_connection *
    -find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GSList *cur;
    - struct chat_connection *cc;
    -
    - for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
    - {
    - cc = (struct chat_connection *)cur->data;
    - if (cc->conn == conn)
    - return cc;
    - }
    -
    - return NULL;
    -}
    -
    -static struct chat_connection *
    -find_oscar_chat_by_conv(PurpleConnection *gc, PurpleChatConversation *conv)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GSList *cur;
    - struct chat_connection *cc;
    -
    - for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
    - {
    - cc = (struct chat_connection *)cur->data;
    - if (cc->conv == conv)
    - return cc;
    - }
    -
    - return NULL;
    -}
    -
    -void
    -oscar_chat_destroy(struct chat_connection *cc)
    -{
    - g_free(cc->name);
    - g_free(cc->show);
    - g_free(cc);
    -}
    -
    -static void
    -oscar_chat_kill(PurpleConnection *gc, struct chat_connection *cc)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - /* Notify the conversation window that we've left the chat */
    - purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(cc->conv));
    -
    - /* Destroy the chat_connection */
    - od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
    - oscar_chat_destroy(cc);
    -}
    -
    -/**
    - * This is called from the callback functions for establishing
    - * a TCP connection with an oscar host if an error occurred.
    - */
    -static void
    -connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
    -{
    - OscarData *od;
    - PurpleConnection *gc;
    -
    - od = conn->od;
    - gc = od->gc;
    -
    - purple_debug_error("oscar", "unable to connect to FLAP "
    - "server of type 0x%04hx\n", conn->type);
    -
    - if (conn->type == SNAC_FAMILY_AUTH)
    - {
    - /* This only happens when connecting with the old-style BUCP login */
    - gchar *msg;
    - msg = g_strdup_printf(_("Unable to connect to authentication server: %s"),
    - error_message);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - }
    - else if (conn->type == SNAC_FAMILY_LOCATE)
    - {
    - gchar *msg;
    - msg = g_strdup_printf(_("Unable to connect to BOS server: %s"),
    - error_message);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
    - g_free(msg);
    - }
    - else
    - {
    - /* Maybe we should call this for BOS connections, too? */
    - flap_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message);
    - }
    -}
    -
    -/**
    - * This is called from the callback functions for establishing
    - * a TCP connection with an oscar host. Depending on the type
    - * of host, we do a few different things here.
    - */
    -static void
    -connection_common_established_cb(FlapConnection *conn)
    -{
    - OscarData *od;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    -
    - od = conn->od;
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
    - conn->type);
    -
    - if (conn->cookie == NULL)
    - flap_connection_send_version(od, conn);
    - else
    - {
    - const gchar *login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN);
    -
    - if (!purple_strequal(login_type, OSCAR_MD5_LOGIN))
    - {
    - ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
    - ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
    - flap_connection_send_version_with_cookie_and_clientinfo(od,
    - conn, conn->cookielen, conn->cookie,
    - od->icq ? &icqinfo : &aiminfo,
    - purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
    - } else {
    - flap_connection_send_version_with_cookie(od, conn,
    - conn->cookielen, conn->cookie);
    - }
    -
    -
    - g_free(conn->cookie);
    - conn->cookie = NULL;
    - }
    -
    - if (conn->type == SNAC_FAMILY_AUTH)
    - {
    - /* This only happens when connecting with the old-style BUCP login */
    - aim_request_login(od, conn, purple_account_get_username(account));
    - purple_debug_info("oscar", "Username sent, waiting for response\n");
    - purple_connection_update_progress(gc, _("Username sent"), 1, OSCAR_CONNECT_STEPS);
    - }
    - else if (conn->type == SNAC_FAMILY_LOCATE)
    - {
    - purple_connection_update_progress(gc, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS);
    - }
    - else if (conn->type == SNAC_FAMILY_CHAT)
    - {
    - od->oscar_chats = g_slist_prepend(od->oscar_chats, conn->new_conn_data);
    - conn->new_conn_data = NULL;
    - }
    -}
    -
    -static void
    -connection_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - FlapConnection *conn;
    -
    - conn = data;
    -
    - conn->connect_data = NULL;
    - conn->fd = source;
    -
    - if (source < 0)
    - {
    - connection_common_error_cb(conn, error_message);
    - return;
    - }
    -
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, flap_connection_recv_cb, conn);
    - connection_common_established_cb(conn);
    -}
    -
    -static void
    -ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc,
    - PurpleInputCondition cond)
    -{
    - FlapConnection *conn;
    -
    - conn = data;
    -
    - purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn);
    - connection_common_established_cb(conn);
    -}
    -
    -static void
    -ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
    - gpointer data)
    -{
    - FlapConnection *conn;
    -
    - conn = data;
    -
    - if (conn->watcher_outgoing)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - }
    -
    - /* sslconn frees the connection on error */
    - conn->gsc = NULL;
    -
    - connection_common_error_cb(conn, purple_ssl_strerror(error));
    -}
    -
    -static void
    -flap_connection_established_bos(OscarData *od, FlapConnection *conn)
    -{
    - PurpleConnection *gc = od->gc;
    -
    - aim_srv_reqpersonalinfo(od, conn);
    -
    - purple_debug_info("oscar", "ssi: requesting rights and list\n");
    - aim_ssi_reqrights(od);
    - aim_ssi_reqdata(od);
    - if (od->getblisttimer > 0)
    - g_source_remove(od->getblisttimer);
    - od->getblisttimer = g_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
    -
    - aim_locate_reqrights(od);
    - aim_buddylist_reqrights(od, conn);
    - aim_im_reqparams(od);
    - aim_bos_reqrights(od, conn); /* TODO: Don't call this with ssi */
    -
    - purple_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS);
    -}
    -
    -static void
    -flap_connection_established_admin(OscarData *od, FlapConnection *conn)
    -{
    - aim_srv_clientready(od, conn);
    - purple_debug_info("oscar", "connected to admin\n");
    -
    - if (od->chpass) {
    - purple_debug_info("oscar", "changing password\n");
    - aim_admin_changepasswd(od, conn, od->newp, od->oldp);
    - g_free(od->oldp);
    - od->oldp = NULL;
    - g_free(od->newp);
    - od->newp = NULL;
    - od->chpass = FALSE;
    - }
    - if (od->setnick) {
    - purple_debug_info("oscar", "formatting username\n");
    - aim_admin_setnick(od, conn, od->newformatting);
    - g_free(od->newformatting);
    - od->newformatting = NULL;
    - od->setnick = FALSE;
    - }
    - if (od->conf) {
    - purple_debug_info("oscar", "confirming account\n");
    - aim_admin_reqconfirm(od, conn);
    - od->conf = FALSE;
    - }
    - if (od->reqemail) {
    - purple_debug_info("oscar", "requesting email address\n");
    - aim_admin_getinfo(od, conn, 0x0011);
    - od->reqemail = FALSE;
    - }
    - if (od->setemail) {
    - purple_debug_info("oscar", "setting email address\n");
    - aim_admin_setemail(od, conn, od->email);
    - g_free(od->email);
    - od->email = NULL;
    - od->setemail = FALSE;
    - }
    -}
    -
    -static void
    -flap_connection_established_chat(OscarData *od, FlapConnection *conn)
    -{
    - PurpleConnection *gc = od->gc;
    - struct chat_connection *chatcon;
    - static int id = 1;
    -
    - aim_srv_clientready(od, conn);
    -
    - chatcon = find_oscar_chat_by_conn(gc, conn);
    - if (chatcon) {
    - chatcon->id = id;
    - chatcon->conv = purple_serv_got_joined_chat(gc, id++, chatcon->show);
    - }
    -}
    -
    -static void
    -flap_connection_established_chatnav(OscarData *od, FlapConnection *conn)
    -{
    - aim_srv_clientready(od, conn);
    - aim_chatnav_reqrights(od, conn);
    -}
    -
    -static void
    -flap_connection_established_alert(OscarData *od, FlapConnection *conn)
    -{
    - aim_email_sendcookies(od);
    - aim_email_activate(od);
    - aim_srv_clientready(od, conn);
    -}
    -
    -static void
    -flap_connection_established_bart(OscarData *od, FlapConnection *conn)
    -{
    - PurpleConnection *gc = od->gc;
    -
    - aim_srv_clientready(od, conn);
    -
    - od->iconconnecting = FALSE;
    -
    - purple_icons_fetch(gc);
    -}
    -
    -static int
    -flap_connection_established(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - conn->connected = TRUE;
    - purple_debug_info("oscar", "FLAP connection of type 0x%04hx is "
    - "now fully connected\n", conn->type);
    - if (conn->type == SNAC_FAMILY_LOCATE)
    - flap_connection_established_bos(od, conn);
    - else if (conn->type == SNAC_FAMILY_ADMIN)
    - flap_connection_established_admin(od, conn);
    - else if (conn->type == SNAC_FAMILY_CHAT)
    - flap_connection_established_chat(od, conn);
    - else if (conn->type == SNAC_FAMILY_CHATNAV)
    - flap_connection_established_chatnav(od, conn);
    - else if (conn->type == SNAC_FAMILY_ALERT)
    - flap_connection_established_alert(od, conn);
    - else if (conn->type == SNAC_FAMILY_BART)
    - flap_connection_established_bart(od, conn);
    -
    - return 1;
    -}
    -
    -static void
    -idle_reporting_pref_cb(const char *name, PurplePrefType type,
    - gconstpointer value, gpointer data)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - gboolean report_idle;
    - guint32 presence;
    -
    - gc = data;
    - od = purple_connection_get_protocol_data(gc);
    - report_idle = !purple_strequal((const char *)value, "none");
    - presence = aim_ssi_getpresence(&od->ssi.local);
    -
    - if (report_idle)
    - aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    - else
    - aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    -}
    -
    -/**
    - * Should probably make a "Use recent buddies group" account preference
    - * so that this option is surfaced to the user.
    - */
    -static void
    -recent_buddies_pref_cb(const char *name, PurplePrefType type,
    - gconstpointer value, gpointer data)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - guint32 presence;
    -
    - gc = data;
    - od = purple_connection_get_protocol_data(gc);
    - presence = aim_ssi_getpresence(&od->ssi.local);
    -
    - if (value)
    - aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
    - else
    - aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
    -}
    -
    -static const gchar *login_servers[] = {
    - AIM_DEFAULT_LOGIN_SERVER,
    - AIM_DEFAULT_SSL_LOGIN_SERVER,
    - ICQ_DEFAULT_LOGIN_SERVER,
    - ICQ_DEFAULT_SSL_LOGIN_SERVER,
    -};
    -
    -const gchar *
    -oscar_get_login_server(gboolean is_icq, gboolean use_ssl)
    -{
    - return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)];
    -}
    -
    -static gint
    -compare_handlers(gconstpointer a, gconstpointer b)
    -{
    - guint aa = GPOINTER_TO_UINT(a);
    - guint bb = GPOINTER_TO_UINT(b);
    - guint family1 = aa >> 16;
    - guint family2 = bb >> 16;
    - guint subtype1 = aa & 0xFFFF;
    - guint subtype2 = bb & 0xFFFF;
    - if (family1 != family2) {
    - return family1 - family2;
    - }
    - return subtype1 - subtype2;
    -}
    -
    -void
    -oscar_login(PurpleAccount *account)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - const gchar *encryption_type;
    - const gchar *login_type;
    - GList *handlers;
    - GList *sorted_handlers;
    - GList *cur;
    - GString *msg = g_string_new("");
    - PurpleConnectionFlags flags;
    -
    - gc = purple_account_get_connection(account);
    - od = oscar_data_new();
    - od->gc = gc;
    - purple_connection_set_protocol_data(gc, od);
    -
    - oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, purple_connerr, 0);
    - oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, flap_connection_established, 0);
    -
    - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0003, purple_info_change, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0005, purple_info_change, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0007, purple_account_confirm, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ALERT, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ALERT, SNAC_SUBTYPE_ALERT_MAILSTATUS, purple_email_parseupdate, 0);
    -
    - /* These are only needed when connecting with the old-style BUCP login */
    - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0);
    -
    - oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, purple_bosrights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_RIGHTSINFO, purple_parse_buddyrights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_ONCOMING, purple_parse_oncoming, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_OFFGOING, purple_parse_offgoing, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, purple_chat_conversation_join, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, purple_chat_conversation_left, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, purple_chat_conversation_info_update, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, purple_chat_conversation_incoming_msg, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, SNAC_SUBTYPE_CHATNAV_INFO, purple_chatnav_info, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ERROR, purple_ssi_parseerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO, purple_ssi_parserights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_LIST, purple_ssi_parselist, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SRVACK, purple_ssi_parseack, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, purple_ssi_parseaddmod, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_MOD, purple_ssi_parseaddmod, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTH, purple_ssi_authgiven, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, purple_parse_genericerr, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, purple_handle_redirect, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, purple_parse_motd, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, purple_popup, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, purple_parse_searcherror, 0);
    - oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0);
    -
    - g_string_append(msg, "Registered handlers: ");
    - handlers = g_hash_table_get_keys(od->handlerlist);
    - sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers);
    - for (cur = sorted_handlers; cur; cur = cur->next) {
    - guint x = GPOINTER_TO_UINT(cur->data);
    - g_string_append_printf(msg, "%04x/%04x, ", x >> 16, x & 0xFFFF);
    - }
    - g_list_free(sorted_handlers);
    - g_list_free(handlers);
    - purple_debug_misc("oscar", "%s\n", msg->str);
    - g_string_free(msg, TRUE);
    -
    - purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc);
    -
    - if (!oscar_util_valid_name(purple_account_get_username(account))) {
    - gchar *buf;
    - buf = g_strdup_printf(_("Unable to sign on as %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
    - g_free(buf);
    - return;
    - }
    -
    - flags = PURPLE_CONNECTION_FLAG_HTML;
    - if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
    - od->icq = TRUE;
    - } else {
    - flags |= PURPLE_CONNECTION_FLAG_AUTO_RESP;
    - }
    -
    - /* Set this flag based on the protocol_id rather than the username,
    - because that is what's tied to the get_moods protocol callback. */
    - if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq"))
    - flags |= PURPLE_CONNECTION_FLAG_SUPPORT_MOODS;
    -
    - purple_connection_set_flags(gc, flags);
    -
    - od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT);
    -
    - login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN);
    - encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
    - od->use_ssl = purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION) == FALSE;
    -
    - /* Connect to core Purple signals */
    - purple_prefs_connect_callback(purple_connection_get_protocol(gc), "/purple/away/idle_reporting", idle_reporting_pref_cb, gc);
    - purple_prefs_connect_callback(purple_connection_get_protocol(gc), "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc);
    -
    - if (purple_strequal(login_type, OSCAR_CLIENT_LOGIN) ||
    - purple_strequal(login_type, OSCAR_KERBEROS_LOGIN)) {
    - /* Create a SoupSession to be used for HTTP login requests. */
    - GProxyResolver *resolver;
    - GError *error;
    -
    - resolver = purple_proxy_get_proxy_resolver(account, &error);
    - if (resolver == NULL) {
    - purple_debug_error("oscar",
    - "Unable to get account proxy resolver: %s",
    - error->message);
    - purple_connection_g_error(gc, error);
    - return;
    - }
    -
    - od->http_conns = soup_session_new_with_options(
    - SOUP_SESSION_PROXY_RESOLVER, resolver, NULL);
    - g_object_unref(resolver);
    - }
    -
    - /*
    - * On 2008-03-05 AOL released some documentation on the OSCAR protocol
    - * which includes a new login method called clientLogin. It is similar
    - * (though not the same?) as what the AIM 6.0 series uses to
    - * authenticate.
    - *
    - * AIM 5.9 and lower use an MD5-based login procedure called "BUCP".
    - * This authentication method is used for both ICQ and AIM when
    - * clientLogin is not enabled.
    - */
    - if (purple_strequal(login_type, OSCAR_CLIENT_LOGIN)) {
    - /* Note: Actual server/port configuration is ignored here */
    - send_client_login(od, purple_account_get_username(account));
    - } else if (purple_strequal(login_type, OSCAR_KERBEROS_LOGIN)) {
    - const char *server;
    -
    - if (!od->use_ssl) {
    - purple_connection_error(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required Kerberos authentication but encryption is disabled in your account settings."));
    - return;
    - }
    - server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
    - /*
    - * If the account's server is what the oscar protocol has offered as
    - * the default login server through the vast eons (all two of
    - * said default options, AFAIK) and the user wants KDC, we'll
    - * do what we know is best for them and change the setting out
    - * from under them to the KDC login server.
    - */
    - if (purple_strequal(server, oscar_get_login_server(od->icq, FALSE)) ||
    - purple_strequal(server, oscar_get_login_server(od->icq, TRUE)) ||
    - purple_strequal(server, AIM_ALT_LOGIN_SERVER) ||
    - purple_strequal(server, "")) {
    - purple_debug_info("oscar", "Account uses Kerberos auth, so changing server to default KDC server\n");
    - purple_account_set_string(account, "server", AIM_DEFAULT_KDC_SERVER);
    - purple_account_set_int(account, "port", AIM_DEFAULT_KDC_PORT);
    - }
    - send_kerberos_login(od, purple_account_get_username(account));
    - } else {
    - FlapConnection *newconn;
    - const char *server;
    -
    - newconn = flap_connection_new(od, SNAC_FAMILY_AUTH);
    -
    - if (od->use_ssl) {
    - server = purple_account_get_string(account, "server", oscar_get_login_server(od->icq, TRUE));
    -
    - /*
    - * If the account's server is what the oscar protocol has offered as
    - * the default login server through the vast eons (all two of
    - * said default options, AFAIK) and the user wants SSL, we'll
    - * do what we know is best for them and change the setting out
    - * from under them to the SSL login server.
    - */
    - if (purple_strequal(server, oscar_get_login_server(od->icq, FALSE)) ||
    - purple_strequal(server, AIM_ALT_LOGIN_SERVER) ||
    - purple_strequal(server, AIM_DEFAULT_KDC_SERVER) ||
    - purple_strequal(server, "")) {
    - purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n");
    - purple_account_set_string(account, "server", oscar_get_login_server(od->icq, TRUE));
    - purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - server = oscar_get_login_server(od->icq, TRUE);
    - }
    -
    - newconn->gsc = purple_ssl_connect(account, server,
    - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - ssl_connection_established_cb, ssl_connection_error_cb, newconn);
    - } else {
    - server = purple_account_get_string(account, "server", oscar_get_login_server(od->icq, FALSE));
    -
    - /*
    - * See the comment above. We do the reverse here. If they don't want
    - * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER,
    - * set it back to the default.
    - */
    - if (purple_strequal(server, oscar_get_login_server(od->icq, TRUE)) ||
    - purple_strequal(server, AIM_DEFAULT_KDC_SERVER) ||
    - purple_strequal(server, "")) {
    - purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n");
    - purple_account_set_string(account, "server", oscar_get_login_server(od->icq, FALSE));
    - purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - server = oscar_get_login_server(od->icq, FALSE);
    - }
    -
    - newconn->connect_data = purple_proxy_connect(NULL, account, server,
    - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
    - connection_established_cb, newconn);
    - }
    -
    - if (newconn->gsc == NULL && newconn->connect_data == NULL) {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - return;
    - }
    - }
    -
    - purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS);
    -}
    -
    -void
    -oscar_close(PurpleConnection *gc)
    -{
    - OscarData *od;
    -
    - od = purple_connection_get_protocol_data(gc);
    -
    - g_slist_free_full(od->oscar_chats, (GDestroyNotify)oscar_chat_destroy);
    - while (od->create_rooms)
    - {
    - struct create_room *cr = od->create_rooms->data;
    - g_free(cr->name);
    - od->create_rooms = g_slist_remove(od->create_rooms, cr);
    - g_free(cr);
    - }
    - oscar_data_destroy(od);
    - purple_connection_set_protocol_data(gc, NULL);
    -
    - purple_prefs_disconnect_by_handle(gc);
    -
    - purple_debug_info("oscar", "Signed off.\n");
    -}
    -
    -int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname)
    -{
    - PurpleAccount *account;
    - FlapConnection *conn;
    -
    - account = purple_connection_get_account(gc);
    -
    - conn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
    - conn->cookielen = cookielen;
    - conn->cookie = g_memdup(cookie, cookielen);
    -
    - /*
    - * Use TLS only if the server provided us with a tls_certname. The server might not specify a tls_certname even if we requested to use TLS,
    - * and that is something we should be prepared to.
    - */
    - if (tls_certname)
    - {
    - conn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
    - ssl_connection_established_cb, ssl_connection_error_cb,
    - tls_certname, conn);
    - }
    - else
    - {
    - conn->connect_data = purple_proxy_connect(NULL,
    - account, host, port,
    - connection_established_cb, conn);
    - }
    -
    - if (conn->gsc == NULL && conn->connect_data == NULL)
    - {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
    - return 0;
    - }
    -
    - od->default_port = port;
    -
    - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
    -
    - return 1;
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static int
    -purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - char *host; int port;
    - gsize i;
    - FlapConnection *newconn;
    - va_list ap;
    - struct aim_authresp_info *info;
    -
    - port = purple_account_get_int(account, "port", od->default_port);
    -
    - va_start(ap, fr);
    - info = va_arg(ap, struct aim_authresp_info *);
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "inside auth_resp (Username: %s)\n", info->bn);
    -
    - if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
    - char buf[256];
    - switch (info->errorcode) {
    - case 0x01:
    - /* Unregistered username */
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist"));
    - break;
    - case 0x05:
    - /* Incorrect password */
    - if (!purple_account_get_remember_password(account))
    - purple_account_set_password(account, NULL, NULL, NULL);
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
    - break;
    - case 0x11:
    - /* Suspended account */
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended"));
    - break;
    - case 0x02:
    - case 0x14:
    - /* service temporarily unavailable */
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
    - break;
    - case 0x18:
    - /* username connecting too frequently */
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
    - break;
    - case 0x1c:
    - {
    - /* client too old */
    - g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"),
    - oscar_get_ui_info_string("website", PURPLE_WEBSITE));
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf);
    - break;
    - }
    - case 0x1d:
    - /* IP address connecting too frequently */
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."));
    - break;
    - default:
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason"));
    - break;
    - }
    - purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
    - purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : "");
    - return 1;
    - }
    -
    - purple_debug_misc("oscar",
    - "Reg status: %hu\n"
    - "Email: %s\n"
    - "BOSIP: %s\n",
    - info->regstatus, info->email ? info->email : "null",
    - info->bosip);
    - purple_debug_info("oscar", "Closing auth connection...\n");
    - flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL);
    -
    - for (i = 0; i < strlen(info->bosip); i++) {
    - if (info->bosip[i] == ':') {
    - port = atoi(&(info->bosip[i+1]));
    - break;
    - }
    - }
    - host = g_strndup(info->bosip, i);
    - newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
    - newconn->cookielen = info->cookielen;
    - newconn->cookie = g_memdup(info->cookie, info->cookielen);
    -
    - if (od->use_ssl)
    - {
    - /*
    - * This shouldn't be hardcoded to "bos.oscar.aol.com" except that
    - * the server isn't sending us a name to use for comparing the
    - * certificate common name.
    - */
    - newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
    - ssl_connection_established_cb, ssl_connection_error_cb,
    - "bos.oscar.aol.com", newconn);
    - }
    - else
    - {
    - newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
    - connection_established_cb, newconn);
    - }
    -
    - g_free(host);
    - if (newconn->gsc == NULL && newconn->connect_data == NULL)
    - {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
    - return 0;
    - }
    -
    - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
    -
    - return 1;
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static void
    -purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg)
    -{
    - PurpleConnection *gc = user_data;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - aim_auth_securid_send(od, msg);
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static void
    -purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value)
    -{
    - PurpleConnection *gc = user_data;
    -
    - /* Disconnect */
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
    - _("The SecurID key entered is invalid"));
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static int
    -purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - gchar *primary;
    -
    - purple_debug_info("oscar", "Got SecurID request\n");
    -
    - primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account));
    - purple_request_input(gc, NULL, _("Enter SecurID"), primary,
    - _("Enter the 6 digit number from the digital display."),
    - FALSE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb),
    - _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb),
    - purple_request_cpar_from_connection(gc),
    - gc);
    - g_free(primary);
    -
    - return 1;
    -}
    -
    -static int
    -purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - char *host, *separator;
    - int port;
    - FlapConnection *newconn;
    - va_list ap;
    - struct aim_redirect_data *redir;
    -
    - va_start(ap, fr);
    - redir = va_arg(ap, struct aim_redirect_data *);
    - va_end(ap);
    -
    - port = od->default_port;
    - separator = strchr(redir->ip, ':');
    - if (separator != NULL)
    - {
    - host = g_strndup(redir->ip, separator - redir->ip);
    - port = atoi(separator + 1);
    - }
    - else
    - host = g_strdup(redir->ip);
    -
    - if (!redir->use_ssl) {
    - const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
    - if (purple_strequal(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION)) {
    - purple_debug_warning("oscar", "We won't use SSL for FLAP type 0x%04hx.\n", redir->group);
    - } else if (purple_strequal(encryption_type, OSCAR_REQUIRE_ENCRYPTION)) {
    - purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.\n", host, port, redir->group);
    - purple_connection_error(
    - gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("You required encryption in your account settings, but one of the servers doesn't support it."));
    - return 0;
    - }
    - }
    -
    - /*
    - * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts
    - * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext.
    - */
    - if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN ||
    - redir->group == SNAC_FAMILY_BART))
    - {
    - purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", redir->group);
    - redir->use_ssl = 0;
    - }
    -
    - purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n", host, port, redir->group);
    -
    - newconn = flap_connection_new(od, redir->group);
    - newconn->cookielen = redir->cookielen;
    - newconn->cookie = g_memdup(redir->cookie, redir->cookielen);
    - if (newconn->type == SNAC_FAMILY_CHAT)
    - {
    - struct chat_connection *cc;
    - cc = g_new0(struct chat_connection, 1);
    - cc->conn = newconn;
    - cc->gc = gc;
    - cc->name = g_strdup(redir->chat.room);
    - cc->exchange = redir->chat.exchange;
    - cc->instance = redir->chat.instance;
    - cc->show = extract_name(redir->chat.room);
    - newconn->new_conn_data = cc;
    - purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange);
    - }
    -
    -
    - if (redir->use_ssl)
    - {
    - newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
    - ssl_connection_established_cb, ssl_connection_error_cb,
    - redir->ssl_cert_cn, newconn);
    - }
    - else
    - {
    - newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
    - connection_established_cb, newconn);
    - }
    -
    - if (newconn->gsc == NULL && newconn->connect_data == NULL)
    - {
    - flap_connection_schedule_destroy(newconn,
    - OSCAR_DISCONNECT_COULD_NOT_CONNECT,
    - _("Unable to initialize connection"));
    - purple_debug_error("oscar", "Unable to connect to FLAP server "
    - "of type 0x%04hx\n", redir->group);
    - }
    - g_free(host);
    -
    - return 1;
    -}
    -
    -
    -static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleBuddy *buddy = NULL;
    - PurpleStatus *previous_status = NULL;
    - struct buddyinfo *bi;
    - time_t time_idle = 0, signon = 0;
    - int type = 0;
    - gboolean buddy_is_away = FALSE;
    - const char *status_id;
    - va_list ap;
    - aim_userinfo_t *info;
    - char *message;
    - char *itmsurl = NULL;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - g_return_val_if_fail(info != NULL, 1);
    - g_return_val_if_fail(info->bn != NULL, 1);
    -
    - buddy = purple_blist_find_buddy(account, info->bn);
    - if (buddy) {
    - previous_status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
    - }
    -
    - /*
    - * If this is an AIM buddy and their name has formatting, set their
    - * server alias.
    - */
    - if (!oscar_util_valid_name_icq(info->bn)) {
    - gboolean bn_has_formatting = FALSE;
    - char *c;
    - for (c = info->bn; *c != '\0'; c++) {
    - if (!islower(*c)) {
    - bn_has_formatting = TRUE;
    - break;
    - }
    - }
    - purple_serv_got_alias(gc, info->bn,
    - bn_has_formatting ? info->bn : NULL);
    - }
    -
    - if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
    - if (info->flags & AIM_FLAG_AWAY)
    - buddy_is_away = TRUE;
    - }
    - if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
    - type = info->icqinfo.status;
    - if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
    - (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
    - buddy_is_away = TRUE;
    - }
    - }
    -
    - if (oscar_util_valid_name_icq(info->bn)) {
    - if (type & AIM_ICQ_STATE_CHAT)
    - status_id = OSCAR_STATUS_ID_FREE4CHAT;
    - else if (type & AIM_ICQ_STATE_DND)
    - status_id = OSCAR_STATUS_ID_DND;
    - else if (type & AIM_ICQ_STATE_OUT)
    - status_id = OSCAR_STATUS_ID_NA;
    - else if (type & AIM_ICQ_STATE_BUSY)
    - status_id = OSCAR_STATUS_ID_OCCUPIED;
    - else if (type & AIM_ICQ_STATE_AWAY)
    - status_id = OSCAR_STATUS_ID_AWAY;
    - else if (type & AIM_ICQ_STATE_INVISIBLE)
    - status_id = OSCAR_STATUS_ID_INVISIBLE;
    - else if (type & AIM_ICQ_STATE_EVIL)
    - status_id = OSCAR_STATUS_ID_EVIL;
    - else if (type & AIM_ICQ_STATE_DEPRESSION)
    - status_id = OSCAR_STATUS_ID_DEPRESSION;
    - else if (type & AIM_ICQ_STATE_ATHOME)
    - status_id = OSCAR_STATUS_ID_ATHOME;
    - else if (type & AIM_ICQ_STATE_ATWORK)
    - status_id = OSCAR_STATUS_ID_ATWORK;
    - else if (type & AIM_ICQ_STATE_LUNCH)
    - status_id = OSCAR_STATUS_ID_LUNCH;
    - else
    - status_id = OSCAR_STATUS_ID_AVAILABLE;
    - } else {
    - if (type & AIM_ICQ_STATE_INVISIBLE)
    - status_id = OSCAR_STATUS_ID_INVISIBLE;
    - else if (buddy_is_away)
    - status_id = OSCAR_STATUS_ID_AWAY;
    - else
    - status_id = OSCAR_STATUS_ID_AVAILABLE;
    - }
    -
    - if (info->flags & AIM_FLAG_WIRELESS) {
    - purple_protocol_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL);
    - } else {
    - purple_protocol_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
    - }
    -
    - message = (info->status && info->status_len > 0)
    - ? oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len)
    - : NULL;
    -
    - if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE)) {
    - /* TODO: If itmsurl is NULL, does that mean the URL has been
    - cleared? Or does it mean the URL should remain unchanged? */
    - if (info->itmsurl != NULL) {
    - itmsurl = (info->itmsurl_len > 0) ? oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len) : NULL;
    - } else if (previous_status != NULL && purple_status_is_available(previous_status)) {
    - itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl"));
    - }
    - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message ? message : "(null)", itmsurl ? itmsurl : "(null)");
    - purple_protocol_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL);
    - } else {
    - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message ? message : "(null)");
    - purple_protocol_got_user_status(account, info->bn, status_id, "message", message, NULL);
    - }
    -
    - g_free(message);
    - g_free(itmsurl);
    -
    - /* Login time stuff */
    - if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
    - signon = info->onlinesince;
    - else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
    - signon = time(NULL) - info->sessionlen;
    - purple_protocol_got_user_login_time(account, info->bn, signon);
    -
    - /* Idle time stuff */
    - /* info->idletime is the number of minutes that this user has been idle */
    - if (info->present & AIM_USERINFO_PRESENT_IDLE)
    - time_idle = time(NULL) - info->idletime * 60;
    -
    - if (time_idle > 0)
    - purple_protocol_got_user_idle(account, info->bn, TRUE, time_idle);
    - else
    - purple_protocol_got_user_idle(account, info->bn, FALSE, 0);
    -
    - /* Server stored icon stuff */
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->bn));
    - if (!bi) {
    - bi = g_new0(struct buddyinfo, 1);
    - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->bn)), bi);
    - }
    - bi->typingnot = FALSE;
    - bi->ico_informed = FALSE;
    - bi->ipaddr = info->icqinfo.ipaddr;
    -
    - if (info->iconcsumlen) {
    - const char *saved_b16 = NULL;
    - char *b16 = NULL;
    - PurpleBuddy *b = NULL;
    -
    - b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen);
    - b = purple_blist_find_buddy(account, info->bn);
    - if (b != NULL)
    - saved_b16 = purple_buddy_icons_get_checksum_for_user(b);
    -
    - if (!b16 || !saved_b16 || !purple_strequal(b16, saved_b16)) {
    - /* Invalidate the old icon for this user */
    - purple_buddy_icons_set_for_user(account, info->bn, NULL, 0, NULL);
    -
    - /* Fetch the new icon (if we're not already doing so) */
    - if (g_slist_find_custom(od->requesticon, info->bn,
    - (GCompareFunc)oscar_util_name_compare) == NULL)
    - {
    - od->requesticon = g_slist_prepend(od->requesticon,
    - g_strdup(purple_normalize(account, info->bn)));
    - purple_icons_fetch(gc);
    - }
    - }
    - g_free(b16);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - va_list ap;
    - aim_userinfo_t *info;
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - purple_protocol_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL);
    - purple_protocol_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
    - g_hash_table_remove(od->buddyinfo, purple_normalize(purple_connection_get_account(gc), info->bn));
    -
    - return 1;
    -}
    -
    -static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleMessageFlags flags = 0;
    - struct buddyinfo *bi;
    - PurpleImage *img;
    - gchar *tmp;
    - const char *start, *end;
    - GData *attribs;
    -
    - purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn);
    -
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
    - if (!bi) {
    - bi = g_new0(struct buddyinfo, 1);
    - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, userinfo->bn)), bi);
    - }
    -
    - if (args->icbmflags & AIM_IMFLAGS_AWAY)
    - flags |= PURPLE_MESSAGE_AUTO_RESP;
    -
    - if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT)
    - bi->typingnot = TRUE;
    - else
    - bi->typingnot = FALSE;
    -
    - if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
    - purple_debug_misc("oscar", "%s has an icon\n", userinfo->bn);
    - if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
    - bi->ico_need = TRUE;
    - bi->ico_len = args->iconlen;
    - bi->ico_csum = args->iconsum;
    - bi->ico_time = args->iconstamp;
    - }
    - }
    -
    - img = purple_buddy_icons_find_account_icon(account);
    - if ((img != NULL) &&
    - (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
    - gconstpointer data = purple_image_get_data(img);
    - size_t len = purple_image_get_data_size(img);
    - purple_debug_info("oscar",
    - "Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n",
    - userinfo->bn, len);
    - aim_im_sendch2_icon(od, userinfo->bn, data, len,
    - purple_buddy_icons_get_account_icon_timestamp(account),
    - aimutil_iconsum(data, len));
    - }
    - g_object_unref(img);
    -
    - tmp = g_strdup(args->msg);
    -
    - /*
    - * Convert iChat color tags to normal font tags.
    - */
    - if (purple_markup_find_tag("body", tmp, &start, &end, &attribs))
    - {
    - int len;
    - char *tmp2, *body;
    - const char *ichattextcolor, *ichatballooncolor;
    - const char *slash_body_start, *slash_body_end = NULL; /* </body> */
    - GData *unused;
    -
    - /*
    - * Find the ending </body> so we can strip off the outer <html/>
    - * and <body/>
    - */
    - if (purple_markup_find_tag("/body", end + 1, &slash_body_start, &slash_body_end, &unused))
    - {
    - body = g_strndup(start, slash_body_end - start + 1);
    - g_datalist_clear(&unused);
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Broken message contains <body> but not </body>!\n");
    - /* Take everything after <body> */
    - body = g_strdup(start);
    - }
    -
    - ichattextcolor = g_datalist_get_data(&attribs, "ichattextcolor");
    - if (ichattextcolor != NULL)
    - {
    - tmp2 = g_strdup_printf("<font color=\"%s\">%s</font>", ichattextcolor, body);
    - g_free(body);
    - body = tmp2;
    - }
    -
    - ichatballooncolor = g_datalist_get_data(&attribs, "ichatballooncolor");
    - if (ichatballooncolor != NULL)
    - {
    - tmp2 = g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor, body);
    - g_free(body);
    - body = tmp2;
    - }
    -
    - g_datalist_clear(&attribs);
    -
    - len = start - tmp;
    - tmp2 = g_strdup_printf("%.*s%s%s", len, tmp, body, slash_body_end ? slash_body_end + 1: "</body>");
    - g_free(tmp);
    - g_free(body);
    -
    - tmp = tmp2;
    - }
    -
    - /*
    - * Are there <html/> surrounding tags? If so, strip them out, too.
    - */
    - if (purple_markup_find_tag("html", tmp, &start, &end, &attribs))
    - {
    - gchar *tmp2;
    - int len;
    -
    - g_datalist_clear(&attribs);
    -
    - len = start - tmp;
    - tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
    - g_free(tmp);
    - tmp = tmp2;
    - }
    -
    - if (purple_markup_find_tag("/html", tmp, &start, &end, &attribs))
    - {
    - gchar *tmp2;
    - int len;
    -
    - g_datalist_clear(&attribs);
    -
    - len = start - tmp;
    - tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
    - g_free(tmp);
    - tmp = tmp2;
    - }
    -
    - purple_serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
    - g_free(tmp);
    -
    - return 1;
    -}
    -
    -static int
    -incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, IcbmArgsCh2 *args)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleMessageFlags flags = 0;
    - char *message = NULL;
    -
    - g_return_val_if_fail(od != NULL, 0);
    - g_return_val_if_fail(od->gc != NULL, 0);
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    - od = purple_connection_get_protocol_data(gc);
    -
    - if (args == NULL)
    - return 0;
    -
    - purple_debug_misc("oscar", "Incoming rendezvous message of type %"
    - G_GUINT64_FORMAT ", user %s, status %hu\n",
    - args->type, userinfo->bn, args->status);
    -
    - if (args->msg != NULL) {
    - message = oscar_encoding_to_utf8(args->encoding, args->msg, args->msglen);
    - }
    -
    - if (args->type & OSCAR_CAPABILITY_CHAT)
    - {
    - char *utf8name, *tmp;
    - GHashTable *components;
    -
    - if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
    - g_free(message);
    - return 1;
    - }
    - utf8name = oscar_encoding_to_utf8(args->encoding, args->info.chat.roominfo.name, args->info.chat.roominfo.namelen);
    -
    - tmp = extract_name(utf8name);
    - if (tmp != NULL)
    - {
    - g_free(utf8name);
    - utf8name = tmp;
    - }
    -
    - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
    - g_free);
    - g_hash_table_replace(components, g_strdup("room"), utf8name);
    - g_hash_table_replace(components, g_strdup("exchange"),
    - g_strdup_printf("%d", args->info.chat.roominfo.exchange));
    - purple_serv_got_chat_invite(gc,
    - utf8name,
    - userinfo->bn,
    - message,
    - components);
    - }
    -
    - else if ((args->type & OSCAR_CAPABILITY_SENDFILE) || (args->type & OSCAR_CAPABILITY_DIRECTIM))
    - {
    - if (args->status == AIM_RENDEZVOUS_PROPOSE)
    - {
    - peer_connection_got_proposition(od, userinfo->bn, message, args);
    - }
    - else if (args->status == AIM_RENDEZVOUS_CANCEL)
    - {
    - /* The other user cancelled a peer request */
    - PeerConnection *conn;
    -
    - conn = peer_connection_find_by_cookie(od, userinfo->bn, args->cookie);
    - /*
    - * If conn is NULL it means we haven't tried to create
    - * a connection with that user. They may be trying to
    - * do something malicious.
    - */
    - if (conn != NULL)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - }
    - }
    - else if (args->status == AIM_RENDEZVOUS_CONNECTED)
    - {
    - /*
    - * Remote user has accepted our peer request. If we
    - * wanted to we could look up the PeerConnection using
    - * args->cookie, but we don't need to do anything here.
    - */
    - }
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_GETFILE)
    - {
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_TALK)
    - {
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_BUDDYICON)
    - {
    - purple_buddy_icons_set_for_user(account, userinfo->bn,
    - g_memdup(args->info.icon.icon, args->info.icon.length),
    - args->info.icon.length,
    - NULL);
    - }
    -
    - else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY)
    - {
    - purple_debug_info("oscar", "Got an ICQ Server Relay message of "
    - "type %d\n", args->info.rtfmsg.msgtype);
    -
    - if (args->info.rtfmsg.msgtype == 1) {
    - if (args->info.rtfmsg.msg != NULL) {
    - char *rtfmsg;
    - const char *encoding = args->encoding;
    - size_t len = strlen(args->info.rtfmsg.msg);
    - char *tmp, *tmp2;
    -
    - if (encoding == NULL && !g_utf8_validate(args->info.rtfmsg.msg, len, NULL)) {
    - /* Yet another wonderful Miranda-related hack. If their user disables the "Send Unicode messages" setting,
    - * Miranda sends us ch2 messages in whatever Windows codepage is set as default on their user's system (instead of UTF-8).
    - * Of course, they don't bother to specify that codepage. Let's just fallback to the encoding OUR users can
    - * specify in account options as a last resort.
    - */
    - encoding = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - purple_debug_info("oscar", "Miranda, is that you? Using '%s' as encoding\n", encoding);
    - }
    -
    - rtfmsg = oscar_encoding_to_utf8(encoding, args->info.rtfmsg.msg, len);
    -
    - /* Channel 2 messages are supposed to be plain-text (never mind the name "rtfmsg", even
    - * the official client doesn't parse them as RTF). Therefore, we should escape them before
    - * showing to the user. */
    - tmp = g_markup_escape_text(rtfmsg, -1);
    - g_free(rtfmsg);
    - tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
    - g_free(tmp);
    -
    - purple_serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
    - aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie);
    - g_free(tmp2);
    - }
    - } else if (args->info.rtfmsg.msgtype == 26) {
    - purple_debug_info("oscar", "Sending X-Status Reply\n");
    - icq_relay_xstatus(od, userinfo->bn, args->cookie);
    - }
    - }
    - else
    - {
    - purple_debug_error("oscar", "Unknown request class %"
    - G_GUINT64_FORMAT "\n", args->type);
    - }
    -
    - g_free(message);
    -
    - return 1;
    -}
    -
    -/* When someone sends you buddies */
    -static void
    -purple_icq_buddyadd(struct name_data *data)
    -{
    - PurpleConnection *gc = data->gc;
    -
    - purple_blist_request_add_buddy(purple_connection_get_account(gc), data->name, NULL, data->nick);
    -
    - oscar_free_name_data(data);
    -}
    -
    -static int
    -incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo,
    - struct aim_incomingim_ch4_args *args)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - gchar **msg1, **msg2;
    - int i, numtoks;
    -
    - if (!args->type || !args->msg || !args->uin)
    - return 1;
    -
    - purple_debug_info("oscar",
    - "Received a channel 4 message of type 0x%02hx.",
    - (guint16)args->type);
    -
    - /*
    - * Split up the message at the delimeter character, then convert each
    - * string to UTF-8. Unless, of course, this is a type 1 message. If
    - * this is a type 1 message, then the delimiter 0xfe could be a valid
    - * character in whatever encoding the message was sent in. Type 1
    - * messages are always made up of only one part, so we can easily account
    - * for this suck-ass part of the protocol by splitting the string into at
    - * most 1 baby string.
    - */
    - msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0));
    - for (numtoks=0; msg1[numtoks]; numtoks++);
    - msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
    - for (i=0; msg1[i]; i++) {
    - gchar *uin = g_strdup_printf("%u", args->uin);
    -
    - purple_str_strip_char(msg1[i], '\r');
    - /* TODO: Should use an encoding other than ASCII? */
    - msg2[i] = oscar_decode_im(account, uin, AIM_CHARSET_ASCII, msg1[i], strlen(msg1[i]));
    - g_free(uin);
    - }
    - msg2[i] = NULL;
    -
    - switch (args->type) {
    - case 0x01: { /* MacICQ message or basic offline message */
    - if (i >= 1) {
    - gchar *uin = g_strdup_printf("%u", args->uin);
    - gchar *tmp;
    -
    - /* If the message came from an ICQ user then escape any HTML */
    - tmp = g_markup_escape_text(msg2[0], -1);
    -
    - purple_serv_got_im(gc, uin, tmp, 0, time(NULL));
    -
    - g_free(uin);
    - g_free(tmp);
    - }
    - } break;
    -
    - case 0x04: { /* Someone sent you a URL */
    - if (i >= 2) {
    - if (msg2[1] != NULL) {
    - gchar *uin = g_strdup_printf("%u", args->uin);
    - gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>",
    - msg2[1],
    - (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]);
    - purple_serv_got_im(gc, uin, message, 0, time(NULL));
    - g_free(uin);
    - g_free(message);
    - }
    - }
    - } break;
    -
    - case 0x06: { /* Someone requested authorization */
    - if (i >= 6) {
    - gchar *bn = g_strdup_printf("%u", args->uin);
    - gchar *reason = NULL;
    -
    - if (msg2[5] != NULL)
    - reason = oscar_decode_im(account, bn, AIM_CHARSET_LATIN_1, msg2[5], strlen(msg2[5]));
    -
    - purple_debug_info("oscar",
    - "Received an authorization request from UIN %u\n",
    - args->uin);
    - aim_icq_getalias(od, bn, TRUE, reason);
    - g_free(bn);
    - g_free(reason);
    - }
    - } break;
    -
    - case 0x07: { /* Someone has denied you authorization */
    - if (i >= 1) {
    - gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
    - purple_notify_info(gc, NULL, _("ICQ authorization denied."), dialog_msg, purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x08: { /* Someone has granted you authorization */
    - gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin);
    - purple_notify_info(gc, NULL, "ICQ authorization accepted.", dialog_msg, purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - } break;
    -
    - case 0x09: { /* Message from the Godly ICQ server itself, I think */
    - if (i >= 5) {
    - gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
    - purple_notify_info(gc, NULL, "ICQ Server Message", dialog_msg, purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
    - if (i >= 6) {
    - gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
    - purple_notify_info(gc, NULL, "ICQ Page", dialog_msg, purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
    - if (i >= 6) {
    - gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
    - purple_notify_info(gc, NULL, "ICQ Email", dialog_msg, purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - }
    - } break;
    -
    - case 0x12: {
    - /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
    - /* Someone added you to their buddy list? */
    - } break;
    -
    - case 0x13: { /* Someone has sent you some ICQ buddies */
    - guint i, num;
    - gchar **text;
    - text = g_strsplit(args->msg, "\376", 0);
    - if (text) {
    - /* Read the number of contacts that we were sent */
    - errno = 0;
    - num = text[0] ? strtoul(text[0], NULL, 10) : 0;
    -
    - if (num > 0 && errno == 0) {
    - for (i=0; i<num; i++) {
    - struct name_data *data;
    - gchar *message;
    -
    - if (!text[i*2 + 1] || !text[i*2 + 2]) {
    - /* We're missing the contact name or nickname. Bail out. */
    - gchar *tmp = g_strescape(args->msg, NULL);
    - purple_debug_error("oscar", "Unknown syntax parsing "
    - "ICQ buddies. args->msg=%s\n", tmp);
    - g_free(tmp);
    - break;
    - }
    -
    - message = g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]);
    -
    - data = g_new(struct name_data, 1);
    - data->gc = gc;
    - data->name = g_strdup(text[i*2+1]);
    - data->nick = g_strdup(text[i*2+2]);
    -
    - purple_request_action(gc, NULL, message,
    - _("Do you want to add this buddy "
    - "to your buddy list?"),
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_request_cpar_from_connection(gc),
    - data, 2,
    - _("_Add"), G_CALLBACK(purple_icq_buddyadd),
    - _("_Decline"), G_CALLBACK(oscar_free_name_data));
    - g_free(message);
    - }
    - } else {
    - gchar *tmp = g_strescape(args->msg, NULL);
    - purple_debug_error("oscar", "Unknown syntax parsing "
    - "ICQ buddies. args->msg=%s\n", tmp);
    - g_free(tmp);
    - }
    - g_strfreev(text);
    - }
    - } break;
    -
    - case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */
    - ByteStream qbs;
    - guint16 smstype;
    - guint32 taglen, smslen;
    - char *tagstr = NULL, *smsmsg = NULL;
    - PurpleXmlNode *xmlroot = NULL, *xmltmp = NULL;
    - gchar *uin = NULL, *message = NULL;
    -
    - /* From libicq2000-0.3.2/src/ICQ.cpp */
    - byte_stream_init(&qbs, (guint8 *)args->msg, args->msglen);
    - byte_stream_advance(&qbs, 21);
    - /* expected: 01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/
    - /* unexpected: 00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */
    - smstype = byte_stream_getle16(&qbs);
    - if (smstype != 0)
    - break;
    - taglen = byte_stream_getle32(&qbs);
    - if (taglen > 2000) {
    - /* Avoid trying to allocate large amounts of memory, in
    - case we get something unexpected. */
    - break;
    - }
    - tagstr = byte_stream_getstr(&qbs, taglen);
    - if (tagstr == NULL)
    - break;
    - byte_stream_advance(&qbs, 3);
    - byte_stream_advance(&qbs, 4);
    - smslen = byte_stream_getle32(&qbs);
    - if (smslen > 2000) {
    - /* Avoid trying to allocate large amounts of memory, in
    - case we get something unexpected. */
    - g_free(tagstr);
    - break;
    - }
    - smsmsg = byte_stream_getstr(&qbs, smslen);
    -
    - /* Check if this is an SMS being sent from server */
    - if (purple_strequal(tagstr, "ICQSMS") && smsmsg != NULL) {
    - xmlroot = purple_xmlnode_from_str(smsmsg, -1);
    - if (xmlroot != NULL)
    - {
    - xmltmp = purple_xmlnode_get_child(xmlroot, "sender");
    - if (xmltmp != NULL)
    - uin = purple_xmlnode_get_data(xmltmp);
    -
    - xmltmp = purple_xmlnode_get_child(xmlroot, "text");
    - if (xmltmp != NULL)
    - message = purple_xmlnode_get_data(xmltmp);
    -
    - if ((uin != NULL) && (message != NULL))
    - purple_serv_got_im(gc, uin, message, 0, time(NULL));
    -
    - g_free(uin);
    - g_free(message);
    - purple_xmlnode_free(xmlroot);
    - }
    - }
    - g_free(tagstr);
    - g_free(smsmsg);
    - } break;
    -
    - default: {
    - purple_debug_info("oscar",
    - "Received a channel 4 message of unknown type "
    - "(type 0x%02x).\n", args->type & 0xFF);
    - } break;
    - }
    -
    - g_strfreev(msg1);
    - g_strfreev(msg2);
    -
    - return 1;
    -}
    -
    -static int purple_parse_incoming_im(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - guint16 channel;
    - int ret = 0;
    - aim_userinfo_t *userinfo;
    - va_list ap;
    -
    - va_start(ap, fr);
    - channel = (guint16)va_arg(ap, unsigned int);
    - userinfo = va_arg(ap, aim_userinfo_t *);
    -
    - switch (channel) {
    - case 1: { /* standard message */
    - struct aim_incomingim_ch1_args *args;
    - args = va_arg(ap, struct aim_incomingim_ch1_args *);
    - ret = incomingim_chan1(od, conn, userinfo, args);
    - } break;
    -
    - case 2: { /* rendezvous */
    - IcbmArgsCh2 *args;
    - args = va_arg(ap, IcbmArgsCh2 *);
    - ret = incomingim_chan2(od, conn, userinfo, args);
    - } break;
    -
    - case 4: { /* ICQ */
    - struct aim_incomingim_ch4_args *args;
    - args = va_arg(ap, struct aim_incomingim_ch4_args *);
    - ret = incomingim_chan4(od, conn, userinfo, args);
    - } break;
    -
    - default: {
    - purple_debug_warning("oscar",
    - "ICBM received on unsupported channel (channel "
    - "0x%04hx).", channel);
    - } break;
    - }
    -
    - va_end(ap);
    -
    - return ret;
    -}
    -
    -static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - char *buf;
    - va_list ap;
    - guint16 nummissed, reason;
    - aim_userinfo_t *userinfo;
    -
    - va_start(ap, fr);
    - va_arg(ap, unsigned int); /* guint16 chan */
    - userinfo = va_arg(ap, aim_userinfo_t *);
    - nummissed = (guint16)va_arg(ap, unsigned int);
    - reason = (guint16)va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - switch(reason) {
    - case 0: /* Invalid (0) */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because it was invalid.",
    - "You missed %hu messages from %s because they were invalid.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 1: /* Message too large */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because it was too large.",
    - "You missed %hu messages from %s because they were too large.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 2: /* Rate exceeded */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because the rate limit has been exceeded.",
    - "You missed %hu messages from %s because the rate limit has been exceeded.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 3: /* Evil Sender */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because his/her warning level is too high.",
    - "You missed %hu messages from %s because his/her warning level is too high.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - case 4: /* Evil Receiver */
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s because your warning level is too high.",
    - "You missed %hu messages from %s because your warning level is too high.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - default:
    - buf = g_strdup_printf(
    - dngettext(PACKAGE,
    - "You missed %hu message from %s for an unknown reason.",
    - "You missed %hu messages from %s for an unknown reason.",
    - nummissed),
    - nummissed,
    - userinfo->bn);
    - break;
    - }
    -
    - if (!purple_conversation_present_error(userinfo->bn, account, buf)) {
    - purple_notify_error(od->gc, NULL, buf, NULL,
    - purple_request_cpar_from_connection(od->gc));
    - }
    - g_free(buf);
    -
    - return 1;
    -}
    -
    -static int
    -purple_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, const guchar *cookie)
    -{
    - if (reason == 0x0003)
    - {
    - /* Rendezvous was refused. */
    - PeerConnection *conn;
    -
    - conn = peer_connection_find_by_cookie(od, who, cookie);
    -
    - if (conn == NULL)
    - {
    - purple_debug_info("oscar", "Received a rendezvous cancel message "
    - "for a nonexistant connection from %s.\n", who);
    - }
    - else
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_REFUSED, NULL);
    - }
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Received an unknown rendezvous "
    - "message from %s. Type 0x%04hx\n", who, reason);
    - }
    -
    - return 0;
    -}
    -
    -static int purple_parse_clientauto_ch4(OscarData *od, const char *who, guint16 reason, guint32 state, char *msg) {
    - PurpleConnection *gc = od->gc;
    -
    - switch(reason) {
    - /* Reply from an ICQ status message request */
    - case 0x0003:
    - case 0x0006: {
    - char *statusmsg, **splitmsg;
    - PurpleNotifyUserInfo *user_info;
    -
    - statusmsg = oscar_icqstatus(state);
    -
    - /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
    - /* TODO: Don't we need to escape each piece? */
    - splitmsg = g_strsplit(msg, "\r\n", 0);
    -
    - user_info = purple_notify_user_info_new();
    -
    - purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who);
    - /* TODO: Check whether it's correct to call add_pair_html,
    - or if we should be using add_pair_plaintext */
    - purple_notify_user_info_add_pair_html(user_info, _("Status"), statusmsg);
    - purple_notify_user_info_add_section_break(user_info);
    - purple_notify_user_info_add_pair_html(user_info, NULL, g_strjoinv("<BR>", splitmsg));
    -
    - g_free(statusmsg);
    - g_strfreev(splitmsg);
    -
    - purple_notify_userinfo(gc, who, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -
    - } break;
    -
    - default: {
    - purple_debug_warning("oscar",
    - "Received an unknown client auto-response from %s. "
    - "Type 0x%04hx\n", who, reason);
    - } break;
    - } /* end of switch */
    -
    - return 0;
    -}
    -
    -static int purple_parse_clientauto(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 chan, reason;
    - char *who;
    - int ret = 1;
    -
    - va_start(ap, fr);
    - chan = (guint16)va_arg(ap, unsigned int);
    - who = va_arg(ap, char *);
    - reason = (guint16)va_arg(ap, unsigned int);
    -
    - if (chan == 0x0002) { /* File transfer declined */
    - guchar *cookie = va_arg(ap, guchar *);
    - ret = purple_parse_clientauto_ch2(od, who, reason, cookie);
    - } else if (chan == 0x0004) { /* ICQ message */
    - guint32 state = 0;
    - char *msg = NULL;
    - if (reason == 0x0003) {
    - state = va_arg(ap, guint32);
    - msg = va_arg(ap, char *);
    - }
    - ret = purple_parse_clientauto_ch4(od, who, reason, state, msg);
    - }
    -
    - va_end(ap);
    -
    - return ret;
    -}
    -
    -static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 reason;
    -
    - va_start(ap, fr);
    - reason = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_error("oscar", "snac threw error (reason 0x%04hx: %s)\n",
    - reason, oscar_get_msgerr_reason(reason));
    - return 1;
    -}
    -
    -static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 channel, event;
    - char *bn;
    -
    - va_start(ap, fr);
    - channel = (guint16) va_arg(ap, unsigned int);
    - bn = va_arg(ap, char *);
    - event = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - switch (event) {
    - case 0x0000: /* Text has been cleared */
    - case 0x000f: /* Closed IM window */
    - purple_serv_got_typing_stopped(gc, bn);
    - break;
    -
    - case 0x0001: /* Paused typing */
    - purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPED);
    - break;
    -
    - case 0x0002: /* Typing */
    - purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPING);
    - break;
    -
    - default:
    - purple_debug_info("oscar", "Received unknown typing "
    - "notification message from %s. Channel is 0x%04x "
    - "and event is 0x%04hx.\n", bn, channel, event);
    - break;
    - }
    -
    - return 1;
    -}
    -
    -static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - char *msg;
    - guint16 id;
    - va_list ap;
    -
    - va_start(ap, fr);
    - id = (guint16) va_arg(ap, unsigned int);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
    - if (id < 4) {
    - purple_notify_warning(od->gc, NULL,
    - _("Your AIM connection may be lost."), NULL,
    - purple_request_cpar_from_connection(od->gc));
    - }
    -
    - return 1;
    -}
    -
    -static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 type;
    -
    - va_start(ap, fr);
    - type = (guint16) va_arg(ap, unsigned int);
    -
    - switch(type) {
    - case 0x0002: {
    - GString *msg = g_string_new("");
    - guint8 maxrooms;
    - struct aim_chat_exchangeinfo *exchanges;
    - int exchangecount, i;
    -
    - maxrooms = (guint8) va_arg(ap, unsigned int);
    - exchangecount = va_arg(ap, int);
    - exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
    -
    - g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %d, Exchange List (%d total): ", (int)maxrooms, exchangecount);
    - for (i = 0; i < exchangecount; i++) {
    - g_string_append_printf(msg, "%hu", exchanges[i].number);
    - if (exchanges[i].name) {
    - g_string_append_printf(msg, " %s", exchanges[i].name);
    - }
    - g_string_append(msg, ", ");
    - }
    - purple_debug_misc("oscar", "%s\n", msg->str);
    - g_string_free(msg, TRUE);
    -
    - while (od->create_rooms) {
    - struct create_room *cr = od->create_rooms->data;
    - purple_debug_info("oscar",
    - "creating room %s\n", cr->name);
    - aim_chatnav_createroom(od, conn, cr->name, cr->exchange);
    - g_free(cr->name);
    - od->create_rooms = g_slist_remove(od->create_rooms, cr);
    - g_free(cr);
    - }
    - }
    - break;
    - case 0x0008: {
    - char *fqcn, *name, *ck;
    - guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
    - guint8 createperms;
    - guint32 createtime;
    -
    - fqcn = va_arg(ap, char *);
    - instance = (guint16)va_arg(ap, unsigned int);
    - exchange = (guint16)va_arg(ap, unsigned int);
    - flags = (guint16)va_arg(ap, unsigned int);
    - createtime = va_arg(ap, guint32);
    - maxmsglen = (guint16)va_arg(ap, unsigned int);
    - maxoccupancy = (guint16)va_arg(ap, unsigned int);
    - createperms = (guint8)va_arg(ap, unsigned int);
    - unknown = (guint16)va_arg(ap, unsigned int);
    - name = va_arg(ap, char *);
    - ck = va_arg(ap, char *);
    -
    - purple_debug_misc("oscar",
    - "created room: %s %hu %hu %hu %u %hu %hu %u %hu %s %s\n",
    - fqcn ? fqcn : "(null)", exchange, instance, flags, createtime,
    - maxmsglen, maxoccupancy, (guint)createperms, unknown,
    - name ? name : "(null)", ck);
    - aim_chat_join(od, exchange, ck, instance);
    - }
    - break;
    - default:
    - purple_debug_warning("oscar",
    - "chatnav info: unknown type (%04hx)\n", type);
    - break;
    - }
    -
    - va_end(ap);
    -
    - return 1;
    -}
    -
    -static int purple_chat_conversation_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - int count, i;
    - aim_userinfo_t *info;
    - PurpleConnection *gc = od->gc;
    -
    - struct chat_connection *c = NULL;
    -
    - va_start(ap, fr);
    - count = va_arg(ap, int);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - c = find_oscar_chat_by_conn(gc, conn);
    - if (!c)
    - return 1;
    -
    - for (i = 0; i < count; i++)
    - purple_chat_conversation_add_user(c->conv, info[i].bn, NULL, PURPLE_CHAT_USER_NONE, TRUE);
    -
    - return 1;
    -}
    -
    -static int purple_chat_conversation_left(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - int count, i;
    - aim_userinfo_t *info;
    - PurpleConnection *gc = od->gc;
    -
    - struct chat_connection *c = NULL;
    -
    - va_start(ap, fr);
    - count = va_arg(ap, int);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - c = find_oscar_chat_by_conn(gc, conn);
    - if (!c)
    - return 1;
    -
    - for (i = 0; i < count; i++)
    - purple_chat_conversation_remove_user(c->conv, info[i].bn, NULL);
    -
    - return 1;
    -}
    -
    -static int purple_chat_conversation_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 maxmsglen, maxvisiblemsglen;
    - PurpleConnection *gc = od->gc;
    - struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
    -
    - if (!ccon)
    - return 1;
    -
    - va_start(ap, fr);
    - maxmsglen = (guint16)va_arg(ap, unsigned int);
    - maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
    - maxmsglen, maxvisiblemsglen);
    -
    - ccon->maxlen = maxmsglen;
    - ccon->maxvis = maxvisiblemsglen;
    -
    - return 1;
    -}
    -
    -static int purple_chat_conversation_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
    - gchar *utf8;
    - va_list ap;
    - aim_userinfo_t *info;
    - int len;
    - char *msg;
    - char *charset;
    -
    - if (!ccon)
    - return 1;
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - len = va_arg(ap, int);
    - msg = va_arg(ap, char *);
    - charset = va_arg(ap, char *);
    - va_end(ap);
    -
    - utf8 = oscar_encoding_to_utf8(charset, msg, len);
    - purple_serv_got_chat_in(gc, ccon->id, info->bn,
    - PURPLE_MESSAGE_RECV, utf8, time(NULL));
    - g_free(utf8);
    -
    - return 1;
    -}
    -
    -static int purple_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - struct aim_emailinfo *emailinfo;
    - int havenewmail;
    - char *alertitle, *alerturl;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - emailinfo = va_arg(ap, struct aim_emailinfo *);
    - havenewmail = va_arg(ap, int);
    - alertitle = va_arg(ap, char *);
    - alerturl = va_arg(ap, char *);
    - va_end(ap);
    -
    - if (account != NULL && emailinfo != NULL && purple_account_get_check_mail(account) &&
    - emailinfo->unread && havenewmail) {
    - gchar *to = g_strdup_printf("%s%s%s",
    - purple_account_get_username(account),
    - emailinfo->domain ? "@" : "",
    - emailinfo->domain ? emailinfo->domain : "");
    - const char *tos[2] = { to };
    - const char *urls[2] = { emailinfo->url };
    - purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL,
    - tos, urls, NULL, NULL);
    - g_free(to);
    - }
    -
    - if (alertitle)
    - purple_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : "");
    -
    - return 1;
    -}
    -
    -static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - char *bn;
    - guint8 *iconcsum, *icon;
    - guint16 iconcsumlen, iconlen;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - va_arg(ap, int); /* iconsumtype */
    - iconcsum = va_arg(ap, guint8 *);
    - iconcsumlen = va_arg(ap, int);
    - icon = va_arg(ap, guint8 *);
    - iconlen = va_arg(ap, int);
    - va_end(ap);
    -
    - /*
    - * Some AIM clients will send a blank GIF image with iconlen 90 when
    - * no icon is set. Ignore these.
    - */
    - if ((iconlen > 0) && (iconlen != 90)) {
    - char *b16 = purple_base16_encode(iconcsum, iconcsumlen);
    - purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
    - bn, g_memdup(icon, iconlen), iconlen, b16);
    - g_free(b16);
    - }
    -
    - return 1;
    -}
    -
    -static void
    -purple_icons_fetch(PurpleConnection *gc)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - aim_userinfo_t *userinfo;
    - FlapConnection *conn;
    -
    - conn = flap_connection_getbytype(od, SNAC_FAMILY_BART);
    - if (!conn) {
    - if (!od->iconconnecting) {
    - aim_srv_requestnew(od, SNAC_FAMILY_BART);
    - od->iconconnecting = TRUE;
    - }
    - return;
    - }
    -
    - if (od->set_icon) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleImage *img = purple_buddy_icons_find_account_icon(account);
    - if (img == NULL) {
    - aim_ssi_delicon(od);
    - } else {
    - purple_debug_info("oscar",
    - "Uploading icon to icon server");
    - aim_bart_upload(od, purple_image_get_data(img),
    - purple_image_get_data_size(img));
    - g_object_unref(img);
    - }
    - od->set_icon = FALSE;
    - }
    -
    - while (od->requesticon != NULL)
    - {
    - userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data);
    - if ((userinfo != NULL) && (userinfo->iconcsumlen > 0))
    - aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen);
    -
    - g_free(od->requesticon->data);
    - od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon);
    - }
    -
    - purple_debug_misc("oscar", "no more icons to request\n");
    -}
    -
    -static int purple_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - aim_userinfo_t *info;
    -
    - va_start(ap, fr);
    - info = va_arg(ap, aim_userinfo_t *);
    - va_end(ap);
    -
    - purple_connection_set_display_name(od->gc, info->bn);
    -
    - return 1;
    -}
    -
    -static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 code;
    - char *msg;
    -
    - va_start(ap, fr);
    - code = (guint16)va_arg(ap, int);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_info("oscar", "Disconnected. Code is 0x%04x and msg is %s\n",
    - code, (msg != NULL ? msg : ""));
    -
    - g_return_val_if_fail(conn != NULL, 1);
    -
    - if (conn->type == SNAC_FAMILY_CHAT) {
    - struct chat_connection *cc;
    - PurpleChatConversation *chat = NULL;
    -
    - cc = find_oscar_chat_by_conn(gc, conn);
    - if (cc != NULL)
    - {
    - chat = purple_conversations_find_chat(gc, cc->id);
    -
    - if (chat != NULL)
    - {
    - /*
    - * TOOD: Have flap_connection_destroy_cb() send us the
    - * error message stored in 'tmp', which should be
    - * human-friendly, and print that to the chat room.
    - */
    - gchar *buf;
    - buf = g_strdup_printf(_("You have been disconnected from chat "
    - "room %s."), cc->name);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(chat), buf,
    - PURPLE_MESSAGE_ERROR);
    - g_free(buf);
    - }
    - oscar_chat_kill(gc, cc);
    - }
    - }
    -
    - return 1;
    -}
    -
    -static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - va_list ap;
    - guint16 maxsiglen;
    -
    - va_start(ap, fr);
    - maxsiglen = (guint16) va_arg(ap, int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "locate rights: max sig len = %d\n", maxsiglen);
    -
    - od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
    -
    - aim_locate_setcaps(od, purple_caps);
    - oscar_set_info_and_status(account, TRUE, purple_account_get_user_info(account), TRUE,
    - purple_account_get_active_status(account));
    -
    - return 1;
    -}
    -
    -static int purple_parse_buddyrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - guint16 maxbuddies, maxwatchers;
    -
    - va_start(ap, fr);
    - maxbuddies = (guint16) va_arg(ap, unsigned int);
    - maxwatchers = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
    -
    - od->rights.maxbuddies = (guint)maxbuddies;
    - od->rights.maxwatchers = (guint)maxwatchers;
    -
    - return 1;
    -}
    -
    -static void oscar_format_username(PurpleConnection *gc, const char *new_display_name)
    -{
    - OscarData *od;
    - const char *old_display_name, *username;
    - char *tmp, *at_sign;
    -
    - old_display_name = purple_connection_get_display_name(gc);
    - if (old_display_name && strchr(old_display_name, '@')) {
    - purple_debug_info("oscar", "Cowardly refusing to attempt to format "
    - "screen name because the current formatting according to "
    - "the server (%s) appears to be an email address\n",
    - old_display_name);
    - return;
    - }
    -
    - username = purple_account_get_username(purple_connection_get_account(gc));
    - if (oscar_util_name_compare(username, new_display_name)) {
    - purple_notify_error(gc, NULL, _("The new formatting is invalid."),
    - _("Username formatting can change only capitalization and whitespace."),
    - purple_request_cpar_from_connection(gc));
    - return;
    - }
    -
    - tmp = g_strdup(new_display_name);
    -
    - /*
    - * If our local username is an email address then strip off the domain.
    - * This allows formatting to work if the user entered their username as
    - * 'something@aim.com' or possibly other AOL-owned domains.
    - */
    - at_sign = strchr(tmp, '@');
    - if (at_sign)
    - at_sign[0] = '\0';
    -
    - od = purple_connection_get_protocol_data(gc);
    - if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) {
    - /* We don't have a connection to an "admin" server. Make one. */
    - od->setnick = TRUE;
    - g_free(od->newformatting);
    - od->newformatting = tmp;
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - } else {
    - aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), tmp);
    - g_free(tmp);
    - }
    -}
    -
    -static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleStatus *status;
    - gboolean is_available;
    - PurplePresence *presence;
    - const char *username, *message, *itmsurl;
    - char *tmp;
    - va_list ap;
    - guint16 maxpermits, maxdenies;
    -
    - gc = od->gc;
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - maxpermits = (guint16) va_arg(ap, unsigned int);
    - maxdenies = (guint16) va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
    -
    - od->rights.maxpermits = (guint)maxpermits;
    - od->rights.maxdenies = (guint)maxdenies;
    -
    - purple_debug_info("oscar", "buddy list loaded\n");
    -
    - if (purple_account_get_user_info(account) != NULL)
    - purple_serv_set_info(gc, purple_account_get_user_info(account));
    -
    - username = purple_account_get_username(account);
    - if (!od->icq && !purple_strequal(username, purple_connection_get_display_name(gc))) {
    - /*
    - * Format the username for AIM accounts if it's different
    - * than what's currently set.
    - */
    - oscar_format_username(gc, username);
    - }
    -
    - /* Set our available message based on the current status */
    - status = purple_account_get_active_status(account);
    - is_available = purple_status_is_available(status);
    - if (is_available)
    - message = purple_status_get_attr_string(status, "message");
    - else
    - message = NULL;
    - tmp = purple_markup_strip_html(message);
    - itmsurl = purple_status_get_attr_string(status, "itmsurl");
    - aim_srv_setextrainfo(od, FALSE, 0, is_available, tmp, itmsurl);
    - aim_srv_set_dc_info(od);
    - g_free(tmp);
    -
    - presence = purple_status_get_presence(status);
    - aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
    -
    - if (od->icq) {
    - oscar_set_extended_status(gc);
    - aim_icq_setsecurity(od,
    - purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION),
    - purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE));
    - }
    -
    - aim_srv_requestnew(od, SNAC_FAMILY_ALERT);
    - aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
    -
    - od->bos.have_rights = TRUE;
    -
    - /*
    - * If we've already received our feedbag data then we're not waiting on
    - * anything else, so send the server clientready.
    - *
    - * Normally we get bos rights before we get our feedbag data, so this
    - * rarely (never?) happens. And I'm not sure it actually matters if we
    - * wait for bos rights before calling clientready. But it seems safer
    - * to do it this way.
    - */
    - if (od->ssi.received_data) {
    - aim_srv_clientready(od, conn);
    -
    - /* Request offline messages for AIM and ICQ */
    - aim_im_reqofflinemsgs(od);
    -
    - purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - gchar *text;
    - va_list ap;
    - char *msg, *url;
    -
    - va_start(ap, fr);
    - msg = va_arg(ap, char *);
    - url = va_arg(ap, char *);
    - va_arg(ap, int); /* guint16 wid */
    - va_arg(ap, int); /* guint16 hei */
    - va_arg(ap, int); /* guint16 delay */
    - va_end(ap);
    -
    - text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url);
    - purple_notify_formatted(gc, NULL, _("Pop-Up Message"), NULL, text, NULL, NULL);
    - g_free(text);
    -
    - return 1;
    -}
    -
    -static void oscar_searchresults_add_buddy_cb(PurpleConnection *gc, GList *row, void *user_data)
    -{
    - purple_blist_request_add_buddy(purple_connection_get_account(gc),
    - g_list_nth_data(row, 0), NULL, NULL);
    -}
    -
    -static int purple_parse_searchreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleNotifySearchResults *results;
    - PurpleNotifySearchColumn *column;
    - gchar *secondary;
    - int i, num;
    - va_list ap;
    - char *email, *usernames;
    -
    - va_start(ap, fr);
    - email = va_arg(ap, char *);
    - num = va_arg(ap, int);
    - usernames = va_arg(ap, char *);
    - va_end(ap);
    -
    - results = purple_notify_searchresults_new();
    -
    - if (results == NULL) {
    - purple_debug_error("oscar", "purple_parse_searchreply: "
    - "Unable to display the search results.\n");
    - purple_notify_error(gc, NULL, _("Unable to display the search "
    - "results."), NULL,
    - purple_request_cpar_from_connection(gc));
    - return 1;
    - }
    -
    - secondary = g_strdup_printf(
    - dngettext(PACKAGE, "The following username is associated with %s",
    - "The following usernames are associated with %s",
    - num),
    - email);
    -
    - column = purple_notify_searchresults_column_new(_("Username"));
    - purple_notify_searchresults_column_add(results, column);
    -
    - for (i = 0; i < num; i++) {
    - GList *row;
    - row = g_list_append(NULL, g_strdup(&usernames[i * (MAXSNLEN + 1)]));
    - purple_notify_searchresults_row_add(results, row);
    - }
    - purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD,
    - oscar_searchresults_add_buddy_cb);
    - purple_notify_searchresults(gc, NULL, NULL, secondary, results, NULL, NULL);
    -
    - g_free(secondary);
    -
    - return 1;
    -}
    -
    -static int purple_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - va_list ap;
    - char *email;
    - char *buf;
    -
    - va_start(ap, fr);
    - email = va_arg(ap, char *);
    - va_end(ap);
    -
    - buf = g_strdup_printf(_("No results found for email address %s"), email);
    - purple_notify_error(od->gc, NULL, buf, NULL,
    - purple_request_cpar_from_connection(od->gc));
    - g_free(buf);
    -
    - return 1;
    -}
    -
    -static int purple_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - guint16 status;
    - va_list ap;
    - char msg[256];
    -
    - va_start(ap, fr);
    - status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "account confirmation returned status 0x%04x (%s)\n", status,
    - status ? "unknown" : "email sent");
    - if (!status) {
    - g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."),
    - purple_account_get_username(purple_connection_get_account(gc)));
    - purple_notify_info(gc, NULL, _("Account Confirmation Requested"),
    - msg, purple_request_cpar_from_connection(gc));
    - }
    -
    - return 1;
    -}
    -
    -static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 perms, err;
    - char *url, *bn, *email;
    - int change;
    -
    - va_start(ap, fr);
    - change = va_arg(ap, int);
    - perms = (guint16) va_arg(ap, unsigned int);
    - err = (guint16) va_arg(ap, unsigned int);
    - url = va_arg(ap, char *);
    - bn = va_arg(ap, char *);
    - email = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_misc("oscar",
    - "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, bn=%s, email=%s\n",
    - change ? "change" : "request", perms, err,
    - (url != NULL) ? url : "(null)",
    - (bn != NULL) ? bn : "(null)",
    - (email != NULL) ? email : "(null)");
    -
    - if ((err > 0) && (url != NULL)) {
    - char *dialog_msg;
    -
    - if (err == 0x0001)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name differs from the original."), err);
    - else if (err == 0x0006)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because it is invalid."), err);
    - else if (err == 0x00b)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name is too long."), err);
    - else if (err == 0x001d)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this username."), err);
    - else if (err == 0x0021)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many usernames associated with it."), err);
    - else if (err == 0x0023)
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err);
    - else
    - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
    - purple_notify_error(gc, NULL, _("Error Changing Account Info"),
    - dialog_msg, purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - return 1;
    - }
    -
    - if (email != NULL) {
    - char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"),
    - purple_account_get_username(purple_connection_get_account(gc)), email);
    - purple_notify_info(gc, NULL, _("Account Info"), dialog_msg,
    - purple_request_cpar_from_connection(gc));
    - g_free(dialog_msg);
    - }
    -
    - return 1;
    -}
    -
    -void
    -oscar_keepalive(PurpleConnection *gc)
    -{
    - OscarData *od;
    - GSList *l;
    -
    - od = purple_connection_get_protocol_data(gc);
    - for (l = od->oscar_connections; l; l = l->next) {
    - flap_connection_send_keepalive(od, l->data);
    - }
    -}
    -
    -unsigned int
    -oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state)
    -{
    - OscarData *od;
    - PeerConnection *conn;
    -
    - od = purple_connection_get_protocol_data(gc);
    - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if ((conn != NULL) && (conn->ready))
    - {
    - peer_odc_send_typing(conn, state);
    - }
    - else {
    - /* Don't send if this turkey is in our deny list */
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *list = purple_account_privacy_get_denied(account);
    -
    - if (!g_slist_find_custom(list, name, (GCompareFunc)oscar_util_name_compare)) {
    - struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
    - if (bi && bi->typingnot) {
    - if (state == PURPLE_IM_TYPING)
    - aim_im_sendmtn(od, 0x0001, name, 0x0002);
    - else if (state == PURPLE_IM_TYPED)
    - aim_im_sendmtn(od, 0x0001, name, 0x0001);
    - else
    - aim_im_sendmtn(od, 0x0001, name, 0x0000);
    - }
    - }
    - }
    - return 0;
    -}
    -
    -/* TODO: Move this into odc.c! */
    -static void
    -purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags imflags)
    -{
    - GString *msg;
    - GString *data;
    - gchar *tmp;
    - gsize tmplen;
    - guint16 charset;
    - GData *attribs;
    - const char *start, *end, *last;
    - int oscar_id = 0;
    -
    - msg = g_string_new("<HTML><BODY>");
    - data = g_string_new("<BINARY>");
    - last = message;
    -
    - /* for each valid IMG tag... */
    - while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs))
    - {
    - PurpleImage *image = NULL;
    - const gchar *src;
    -
    - if (start - last) {
    - g_string_append_len(msg, last, start - last);
    - }
    -
    - src = g_datalist_get_data(&attribs, "src");
    - if (src)
    - image = purple_image_store_get_from_uri(src);
    -
    - /* ... if it refers to a valid purple image ... */
    - if (image) {
    - /* ... append the message from start to the tag ... */
    - unsigned long size = purple_image_get_data_size(image);
    - const gchar *filename = purple_image_get_friendly_filename(image);
    - gconstpointer imgdata = purple_image_get_data(image);
    -
    - oscar_id++;
    -
    - /* ... insert a new img tag with the oscar id ... */
    - if (filename)
    - g_string_append_printf(msg,
    - "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">",
    - filename, oscar_id, size);
    - else
    - g_string_append_printf(msg,
    - "<IMG ID=\"%d\" DATASIZE=\"%lu\">",
    - oscar_id, size);
    -
    - /* ... and append the data to the binary section ... */
    - g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%lu\">",
    - oscar_id, size);
    - g_string_append_len(data, imgdata, size);
    - g_string_append(data, "</DATA>");
    - }
    - /* If the tag is invalid, skip it, thus no else here */
    -
    - g_datalist_clear(&attribs);
    -
    - /* continue from the end of the tag */
    - last = end + 1;
    - }
    -
    - /* append any remaining message data */
    - if (last && *last)
    - g_string_append(msg, last);
    -
    - g_string_append(msg, "</BODY></HTML>");
    -
    - /* Convert the message to a good encoding */
    - tmp = oscar_encode_im(msg->str, &tmplen, &charset, NULL);
    - g_string_free(msg, TRUE);
    - msg = g_string_new_len(tmp, tmplen);
    - g_free(tmp);
    -
    - /* Append any binary data that we may have */
    - if (oscar_id) {
    - msg = g_string_append_len(msg, data->str, data->len);
    - msg = g_string_append(msg, "</BINARY>");
    - }
    - g_string_free(data, TRUE);
    -
    - purple_debug_info("oscar", "sending direct IM %s using charset %i", msg->str, charset);
    -
    - peer_odc_send_im(conn, msg->str, msg->len, charset,
    - imflags & PURPLE_MESSAGE_AUTO_RESP);
    - g_string_free(msg, TRUE);
    -}
    -
    -int
    -oscar_send_im(PurpleConnection *gc, PurpleMessage *msg)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - PeerConnection *conn;
    - int ret;
    - char *tmp1, *tmp2;
    - gboolean is_sms, is_html;
    - const gchar *name, *message;
    - PurpleMessageFlags imflags;
    -
    - name = purple_message_get_recipient(msg);
    - message = purple_message_get_contents(msg);
    - imflags = purple_message_get_flags(msg);
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    - ret = 0;
    -
    - is_sms = oscar_util_valid_name_sms(name);
    -
    - if (od->icq && is_sms) {
    - /*
    - * We're sending to a phone number and this is ICQ,
    - * so send the message as an SMS using aim_icq_sendsms()
    - */
    - int ret;
    - purple_debug_info("oscar", "Sending SMS to %s.\n", name);
    - ret = aim_icq_sendsms(od, name, message, purple_account_get_username(account));
    - return (ret >= 0 ? 1 : ret);
    - }
    -
    - if (imflags & PURPLE_MESSAGE_AUTO_RESP)
    - tmp1 = oscar_util_format_string(message, name);
    - else
    - tmp1 = g_strdup(message);
    -
    - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
    - if ((conn != NULL) && (conn->ready))
    - {
    - /* If we're directly connected, send a direct IM */
    - purple_debug_info("oscar", "Sending direct IM with flags %i\n", imflags);
    - purple_odc_send_im(conn, tmp1, imflags);
    - } else {
    - struct buddyinfo *bi;
    - struct aim_sendimext_args args;
    - PurpleIMConversation *im;
    - PurpleImage *img;
    - PurpleBuddy *buddy;
    -
    - im = purple_conversations_find_im_with_account(name, account);
    -
    - if (strstr(tmp1, "<img "))
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(im),
    - _("Your IM Image was not sent. "
    - "You must be Direct Connected to send IM Images."),
    - PURPLE_MESSAGE_ERROR);
    -
    - buddy = purple_blist_find_buddy(account, name);
    -
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
    - if (!bi) {
    - bi = g_new0(struct buddyinfo, 1);
    - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi);
    - }
    -
    - args.flags = 0;
    -
    - if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy)))
    - args.flags |= AIM_IMFLAGS_OFFLINE;
    -
    - if (od->icq) {
    - args.features = features_icq;
    - args.featureslen = sizeof(features_icq);
    - } else {
    - args.features = features_aim;
    - args.featureslen = sizeof(features_aim);
    -
    - if (imflags & PURPLE_MESSAGE_AUTO_RESP)
    - args.flags |= AIM_IMFLAGS_AWAY;
    - }
    -
    - if (bi->ico_need) {
    - purple_debug_info("oscar",
    - "Sending buddy icon request with message\n");
    - args.flags |= AIM_IMFLAGS_BUDDYREQ;
    - bi->ico_need = FALSE;
    - }
    -
    - img = purple_buddy_icons_find_account_icon(account);
    - if (img) {
    - gconstpointer data = purple_image_get_data(img);
    - args.iconlen = purple_image_get_data_size(img);
    - args.iconsum = aimutil_iconsum(data, args.iconlen);
    - args.iconstamp = purple_buddy_icons_get_account_icon_timestamp(account);
    -
    - if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) {
    - bi->ico_informed = FALSE;
    - bi->ico_sent = FALSE;
    - }
    -
    - /*
    - * TODO:
    - * For some reason sending our icon to people only works
    - * when we're the ones who initiated the conversation. If
    - * the other person sends the first IM then they never get
    - * the icon. We should fix that.
    - */
    - if (!bi->ico_informed) {
    - purple_debug_info("oscar",
    - "Claiming to have a buddy icon\n");
    - args.flags |= AIM_IMFLAGS_HASICON;
    - bi->ico_me_len = args.iconlen;
    - bi->ico_me_csum = args.iconsum;
    - bi->ico_me_time = args.iconstamp;
    - bi->ico_informed = TRUE;
    - }
    -
    - g_object_unref(img);
    - }
    -
    - args.destbn = name;
    -
    - if (oscar_util_valid_name_sms(name)) {
    - /* Messaging an SMS (mobile) user--strip HTML */
    - tmp2 = purple_markup_strip_html(tmp1);
    - is_html = FALSE;
    - } else {
    - /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */
    - tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1);
    - is_html = TRUE;
    - }
    - g_free(tmp1);
    - tmp1 = tmp2;
    -
    - args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
    - if (is_html && (args.msglen > MAXMSGLEN)) {
    - /* If the length was too long, try stripping the HTML and then running it back through
    - * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
    - g_free((char *)args.msg);
    -
    - tmp2 = purple_markup_strip_html(tmp1);
    - g_free(tmp1);
    -
    - /* re-escape the entities */
    - tmp1 = g_markup_escape_text(tmp2, -1);
    - g_free(tmp2);
    -
    - tmp2 = purple_strdup_withhtml(tmp1);
    - g_free(tmp1);
    - tmp1 = tmp2;
    -
    - args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
    - purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
    - message, (char *)args.msg);
    - }
    -
    - purple_debug_info("oscar", "Sending IM, charset=0x%04hx, length=%" G_GSIZE_FORMAT "\n", args.charset, args.msglen);
    - ret = aim_im_sendch1_ext(od, &args);
    - g_free((char *)args.msg);
    - }
    -
    - g_free(tmp1);
    -
    - if (ret >= 0)
    - return 1;
    -
    - return ret;
    -}
    -
    -/*
    - * As of 26 June 2006, ICQ users can request AIM info from
    - * everyone, and can request ICQ info from ICQ users, and
    - * AIM users can only request AIM info.
    - */
    -void oscar_get_info(PurpleConnection *gc, const char *name) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->icq && oscar_util_valid_name_icq(name))
    - aim_icq_getallinfo(od, name);
    - else
    - aim_locate_getinfoshort(od, name, 0x00000003);
    -}
    -
    -void oscar_set_idle(PurpleConnection *gc, int time) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - aim_srv_setidle(od, time);
    -}
    -
    -void
    -oscar_set_info(PurpleConnection *gc, const char *rawinfo)
    -{
    - PurpleAccount *account;
    - PurpleStatus *status;
    -
    - account = purple_connection_get_account(gc);
    - status = purple_account_get_active_status(account);
    - oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status);
    -}
    -
    -static guint32
    -oscar_get_extended_status(PurpleConnection *gc)
    -{
    - PurpleAccount *account;
    - PurpleStatus *status;
    - const gchar *status_id;
    - guint32 data = 0x00000000;
    -
    - account = purple_connection_get_account(gc);
    - status = purple_account_get_active_status(account);
    - status_id = purple_status_get_id(status);
    -
    - data |= AIM_ICQ_STATE_HIDEIP;
    - if (purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE))
    - data |= AIM_ICQ_STATE_WEBAWARE;
    -
    - if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE))
    - data |= AIM_ICQ_STATE_NORMAL;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_AWAY))
    - data |= AIM_ICQ_STATE_AWAY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_DND))
    - data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_NA))
    - data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_OCCUPIED))
    - data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_FREE4CHAT))
    - data |= AIM_ICQ_STATE_CHAT;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_INVISIBLE))
    - data |= AIM_ICQ_STATE_INVISIBLE;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_EVIL))
    - data |= AIM_ICQ_STATE_EVIL;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_DEPRESSION))
    - data |= AIM_ICQ_STATE_DEPRESSION;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATWORK))
    - data |= AIM_ICQ_STATE_ATWORK;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATHOME))
    - data |= AIM_ICQ_STATE_ATHOME;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_LUNCH))
    - data |= AIM_ICQ_STATE_LUNCH;
    - else if (purple_strequal(status_id, OSCAR_STATUS_ID_CUSTOM))
    - data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
    -
    - return data;
    -}
    -
    -static void
    -oscar_set_extended_status(PurpleConnection *gc)
    -{
    - aim_srv_setextrainfo(purple_connection_get_protocol_data(gc), TRUE, oscar_get_extended_status(gc), FALSE, NULL, NULL);
    -}
    -
    -static void
    -oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo,
    - gboolean setstatus, PurpleStatus *status)
    -{
    - PurpleConnection *gc = purple_account_get_connection(account);
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleStatusType *status_type;
    - PurpleStatusPrimitive primitive;
    -
    - char *info_encoding = NULL;
    - char *info = NULL;
    - gsize infolen = 0;
    -
    - char *away_encoding = NULL;
    - char *away = NULL;
    - gsize awaylen = 0;
    -
    - char *status_text = NULL;
    - const char *itmsurl = NULL;
    -
    - status_type = purple_status_get_status_type(status);
    - primitive = purple_status_type_get_primitive(status_type);
    -
    - if (!setinfo)
    - {
    - /* Do nothing! */
    - }
    - else if (od->rights.maxsiglen == 0)
    - {
    - purple_notify_warning(gc, NULL, _("Unable to set AIM profile."),
    - _("You have probably requested to set your "
    - "profile before the login procedure completed. "
    - "Your profile remains unset; try setting it "
    - "again when you are fully connected."),
    - purple_request_cpar_from_connection(gc));
    - }
    - else if (rawinfo != NULL)
    - {
    - char *htmlinfo = purple_strdup_withhtml(rawinfo);
    - info = oscar_encode_im(htmlinfo, &infolen, NULL, &info_encoding);
    - g_free(htmlinfo);
    -
    - if (infolen > od->rights.maxsiglen)
    - {
    - gchar *errstr;
    - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum profile length of %d byte "
    - "has been exceeded. It has been truncated for you.",
    - "The maximum profile length of %d bytes "
    - "has been exceeded. It has been truncated for you.",
    - od->rights.maxsiglen), od->rights.maxsiglen);
    - purple_notify_warning(gc, NULL, _("Profile too long."),
    - errstr, purple_request_cpar_from_connection(gc));
    - g_free(errstr);
    - }
    - }
    -
    - if (setstatus)
    - {
    - const char *status_html;
    -
    - status_html = purple_status_get_attr_string(status, "message");
    -
    - if (status_html == NULL || primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
    - {
    - /* This is needed for us to un-set any previous away message. */
    - away = g_strdup("");
    - }
    - else
    - {
    - gchar *linkified;
    -
    - /* We do this for icq too so that they work for old third party clients */
    - linkified = purple_markup_linkify(status_html);
    - away = oscar_encode_im(linkified, &awaylen, NULL, &away_encoding);
    - g_free(linkified);
    -
    - if (awaylen > od->rights.maxawaymsglen)
    - {
    - gchar *errstr;
    -
    - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
    - "has been exceeded. It has been truncated for you.",
    - "The maximum away message length of %d bytes "
    - "has been exceeded. It has been truncated for you.",
    - od->rights.maxawaymsglen), od->rights.maxawaymsglen);
    - purple_notify_warning(gc, NULL,
    - _("Away message too long."), errstr,
    - purple_request_cpar_from_connection(gc));
    - g_free(errstr);
    - }
    - }
    - }
    -
    - aim_locate_setprofile(od,
    - info_encoding, info, MIN(infolen, od->rights.maxsiglen),
    - away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
    - g_free(info);
    - g_free(away);
    -
    - if (setstatus)
    - {
    - const char *status_html;
    -
    - status_html = purple_status_get_attr_string(status, "message");
    - if (status_html != NULL)
    - {
    - status_text = purple_markup_strip_html(status_html);
    - /* If the status_text is longer than 251 characters then truncate it */
    - if (strlen(status_text) > MAXAVAILMSGLEN)
    - {
    - char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
    - strcpy(tmp, "...");
    - }
    - }
    -
    - itmsurl = purple_status_get_attr_string(status, "itmsurl");
    -
    - aim_srv_setextrainfo(od, TRUE, oscar_get_extended_status(gc), TRUE, status_text, itmsurl);
    - g_free(status_text);
    - }
    -}
    -
    -static void
    -oscar_set_icq_permdeny(PurpleAccount *account)
    -{
    - PurpleConnection *gc = purple_account_get_connection(account);
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
    -
    - /*
    - * For ICQ the permit/deny setting controls who can see you
    - * online. Mimicking the official client's behavior, we use PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS
    - * when our status is "invisible" and PURPLE_ACCOUNT_PRIVACY_DENY_USERS otherwise.
    - * In the former case, we are visible only to buddies on our "permanently visible" list.
    - * In the latter, we are invisible only to buddies on our "permanently invisible" list.
    - */
    - aim_ssi_setpermdeny(od, invisible ? PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS : PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
    -}
    -
    -void
    -oscar_set_status(PurpleAccount *account, PurpleStatus *status)
    -{
    - PurpleConnection *pc;
    - OscarData *od;
    -
    - purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status));
    -
    - /* Either setting a new status active or setting a status inactive.
    - * (Only possible for independent status (i.e. X-Status moods.) */
    - if (!purple_status_is_active(status) && !purple_status_is_independent(status))
    - return;
    -
    - if (!purple_account_is_connected(account))
    - return;
    -
    - pc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(pc);
    -
    - /* There's no need to do the stuff below for mood updates. */
    - if (purple_status_type_get_primitive(purple_status_get_status_type(status)) == PURPLE_STATUS_MOOD) {
    - aim_locate_setcaps(od, purple_caps);
    - return;
    - }
    -
    - if (od->icq) {
    - /* Set visibility */
    - oscar_set_icq_permdeny(account);
    - }
    -
    - /* Set the AIM-style away message for both AIM and ICQ accounts */
    - oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
    -}
    -
    -void
    -oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - const char *bname, *gname;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    - bname = purple_buddy_get_name(buddy);
    - gname = purple_group_get_name(group);
    -
    - if (!oscar_util_valid_name(bname)) {
    - gchar *buf;
    - buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname);
    - if (!purple_conversation_present_error(bname, account, buf))
    - purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc));
    - g_free(buf);
    -
    - /* Remove from local list */
    - purple_blist_remove_buddy(buddy);
    -
    - return;
    - }
    -
    - if (od->ssi.received_data) {
    - if (!aim_ssi_itemlist_finditem(&od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) {
    - purple_debug_info("oscar",
    - "ssi: adding buddy %s to group %s\n", bname, gname);
    - aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
    -
    - /* Mobile users should always be online */
    - if (bname[0] == '+') {
    - purple_protocol_got_user_status(account, bname,
    - OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_protocol_got_user_status(account, bname,
    - OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    - } else if (aim_ssi_waitingforauth(&od->ssi.local,
    - aim_ssi_itemlist_findparentname(&od->ssi.local, bname),
    - bname)) {
    - /* Not authorized -- Re-request authorization */
    - oscar_auth_sendrequest(gc, bname, msg);
    - }
    - }
    -
    - /* XXX - Should this be done from AIM accounts, as well? */
    - if (od->icq)
    - aim_icq_getalias(od, bname, FALSE, NULL);
    -}
    -
    -void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data) {
    - const char *gname = purple_group_get_name(group);
    - const char *bname = purple_buddy_get_name(buddy);
    - purple_debug_info("oscar",
    - "ssi: deleting buddy %s from group %s\n", bname, gname);
    - aim_ssi_delbuddy(od, bname, gname);
    - }
    -}
    -
    -void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data && !purple_strequal(old_group, new_group)) {
    - purple_debug_info("oscar",
    - "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
    - aim_ssi_movebuddy(od, old_group, new_group, name);
    - }
    -}
    -
    -void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data) {
    - char *gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name);
    - if (gname) {
    - purple_debug_info("oscar",
    - "ssi: changing the alias for buddy %s to %s\n", name, alias ? alias : "(none)");
    - aim_ssi_aliasbuddy(od, gname, name, alias);
    - }
    - }
    -}
    -
    -/*
    - * FYI, the OSCAR SSI code removes empty groups automatically.
    - */
    -void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->ssi.received_data) {
    - const char *gname = purple_group_get_name(group);
    - if (aim_ssi_itemlist_finditem(&od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) {
    - GList *cur, *groups = NULL;
    - PurpleAccount *account = purple_connection_get_account(gc);
    -
    - /* Make a list of what the groups each buddy is in */
    - for (cur = moved_buddies; cur != NULL; cur = cur->next) {
    - PurpleBlistNode *node = cur->data;
    - /* node is PurpleBuddy, parent is a PurpleContact.
    - * We must go two levels up to get the Group */
    - groups = g_list_append(groups,
    - purple_buddy_get_group((PurpleBuddy*)node));
    - }
    -
    - purple_account_remove_buddies(account, moved_buddies, groups);
    - purple_account_add_buddies(account, moved_buddies, NULL);
    - g_list_free(groups);
    - purple_debug_info("oscar",
    - "ssi: moved all buddies from group %s to %s\n", old_name, gname);
    - } else {
    - aim_ssi_rename_group(od, old_name, gname);
    - purple_debug_info("oscar",
    - "ssi: renamed group %s to %s\n", old_name, gname);
    - }
    - }
    -}
    -
    -void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group)
    -{
    - aim_ssi_delgroup(purple_connection_get_protocol_data(gc), purple_group_get_name(group));
    -}
    -
    -static gboolean purple_ssi_rerequestdata(gpointer data) {
    - OscarData *od = data;
    -
    - aim_ssi_reqdata(od);
    -
    - return TRUE;
    -}
    -
    -static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - guint16 reason;
    -
    - va_start(ap, fr);
    - reason = (guint16)va_arg(ap, unsigned int);
    - va_end(ap);
    -
    - purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason);
    -
    - if (reason == 0x0005) {
    - if (od->getblisttimer > 0)
    - g_source_remove(od->getblisttimer);
    - else
    - /* We only show this error the first time it happens */
    - purple_notify_error(gc, NULL,
    - _("Unable to Retrieve Buddy List"),
    - _("The AIM servers were temporarily unable to send "
    - "your buddy list. Your buddy list is not lost, and "
    - "will probably become available in a few minutes."),
    - purple_request_cpar_from_connection(gc));
    - od->getblisttimer = g_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
    - return 1;
    - }
    -
    - return 1;
    -}
    -
    -static int purple_ssi_parserights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - int i;
    - va_list ap;
    - int numtypes;
    - guint16 *maxitems;
    - GString *msg;
    -
    - va_start(ap, fr);
    - numtypes = va_arg(ap, int);
    - maxitems = va_arg(ap, guint16 *);
    - va_end(ap);
    -
    - msg = g_string_new("ssi rights:");
    - for (i=0; i<numtypes; i++)
    - g_string_append_printf(msg, " max type 0x%04x=%hd,", i, maxitems[i]);
    - g_string_append(msg, "\n");
    - purple_debug_misc("oscar", "%s", msg->str);
    - g_string_free(msg, TRUE);
    -
    - if (numtypes >= 0)
    - od->rights.maxbuddies = maxitems[0];
    - if (numtypes >= 1)
    - od->rights.maxgroups = maxitems[1];
    - if (numtypes >= 2)
    - od->rights.maxpermits = maxitems[2];
    - if (numtypes >= 3)
    - od->rights.maxdenies = maxitems[3];
    -
    - return 1;
    -}
    -
    -static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleGroup *g;
    - PurpleBuddy *b;
    - GSList *cur, *next, *buddies;
    - struct aim_ssi_item *curitem;
    - guint32 tmp;
    - PurpleImage *img;
    - va_list ap;
    - guint16 deny_entry_type = aim_ssi_getdenyentrytype(od);
    -
    - gc = od->gc;
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - va_arg(ap, int); /* guint16 fmtver */
    - va_arg(ap, int); /* guint16 numitems */
    - va_arg(ap, guint32); /* timestamp */
    - va_end(ap);
    -
    - /* Don't attempt to re-request our buddy list later */
    - if (od->getblisttimer != 0) {
    - g_source_remove(od->getblisttimer);
    - od->getblisttimer = 0;
    - }
    -
    - purple_debug_info("oscar", "ssi: syncing local list and server list\n");
    -
    - /*** Begin code for pruning buddies from local list if they're not in server list ***/
    -
    - /* Buddies */
    - cur = NULL;
    - for (buddies = purple_blist_find_buddies(account, NULL);
    - buddies;
    - buddies = g_slist_delete_link(buddies, buddies))
    - {
    - PurpleGroup *g;
    - const char *gname;
    - const char *bname;
    -
    - b = buddies->data;
    - g = purple_buddy_get_group(b);
    - gname = purple_group_get_name(g);
    - bname = purple_buddy_get_name(b);
    -
    - if (aim_ssi_itemlist_exists(&od->ssi.local, bname)) {
    - /* If the buddy is an ICQ user then load his nickname */
    - const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
    - char *alias;
    - const char *balias;
    - if (servernick)
    - purple_serv_got_alias(gc, bname, servernick);
    -
    - /* Store local alias on server */
    - alias = aim_ssi_getalias(&od->ssi.local, gname, bname);
    - balias = purple_buddy_get_local_alias(b);
    - if (!alias && balias && *balias)
    - aim_ssi_aliasbuddy(od, gname, bname, balias);
    - g_free(alias);
    - } else {
    - purple_debug_info("oscar",
    - "ssi: removing buddy %s from local list\n", bname);
    - /* Queue the buddy for removal from the local list */
    - cur = g_slist_prepend(cur, b);
    - }
    - }
    - g_slist_free_full(cur, (GDestroyNotify)purple_blist_remove_buddy);
    -
    - /* Permit list (ICQ doesn't have one) */
    - if (!od->icq) {
    - next = purple_account_privacy_get_permitted(account);
    - while (next != NULL) {
    - cur = next;
    - next = next->next;
    - if (!aim_ssi_itemlist_finditem(&od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
    - purple_debug_info("oscar",
    - "ssi: removing permit %s from local list\n", (const char *)cur->data);
    - purple_account_privacy_permit_remove(account, cur->data, TRUE);
    - }
    - }
    - }
    -
    - /* Deny list */
    - next = purple_account_privacy_get_denied(account);
    - while (next != NULL) {
    - cur = next;
    - next = next->next;
    - if (!aim_ssi_itemlist_finditem(&od->ssi.local, NULL, cur->data, deny_entry_type)) {
    - purple_debug_info("oscar",
    - "ssi: removing deny %s from local list\n", (const char *)cur->data);
    - purple_account_privacy_deny_remove(account, cur->data, TRUE);
    - }
    - }
    -
    - /* Presence settings (idle time visibility) */
    - tmp = aim_ssi_getpresence(&od->ssi.local);
    - if (tmp != 0xFFFFFFFF) {
    - const char *idle_reporting_pref;
    - gboolean report_idle;
    -
    - idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting");
    - report_idle = !purple_strequal(idle_reporting_pref, "none");
    -
    - if (report_idle)
    - aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    - else
    - aim_ssi_setpresence(od, tmp & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
    - }
    -
    - /*** End code for pruning buddies from local list ***/
    -
    - /*** Begin code for adding from server list to local list ***/
    -
    - for (curitem=od->ssi.local.data; curitem; curitem=curitem->next) {
    - if (curitem->name && !g_utf8_validate(curitem->name, -1, NULL)) {
    - /* Got node with invalid UTF-8 in the name. Skip it. */
    - purple_debug_warning("oscar", "ssi: server list contains item of "
    - "type 0x%04hx with a non-utf8 name\n", curitem->type);
    - continue;
    - }
    -
    - switch (curitem->type) {
    - case AIM_SSI_TYPE_BUDDY: { /* Buddy */
    - if (curitem->name) {
    - struct aim_ssi_item *groupitem;
    - char *gname, *gname_utf8, *alias, *alias_utf8;
    -
    - groupitem = aim_ssi_itemlist_find(&od->ssi.local, curitem->gid, 0x0000);
    - gname = groupitem ? groupitem->name : NULL;
    - gname_utf8 = oscar_utf8_try_convert(account, od, gname);
    -
    - g = purple_blist_find_group(gname_utf8);
    - if (g == NULL) {
    - g = purple_group_new(gname_utf8);
    - purple_blist_add_group(g, NULL);
    - }
    -
    - alias = aim_ssi_getalias_from_item(curitem);
    - alias_utf8 = oscar_utf8_try_convert(account, od, alias);
    -
    - b = purple_blist_find_buddy_in_group(account, curitem->name, g);
    - if (b) {
    - /* Get server stored alias */
    - purple_buddy_set_local_alias(b, alias_utf8);
    - } else {
    - b = purple_buddy_new(account, curitem->name, alias_utf8);
    -
    - purple_debug_info("oscar",
    - "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname);
    - purple_blist_add_buddy(b, NULL, g, NULL);
    - }
    -
    - /* Mobile users should always be online */
    - if (curitem->name[0] == '+') {
    - purple_protocol_got_user_status(account,
    - purple_buddy_get_name(b),
    - OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_protocol_got_user_status(account,
    - purple_buddy_get_name(b),
    - OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    -
    - g_free(gname_utf8);
    - g_free(alias);
    - g_free(alias_utf8);
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_GROUP: { /* Group */
    - if (curitem->name != NULL && purple_blist_find_group(curitem->name) == NULL) {
    - g = purple_group_new(curitem->name);
    - purple_blist_add_group(g, NULL);
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_PERMIT: { /* Permit buddy (unless we're on ICQ) */
    - if (!od->icq && curitem->name) {
    - cur = purple_account_privacy_get_permitted(account);
    - if (!g_slist_find_custom(cur, curitem->name, (GCompareFunc)oscar_util_name_compare)) {
    - purple_debug_info("oscar",
    - "ssi: adding permit buddy %s to local list\n", curitem->name);
    - purple_account_privacy_permit_add(account, curitem->name, TRUE);
    - }
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_ICQDENY:
    - case AIM_SSI_TYPE_DENY: { /* Deny buddy */
    - if (curitem->type == deny_entry_type && curitem->name) {
    - cur = purple_account_privacy_get_denied(account);
    - if (!g_slist_find_custom(cur, curitem->name, (GCompareFunc)oscar_util_name_compare)) {
    - purple_debug_info("oscar",
    - "ssi: adding deny buddy %s to local list\n", curitem->name);
    - purple_account_privacy_deny_add(account, curitem->name, TRUE);
    - }
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_PDINFO: { /* Permit/deny setting */
    - /*
    - * We don't inherit the permit/deny setting from the server
    - * for ICQ because, for ICQ, this setting controls who can
    - * see your online status when you are invisible. Thus it is
    - * a part of your status and not really related to blocking.
    - */
    - if (!od->icq && curitem->data) {
    - guint8 perm_deny = aim_ssi_getpermdeny(&od->ssi.local);
    - if (perm_deny != 0 && perm_deny != purple_account_get_privacy_type(account))
    - {
    - purple_debug_info("oscar",
    - "ssi: changing permdeny from %d to %u\n", purple_account_get_privacy_type(account), (guint)perm_deny);
    - purple_account_set_privacy_type(account, perm_deny);
    - }
    - }
    - } break;
    -
    - case AIM_SSI_TYPE_PRESENCEPREFS: { /* Presence setting */
    - /* We don't want to change Purple's setting because it applies to all accounts */
    - } break;
    - } /* End of switch on curitem->type */
    - } /* End of for loop */
    -
    - /*** End code for adding from server list to local list ***/
    -
    - if (od->icq) {
    - oscar_set_icq_permdeny(account);
    - } else {
    - oscar_set_aim_permdeny(gc);
    - }
    -
    - /* Activate SSI */
    - /* Sending the enable causes other people to be able to see you, and you to see them */
    - /* Make sure your privacy setting/invisibility is set how you want it before this! */
    - purple_debug_info("oscar",
    - "ssi: activating server-stored buddy list\n");
    - aim_ssi_enable(od);
    -
    - /*
    - * Make sure our server-stored icon is updated correctly in
    - * the event that the local user set a new icon while this
    - * account was offline.
    - */
    - img = purple_buddy_icons_find_account_icon(account);
    - oscar_set_icon(gc, img);
    - g_object_unref(img);
    -
    - /*
    - * If we've already received our bos rights then we're not waiting on
    - * anything else, so send the server clientready.
    - */
    - if (od->bos.have_rights) {
    - aim_srv_clientready(od, conn);
    -
    - /* Request offline messages for AIM and ICQ */
    - aim_im_reqofflinemsgs(od);
    -
    - purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
    - }
    -
    - return 1;
    -}
    -
    -static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - struct aim_ssi_tmp *retval;
    -
    - va_start(ap, fr);
    - retval = va_arg(ap, struct aim_ssi_tmp *);
    - va_end(ap);
    -
    - while (retval) {
    - purple_debug_misc("oscar",
    - "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack, retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item");
    -
    - if (retval->ack != 0xffff)
    - switch (retval->ack) {
    - case 0x0000: { /* added successfully */
    - } break;
    -
    - case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
    - gchar *buf;
    - buf = g_strdup_printf(_("Unable to add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)")));
    - if ((retval->name != NULL) && !purple_conversation_present_error(retval->name, purple_connection_get_account(gc), buf))
    - purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc));
    - g_free(buf);
    - } break;
    -
    - case 0x000e: { /* buddy requires authorization */
    - if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name))
    - oscar_auth_sendrequest(gc, retval->name, NULL);
    - } break;
    -
    - default: { /* La la la */
    - gchar *buf;
    - purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
    - buf = g_strdup_printf(_("Unable to add the buddy %s for an unknown reason."),
    - (retval->name ? retval->name : _("(no name)")));
    - if ((retval->name != NULL) && !purple_conversation_present_error(retval->name, purple_connection_get_account(gc), buf))
    - purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc));
    - g_free(buf);
    - } break;
    - }
    -
    - retval = retval->next;
    - }
    -
    - return 1;
    -}
    -
    -static int
    -purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - char *gname, *gname_utf8, *alias, *alias_utf8;
    - PurpleBuddy *b;
    - PurpleGroup *g;
    - struct aim_ssi_item *ssi_item;
    - va_list ap;
    - guint16 snac_subtype, type;
    - const char *name;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - va_start(ap, fr);
    - snac_subtype = (guint16)va_arg(ap, int);
    - type = (guint16)va_arg(ap, int);
    - name = va_arg(ap, char *);
    - va_end(ap);
    -
    - if ((type != 0x0000) || (name == NULL))
    - return 1;
    -
    - gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name);
    - gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
    -
    - alias = aim_ssi_getalias(&od->ssi.local, gname, name);
    - alias_utf8 = oscar_utf8_try_convert(account, od, alias);
    - g_free(alias);
    -
    - b = purple_blist_find_buddy(account, name);
    - if (b) {
    - /*
    - * You're logged in somewhere else and you aliased one
    - * of your buddies, so update our local buddy list with
    - * the person's new alias.
    - */
    - purple_buddy_set_local_alias(b, alias_utf8);
    - } else if (snac_subtype == 0x0008) {
    - /*
    - * You're logged in somewhere else and you added a buddy to
    - * your server list, so add them to your local buddy list.
    - */
    - b = purple_buddy_new(account, name, alias_utf8);
    -
    - if (!(g = purple_blist_find_group(gname_utf8))) {
    - g = purple_group_new(gname_utf8);
    - purple_blist_add_group(g, NULL);
    - }
    -
    - purple_debug_info("oscar", "ssi: adding buddy %s to group %s to"
    - " local list", name, gname_utf8 ? gname_utf8 : "(default)");
    - purple_blist_add_buddy(b, NULL, g, NULL);
    -
    - /* Mobile users should always be online */
    - if (name[0] == '+') {
    - purple_protocol_got_user_status(account,
    - name, OSCAR_STATUS_ID_AVAILABLE, NULL);
    - purple_protocol_got_user_status(account,
    - name, OSCAR_STATUS_ID_MOBILE, NULL);
    - }
    -
    - }
    -
    - ssi_item = aim_ssi_itemlist_finditem(&od->ssi.local,
    - gname, name, AIM_SSI_TYPE_BUDDY);
    - if (ssi_item == NULL)
    - {
    - purple_debug_error("oscar", "purple_ssi_parseaddmod: "
    - "Could not find ssi item for oncoming buddy %s, "
    - "group %s\n", name, gname);
    - }
    -
    - g_free(gname_utf8);
    - g_free(alias_utf8);
    -
    - return 1;
    -}
    -
    -static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - char *bn;
    - gchar *dialog_msg, *nombre;
    - struct name_data *data;
    - PurpleBuddy *buddy;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - va_arg(ap, char *); /* msg */
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "ssi: %s has given you permission to add him to your buddy list\n", bn);
    -
    - buddy = purple_blist_find_buddy(purple_connection_get_account(gc), bn);
    - if (buddy && (purple_buddy_get_alias_only(buddy)))
    - nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
    - else
    - nombre = g_strdup(bn);
    -
    - dialog_msg = g_strdup_printf(_("The user %s has given you permission to add him or her to your buddy list. Do you want to add this user?"), nombre);
    - g_free(nombre);
    -
    - data = g_new(struct name_data, 1);
    - data->gc = gc;
    - data->name = g_strdup(bn);
    - data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
    -
    - purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_request_cpar_from_connection(gc),
    - data,
    - G_CALLBACK(purple_icq_buddyadd),
    - G_CALLBACK(oscar_free_name_data));
    - g_free(dialog_msg);
    -
    - return 1;
    -}
    -
    -static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
    -{
    - va_list ap;
    - const char *bn;
    - char *msg;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, const char *);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "ssi: received authorization request from %s\n", bn);
    -
    - if (!msg) {
    - purple_debug_warning("oscar", "Received auth request from %s with "
    - "empty message\n", bn);
    - } else if (!g_utf8_validate(msg, -1, NULL)) {
    - purple_debug_warning("oscar", "Received auth request from %s with "
    - "invalid UTF-8 message\n", bn);
    - msg = NULL;
    - }
    -
    - aim_icq_getalias(od, bn, TRUE, msg);
    - return 1;
    -}
    -
    -static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - va_list ap;
    - char *bn, *msg;
    - gchar *dialog_msg, *nombre;
    - guint8 reply;
    - PurpleBuddy *buddy;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - reply = (guint8)va_arg(ap, int);
    - msg = va_arg(ap, char *);
    - va_end(ap);
    -
    - purple_debug_info("oscar",
    - "ssi: received authorization reply from %s. Reply is 0x%02hx\n", bn, (guint16)reply);
    -
    - buddy = purple_blist_find_buddy(purple_connection_get_account(gc), bn);
    - if (buddy && (purple_buddy_get_alias_only(buddy)))
    - nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
    - else
    - nombre = g_strdup(bn);
    -
    - if (reply) {
    - /* Granted */
    - dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre);
    - purple_notify_info(gc, NULL, _("Authorization Granted"),
    - dialog_msg, purple_request_cpar_from_connection(gc));
    - } else {
    - /* Denied */
    - dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
    - purple_notify_info(gc, NULL, _("Authorization Denied"),
    - dialog_msg, purple_request_cpar_from_connection(gc));
    - }
    - g_free(dialog_msg);
    - g_free(nombre);
    -
    - return 1;
    -}
    -
    -static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - va_list ap;
    - char *bn;
    - PurpleBuddy *buddy;
    -
    - va_start(ap, fr);
    - bn = va_arg(ap, char *);
    - va_end(ap);
    -
    - buddy = purple_blist_find_buddy(account, bn);
    - purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn);
    - purple_account_notify_added(account, bn, NULL,
    - (buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL);
    -
    - return 1;
    -}
    -
    -GList *oscar_chat_info(PurpleConnection *gc) {
    - GList *m = NULL;
    - PurpleProtocolChatEntry *pce;
    -
    - pce = g_new0(PurpleProtocolChatEntry, 1);
    - pce->label = _("_Room:");
    - pce->identifier = "room";
    - pce->required = TRUE;
    - m = g_list_append(m, pce);
    -
    - pce = g_new0(PurpleProtocolChatEntry, 1);
    - pce->label = _("_Exchange:");
    - pce->identifier = "exchange";
    - pce->required = TRUE;
    - pce->is_int = TRUE;
    - pce->min = 4;
    - pce->max = 20;
    - m = g_list_append(m, pce);
    -
    - return m;
    -}
    -
    -GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
    -{
    - GHashTable *defaults;
    -
    - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
    -
    - if (chat_name != NULL)
    - g_hash_table_insert(defaults, "room", g_strdup(chat_name));
    - g_hash_table_insert(defaults, "exchange", g_strdup("4"));
    -
    - return defaults;
    -}
    -
    -char *
    -oscar_get_chat_name(GHashTable *data)
    -{
    - return g_strdup(g_hash_table_lookup(data, "room"));
    -}
    -
    -void
    -oscar_join_chat(PurpleConnection *gc, GHashTable *data)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - FlapConnection *conn;
    - char *name, *exchange;
    - int exchange_int;
    -
    - name = g_hash_table_lookup(data, "room");
    - exchange = g_hash_table_lookup(data, "exchange");
    -
    - g_return_if_fail(name != NULL && *name != '\0');
    - g_return_if_fail(exchange != NULL);
    -
    - errno = 0;
    - exchange_int = strtol(exchange, NULL, 10);
    - g_return_if_fail(errno == 0);
    -
    - purple_debug_info("oscar", "Attempting to join chat room %s.\n", name);
    -
    - if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV)))
    - {
    - purple_debug_info("oscar", "chatnav exists, creating room\n");
    - aim_chatnav_createroom(od, conn, name, exchange_int);
    - } else {
    - /* this gets tricky */
    - struct create_room *cr = g_new0(struct create_room, 1);
    - purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
    - cr->exchange = exchange_int;
    - cr->name = g_strdup(name);
    - od->create_rooms = g_slist_prepend(od->create_rooms, cr);
    - aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
    - }
    -}
    -
    -void
    -oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - struct chat_connection *ccon = find_oscar_chat(gc, id);
    -
    - if (ccon == NULL)
    - return;
    -
    - aim_im_sendch2_chatinvite(od, name, message ? message : "",
    - ccon->exchange, ccon->name, 0x0);
    -}
    -
    -void
    -oscar_chat_leave(PurpleConnection *gc, int id)
    -{
    - PurpleChatConversation *conv;
    - struct chat_connection *cc;
    -
    - conv = purple_conversations_find_chat(gc, id);
    -
    - g_return_if_fail(conv != NULL);
    -
    - purple_debug_info("oscar", "Leaving chat room %s\n",
    - purple_conversation_get_name(PURPLE_CONVERSATION(conv)));
    -
    - cc = find_oscar_chat(gc, purple_chat_conversation_get_id(conv));
    - flap_connection_schedule_destroy(cc->conn, OSCAR_DISCONNECT_DONE, NULL);
    - oscar_chat_kill(gc, cc);
    -}
    -
    -int oscar_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleChatConversation *conv = NULL;
    - struct chat_connection *c = NULL;
    - char *buf, *buf2, *buf3;
    - guint16 charset;
    - char *charsetstr;
    - gsize len;
    - const gchar *message = purple_message_get_contents(msg);
    -
    - if (!(conv = purple_conversations_find_chat(gc, id)))
    - return -EINVAL;
    -
    - if (!(c = find_oscar_chat_by_conv(gc, conv)))
    - return -EINVAL;
    -
    - buf = purple_strdup_withhtml(message);
    -
    - if (strstr(buf, "<img ")) {
    - purple_conversation_write_system_message(PURPLE_CONVERSATION(conv),
    - _("Your IM Image was not sent. "
    - "You cannot send IM Images in AIM chats."),
    - PURPLE_MESSAGE_ERROR);
    - }
    -
    - buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
    - /*
    - * Evan S. suggested that maxvis really does mean "number of
    - * visible characters" and not "number of bytes"
    - */
    - if ((len > c->maxlen) || (len > c->maxvis)) {
    - /* If the length was too long, try stripping the HTML and then running it back through
    - * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
    - g_free(buf2);
    -
    - buf3 = purple_markup_strip_html(buf);
    - g_free(buf);
    -
    - buf = purple_strdup_withhtml(buf3);
    - g_free(buf3);
    -
    - buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
    -
    - if ((len > c->maxlen) || (len > c->maxvis)) {
    - purple_debug_warning("oscar",
    - "Could not send %s because (%" G_GSIZE_FORMAT " > maxlen %i) or (%" G_GSIZE_FORMAT " > maxvis %i)\n",
    - buf2, len, c->maxlen, len, c->maxvis);
    - g_free(buf);
    - g_free(buf2);
    - return -E2BIG;
    - }
    -
    - purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
    - message, buf2);
    - }
    -
    - aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
    - g_free(buf2);
    - g_free(buf);
    -
    - return 0;
    -}
    -
    -PurpleMood* oscar_get_purple_moods(PurpleAccount *account)
    -{
    - return icq_get_purple_moods(account);
    -}
    -
    -const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b)
    -{
    - const char *name = b ? purple_buddy_get_name(b) : NULL;
    - if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name))
    - return "icq";
    -
    - return "icq";
    -}
    -
    -const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b)
    -{
    - const char *name = b ? purple_buddy_get_name(b) : NULL;
    - if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name))
    - return "icq";
    -
    - return "aim";
    -}
    -
    -const char *oscar_list_emblem(PurpleBuddy *b)
    -{
    - PurpleConnection *gc = NULL;
    - OscarData *od = NULL;
    - PurpleAccount *account = NULL;
    - PurplePresence *presence;
    - aim_userinfo_t *userinfo = NULL;
    - const char *name;
    -
    - account = purple_buddy_get_account(b);
    - name = purple_buddy_get_name(b);
    - if (account != NULL)
    - gc = purple_account_get_connection(account);
    - if (gc != NULL)
    - od = purple_connection_get_protocol_data(gc);
    - if (od != NULL)
    - userinfo = aim_locate_finduserinfo(od, name);
    -
    - presence = purple_buddy_get_presence(b);
    -
    - if (purple_presence_is_online(presence) == FALSE) {
    - char *gname;
    - if ((name) && (od) && (od->ssi.received_data) &&
    - (gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name)) &&
    - (aim_ssi_waitingforauth(&od->ssi.local, gname, name))) {
    - return "not-authorized";
    - }
    - }
    -
    - if (userinfo != NULL ) {
    - if (userinfo->flags & AIM_FLAG_ADMINISTRATOR)
    - return "admin";
    - if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
    - return "bot";
    - if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM)
    - return "secure";
    - if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
    - return "birthday";
    -
    - /* Make the mood icon override anything below this. */
    - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
    - return NULL;
    -
    - if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
    - return "hiptop";
    - }
    - return NULL;
    -}
    -
    -void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - OscarData *od;
    - aim_userinfo_t *userinfo;
    -
    - if (!PURPLE_BUDDY_IS_ONLINE(b))
    - return;
    -
    - account = purple_buddy_get_account(b);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
    -
    - oscar_user_info_append_status(gc, user_info, b, userinfo, /* use_html_status */ FALSE);
    -
    - if (full)
    - oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
    -}
    -
    -char *oscar_status_text(PurpleBuddy *b)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - OscarData *od;
    - PurplePresence *presence;
    - PurpleStatus *status;
    - const char *message;
    - gchar *ret = NULL;
    -
    - gc = purple_account_get_connection(purple_buddy_get_account(b));
    - account = purple_connection_get_account(gc);
    - od = purple_connection_get_protocol_data(gc);
    - presence = purple_buddy_get_presence(b);
    - status = purple_presence_get_active_status(presence);
    -
    - if ((od != NULL) && !purple_presence_is_online(presence))
    - {
    - const char *name = purple_buddy_get_name(b);
    - char *gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name);
    - if (aim_ssi_waitingforauth(&od->ssi.local, gname, name))
    - ret = g_strdup(_("Not Authorized"));
    - else
    - ret = g_strdup(_("Offline"));
    - }
    - else
    - {
    - message = purple_status_get_attr_string(status, "message");
    - if (message != NULL)
    - {
    - gchar *tmp = oscar_util_format_string(message, purple_account_get_username(account));
    - ret = purple_markup_escape_text(tmp, -1);
    - g_free(tmp);
    - }
    - else if (purple_status_is_available(status))
    - {
    - /* Don't show "Available" as status message in case buddy doesn't have a status message */
    - }
    - else
    - {
    - ret = g_strdup(purple_status_get_name(status));
    - }
    - }
    -
    - return ret;
    -}
    -
    -void oscar_set_aim_permdeny(PurpleConnection *gc) {
    - PurpleAccount *account = purple_connection_get_account(gc);
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - /*
    - * Conveniently there is a one-to-one mapping between the
    - * values of libpurple's PurplePrivacyType and the values used
    - * by the oscar protocol.
    - */
    - aim_ssi_setpermdeny(od, purple_account_get_privacy_type(account));
    -}
    -
    -void oscar_add_permit(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to add a permit\n");
    - aim_ssi_add_to_private_list(od, who, AIM_SSI_TYPE_PERMIT);
    -}
    -
    -void oscar_add_deny(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to add a deny\n");
    - aim_ssi_add_to_private_list(od, who, aim_ssi_getdenyentrytype(od));
    -}
    -
    -void oscar_rem_permit(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to delete a permit\n");
    - aim_ssi_del_from_private_list(od, who, AIM_SSI_TYPE_PERMIT);
    -}
    -
    -void oscar_rem_deny(PurpleConnection *gc, const char *who) {
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - purple_debug_info("oscar", "ssi: About to delete a deny\n");
    - aim_ssi_del_from_private_list(od, who, aim_ssi_getdenyentrytype(od));
    -}
    -
    -GList *
    -oscar_status_types(PurpleAccount *account)
    -{
    - gboolean is_icq;
    - GList *status_types = NULL;
    - PurpleStatusType *type;
    -
    - g_return_val_if_fail(account != NULL, NULL);
    -
    - /* Used to flag some statuses as "user settable" or not */
    - is_icq = oscar_util_valid_name_icq(purple_account_get_username(account));
    -
    - /* Common status types */
    - /* Really the available message should only be settable for AIM accounts */
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_AVAILABLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING),
    - "itmsurl", _("iTunes Music Store Link"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_FREE4CHAT,
    - _("Free For Chat"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_EVIL,
    - _("Evil"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_DEPRESSION,
    - _("Depression"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_ATHOME,
    - _("At home"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_ATWORK,
    - _("At work"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
    - OSCAR_STATUS_ID_LUNCH,
    - _("Lunch"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
    - OSCAR_STATUS_ID_AWAY,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
    - OSCAR_STATUS_ID_INVISIBLE,
    - NULL, TRUE, TRUE, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    -
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE);
    - status_types = g_list_prepend(status_types, type);
    -
    - /* ICQ-specific status types */
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
    - OSCAR_STATUS_ID_OCCUPIED,
    - _("Occupied"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
    - OSCAR_STATUS_ID_DND,
    - _("Do Not Disturb"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY,
    - OSCAR_STATUS_ID_NA,
    - _("Not Available"), TRUE, is_icq, FALSE,
    - "message", _("Message"),
    - purple_value_new(G_TYPE_STRING), NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
    - OSCAR_STATUS_ID_OFFLINE,
    - NULL, TRUE, TRUE, FALSE);
    - status_types = g_list_prepend(status_types, type);
    -
    - type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
    - "mood", NULL, TRUE, is_icq, TRUE,
    - PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(G_TYPE_STRING),
    - PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(G_TYPE_STRING),
    - NULL);
    - status_types = g_list_prepend(status_types, type);
    -
    - return g_list_reverse(status_types);
    -}
    -
    -static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - OscarData *od;
    - PurpleBuddy *b;
    - PurpleGroup *g;
    -
    - gc = data->gc;
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - b = purple_blist_find_buddy(account, data->name);
    - if (b == NULL) {
    - oscar_free_name_data(data);
    - return;
    - }
    -
    - g = purple_buddy_get_group(b);
    - if (g == NULL) {
    - oscar_free_name_data(data);
    - return;
    - }
    -
    - aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text);
    - oscar_free_name_data(data);
    -}
    -
    -static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) {
    -
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - OscarData *od;
    - struct name_data *data;
    - PurpleGroup *g;
    - char *comment;
    - gchar *comment_utf8;
    - gchar *title;
    - PurpleAccount *account;
    - const char *name;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *) node;
    - name = purple_buddy_get_name(buddy);
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    -
    - if (!(g = purple_buddy_get_group(buddy)))
    - return;
    -
    - data = g_new(struct name_data, 1);
    -
    - comment = aim_ssi_getcomment(&od->ssi.local, purple_group_get_name(g), name);
    - comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL;
    -
    - data->gc = gc;
    - data->name = g_strdup(name);
    - data->nick = g_strdup(purple_buddy_get_alias_only(buddy));
    -
    - title = g_strdup_printf(_("Buddy Comment for %s"), data->name);
    - purple_request_input(gc, title, _("Buddy Comment:"), NULL,
    - comment_utf8, TRUE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(oscar_ssi_editcomment),
    - _("_Cancel"), G_CALLBACK(oscar_free_name_data),
    - purple_request_cpar_from_connection(gc),
    - data);
    - g_free(title);
    -
    - g_free(comment);
    - g_free(comment_utf8);
    -}
    -
    -static void
    -oscar_ask_directim_yes_cb(struct oscar_ask_directim_data *data)
    -{
    - peer_connection_propose(data->od, OSCAR_CAPABILITY_DIRECTIM, data->who);
    - g_free(data->who);
    - g_free(data);
    -}
    -
    -static void
    -oscar_ask_directim_no_cb(struct oscar_ask_directim_data *data)
    -{
    - g_free(data->who);
    - g_free(data);
    -}
    -
    -/* This is called from right-click menu on a buddy node. */
    -static void
    -oscar_ask_directim(gpointer object, gpointer ignored)
    -{
    - PurpleBlistNode *node;
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - gchar *buf;
    - struct oscar_ask_directim_data *data;
    - PurpleAccount *account;
    -
    - node = object;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *)node;
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    -
    - data = g_new0(struct oscar_ask_directim_data, 1);
    - data->who = g_strdup(purple_buddy_get_name(buddy));
    - data->od = purple_connection_get_protocol_data(gc);
    - buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
    - data->who);
    -
    - purple_request_action(gc, NULL, buf,
    - _("Because this reveals your IP address, it "
    - "may be considered a security risk. Do you "
    - "wish to continue?"),
    - 0, /* Default action is "connect" */
    - purple_request_cpar_from_account(account),
    - data, 2,
    - _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
    - _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
    - g_free(buf);
    -}
    -
    -static void
    -oscar_close_directim(gpointer object, gpointer ignored)
    -{
    - PurpleBlistNode *node;
    - PurpleBuddy *buddy;
    - PurpleAccount *account;
    - PurpleConnection *gc;
    - PurpleIMConversation *im;
    - OscarData *od;
    - PeerConnection *conn;
    - const char *name;
    -
    - node = object;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy*)node;
    - name = purple_buddy_get_name(buddy);
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if (conn != NULL)
    - {
    - if (!conn->ready)
    - aim_im_sendch2_cancel(conn);
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -
    - /* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo
    - * window. Let the user know that we cancelled the Direct IM. */
    - im = purple_im_conversation_new(account, name);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), _("You closed the connection."), 0);
    - }
    -}
    -
    -static void oscar_get_icqxstatusmsg(PurpleBlistNode *node, gpointer ignore)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    - OscarData *od;
    - PurpleAccount *account;
    - const char *bname;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *)node;
    - bname = purple_buddy_get_name(buddy);
    -
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    -
    - purple_debug_info("oscar", "Manual X-Status Get From %s to %s:\n", bname, purple_account_get_username(account));
    -
    - icq_im_xstatus_request(od, bname);
    -}
    -
    -static void
    -oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
    -{
    - PurpleBuddy *buddy;
    - PurpleConnection *gc;
    -
    - g_return_if_fail(PURPLE_IS_BUDDY(node));
    -
    - buddy = (PurpleBuddy *)node;
    - gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    -
    - aim_locate_getinfoshort(purple_connection_get_protocol_data(gc),
    - purple_buddy_get_name(buddy), 0x00000003);
    -}
    -
    -static GList *
    -oscar_buddy_menu(PurpleBuddy *buddy) {
    - PurpleConnection *gc;
    - OscarData *od;
    - GList *menu;
    - PurpleActionMenu *act;
    - aim_userinfo_t *userinfo;
    - PurpleAccount *account;
    - const char *bname = purple_buddy_get_name(buddy);
    -
    - account = purple_buddy_get_account(buddy);
    - gc = purple_account_get_connection(account);
    - od = purple_connection_get_protocol_data(gc);
    - userinfo = aim_locate_finduserinfo(od, bname);
    - menu = NULL;
    -
    - if (od->icq && oscar_util_valid_name_icq(bname))
    - {
    - act = purple_action_menu_new(_("Get AIM Info"),
    - PURPLE_CALLBACK(oscar_get_aim_info_cb),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - if (purple_buddy_get_group(buddy) != NULL)
    - {
    - /* We only do this if the user is in our buddy list */
    - act = purple_action_menu_new(_("Edit Buddy Comment"),
    - PURPLE_CALLBACK(oscar_buddycb_edit_comment),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - if (od->icq)
    - {
    - act = purple_action_menu_new(_("Get X-Status Msg"),
    - PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - menu = g_list_prepend(menu, create_visibility_menu_item(od, bname));
    - }
    -
    - if (userinfo &&
    - oscar_util_name_compare(purple_account_get_username(account), bname) &&
    - PURPLE_BUDDY_IS_ONLINE(buddy))
    - {
    - PeerConnection *conn;
    - conn = peer_connection_find_by_type(od, bname, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
    - {
    - if (conn)
    - {
    - act = purple_action_menu_new(_("End Direct IM Session"),
    - PURPLE_CALLBACK(oscar_close_directim),
    - NULL, NULL);
    - }
    - else
    - {
    - act = purple_action_menu_new(_("Direct IM"),
    - PURPLE_CALLBACK(oscar_ask_directim),
    - NULL, NULL);
    - }
    - menu = g_list_prepend(menu, act);
    - }
    - }
    -
    - if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL)
    - {
    - /*
    - * We only do this if the user is in our buddy list and we're
    - * waiting for authorization.
    - */
    - char *gname;
    - gname = aim_ssi_itemlist_findparentname(&od->ssi.local, bname);
    - if (gname && aim_ssi_waitingforauth(&od->ssi.local, gname, bname))
    - {
    - act = purple_action_menu_new(_("Re-request Authorization"),
    - PURPLE_CALLBACK(oscar_auth_sendrequest_menu),
    - NULL, NULL);
    - menu = g_list_prepend(menu, act);
    - }
    - }
    -
    - menu = g_list_reverse(menu);
    -
    - return menu;
    -}
    -
    -
    -GList *oscar_blist_node_menu(PurpleBlistNode *node) {
    - if(PURPLE_IS_BUDDY(node)) {
    - return oscar_buddy_menu((PurpleBuddy *) node);
    - } else {
    - return NULL;
    - }
    -}
    -
    -static void
    -oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleRequestField *f;
    - gboolean auth, web_aware;
    -
    - f = purple_request_fields_get_field(fields, "authorization");
    - auth = purple_request_field_bool_get_value(f);
    -
    - f = purple_request_fields_get_field(fields, "web_aware");
    - web_aware = purple_request_field_bool_get_value(f);
    -
    - purple_account_set_bool(account, "authorization", auth);
    - purple_account_set_bool(account, "web_aware", web_aware);
    -
    - oscar_set_extended_status(gc);
    - aim_icq_setsecurity(od, auth, web_aware);
    -}
    -
    -static void
    -oscar_show_icq_privacy_opts(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleRequestFields *fields;
    - PurpleRequestFieldGroup *g;
    - PurpleRequestField *f;
    - gboolean auth, web_aware;
    -
    - auth = purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION);
    - web_aware = purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE);
    -
    - fields = purple_request_fields_new();
    -
    - g = purple_request_field_group_new(NULL);
    -
    - f = purple_request_field_bool_new("authorization", _("Require authorization"), auth);
    - purple_request_field_group_add_field(g, f);
    -
    - f = purple_request_field_bool_new("web_aware", _("Web aware (enabling this will cause you to receive SPAM!)"), web_aware);
    - purple_request_field_group_add_field(g, f);
    -
    - purple_request_fields_add_group(fields, g);
    -
    - purple_request_fields(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
    - NULL, fields,
    - _("OK"), G_CALLBACK(oscar_icq_privacy_opts),
    - _("Cancel"), NULL,
    - purple_request_cpar_from_connection(gc),
    - gc);
    -}
    -
    -static void oscar_confirm_account(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc;
    - OscarData *od;
    - FlapConnection *conn;
    -
    - gc = action->connection;
    - od = purple_connection_get_protocol_data(gc);
    -
    - conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    - if (conn != NULL) {
    - aim_admin_reqconfirm(od, conn);
    - } else {
    - od->conf = TRUE;
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    -}
    -
    -static void oscar_show_email(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    -
    - if (conn) {
    - aim_admin_getinfo(od, conn, 0x11);
    - } else {
    - od->reqemail = TRUE;
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    -}
    -
    -static void oscar_change_email(PurpleConnection *gc, const char *email)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    -
    - if (conn) {
    - aim_admin_setemail(od, conn, email);
    - } else {
    - od->setemail = TRUE;
    - od->email = g_strdup(email);
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    -}
    -
    -static void oscar_show_change_email(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_request_input(gc, NULL, _("Change Address To:"), NULL, NULL,
    - FALSE, FALSE, NULL,
    - _("_OK"), G_CALLBACK(oscar_change_email),
    - _("_Cancel"), NULL,
    - purple_request_cpar_from_connection(gc),
    - gc);
    -}
    -
    -static void oscar_show_awaitingauth(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *buddies, *filtered_buddies, *cur;
    - gchar *text;
    -
    - buddies = purple_blist_find_buddies(account, NULL);
    - filtered_buddies = NULL;
    - for (cur = buddies; cur != NULL; cur = cur->next) {
    - PurpleBuddy *buddy;
    - const gchar *bname, *gname;
    -
    - buddy = cur->data;
    - bname = purple_buddy_get_name(buddy);
    - gname = purple_group_get_name(purple_buddy_get_group(buddy));
    - if (aim_ssi_waitingforauth(&od->ssi.local, gname, bname)) {
    - filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
    - }
    - }
    -
    - g_slist_free(buddies);
    -
    - filtered_buddies = g_slist_reverse(filtered_buddies);
    - text = oscar_format_buddies(filtered_buddies, _("you are not waiting for authorization"));
    - g_slist_free(filtered_buddies);
    -
    - purple_notify_formatted(gc, NULL, _("You are awaiting authorization from "
    - "the following buddies"), _("You can re-request "
    - "authorization from these buddies by "
    - "right-clicking on them and selecting "
    - "\"Re-request Authorization.\""), text, NULL, NULL);
    - g_free(text);
    -}
    -
    -static void search_by_email_cb(PurpleConnection *gc, const char *email)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - aim_search_address(od, email);
    -}
    -
    -static void oscar_show_find_email(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_request_input(gc, _("Find Buddy by Email"),
    - _("Search for a buddy by email address"),
    - _("Type the email address of the buddy you are "
    - "searching for."),
    - NULL, FALSE, FALSE, NULL,
    - _("_Search"), G_CALLBACK(search_by_email_cb),
    - _("_Cancel"), NULL,
    - purple_request_cpar_from_connection(gc),
    - gc);
    -}
    -
    -static void oscar_show_set_info(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_account_request_change_user_info(purple_connection_get_account(gc));
    -}
    -
    -static void oscar_show_set_info_icqurl(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_notify_uri(gc, "http://www.icq.com/whitepages/user_details.php");
    -}
    -
    -static void oscar_change_pass(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_account_request_change_password(purple_connection_get_account(gc));
    -}
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -static void oscar_show_chpassurl(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - gchar *substituted = purple_strreplace(od->authinfo->chpassurl, "%s", purple_account_get_username(purple_connection_get_account(gc)));
    - purple_notify_uri(gc, substituted);
    - g_free(substituted);
    -}
    -
    -static void oscar_show_imforwardingurl(PurpleProtocolAction *action)
    -{
    - PurpleConnection *gc = action->connection;
    - purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
    -}
    -
    -void oscar_set_icon(PurpleConnection *gc, PurpleImage *img)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (img == NULL) {
    - aim_ssi_delicon(od);
    - } else {
    - GChecksum *hash;
    - guchar md5[16];
    - gsize digest_len = 16;
    - gconstpointer data = purple_image_get_data(img);
    - size_t len = purple_image_get_data_size(img);
    -
    - hash = g_checksum_new(G_CHECKSUM_MD5);
    - g_checksum_update(hash, data, len);
    - g_checksum_get_digest(hash, md5, &digest_len);
    - g_checksum_free(hash);
    -
    - aim_ssi_seticon(od, md5, 16);
    - }
    -}
    -
    -/**
    - * Called by the Purple core to determine whether or not we're
    - * allowed to send a file to this user.
    - */
    -gboolean
    -oscar_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - if (od != NULL)
    - {
    - aim_userinfo_t *userinfo;
    - userinfo = aim_locate_finduserinfo(od, who);
    -
    - /*
    - * Don't allowing sending a file to a user that does not support
    - * file transfer, and don't allow sending to ourselves.
    - */
    - if (((userinfo == NULL) ||
    - (userinfo->capabilities & OSCAR_CAPABILITY_SENDFILE)) &&
    - oscar_util_name_compare(who, purple_account_get_username(account)))
    - {
    - return TRUE;
    - }
    - }
    -
    - return FALSE;
    -}
    -
    -PurpleXfer *
    -oscar_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
    -{
    - OscarXfer *xfer;
    - OscarData *od;
    - PurpleAccount *account;
    - PeerConnection *conn;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who);
    - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    -
    - xfer = g_object_new(
    - OSCAR_TYPE_XFER,
    - "account", account,
    - "type", PURPLE_XFER_TYPE_SEND,
    - "remote-user", who,
    - "conn", conn,
    - NULL
    - );
    -
    - aim_icbm_makecookie(conn->cookie);
    - conn->xfer = PURPLE_XFER(xfer);
    -
    - return PURPLE_XFER(xfer);
    -}
    -
    -/*
    - * Called by the Purple core when the user indicates that a
    - * file is to be sent to a special someone.
    - */
    -void
    -oscar_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file)
    -{
    - PurpleXfer *xfer;
    -
    - xfer = oscar_new_xfer(prplxfer, gc, who);
    -
    - if (file != NULL)
    - purple_xfer_request_accepted(xfer, file);
    - else
    - purple_xfer_request(xfer);
    -}
    -
    -GList *
    -oscar_get_actions(PurpleConnection *gc)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - GList *menu = NULL;
    - PurpleProtocolAction *act;
    -
    - act = purple_protocol_action_new(_("Set User Info..."),
    - oscar_show_set_info);
    - menu = g_list_prepend(menu, act);
    -
    - if (od->icq)
    - {
    - act = purple_protocol_action_new(_("Set User Info (web)..."),
    - oscar_show_set_info_icqurl);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - act = purple_protocol_action_new(_("Change Password..."),
    - oscar_change_pass);
    - menu = g_list_prepend(menu, act);
    -
    - if (od->authinfo != NULL && od->authinfo->chpassurl != NULL)
    - {
    - /* This only happens when connecting with the old-style BUCP login */
    - act = purple_protocol_action_new(_("Change Password (web)"),
    - oscar_show_chpassurl);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - if (!od->icq)
    - {
    - act = purple_protocol_action_new(_("Configure IM Forwarding (web)"),
    - oscar_show_imforwardingurl);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - menu = g_list_prepend(menu, NULL);
    -
    - if (od->icq)
    - {
    - /* ICQ actions */
    - act = purple_protocol_action_new(_("Set Privacy Options..."),
    - oscar_show_icq_privacy_opts);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_protocol_action_new(_("Show Visible List"), oscar_show_visible_list);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_protocol_action_new(_("Show Invisible List"), oscar_show_invisible_list);
    - menu = g_list_prepend(menu, act);
    - }
    - else
    - {
    - /* AIM actions */
    - act = purple_protocol_action_new(_("Confirm Account"),
    - oscar_confirm_account);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_protocol_action_new(_("Display Currently Registered Email Address"),
    - oscar_show_email);
    - menu = g_list_prepend(menu, act);
    -
    - act = purple_protocol_action_new(_("Change Currently Registered Email Address..."),
    - oscar_show_change_email);
    - menu = g_list_prepend(menu, act);
    - }
    -
    - menu = g_list_prepend(menu, NULL);
    -
    - act = purple_protocol_action_new(_("Show Buddies Awaiting Authorization"),
    - oscar_show_awaitingauth);
    - menu = g_list_prepend(menu, act);
    -
    - menu = g_list_prepend(menu, NULL);
    -
    - act = purple_protocol_action_new(_("Search for Buddy by Email Address..."),
    - oscar_show_find_email);
    - menu = g_list_prepend(menu, act);
    -
    - menu = g_list_reverse(menu);
    -
    - return menu;
    -}
    -
    -void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new)
    -{
    - OscarData *od = purple_connection_get_protocol_data(gc);
    -
    - if (od->icq) {
    - aim_icq_changepasswd(od, new);
    - } else {
    - FlapConnection *conn;
    - conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
    - if (conn) {
    - aim_admin_changepasswd(od, conn, new, old);
    - } else {
    - od->chpass = TRUE;
    - od->oldp = g_strdup(old);
    - od->newp = g_strdup(new);
    - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
    - }
    - }
    -}
    -
    -void
    -oscar_convo_closed(PurpleConnection *gc, const char *who)
    -{
    - OscarData *od;
    - PeerConnection *conn;
    -
    - od = purple_connection_get_protocol_data(gc);
    - conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM);
    -
    - if (conn != NULL)
    - {
    - if (!conn->ready)
    - aim_im_sendch2_cancel(conn);
    -
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    - }
    -}
    -
    -const char *
    -oscar_normalize(const PurpleAccount *account, const char *str)
    -{
    - static char buf[BUF_LEN];
    - char *tmp1, *tmp2;
    - int i, j;
    -
    - g_return_val_if_fail(str != NULL, NULL);
    -
    - /* copy str to buf and skip all blanks */
    - i = 0;
    - for (j = 0; str[j]; j++) {
    - if (str[j] != ' ') {
    - buf[i++] = str[j];
    - if (i >= BUF_LEN - 1)
    - break;
    - }
    - }
    - buf[i] = '\0';
    -
    - tmp1 = g_utf8_strdown(buf, -1);
    - tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT);
    - if (strlen(tmp2) > sizeof(buf) - 1) {
    - purple_debug_error("oscar", "normalized string exceeds buffer length!\n");
    - }
    - g_strlcpy(buf, tmp2, sizeof(buf));
    - g_free(tmp2);
    - g_free(tmp1);
    -
    - return buf;
    -}
    -
    -gboolean
    -oscar_offline_message(const PurpleBuddy *buddy)
    -{
    - return TRUE;
    -}
    -
    -gssize
    -oscar_get_max_message_size(PurpleConversation *conv)
    -{
    - /* XXX: got from pidgin-otr - verify and document it */
    - return 2343;
    -}
    -
    -/* TODO: Find somewhere to put this instead of including it in a bunch of places.
    - * Maybe just change purple_accounts_find() to return anything for the protocol if there is no acct_id.
    - */
    -static PurpleAccount *find_acct(const char *protocol, const char *acct_id)
    -{
    - PurpleAccount *acct = NULL;
    -
    - /* If we have a specific acct, use it */
    - if (acct_id) {
    - acct = purple_accounts_find(acct_id, protocol);
    - if (acct && !purple_account_is_connected(acct))
    - acct = NULL;
    - } else { /* Otherwise find an active account for the protocol */
    - GList *l = purple_accounts_get_all();
    - while (l) {
    - if (purple_strequal(protocol, purple_account_get_protocol_id(l->data))
    - && purple_account_is_connected(l->data)) {
    - acct = l->data;
    - break;
    - }
    - l = l->next;
    - }
    - }
    -
    - return acct;
    -}
    -
    -gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params)
    -{
    - char *acct_id;
    - char prpl[11];
    - PurpleAccount *acct;
    -
    - if (g_ascii_strcasecmp(proto, "aim") && g_ascii_strcasecmp(proto, "icq"))
    - return FALSE;
    -
    - if (params == NULL) {
    - /* All Oscar URI actions require some parameters eventually */
    - purple_debug_warning("oscar",
    - "No required params for handling URI");
    - return FALSE;
    - }
    -
    - acct_id = g_hash_table_lookup(params, "account");
    - g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto);
    -
    - acct = find_acct(proto, acct_id);
    -
    - if (!acct)
    - return FALSE;
    -
    - /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */
    - if (!g_ascii_strcasecmp(cmd, "GoIM")) {
    - char *bname = g_hash_table_lookup(params, "screenname");
    - if (bname) {
    - char *message = g_hash_table_lookup(params, "message");
    -
    - PurpleIMConversation *im = purple_conversations_find_im_with_account(
    - bname, acct);
    - if (im == NULL)
    - im = purple_im_conversation_new(acct, bname);
    - purple_conversation_present(PURPLE_CONVERSATION(im));
    -
    - if (message) {
    - /* Spaces are encoded as '+' */
    - g_strdelimit(message, "+", ' ');
    - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message);
    - }
    - }
    - /*else
    - **If pidgindialogs_im() was in the core, we could use it here.
    - * It is all purple_request_* based, but I'm not sure it really belongs in the core
    - pidgindialogs_im();*/
    -
    - return TRUE;
    - }
    - /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */
    - else if (!g_ascii_strcasecmp(cmd, "GoChat")) {
    - char *rname = g_hash_table_lookup(params, "roomname");
    - if (rname) {
    - /* This is somewhat hacky, but the params aren't useful after this command */
    - g_hash_table_insert(params, g_strdup("exchange"), g_strdup("4"));
    - g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
    - purple_serv_join_chat(purple_account_get_connection(acct), params);
    - }
    - /*else
    - ** Same as above (except that this would have to be re-written using purple_request_*)
    - pidgin_blist_joinchat_show(); */
    -
    - return TRUE;
    - }
    - /* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/
    - else if (!g_ascii_strcasecmp(cmd, "AddBuddy")) {
    - char *bname = g_hash_table_lookup(params, "screenname");
    - char *gname = g_hash_table_lookup(params, "groupname");
    - purple_blist_request_add_buddy(acct, bname, gname, NULL);
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -void oscar_init_account_options(PurpleProtocol *protocol, gboolean is_icq)
    -{
    - PurpleAccountOption *option;
    - static const gchar *encryption_keys[] = {
    - N_("Use encryption if available"),
    - N_("Require encryption"),
    - N_("Don't use encryption"),
    - NULL
    - };
    - static const gchar *encryption_values[] = {
    - OSCAR_OPPORTUNISTIC_ENCRYPTION,
    - OSCAR_REQUIRE_ENCRYPTION,
    - OSCAR_NO_ENCRYPTION,
    - NULL
    - };
    - static const gchar *aim_login_keys[] = {
    - N_("Use clientLogin authentication"),
    - N_("Use Kerberos-based authentication"),
    - N_("Use MD5 based authentication"),
    - NULL
    - };
    - static const gchar *aim_login_values[] = {
    - OSCAR_CLIENT_LOGIN,
    - OSCAR_KERBEROS_LOGIN,
    - OSCAR_MD5_LOGIN,
    - NULL
    - };
    - static const gchar *icq_login_keys[] = {
    - N_("Use clientLogin authentication"),
    - N_("Use MD5 based authentication"),
    - NULL
    - };
    - static const gchar *icq_login_values[] = {
    - OSCAR_CLIENT_LOGIN,
    - OSCAR_MD5_LOGIN,
    - NULL
    - };
    - const gchar **login_keys;
    - const gchar **login_values;
    - GList *encryption_options = NULL;
    - GList *login_options = NULL;
    - int i;
    -
    - option = purple_account_option_string_new(_("Server"), "server", oscar_get_login_server(is_icq, TRUE));
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - for (i = 0; encryption_keys[i]; i++) {
    - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
    - kvp->key = g_strdup(_(encryption_keys[i]));
    - kvp->value = g_strdup(encryption_values[i]);
    - encryption_options = g_list_append(encryption_options, kvp);
    - }
    - option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - if (is_icq) {
    - login_keys = icq_login_keys;
    - login_values = icq_login_values;
    - } else {
    - login_keys = aim_login_keys;
    - login_values = aim_login_values;
    - }
    - for (i = 0; login_keys[i]; i++) {
    - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
    - kvp->key = g_strdup(_(login_keys[i]));
    - kvp->value = g_strdup(login_values[i]);
    - login_options = g_list_append(login_options, kvp);
    - }
    - option = purple_account_option_list_new(_("Authentication method"), "login_type", login_options);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - option = purple_account_option_bool_new(
    - _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
    - OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    -
    - if (is_icq) {
    - option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    - } else {
    - option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
    - OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS);
    - protocol->account_options = g_list_append(protocol->account_options, option);
    - }
    -}
    -
    -static void
    -oscar_protocol_init(OscarProtocol *self)
    -{
    - PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    -
    - protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE |
    - OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE;
    - protocol->icon_spec = purple_buddy_icon_spec_new("gif,jpeg,bmp,ico",
    - 0, 0, 64, 64, 7168,
    - PURPLE_ICON_SCALE_SEND |
    - PURPLE_ICON_SCALE_DISPLAY);
    -}
    -
    -static void
    -oscar_protocol_class_init(OscarProtocolClass *klass)
    -{
    - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    -
    - protocol_class->login = oscar_login;
    - protocol_class->close = oscar_close;
    - protocol_class->status_types = oscar_status_types;
    -}
    -
    -static void
    -oscar_protocol_class_finalize(G_GNUC_UNUSED OscarProtocolClass *klass)
    -{
    -}
    -
    -static void
    -oscar_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    -{
    - client_iface->get_actions = oscar_get_actions;
    - client_iface->list_emblem = oscar_list_emblem;
    - client_iface->status_text = oscar_status_text;
    - client_iface->tooltip_text = oscar_tooltip_text;
    - client_iface->blist_node_menu = oscar_blist_node_menu;
    - client_iface->convo_closed = oscar_convo_closed;
    - client_iface->normalize = oscar_normalize;
    - client_iface->offline_message = oscar_offline_message;
    -}
    -
    -static void
    -oscar_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    -{
    - server_iface->set_info = oscar_set_info;
    - server_iface->get_info = oscar_get_info;
    - server_iface->set_status = oscar_set_status;
    - server_iface->set_idle = oscar_set_idle;
    - server_iface->change_passwd = oscar_change_passwd;
    - server_iface->add_buddy = oscar_add_buddy;
    - server_iface->remove_buddy = oscar_remove_buddy;
    - server_iface->keepalive = oscar_keepalive;
    - server_iface->alias_buddy = oscar_alias_buddy;
    - server_iface->group_buddy = oscar_move_buddy;
    - server_iface->rename_group = oscar_rename_group;
    - server_iface->set_buddy_icon = oscar_set_icon;
    - server_iface->remove_group = oscar_remove_group;
    -}
    -
    -static void
    -oscar_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    -{
    - im_iface->send = oscar_send_im;
    - im_iface->send_typing = oscar_send_typing;
    -}
    -
    -static void
    -oscar_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    -{
    - chat_iface->info = oscar_chat_info;
    - chat_iface->info_defaults = oscar_chat_info_defaults;
    - chat_iface->join = oscar_join_chat;
    - chat_iface->get_name = oscar_get_chat_name;
    - chat_iface->invite = oscar_chat_invite;
    - chat_iface->leave = oscar_chat_leave;
    - chat_iface->send = oscar_send_chat;
    -}
    -
    -static void
    -oscar_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    -{
    - privacy_iface->add_deny = oscar_add_deny;
    - privacy_iface->rem_deny = oscar_rem_deny;
    -}
    -
    -static void
    -oscar_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface)
    -{
    - xfer_iface->can_receive = oscar_can_receive_file;
    - xfer_iface->send_file = oscar_send_file;
    - xfer_iface->new_xfer = oscar_new_xfer;
    -}
    -
    -G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    - OscarProtocol, oscar_protocol, PURPLE_TYPE_PROTOCOL,
    - G_TYPE_FLAG_ABSTRACT,
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    - oscar_protocol_client_iface_init)
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    - oscar_protocol_server_iface_init)
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    - oscar_protocol_im_iface_init)
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    - oscar_protocol_chat_iface_init)
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    - oscar_protocol_privacy_iface_init)
    -
    - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    - oscar_protocol_xfer_iface_init));
    -
    -static PurplePluginInfo *
    -plugin_query(GError **error)
    -{
    - return purple_plugin_info_new(
    - "id", "prpl-oscar",
    - "name", "Oscar Protocols",
    - "version", DISPLAY_VERSION,
    - "category", N_("Protocol"),
    - "summary", N_("Oscar (AIM/ICQ) Protocols Plugin"),
    - "description", N_("Oscar (AIM/ICQ) Protocols Plugin"),
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
    - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
    - NULL
    - );
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin, GError **error)
    -{
    - oscar_protocol_register_type(G_TYPE_MODULE(plugin));
    -
    - aim_protocol_register(plugin);
    - icq_protocol_register(plugin);
    -
    - oscar_xfer_register(G_TYPE_MODULE(plugin));
    -
    - aim_protocol = purple_protocols_add(AIM_TYPE_PROTOCOL, error);
    - if (!aim_protocol)
    - return FALSE;
    -
    - icq_protocol = purple_protocols_add(ICQ_TYPE_PROTOCOL, error);
    - if (!icq_protocol)
    - return FALSE;
    -
    - purple_signal_connect(purple_get_core(), "uri-handler", aim_protocol,
    - PURPLE_CALLBACK(oscar_uri_handler), NULL);
    - purple_signal_connect(purple_get_core(), "uri-handler", icq_protocol,
    - PURPLE_CALLBACK(oscar_uri_handler), NULL);
    -
    - /* Preferences */
    - purple_prefs_add_none("/plugins/prpl/oscar");
    - purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE);
    -
    - purple_prefs_remove("/plugins/prpl/oscar/show_idle");
    - purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy");
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin, GError **error)
    -{
    - if (!purple_protocols_remove(icq_protocol, error))
    - return FALSE;
    -
    - if (!purple_protocols_remove(aim_protocol, error))
    - return FALSE;
    -
    - return TRUE;
    -}
    -
    -PURPLE_PLUGIN_INIT(oscar, plugin_query, plugin_load, plugin_unload);
    --- a/libpurple/protocols/oscar/oscar.h Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1379 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Main libfaim header. Must be included in client for prototypes/macros.
    - *
    - * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
    - * -- Josh Myer
    - *
    - */
    -
    -#ifndef PURPLE_OSCAR_OSCAR_H
    -#define PURPLE_OSCAR_OSCAR_H
    -
    -#include "internal.h"
    -#include "circularbuffer.h"
    -#include "debug.h"
    -#include "eventloop.h"
    -#include "proxy.h"
    -#include "sslconn.h"
    -
    -#include <stdio.h>
    -#include <string.h>
    -#include <fcntl.h>
    -#include <sys/types.h>
    -#include <stdlib.h>
    -#include <stdarg.h>
    -#include <errno.h>
    -#include <time.h>
    -
    -#include <gmodule.h>
    -#include <libsoup/soup.h>
    -
    -#ifndef _WIN32
    -#include <sys/time.h>
    -#include <unistd.h>
    -#include <netdb.h>
    -#include <netinet/in.h>
    -#include <sys/socket.h>
    -#endif
    -
    -#define OSCAR_TYPE_PROTOCOL (oscar_protocol_get_type())
    -#define OSCAR_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OSCAR_TYPE_PROTOCOL, OscarProtocol))
    -#define OSCAR_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OSCAR_TYPE_PROTOCOL, OscarProtocolClass))
    -#define OSCAR_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), OSCAR_TYPE_PROTOCOL))
    -#define OSCAR_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), OSCAR_TYPE_PROTOCOL))
    -#define OSCAR_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), OSCAR_TYPE_PROTOCOL, OscarProtocolClass))
    -
    -typedef struct _ByteStream ByteStream;
    -typedef struct _ClientInfo ClientInfo;
    -typedef struct _FlapConnection FlapConnection;
    -typedef struct _FlapFrame FlapFrame;
    -typedef struct _IcbmArgsCh2 IcbmArgsCh2;
    -typedef struct _IcbmCookie IcbmCookie;
    -typedef struct _OscarData OscarData;
    -typedef struct _QueuedSnac QueuedSnac;
    -
    -typedef guint32 aim_snacid_t;
    -
    -#include "snactypes.h"
    -
    -G_BEGIN_DECLS
    -
    -#define FAIM_SNAC_HASH_SIZE 16
    -
    -/*
    - * Current Maximum Length for usernames (not including NULL)
    - *
    - * Currently only names up to 16 characters can be registered
    - * however it is apparently legal for them to be larger.
    - */
    -#define MAXSNLEN 97
    -
    -/*
    - * Current Maximum Length for Instant Messages
    - *
    - * This was found basically by experiment, but not wholly
    - * accurate experiment. It should not be regarded
    - * as completely correct. But its a decent approximation.
    - *
    - * Note that although we can send this much, its impossible
    - * for WinAIM clients (up through the latest (4.0.1957)) to
    - * send any more than 1kb. Amaze all your windows friends
    - * with utterly oversized instant messages!
    - */
    -#define MAXMSGLEN 2544
    -
    -/*
    - * Maximum size of a Buddy Icon.
    - */
    -#define MAXICONLEN 7168
    -#define AIM_ICONIDENT "AVT1picture.id"
    -
    -/*
    - * Found by trial and error.
    - */
    -#define MAXAVAILMSGLEN 251
    -
    -/**
    - * Maximum length for the password of an ICQ account
    - */
    -#define MAXICQPASSLEN 16
    -
    -#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
    -
    -#define OSCAR_CONNECT_STEPS 6
    -
    -/*
    - * Client info. Filled in by the client and passed in to
    - * aim_send_login(). The information ends up getting passed to OSCAR
    - * through the initial login command.
    - *
    - */
    -struct _ClientInfo
    -{
    - const char *clientstring;
    - guint16 clientid;
    - guint16 major;
    - guint16 minor;
    - guint16 point;
    - guint16 build;
    - guint32 distrib;
    - const char *country; /* two-letter abbrev */
    - const char *lang; /* two-letter abbrev */
    -};
    -
    -/*
    - * We need to use the major-minor-micro versions from the official
    - * AIM and ICQ programs here or AOL won't let us use certain features.
    - *
    - * 0x00000611 is the distid given to us by AOL for use as the default
    - * libpurple distid.
    - */
    -#define CLIENTINFO_PURPLE_AIM { \
    - NULL, \
    - 0x0109, \
    - 0x0005, 0x0001, \
    - 0x0000, 0x0bdc, \
    - 0x00000611, \
    - "us", "en", \
    -}
    -
    -#define CLIENTINFO_PURPLE_ICQ { \
    - NULL, \
    - 0x010a, \
    - 0x0014, 0x0034, \
    - 0x0000, 0x0c18, \
    - 0x00000611, \
    - "us", "en", \
    -}
    -
    -typedef enum
    -{
    - OSCAR_DISCONNECT_DONE, /* not considered an error */
    - OSCAR_DISCONNECT_LOCAL_CLOSED, /* peer connections only, not considered an error */
    - OSCAR_DISCONNECT_REMOTE_CLOSED,
    - OSCAR_DISCONNECT_REMOTE_REFUSED, /* peer connections only */
    - OSCAR_DISCONNECT_LOST_CONNECTION,
    - OSCAR_DISCONNECT_INVALID_DATA,
    - OSCAR_DISCONNECT_COULD_NOT_CONNECT,
    - OSCAR_DISCONNECT_RETRYING /* peer connections only */
    -} OscarDisconnectReason;
    -
    -#define OSCAR_CAPABILITY_BUDDYICON 0x0000000000000001LL
    -#define OSCAR_CAPABILITY_TALK 0x0000000000000002LL
    -#define OSCAR_CAPABILITY_DIRECTIM 0x0000000000000004LL
    -#define OSCAR_CAPABILITY_CHAT 0x0000000000000008LL
    -#define OSCAR_CAPABILITY_GETFILE 0x0000000000000010LL
    -#define OSCAR_CAPABILITY_SENDFILE 0x0000000000000020LL
    -#define OSCAR_CAPABILITY_GAMES 0x0000000000000040LL
    -#define OSCAR_CAPABILITY_ADDINS 0x0000000000000080LL
    -#define OSCAR_CAPABILITY_SENDBUDDYLIST 0x0000000000000100LL
    -#define OSCAR_CAPABILITY_GAMES2 0x0000000000000200LL
    -#define OSCAR_CAPABILITY_ICQ_DIRECT 0x0000000000000400LL
    -#define OSCAR_CAPABILITY_APINFO 0x0000000000000800LL
    -#define OSCAR_CAPABILITY_ICQRTF 0x0000000000001000LL
    -#define OSCAR_CAPABILITY_EMPTY 0x0000000000002000LL
    -#define OSCAR_CAPABILITY_ICQSERVERRELAY 0x0000000000004000LL
    -#define OSCAR_CAPABILITY_UNICODEOLD 0x0000000000008000LL
    -#define OSCAR_CAPABILITY_TRILLIANCRYPT 0x0000000000010000LL
    -#define OSCAR_CAPABILITY_UNICODE 0x0000000000020000LL
    -#define OSCAR_CAPABILITY_INTEROPERATE 0x0000000000040000LL
    -#define OSCAR_CAPABILITY_SHORTCAPS 0x0000000000080000LL
    -#define OSCAR_CAPABILITY_HIPTOP 0x0000000000100000LL
    -#define OSCAR_CAPABILITY_SECUREIM 0x0000000000200000LL
    -#define OSCAR_CAPABILITY_SMS 0x0000000000400000LL
    -#define OSCAR_CAPABILITY_VIDEO 0x0000000000800000LL
    -#define OSCAR_CAPABILITY_ICHATAV 0x0000000001000000LL
    -#define OSCAR_CAPABILITY_LIVEVIDEO 0x0000000002000000LL
    -#define OSCAR_CAPABILITY_CAMERA 0x0000000004000000LL
    -#define OSCAR_CAPABILITY_ICHAT_SCREENSHARE 0x0000000008000000LL
    -#define OSCAR_CAPABILITY_TYPING 0x0000000010000000LL
    -#define OSCAR_CAPABILITY_NEWCAPS 0x0000000020000000LL
    -#define OSCAR_CAPABILITY_XTRAZ 0x0000000040000000LL
    -#define OSCAR_CAPABILITY_GENERICUNKNOWN 0x0000000080000000LL
    -#define OSCAR_CAPABILITY_HTML_MSGS 0x0000000100000000LL
    -#define OSCAR_CAPABILITY_LAST 0x0000000200000000LL
    -
    -#define OSCAR_STATUS_ID_INVISIBLE "invisible"
    -#define OSCAR_STATUS_ID_OFFLINE "offline"
    -#define OSCAR_STATUS_ID_AVAILABLE "available"
    -#define OSCAR_STATUS_ID_AWAY "away"
    -#define OSCAR_STATUS_ID_DND "dnd"
    -#define OSCAR_STATUS_ID_NA "na"
    -#define OSCAR_STATUS_ID_OCCUPIED "occupied"
    -#define OSCAR_STATUS_ID_FREE4CHAT "free4chat"
    -#define OSCAR_STATUS_ID_CUSTOM "custom"
    -#define OSCAR_STATUS_ID_MOBILE "mobile"
    -#define OSCAR_STATUS_ID_EVIL "evil"
    -#define OSCAR_STATUS_ID_DEPRESSION "depression"
    -#define OSCAR_STATUS_ID_ATHOME "athome"
    -#define OSCAR_STATUS_ID_ATWORK "atwork"
    -#define OSCAR_STATUS_ID_LUNCH "lunch"
    -
    -typedef struct
    -{
    - PurpleProtocol parent;
    -} OscarProtocol;
    -
    -typedef struct
    -{
    - PurpleProtocolClass parent_class;
    -} OscarProtocolClass;
    -
    -/**
    - * Returns the GType for the OscarProtocol object.
    - */
    -G_MODULE_EXPORT GType oscar_protocol_get_type(void);
    -
    -/*
    - * Byte Stream type. Sort of.
    - *
    - * Use of this type serves a couple purposes:
    - * - Buffer/buflen pairs are passed all around everywhere. This turns
    - * that into one value, as well as abstracting it slightly.
    - * - Through the abstraction, it is possible to enable bounds checking
    - * for robustness at the cost of performance. But a clean failure on
    - * weird packets is much better than a segfault.
    - * - I like having variables named "bs".
    - *
    - * Don't touch the insides of this struct. Or I'll have to kill you.
    - *
    - */
    -struct _ByteStream
    -{
    - guint8 *data;
    - size_t len;
    - size_t offset;
    -};
    -
    -struct _QueuedSnac
    -{
    - guint16 family;
    - guint16 subtype;
    - FlapFrame *frame;
    -};
    -
    -struct _FlapFrame
    -{
    - guint8 channel;
    - guint16 seqnum;
    - ByteStream data; /* payload stream */
    -};
    -
    -struct _FlapConnection
    -{
    - OscarData *od; /**< Pointer to parent session. */
    - gboolean connected;
    - time_t lastactivity; /**< Time of last transmit. */
    - guint destroy_timeout;
    - OscarDisconnectReason disconnect_reason;
    - gchar *error_message;
    - guint16 disconnect_code;
    -
    - /* A few variables that are only used when connecting */
    - PurpleProxyConnectData *connect_data;
    - guint16 cookielen;
    - guint8 *cookie;
    - gpointer new_conn_data;
    -
    - int fd;
    - PurpleSslConnection *gsc;
    - guint8 header[6];
    - gssize header_received;
    - FlapFrame buffer_incoming;
    - PurpleCircularBuffer *buffer_outgoing;
    - guint watcher_incoming;
    - guint watcher_outgoing;
    -
    - guint16 type;
    - guint16 subtype;
    - guint16 seqnum_out; /**< The sequence number of most recently sent packet. */
    - guint16 seqnum_in; /**< The sequence number of most recently received packet. */
    - GSList *groups;
    - GSList *rateclasses; /* Contains nodes of struct rateclass. */
    - struct rateclass *default_rateclass;
    - GHashTable *rateclass_members; /* Key is family and subtype, value is pointer to the rateclass struct to use. */
    -
    - GQueue *queued_snacs; /**< Contains QueuedSnacs. */
    - GQueue *queued_lowpriority_snacs; /**< Contains QueuedSnacs to send only once queued_snacs is empty */
    - guint queued_timeout;
    -
    - void *internal; /* internal conn-specific libfaim data */
    -};
    -
    -struct _IcbmCookie
    -{
    - guchar cookie[8];
    - int type;
    - void *data;
    - time_t addtime;
    - struct _IcbmCookie *next;
    -};
    -
    -#include "peer.h"
    -
    -struct aim_ssi_itemlist {
    - struct aim_ssi_item *data;
    - GHashTable *idx_gid_bid;
    - GHashTable *idx_all_named_items;
    -};
    -
    -/**
    - * The main client-data interface.
    - */
    -struct _OscarData
    -{
    - /* Only used when connecting with clientLogin or Kerberos. */
    - SoupSession *http_conns;
    -
    - gboolean iconconnecting;
    - gboolean set_icon;
    -
    - GSList *create_rooms;
    -
    - gboolean conf;
    - gboolean reqemail;
    - gboolean setemail;
    - char *email;
    - gboolean setnick;
    - char *newformatting;
    - gboolean chpass;
    - char *oldp;
    - char *newp;
    -
    - GSList *oscar_chats;
    - GHashTable *buddyinfo;
    - GSList *requesticon;
    -
    - gboolean use_ssl;
    - gboolean icq;
    - guint getblisttimer;
    -
    - struct {
    - guint maxwatchers; /* max users who can watch you */
    - guint maxbuddies; /* max users you can watch */
    - guint maxgroups; /* max groups in server list */
    - guint maxpermits; /* max users on permit list */
    - guint maxdenies; /* max users on deny list */
    - guint maxsiglen; /* max size (bytes) of profile */
    - guint maxawaymsglen; /* max size (bytes) of posted away message */
    - } rights;
    -
    - PurpleConnection *gc;
    -
    - void *modlistv;
    -
    - /*
    - * Outstanding snac handling
    - *
    - * TODO: Should these be per-connection? -mid
    - */
    - void *snac_hash[FAIM_SNAC_HASH_SIZE];
    - aim_snacid_t snacid_next;
    -
    - /*
    - * TODO: Data specific to a certain family should go into a
    - * hashtable and the core parts of libfaim shouldn't
    - * need to know about them.
    - */
    -
    - IcbmCookie *msgcookies;
    - GSList *icq_info;
    -
    - /** Only used when connecting with the old-style BUCP login. */
    - struct aim_authresp_info *authinfo;
    - struct aim_emailinfo *emailinfo;
    -
    - struct {
    - struct aim_userinfo_s *userinfo;
    - } locate;
    -
    - struct {
    - gboolean have_rights;
    - } bos;
    -
    - /* Server-stored information (ssi) */
    - struct {
    - gboolean received_data;
    - guint16 numitems;
    - struct aim_ssi_itemlist official;
    - struct aim_ssi_itemlist local;
    - struct aim_ssi_tmp *pending;
    - time_t timestamp;
    - gboolean waiting_for_ack;
    - gboolean in_transaction;
    - } ssi;
    -
    - /** Contains pointers to handler functions for each family/subtype. */
    - GHashTable *handlerlist;
    -
    - /** A linked list containing FlapConnections. */
    - GSList *oscar_connections;
    - guint16 default_port;
    -
    - /** A linked list containing PeerConnections. */
    - GSList *peer_connections;
    -};
    -
    -/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
    -#define AIM_ICQ_STATE_NORMAL 0x00000000
    -#define AIM_ICQ_STATE_AWAY 0x00000001
    -#define AIM_ICQ_STATE_DND 0x00000002
    -#define AIM_ICQ_STATE_OUT 0x00000004
    -#define AIM_ICQ_STATE_BUSY 0x00000010
    -#define AIM_ICQ_STATE_CHAT 0x00000020
    -#define AIM_ICQ_STATE_INVISIBLE 0x00000100
    -#define AIM_ICQ_STATE_EVIL 0x00003000
    -#define AIM_ICQ_STATE_DEPRESSION 0x00004000
    -#define AIM_ICQ_STATE_ATHOME 0x00005000
    -#define AIM_ICQ_STATE_ATWORK 0x00006000
    -#define AIM_ICQ_STATE_LUNCH 0x00002001
    -#define AIM_ICQ_STATE_EVIL 0x00003000
    -#define AIM_ICQ_STATE_WEBAWARE 0x00010000
    -#define AIM_ICQ_STATE_HIDEIP 0x00020000
    -#define AIM_ICQ_STATE_BIRTHDAY 0x00080000
    -#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000
    -#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -struct aim_clientrelease
    -{
    - char *name;
    - guint32 build;
    - char *url;
    - char *info;
    -};
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -struct aim_authresp_info
    -{
    - char *bn;
    - guint16 errorcode;
    - char *errorurl;
    - guint16 regstatus;
    - char *email;
    - char *bosip;
    - guint16 cookielen;
    - guint8 *cookie;
    - char *chpassurl;
    - struct aim_clientrelease latestrelease;
    - struct aim_clientrelease latestbeta;
    -};
    -
    -/* Callback data for redirect. */
    -struct aim_redirect_data
    -{
    - guint16 group;
    - const char *ip;
    - guint16 cookielen;
    - const guint8 *cookie;
    - const char *ssl_cert_cn;
    - guint8 use_ssl;
    - struct { /* group == SNAC_FAMILY_CHAT */
    - guint16 exchange;
    - const char *room;
    - guint16 instance;
    - } chat;
    -};
    -
    -int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname);
    -
    -/* family_auth.c */
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -int aim_request_login(OscarData *od, FlapConnection *conn, const char *bn);
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -int aim_send_login(OscarData *od, FlapConnection *conn, const char *bn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins);
    -
    -/**
    - * Only used when connecting with the old-style BUCP login.
    - */
    -/* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid);
    -
    -/**
    - * Only used when connecting with clientLogin.
    - */
    -void send_client_login(OscarData *od, const char *username);
    -
    -/**
    - * Only used when connecting with kerberos login.
    - */
    -void send_kerberos_login(OscarData *od, const char *username);
    -
    -
    -/* flap_connection.c */
    -FlapConnection *flap_connection_new(OscarData *, int type);
    -void flap_connection_close(OscarData *od, FlapConnection *conn);
    -void flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -void flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -FlapConnection *flap_connection_findbygroup(OscarData *od, guint16 group);
    -FlapConnection *flap_connection_getbytype(OscarData *, int type);
    -FlapConnection *flap_connection_getbytype_all(OscarData *, int type);
    -void flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond);
    -void flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
    -
    -void flap_connection_send(FlapConnection *conn, FlapFrame *frame);
    -void flap_connection_send_version(OscarData *od, FlapConnection *conn);
    -void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy);
    -void flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_login);
    -void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data);
    -void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority);
    -void flap_connection_send_keepalive(OscarData *od, FlapConnection *conn);
    -FlapFrame *flap_frame_new(OscarData *od, guint16 channel, int datalen);
    -
    -/* oscar_data.c */
    -typedef int (*aim_rxcallback_t)(OscarData *od, FlapConnection *conn, FlapFrame *frame, ...);
    -
    -OscarData *oscar_data_new(void);
    -void oscar_data_destroy(OscarData *);
    -void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags);
    -aim_rxcallback_t aim_callhandler(OscarData *od, guint16 family, guint16 subtype);
    -
    -/* 0x0001 - family_oservice.c */
    -/* 0x0002 */ void aim_srv_clientready(OscarData *od, FlapConnection *conn);
    -/* 0x0004 */ void aim_srv_requestnew(OscarData *od, guint16 serviceid);
    -/* 0x0006 */ void aim_srv_reqrates(OscarData *od, FlapConnection *conn);
    -/* 0x0008 */ void aim_srv_rates_addparam(OscarData *od, FlapConnection *conn);
    -/* 0x000e */ void aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn);
    -/* 0x0011 */ void aim_srv_setidle(OscarData *od, guint32 idletime);
    -/* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn);
    -/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl);
    -void aim_srv_set_dc_info(OscarData *od);
    -
    -
    -void aim_bos_reqrights(OscarData *od, FlapConnection *conn);
    -
    -#define AIM_RATE_CODE_LIMIT 0x0003
    -
    -/* family_icbm.c */
    -#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002
    -
    -#define AIM_TRANSFER_DENY_DECLINE 0x0001
    -
    -#define AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED 0x00000001
    -#define AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED 0x00000002
    -#define AIM_IMPARAM_FLAG_EVENTS_ALLOWED 0x00000008
    -#define AIM_IMPARAM_FLAG_SMS_SUPPORTED 0x00000010
    -#define AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED 0x00000100
    -
    -/**
    - * This flag tells the server that we always send HTML in messages
    - * sent from an ICQ account to an ICQ account. (If this flag is
    - * not sent then plaintext is sent ICQ<-->ICQ (HTML is sent in all
    - * other cases)).
    - *
    - * If we send an HTML message to an old client that doesn't support
    - * HTML messages, then the oscar servers will merrily strip the HTML
    - * for us.
    - *
    - * All incoming IMs are treated as HTML.
    - */
    -#define AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ 0x00000400
    -
    -struct aim_icbmparameters
    -{
    - guint16 maxchan;
    - guint32 flags; /* AIM_IMPARAM_FLAG_ */
    - guint16 maxmsglen; /* message size that you will accept */
    - guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */
    - guint16 maxrecverwarn;
    - guint32 minmsginterval; /* in milliseconds? */
    -};
    -
    -/*
    - * TODO: Should probably combine this with struct chat_connection.
    - */
    -struct aim_chat_roominfo
    -{
    - guint16 exchange;
    - char *name;
    - guint8 namelen;
    - guint16 instance;
    -};
    -
    -struct chat_connection
    -{
    - char *name;
    - char *show; /* AOL did something funny to us */
    - guint16 exchange;
    - guint16 instance;
    - FlapConnection *conn;
    - int id;
    - PurpleConnection *gc;
    - PurpleChatConversation *conv;
    - guint16 maxlen;
    - guint16 maxvis;
    -};
    -
    -/*
    - * All this chat struct stuff should be in family_chat.c
    - */
    -void oscar_chat_destroy(struct chat_connection *cc);
    -
    -#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */
    -#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */
    -#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */
    -#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */
    -#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */
    -#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */
    -#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */
    -#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */
    -
    -#define AIM_CHARSET_ASCII 0x0000 /* ISO 646 */
    -#define AIM_CHARSET_UNICODE 0x0002 /* ISO 10646 (UTF-16/UCS-2BE) */
    -#define AIM_CHARSET_LATIN_1 0x0003 /* ISO 8859-1 */
    -
    -/*
    - * Arguments to aim_send_im_ext().
    - *
    - * This is really complicated. But immensely versatile.
    - *
    - */
    -struct aim_sendimext_args
    -{
    - /* These are _required_ */
    - const char *destbn;
    - guint32 flags; /* often 0 */
    -
    - const char *msg;
    - gsize msglen;
    -
    - /* Only used if AIM_IMFLAGS_HASICON is set */
    - guint32 iconlen;
    - time_t iconstamp;
    - guint32 iconsum;
    -
    - guint16 featureslen;
    - guint8 *features;
    -
    - guint16 charset;
    -};
    -
    -/*
    - * This information is provided in the Incoming ICBM callback for
    - * Channel 1 ICBM's.
    - */
    -struct aim_incomingim_ch1_args
    -{
    - guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
    - time_t timestamp; /* Only set for offline messages */
    -
    - gchar *msg;
    -
    - /* Only provided if AIM_IMFLAGS_HASICON is set */
    - time_t iconstamp;
    - guint32 iconlen;
    - guint16 iconsum;
    -};
    -
    -/* Valid values for channel 2 args->status */
    -#define AIM_RENDEZVOUS_PROPOSE 0x0000
    -#define AIM_RENDEZVOUS_CANCEL 0x0001
    -#define AIM_RENDEZVOUS_CONNECTED 0x0002
    -
    -struct _IcbmArgsCh2
    -{
    - guint16 status;
    - guchar cookie[8];
    - guint64 type; /* One of the OSCAR_CAPABILITY_ constants */
    - const char *proxyip;
    - const char *clientip;
    - const char *verifiedip;
    - guint16 port;
    - gboolean use_proxy;
    - guint16 errorcode;
    - const char *msg; /* invite message or file description */
    - guint16 msglen;
    - const char *encoding;
    - const char *language;
    - guint16 requestnumber;
    - union {
    - struct {
    - guint32 checksum;
    - guint32 length;
    - time_t timestamp;
    - guint8 *icon;
    - } icon;
    - struct {
    - struct aim_chat_roominfo roominfo;
    - } chat;
    - struct {
    - guint8 msgtype;
    - const char *msg;
    - } rtfmsg;
    - struct {
    - guint16 subtype;
    - guint16 totfiles;
    - guint32 totsize;
    - char *filename;
    - } sendfile;
    - } info;
    - void *destructor; /* used internally only */
    -};
    -
    -struct aim_incomingim_ch4_args
    -{
    - guint32 uin; /* Of the sender of the ICBM */
    - guint8 type;
    - guint8 flags;
    - gchar *msg; /* Reason for auth request, deny, or accept */
    - int msglen;
    -};
    -
    -/* SNAC sending functions */
    -/* 0x0002 */ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params);
    -/* 0x0004 */ int aim_im_reqparams(OscarData *od);
    -/* 0x0006 */ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args);
    -/* 0x0006 */ int aim_im_sendch1(OscarData *, const char *destbn, guint16 flags, const char *msg);
    -/* 0x0006 */ int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
    -/* 0x0006 */ int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
    -
    -/* 0x0006 */ void aim_im_sendch2_cancel(PeerConnection *peer_conn);
    -/* 0x0006 */ void aim_im_sendch2_connected(PeerConnection *peer_conn);
    -/* 0x0006 */ void aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber);
    -/* 0x0006 */ void aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber);
    -/* 0x0006 */ void aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
    -/* 0x0006 */ void aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
    -
    -/* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code);
    -/* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od);
    -/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2);
    -/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie);
    -void aim_icbm_makecookie(guchar* cookie);
    -void aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie);
    -
    -/* 0x0002 - family_locate.c */
    -/*
    - * AIM User Info, Standard Form.
    - */
    -#define AIM_FLAG_ADMINISTRATOR 0x0002
    -#define AIM_FLAG_AOL 0x0004
    -#define AIM_FLAG_AWAY 0x0020
    -#define AIM_FLAG_WIRELESS 0x0080
    -#define AIM_FLAG_ICQ 0x0040
    -#define AIM_FLAG_ACTIVEBUDDY 0x0400
    -
    -#define AIM_USERINFO_PRESENT_FLAGS 0x00000001
    -#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002
    -#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004
    -#define AIM_USERINFO_PRESENT_IDLE 0x00000008
    -#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010
    -#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020
    -#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040
    -#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080
    -#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100
    -#define AIM_USERINFO_PRESENT_CREATETIME 0x00000200
    -
    -struct userinfo_node
    -{
    - char *bn;
    - struct userinfo_node *next;
    -};
    -
    -typedef struct aim_userinfo_s
    -{
    - char *bn;
    - guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */
    - guint16 idletime; /* in seconds */
    - guint16 flags;
    - guint32 createtime; /* time_t */
    - guint32 membersince; /* time_t */
    - guint32 onlinesince; /* time_t */
    - guint32 sessionlen; /* in seconds */
    - guint64 capabilities;
    - struct {
    - guint32 status;
    - guint32 ipaddr;
    - guint8 crap[0x25]; /* until we figure it out... */
    - } icqinfo;
    - guint32 present;
    -
    - guint8 iconcsumtype;
    - guint16 iconcsumlen;
    - guint8 *iconcsum;
    -
    - char *info;
    - char *info_encoding;
    - guint16 info_len;
    -
    - char *status;
    - char *status_encoding;
    - guint16 status_len;
    -
    - char *itmsurl;
    - char *itmsurl_encoding;
    - guint16 itmsurl_len;
    -
    - char *away;
    - char *away_encoding;
    - guint16 away_len;
    -
    - struct aim_userinfo_s *next;
    -} aim_userinfo_t;
    -
    -struct aim_invite_priv
    -{
    - char *bn;
    - char *roomname;
    - guint16 exchange;
    - guint16 instance;
    -};
    -
    -#define AIM_COOKIETYPE_CHAT 0x01
    -#define AIM_COOKIETYPE_INVITE 0x02
    -
    -aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn);
    -void aim_locate_dorequest(OscarData *od);
    -
    -/* 0x0002 */ int aim_locate_reqrights(OscarData *od);
    -/* 0x0004 */ int aim_locate_setcaps(OscarData *od, guint64 caps);
    -/* 0x0004 */ int aim_locate_setprofile(OscarData *od, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len);
    -/* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags);
    -
    -guint64 aim_locate_getcaps(OscarData *od, ByteStream *bs, int len);
    -guint64 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len);
    -void aim_info_free(aim_userinfo_t *);
    -int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *);
    -int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info);
    -PurpleMood* icq_get_purple_moods(PurpleAccount *account);
    -const char* icq_get_custom_icon_description(const char *mood);
    -guint8* icq_get_custom_icon_data(const char *mood);
    -int icq_im_xstatus_request(OscarData *od, const char *sn);
    -
    -/* 0x0003 - family_buddy.c */
    -/* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *);
    -
    -
    -/* 0x000a - family_userlookup.c */
    -int aim_search_address(OscarData *, const char *);
    -
    -struct aim_chat_exchangeinfo
    -{
    - guint16 number;
    - guint16 flags;
    - char *name;
    - char *charset1;
    - char *lang1;
    - char *charset2;
    - char *lang2;
    -};
    -
    -#define AIM_CHATFLAGS_NOREFLECT 0x0001
    -#define AIM_CHATFLAGS_AWAY 0x0002
    -int aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language);
    -int aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance);
    -
    -void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn);
    -
    -int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange);
    -
    -
    -/* 0x0010 - family_bart.c */
    -int aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen);
    -int aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen);
    -
    -
    -
    -/* 0x0013 - family_feedbag.c */
    -#define AIM_SSI_TYPE_BUDDY 0x0000
    -#define AIM_SSI_TYPE_GROUP 0x0001
    -#define AIM_SSI_TYPE_PERMIT 0x0002
    -#define AIM_SSI_TYPE_DENY 0x0003
    -#define AIM_SSI_TYPE_PDINFO 0x0004
    -#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005
    -#define AIM_SSI_TYPE_ICQDENY 0x000e
    -#define AIM_SSI_TYPE_ICONINFO 0x0014
    -
    -/* These flags are set in the 0x00c9 TLV of SSI type 0x0005 */
    -#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE 0x00000400
    -#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000
    -
    -struct aim_ssi_item
    -{
    - char *name;
    - guint16 gid;
    - guint16 bid;
    - guint16 type;
    - GSList *data;
    - struct aim_ssi_item *next;
    -};
    -
    -struct aim_ssi_tmp
    -{
    - guint16 action;
    - guint16 ack;
    - char *name;
    - struct aim_ssi_item *item;
    - struct aim_ssi_tmp *next;
    -};
    -
    -/* These build the actual SNACs and queue them to be sent */
    -/* 0x0002 */ int aim_ssi_reqrights(OscarData *od);
    -/* 0x0004 */ int aim_ssi_reqdata(OscarData *od);
    -/* 0x0007 */ int aim_ssi_enable(OscarData *od);
    -/* 0x0011 */ int aim_ssi_modbegin(OscarData *od);
    -/* 0x0012 */ int aim_ssi_modend(OscarData *od);
    -/* 0x0018 */ int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg);
    -/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg);
    -
    -/* Client functions for retrieving SSI data */
    -struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_itemlist *list, guint16 gid, guint16 bid);
    -struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_itemlist *list, const char *gn, const char *bn, guint16 type);
    -struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_itemlist *list, const char *bn);
    -char *aim_ssi_itemlist_findparentname(struct aim_ssi_itemlist *list, const char *bn);
    -int aim_ssi_getpermdeny(struct aim_ssi_itemlist *list);
    -guint32 aim_ssi_getpresence(struct aim_ssi_itemlist *list);
    -char *aim_ssi_getalias(struct aim_ssi_itemlist *list, const char *gn, const char *bn);
    -char *aim_ssi_getalias_from_item(struct aim_ssi_item *item);
    -char *aim_ssi_getcomment(struct aim_ssi_itemlist *list, const char *gn, const char *bn);
    -gboolean aim_ssi_waitingforauth(struct aim_ssi_itemlist *list, const char *gn, const char *bn);
    -
    -/* Client functions for changing SSI data */
    -int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *tlvlist, const char *alias, const char *comment, const char *smsnum, gboolean needauth);
    -int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group);
    -int aim_ssi_delgroup(OscarData *od, const char *group);
    -int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn);
    -int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias);
    -int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *alias);
    -int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn);
    -int aim_ssi_deletelist(OscarData *od);
    -int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny);
    -int aim_ssi_setpresence(OscarData *od, guint32 presence);
    -int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen);
    -int aim_ssi_delicon(OscarData *od);
    -int aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type);
    -int aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type);
    -
    -guint16 aim_ssi_getdenyentrytype(OscarData* od);
    -
    -struct aim_icq_info
    -{
    - guint16 reqid;
    -
    - /* simple */
    - guint32 uin;
    -
    - /* general and "home" information (0x00c8) */
    - char *nick;
    - char *first;
    - char *last;
    - char *email;
    - char *homecity;
    - char *homestate;
    - char *homephone;
    - char *homefax;
    - char *homeaddr;
    - char *mobile;
    - char *homezip;
    - guint16 homecountry;
    -/* guint8 timezone;
    - guint8 hideemail; */
    -
    - /* personal (0x00dc) */
    - guint8 age;
    - guint8 unknown;
    - guint8 gender;
    - char *personalwebpage;
    - guint16 birthyear;
    - guint8 birthmonth;
    - guint8 birthday;
    - guint8 language1;
    - guint8 language2;
    - guint8 language3;
    -
    - /* work (0x00d2) */
    - char *workcity;
    - char *workstate;
    - char *workphone;
    - char *workfax;
    - char *workaddr;
    - char *workzip;
    - guint16 workcountry;
    - char *workcompany;
    - char *workdivision;
    - char *workposition;
    - char *workwebpage;
    -
    - /* additional personal information (0x00e6) */
    - char *info;
    -
    - /* email (0x00eb) */
    - guint16 numaddresses;
    - char **email2;
    -
    - /* status note info */
    - guint8 icbm_cookie[8];
    - char *status_note_title;
    -
    - gboolean for_auth_request;
    - char *auth_request_reason;
    -};
    -
    -int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware);
    -int aim_icq_changepasswd(OscarData *od, const char *passwd);
    -int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason);
    -int aim_icq_getallinfo(OscarData *od, const char *uin);
    -int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias);
    -
    -
    -/* 0x0017 - family_auth.c */
    -void aim_sendcookie(OscarData *, FlapConnection *, const guint16 length, const guint8 *);
    -void aim_admin_changepasswd(OscarData *, FlapConnection *, const char *newpw, const char *curpw);
    -void aim_admin_reqconfirm(OscarData *od, FlapConnection *conn);
    -void aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info);
    -void aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail);
    -void aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick);
    -
    -
    -
    -/* 0x0018 - family_alert.c */
    -struct aim_emailinfo
    -{
    - guint8 *cookie16;
    - guint8 *cookie8;
    - char *url;
    - guint16 nummsgs;
    - guint8 unread;
    - char *domain;
    - guint16 flag;
    - struct aim_emailinfo *next;
    -};
    -
    -int aim_email_sendcookies(OscarData *od);
    -int aim_email_activate(OscarData *od);
    -
    -
    -
    -/* tlv.c - TLV handling */
    -
    -/* TLV structure */
    -typedef struct aim_tlv_s
    -{
    - guint16 type;
    - guint16 length;
    - guint8 *value;
    -} aim_tlv_t;
    -
    -/* TLV handling functions */
    -char *aim_tlv_getvalue_as_string(aim_tlv_t *tlv);
    -
    -aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth);
    -int aim_tlv_getlength(GSList *list, const guint16 type, const int nth);
    -char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth);
    -guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth);
    -guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth);
    -guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth);
    -
    -/* TLV list handling functions */
    -GSList *aim_tlvlist_read(ByteStream *bs);
    -GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num);
    -GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len);
    -GSList *aim_tlvlist_copy(GSList *orig);
    -
    -int aim_tlvlist_count(GSList *list);
    -size_t aim_tlvlist_size(GSList *list);
    -int aim_tlvlist_cmp(GSList *one, GSList *two);
    -int aim_tlvlist_write(ByteStream *bs, GSList **list);
    -void aim_tlvlist_free(GSList *list);
    -
    -int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value);
    -int aim_tlvlist_add_noval(GSList **list, const guint16 type);
    -int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value);
    -int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value);
    -int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value);
    -int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value);
    -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood);
    -int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo);
    -int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
    -int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
    -
    -int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 lenth, const guint8 *value);
    -int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str);
    -int aim_tlvlist_replace_noval(GSList **list, const guint16 type);
    -int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value);
    -int aim_tlvlist_replace_16(GSList **list, const guint16 type, const guint16 value);
    -int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value);
    -
    -void aim_tlvlist_remove(GSList **list, const guint16 type);
    -
    -
    -
    -/* util.c */
    -/* These are really ugly. You'd think this was LISP. I wish it was. */
    -#define aimutil_put8(buf, data) ((*(buf) = (guint8)(data)&0xff),1)
    -#define aimutil_get8(buf) ((*(buf))&0xff)
    -#define aimutil_put16(buf, data) ( \
    - (*(buf) = (guint8)((data)>>8)&0xff), \
    - (*((buf)+1) = (guint8)(data)&0xff), \
    - 2)
    -#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
    -#define aimutil_put32(buf, data) ( \
    - (*((buf)) = (guint8)((data)>>24)&0xff), \
    - (*((buf)+1) = (guint8)((data)>>16)&0xff), \
    - (*((buf)+2) = (guint8)((data)>>8)&0xff), \
    - (*((buf)+3) = (guint8)(data)&0xff), \
    - 4)
    -#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
    - (((*((buf)+1))<<16)&0x00ff0000) + \
    - (((*((buf)+2))<< 8)&0x0000ff00) + \
    - (((*((buf)+3) )&0x000000ff)))
    -
    -/* Little-endian versions (damn ICQ) */
    -#define aimutil_putle8(buf, data) ( \
    - (*(buf) = (guint8)(data) & 0xff), \
    - 1)
    -#define aimutil_getle8(buf) ( \
    - (*(buf)) & 0xff \
    - )
    -#define aimutil_putle16(buf, data) ( \
    - (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \
    - (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \
    - 2)
    -#define aimutil_getle16(buf) ( \
    - (((*((buf)+0)) << 0) & 0x00ff) + \
    - (((*((buf)+1)) << 8) & 0xff00) \
    - )
    -#define aimutil_putle32(buf, data) ( \
    - (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \
    - (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \
    - (*((buf)+2) = (guint8)((data) >> 16) & 0xff), \
    - (*((buf)+3) = (guint8)((data) >> 24) & 0xff), \
    - 4)
    -#define aimutil_getle32(buf) ( \
    - (((*((buf)+0)) << 0) & 0x000000ff) + \
    - (((*((buf)+1)) << 8) & 0x0000ff00) + \
    - (((*((buf)+2)) << 16) & 0x00ff0000) + \
    - (((*((buf)+3)) << 24) & 0xff000000))
    -
    -const char *oscar_get_msgerr_reason(size_t reason);
    -int oscar_get_ui_info_int(const char *str, int default_value);
    -const char *oscar_get_ui_info_string(const char *str, const char *default_value);
    -gchar *oscar_get_clientstring(void);
    -
    -guint16 aimutil_iconsum(const guint8 *buf, int buflen);
    -
    -gboolean oscar_util_valid_name(const char *bn);
    -gboolean oscar_util_valid_name_icq(const char *bn);
    -gboolean oscar_util_valid_name_sms(const char *bn);
    -int oscar_util_name_compare(const char *bn1, const char *bn2);
    -gchar *oscar_util_format_string(const char *str, const char *name);
    -gchar *oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message);
    -
    -typedef struct {
    - guint16 family;
    - guint16 subtype;
    - guint16 flags;
    - guint32 id;
    -} aim_modsnac_t;
    -
    -#define AIM_MODULENAME_MAXLEN 16
    -#define AIM_MODFLAG_MULTIFAMILY 0x0001
    -typedef struct aim_module_s
    -{
    - guint16 family;
    - guint16 version;
    - guint16 toolid;
    - guint16 toolversion;
    - guint16 flags;
    - char name[AIM_MODULENAME_MAXLEN+1];
    - int (*snachandler)(OscarData *od, FlapConnection *conn, struct aim_module_s *mod, FlapFrame *rx, aim_modsnac_t *snac, ByteStream *bs);
    - void (*shutdown)(OscarData *od, struct aim_module_s *mod);
    - void *priv;
    - struct aim_module_s *next;
    -} aim_module_t;
    -
    -int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *));
    -void aim__shutdownmodules(OscarData *od);
    -aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group);
    -aim_module_t *aim__findmodule(OscarData *od, const char *name);
    -
    -int admin_modfirst(OscarData *od, aim_module_t *mod);
    -int buddylist_modfirst(OscarData *od, aim_module_t *mod);
    -int bos_modfirst(OscarData *od, aim_module_t *mod);
    -int search_modfirst(OscarData *od, aim_module_t *mod);
    -int stats_modfirst(OscarData *od, aim_module_t *mod);
    -int auth_modfirst(OscarData *od, aim_module_t *mod);
    -int msg_modfirst(OscarData *od, aim_module_t *mod);
    -int misc_modfirst(OscarData *od, aim_module_t *mod);
    -int chatnav_modfirst(OscarData *od, aim_module_t *mod);
    -int chat_modfirst(OscarData *od, aim_module_t *mod);
    -int locate_modfirst(OscarData *od, aim_module_t *mod);
    -int service_modfirst(OscarData *od, aim_module_t *mod);
    -int popups_modfirst(OscarData *od, aim_module_t *mod);
    -int bart_modfirst(OscarData *od, aim_module_t *mod);
    -int ssi_modfirst(OscarData *od, aim_module_t *mod);
    -int icq_modfirst(OscarData *od, aim_module_t *mod);
    -int email_modfirst(OscarData *od, aim_module_t *mod);
    -
    -void aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype);
    -void aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype);
    -void aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *);
    -
    -/* bstream.c */
    -int byte_stream_new(ByteStream *bs, size_t len);
    -int byte_stream_init(ByteStream *bs, guint8 *data, size_t len);
    -void byte_stream_destroy(ByteStream *bs);
    -size_t byte_stream_bytes_left(ByteStream *bs);
    -int byte_stream_curpos(ByteStream *bs);
    -int byte_stream_setpos(ByteStream *bs, size_t off);
    -void byte_stream_rewind(ByteStream *bs);
    -int byte_stream_advance(ByteStream *bs, int n);
    -guint8 byte_stream_get8(ByteStream *bs);
    -guint16 byte_stream_get16(ByteStream *bs);
    -guint32 byte_stream_get32(ByteStream *bs);
    -guint8 byte_stream_getle8(ByteStream *bs);
    -guint16 byte_stream_getle16(ByteStream *bs);
    -guint32 byte_stream_getle32(ByteStream *bs);
    -int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len);
    -guint8 *byte_stream_getraw(ByteStream *bs, size_t len);
    -char *byte_stream_getstr(ByteStream *bs, size_t len);
    -int byte_stream_put8(ByteStream *bs, guint8 v);
    -int byte_stream_put16(ByteStream *bs, guint16 v);
    -int byte_stream_put32(ByteStream *bs, guint32 v);
    -int byte_stream_putle8(ByteStream *bs, guint8 v);
    -int byte_stream_putle16(ByteStream *bs, guint16 v);
    -int byte_stream_putle32(ByteStream *bs, guint32 v);
    -int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len);
    -int byte_stream_putstr(ByteStream *bs, const char *str);
    -int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len);
    -int byte_stream_putuid(ByteStream *bs, OscarData *od);
    -int byte_stream_putcaps(ByteStream *bs, guint64 caps);
    -
    -/**
    - * Inserts a BART asset block into the given byte stream. The flags
    - * and length are set appropriately based on the value of data.
    - */
    -void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data);
    -
    -/**
    - * A helper function that calls byte_stream_put_bart_asset with the
    - * appropriate data ByteStream given the datastr.
    - */
    -void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr);
    -
    -/*
    - * Generic SNAC structure. Rarely if ever used.
    - */
    -typedef struct aim_snac_s {
    - aim_snacid_t id;
    - guint16 family;
    - guint16 type;
    - guint16 flags;
    - void *data;
    - time_t issuetime;
    - struct aim_snac_s *next;
    -} aim_snac_t;
    -
    -/* snac.c */
    -void aim_initsnachash(OscarData *od);
    -aim_snacid_t aim_newsnac(OscarData *, aim_snac_t *newsnac);
    -aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen);
    -aim_snac_t *aim_remsnac(OscarData *, aim_snacid_t id);
    -void aim_cleansnacs(OscarData *, int maxage);
    -int aim_putsnac(ByteStream *, guint16 family, guint16 type, aim_snacid_t id);
    -
    -struct chatsnacinfo {
    - guint16 exchange;
    - char name[128];
    - guint16 instance;
    -};
    -
    -struct rateclass {
    - guint16 classid;
    - guint32 windowsize;
    - guint32 clear;
    - guint32 alert;
    - guint32 limit;
    - guint32 disconnect;
    - guint32 current;
    - guint32 max;
    - guint8 dropping_snacs;
    -
    - struct timeval last; /**< The time when we last sent a SNAC of this rate class. */
    -};
    -
    -int aim_cachecookie(OscarData *od, IcbmCookie *cookie);
    -IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type);
    -IcbmCookie *aim_mkcookie(guint8 *, int, void *);
    -IcbmCookie *aim_checkcookie(OscarData *, const unsigned char *, const int);
    -int aim_freecookie(OscarData *od, IcbmCookie *cookie);
    -int aim_cookie_free(OscarData *od, IcbmCookie *cookie);
    -
    -int aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo);
    -
    -void flap_connection_destroy_chat(OscarData *od, FlapConnection *conn);
    -
    -/* userinfo.c - displaying user information */
    -
    -void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status);
    -void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo);
    -void oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy);
    -void oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info);
    -void oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo);
    -
    -/* authorization.c - OSCAR authorization requests */
    -void oscar_auth_sendrequest(PurpleConnection *gc, const char *name, const char *msg);
    -void oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored);
    -void oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason);
    -
    -void oscar_set_aim_permdeny(PurpleConnection *gc);
    -
    -struct buddyinfo
    -{
    - gboolean typingnot;
    - guint32 ipaddr;
    -
    - unsigned long ico_me_len;
    - unsigned long ico_me_csum;
    - time_t ico_me_time;
    - gboolean ico_informed;
    -
    - unsigned long ico_len;
    - unsigned long ico_csum;
    - time_t ico_time;
    - gboolean ico_need;
    - gboolean ico_sent;
    -};
    -
    -struct name_data
    -{
    - PurpleConnection *gc;
    - gchar *name;
    - gchar *nick;
    -};
    -
    -void oscar_free_name_data(struct name_data *data);
    -
    -void oscar_init_account_options(PurpleProtocol *protocol, gboolean is_icq);
    -
    -G_END_DECLS
    -
    -#endif /* PURPLE_OSCAR_OSCAR_H */
    --- a/libpurple/protocols/oscar/oscar_data.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,167 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -
    -typedef struct _SnacHandler SnacHandler;
    -
    -struct _SnacHandler
    -{
    - guint16 family;
    - guint16 subtype;
    - aim_rxcallback_t handler;
    - guint16 flags;
    -};
    -
    -/**
    - * Allocates a new OscarData and initializes it with default values.
    - */
    -OscarData *
    -oscar_data_new(void)
    -{
    - OscarData *od;
    - aim_module_t *cur;
    - GString *msg;
    -
    - od = g_new0(OscarData, 1);
    -
    - aim_initsnachash(od);
    - od->snacid_next = 0x00000001;
    - od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    - od->handlerlist = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
    -
    - od->ssi.local.idx_gid_bid = g_hash_table_new(g_direct_hash, g_direct_equal);
    - od->ssi.local.idx_all_named_items = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    -
    - od->ssi.official.idx_gid_bid = g_hash_table_new(g_direct_hash, g_direct_equal);
    - od->ssi.official.idx_all_named_items = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    -
    - /*
    - * Register all the modules for this session...
    - */
    - aim__registermodule(od, misc_modfirst); /* load the catch-all first */
    - aim__registermodule(od, service_modfirst);
    - aim__registermodule(od, locate_modfirst);
    - aim__registermodule(od, buddylist_modfirst);
    - aim__registermodule(od, msg_modfirst);
    - aim__registermodule(od, admin_modfirst);
    - aim__registermodule(od, popups_modfirst);
    - aim__registermodule(od, bos_modfirst);
    - aim__registermodule(od, search_modfirst);
    - aim__registermodule(od, stats_modfirst);
    - aim__registermodule(od, chatnav_modfirst);
    - aim__registermodule(od, chat_modfirst);
    - aim__registermodule(od, bart_modfirst);
    - /* missing 0x11 - 0x12 */
    - aim__registermodule(od, ssi_modfirst);
    - /* missing 0x14 */
    - aim__registermodule(od, icq_modfirst);
    - /* missing 0x16 */
    - /* auth_modfirst is only needed if we're connecting with the old-style BUCP login */
    - aim__registermodule(od, auth_modfirst);
    - aim__registermodule(od, email_modfirst);
    -
    - msg = g_string_new("Registered modules: ");
    - for (cur = od->modlistv; cur; cur = cur->next) {
    - g_string_append_printf(
    - msg,
    - "%s (family=0x%04x, version=0x%04x, toolid=0x%04x, toolversion=0x%04x), ",
    - cur->name,
    - cur->family,
    - cur->version,
    - cur->toolid,
    - cur->toolversion);
    - }
    - purple_debug_misc("oscar", "%s\n", msg->str);
    - g_string_free(msg, TRUE);
    -
    - return od;
    -}
    -
    -/**
    - * Logoff and deallocate a session.
    - *
    - * @param od Session to kill
    - */
    -void
    -oscar_data_destroy(OscarData *od)
    -{
    - aim_cleansnacs(od, -1);
    -
    - /* Only used when connecting with clientLogin or Kerberos. */
    - if (od->http_conns) {
    - soup_session_abort(od->http_conns);
    - g_object_unref(od->http_conns);
    - }
    -
    - g_slist_free_full(od->requesticon, g_free);
    - g_free(od->email);
    - g_free(od->newp);
    - g_free(od->oldp);
    - if (od->getblisttimer > 0)
    - g_source_remove(od->getblisttimer);
    - while (od->oscar_connections != NULL)
    - flap_connection_destroy(od->oscar_connections->data,
    - OSCAR_DISCONNECT_DONE, NULL);
    -
    - while (od->peer_connections != NULL)
    - peer_connection_destroy(od->peer_connections->data,
    - OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -
    - aim__shutdownmodules(od);
    -
    - g_hash_table_destroy(od->buddyinfo);
    - g_hash_table_destroy(od->handlerlist);
    -
    - g_hash_table_destroy(od->ssi.local.idx_gid_bid);
    - g_hash_table_destroy(od->ssi.local.idx_all_named_items);
    -
    - g_hash_table_destroy(od->ssi.official.idx_gid_bid);
    - g_hash_table_destroy(od->ssi.official.idx_all_named_items);
    -
    - g_free(od);
    -}
    -
    -void
    -oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags)
    -{
    - SnacHandler *snac_handler;
    -
    - snac_handler = g_new0(SnacHandler, 1);
    -
    - snac_handler->family = family;
    - snac_handler->subtype = subtype;
    - snac_handler->flags = flags;
    - snac_handler->handler = newhandler;
    -
    - g_hash_table_insert(od->handlerlist,
    - GUINT_TO_POINTER((family << 16) + subtype),
    - snac_handler);
    -}
    -
    -aim_rxcallback_t
    -aim_callhandler(OscarData *od, guint16 family, guint16 subtype)
    -{
    - SnacHandler *snac_handler;
    -
    - snac_handler = g_hash_table_lookup(od->handlerlist, GUINT_TO_POINTER((family << 16) + subtype));
    -
    - return snac_handler ? snac_handler->handler : NULL;
    -}
    --- a/libpurple/protocols/oscar/oscarcommon.h Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,131 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -/* oscarcommon.h contains prototypes for the protocol functions used by libaim.c
    - * and libicq.c
    - */
    -
    -#ifndef PURPLE_OSCAR_OSCARCOMMON_H
    -#define PURPLE_OSCAR_OSCARCOMMON_H
    -
    -#include "internal.h"
    -
    -#include "protocol.h"
    -#include "version.h"
    -#include "notify.h"
    -#include "purpleaccountoption.h"
    -#include "status.h"
    -
    -#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com"
    -#define AIM_ALT_LOGIN_SERVER "login.messaging.aol.com"
    -#define AIM_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com"
    -#define ICQ_DEFAULT_LOGIN_SERVER "login.icq.com"
    -#define ICQ_DEFAULT_SSL_LOGIN_SERVER "slogin.icq.com"
    -
    -#define AIM_DEFAULT_KDC_SERVER "kdc.uas.aol.com"
    -#define AIM_DEFAULT_KDC_PORT 443
    -
    -/*
    - * Using clientLogin requires a developer ID. This key is for libpurple.
    - * It is the default key for all libpurple-based clients. AOL encourages
    - * UIs (especially ones with lots of users) to override this with their
    - * own key.
    - */
    -#define ICQ_DEFAULT_DIST_ID 1553
    -#define ICQ_DEFAULT_CLIENT_KEY "ma15d7JTxbmVG-RP"
    -#define AIM_DEFAULT_DIST_ID 1717
    -#define AIM_DEFAULT_CLIENT_KEY "ma19CwYN9i9Mw5nY"
    -
    -#define OSCAR_DEFAULT_LOGIN_PORT 5190
    -
    -#define OSCAR_OPPORTUNISTIC_ENCRYPTION "opportunistic_encryption"
    -#define OSCAR_REQUIRE_ENCRYPTION "require_encryption"
    -#define OSCAR_NO_ENCRYPTION "no_encryption"
    -
    -#define OSCAR_MD5_LOGIN "md5_login"
    -#define OSCAR_CLIENT_LOGIN "client_login"
    -#define OSCAR_KERBEROS_LOGIN "kerberos_login"
    -
    -#ifndef _WIN32
    -#define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1"
    -#else
    -#define OSCAR_DEFAULT_CUSTOM_ENCODING oscar_get_locale_charset()
    -#endif
    -#define OSCAR_DEFAULT_AUTHORIZATION TRUE
    -#define OSCAR_DEFAULT_HIDE_IP TRUE
    -#define OSCAR_DEFAULT_WEB_AWARE FALSE
    -#define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE
    -#define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE
    -#define OSCAR_DEFAULT_LOGIN OSCAR_CLIENT_LOGIN
    -#define OSCAR_DEFAULT_ENCRYPTION OSCAR_OPPORTUNISTIC_ENCRYPTION
    -
    -#ifdef _WIN32
    -const char *oscar_get_locale_charset(void);
    -#endif
    -PurpleMood* oscar_get_purple_moods(PurpleAccount *account);
    -const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b);
    -const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b);
    -const char* oscar_list_emblem(PurpleBuddy *b);
    -char *oscar_status_text(PurpleBuddy *b);
    -void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
    -GList *oscar_status_types(PurpleAccount *account);
    -GList *oscar_blist_node_menu(PurpleBlistNode *node);
    -GList *oscar_chat_info(PurpleConnection *gc);
    -GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name);
    -void oscar_login(PurpleAccount *account);
    -void oscar_close(PurpleConnection *gc);
    -int oscar_send_im(PurpleConnection *gc, PurpleMessage *msg);
    -void oscar_set_info(PurpleConnection *gc, const char *rawinfo);
    -unsigned int oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state);
    -void oscar_get_info(PurpleConnection *gc, const char *name);
    -void oscar_set_status(PurpleAccount *account, PurpleStatus *status);
    -void oscar_set_idle(PurpleConnection *gc, int time);
    -void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new);
    -void oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg);
    -void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
    -void oscar_add_permit(PurpleConnection *gc, const char *who);
    -void oscar_add_deny(PurpleConnection *gc, const char *who);
    -void oscar_rem_permit(PurpleConnection *gc, const char *who);
    -void oscar_rem_deny(PurpleConnection *gc, const char *who);
    -void oscar_join_chat(PurpleConnection *gc, GHashTable *data);
    -char *oscar_get_chat_name(GHashTable *data);
    -void oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name);
    -void oscar_chat_leave(PurpleConnection *gc, int id);
    -int oscar_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg);
    -void oscar_keepalive(PurpleConnection *gc);
    -void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias);
    -void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group);
    -void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies);
    -void oscar_convo_closed(PurpleConnection *gc, const char *who);
    -const char *oscar_normalize(const PurpleAccount *account, const char *str);
    -void oscar_set_icon(PurpleConnection *gc, PurpleImage *img);
    -void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group);
    -gboolean oscar_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    -void oscar_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file);
    -PurpleXfer *oscar_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    -gboolean oscar_offline_message(const PurpleBuddy *buddy);
    -gssize oscar_get_max_message_size(PurpleConversation *conv);
    -GList *oscar_get_actions(PurpleConnection *gc);
    -const gchar *oscar_get_login_server(gboolean is_icq, gboolean use_ssl);
    -gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params);
    -
    -#endif /* PURPLE_OSCAR_OSCARCOMMON_H */
    --- a/libpurple/protocols/oscar/peer.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1212 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Functions dealing with peer connections. This includes the code
    - * used to establish a peer connection for both Oscar File transfer
    - * (OFT) and Oscar Direct Connect (ODC). (ODC is also referred to
    - * as DirectIM and IM Image.)
    - */
    -
    -#ifdef HAVE_CONFIG_H
    -#include <config.h>
    -#endif
    -
    -/* From the oscar PRPL */
    -#include "oscar.h"
    -#include "peer.h"
    -
    -/* From Purple */
    -#include "conversation.h"
    -#include "xfer.h"
    -#include "network.h"
    -#include "notify.h"
    -#include "request.h"
    -#include "util.h"
    -
    -#ifndef _WIN32
    -#include <stdio.h>
    -#include <netdb.h>
    -#include <sys/socket.h>
    -#include <netinet/in.h>
    -#include <arpa/inet.h> /* for inet_ntoa */
    -#include <limits.h> /* for UINT_MAX */
    -#endif
    -
    -/*
    - * I really want to switch all our networking code to using IPv6 only,
    - * but that really isn't a good idea at all. Evan S. of Adium says
    - * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
    - * nothing inherently IPv6 about them. And I feel like Linux kernel
    - * 2.6.5 is doing the same thing. So we REALLY should accept
    - * connections if they're showing up as IPv6. Old OSes (Solaris?)
    - * that might not have full IPv6 support yet will fail if we try
    - * to use PF_INET6 but it isn't defined. --Mark Doliner
    - */
    -#ifndef PF_INET6
    -#define PF_INET6 PF_INET
    -#endif
    -
    -PeerConnection *
    -peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type)
    -{
    - GSList *cur;
    - PeerConnection *conn;
    -
    - for (cur = od->peer_connections; cur != NULL; cur = cur->next)
    - {
    - conn = cur->data;
    - if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn))
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * @param cookie This must be exactly 8 characters.
    - */
    -PeerConnection *
    -peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie)
    -{
    - GSList *cur;
    - PeerConnection *conn;
    -
    - for (cur = od->peer_connections; cur != NULL; cur = cur->next)
    - {
    - conn = cur->data;
    - if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn))
    - return conn;
    - }
    -
    - return NULL;
    -}
    -
    -PeerConnection *
    -peer_connection_new(OscarData *od, guint64 type, const char *bn)
    -{
    - PeerConnection *conn;
    - PurpleAccount *account;
    -
    - account = purple_connection_get_account(od->gc);
    -
    - conn = g_new0(PeerConnection, 1);
    - conn->od = od;
    - conn->type = type;
    - conn->bn = g_strdup(bn);
    - conn->buffer_outgoing = purple_circular_buffer_new(0);
    - conn->listenerfd = -1;
    - conn->fd = -1;
    - conn->lastactivity = time(NULL);
    - conn->use_proxy |= purple_account_get_bool(account, "always_use_rv_proxy", FALSE);
    -
    - if (type == OSCAR_CAPABILITY_DIRECTIM)
    - memcpy(conn->magic, "ODC2", 4);
    - else if (type == OSCAR_CAPABILITY_SENDFILE)
    - memcpy(conn->magic, "OFT2", 4);
    -
    - od->peer_connections = g_slist_prepend(od->peer_connections, conn);
    -
    - return conn;
    -}
    -
    -static void
    -peer_connection_close(PeerConnection *conn)
    -{
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - peer_odc_close(conn);
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - peer_oft_close(conn);
    -
    - if (conn->verified_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->verified_connect_data);
    - conn->verified_connect_data = NULL;
    - }
    -
    - if (conn->client_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->client_connect_data);
    - conn->client_connect_data = NULL;
    - }
    -
    - if (conn->listen_data != NULL)
    - {
    - purple_network_listen_cancel(conn->listen_data);
    - conn->listen_data = NULL;
    - }
    -
    - if (conn->connect_timeout_timer != 0)
    - {
    - g_source_remove(conn->connect_timeout_timer);
    - conn->connect_timeout_timer = 0;
    - }
    -
    - if (conn->watcher_incoming != 0)
    - {
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    - }
    - if (conn->watcher_outgoing != 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - }
    - if (conn->listenerfd >= 0)
    - {
    - close(conn->listenerfd);
    - conn->listenerfd = -1;
    - }
    - if (conn->fd >= 0)
    - {
    - close(conn->fd);
    - conn->fd = -1;
    - }
    -
    - g_free(conn->buffer_incoming.data);
    - conn->buffer_incoming.data = NULL;
    - conn->buffer_incoming.len = 0;
    - conn->buffer_incoming.offset = 0;
    -
    - g_object_unref(G_OBJECT(conn->buffer_outgoing));
    - conn->buffer_outgoing = purple_circular_buffer_new(0);
    -
    - conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;
    -}
    -
    -static gboolean
    -peer_connection_destroy_cb(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - purple_request_close_with_handle(conn);
    -
    - peer_connection_close(conn);
    -
    - if (conn->checksum_data != NULL)
    - peer_oft_checksum_destroy(conn->checksum_data);
    -
    - if (conn->xfer != NULL)
    - {
    - PurpleXferStatus status;
    - status = purple_xfer_get_status(conn->xfer);
    - if ((status != PURPLE_XFER_STATUS_DONE) &&
    - (status != PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
    - (status != PURPLE_XFER_STATUS_CANCEL_REMOTE))
    - {
    - if ((conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) ||
    - (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED))
    - purple_xfer_cancel_remote(conn->xfer);
    - else
    - purple_xfer_cancel_local(conn->xfer);
    - }
    - g_object_unref(conn->xfer);
    - conn->xfer = NULL;
    - }
    -
    - g_free(conn->bn);
    - g_free(conn->error_message);
    - g_free(conn->proxyip);
    - g_free(conn->clientip);
    - g_free(conn->verifiedip);
    - g_free(conn->xferdata.name);
    - g_object_unref(G_OBJECT(conn->buffer_outgoing));
    -
    - conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn);
    -
    - g_free(conn);
    -
    - return FALSE;
    -}
    -
    -void
    -peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - g_source_remove(conn->destroy_timeout);
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - peer_connection_destroy_cb(conn);
    -}
    -
    -void
    -peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
    -{
    - if (conn->destroy_timeout != 0)
    - /* Already taken care of */
    - return;
    -
    - purple_debug_info("oscar", "Scheduling destruction of peer connection\n");
    - conn->disconnect_reason = reason;
    - g_free(conn->error_message);
    - conn->error_message = g_strdup(error_message);
    - conn->destroy_timeout = g_timeout_add(0, peer_connection_destroy_cb, conn);
    -}
    -
    -/*******************************************************************/
    -/* Begin code for receiving data on a peer connection */
    -/*******************************************************************/
    -
    -/**
    - * This should be used to read ODC and OFT framing info. It should
    - * NOT be used to read the payload sent across the connection (IMs,
    - * file data, etc), and it should NOT be used to read proxy negotiation
    - * headers.
    - *
    - * Unlike flap_connection_recv_cb(), this only reads one frame at a
    - * time. This is done so that the watcher can be changed during the
    - * handling of the frame. If the watcher is changed then this
    - * function will not read in any more data. This happens when
    - * reading the payload of a direct IM frame, or when we're
    - * receiving a file from the remote user. Once the data has been
    - * read, the watcher will be switched back to this function to
    - * continue reading the next frame.
    - */
    -void
    -peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - gssize read;
    -
    - conn = data;
    -
    - /* Start reading a new ODC/OFT frame */
    - if (conn->buffer_incoming.data == NULL)
    - {
    - /* Read the first 6 bytes (magic string and frame length) */
    - read = recv(conn->fd, conn->header + conn->header_received,
    - 6 - conn->header_received, 0);
    -
    - /* Check if the remote user closed the connection */
    - if (read == 0)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - return;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - conn->lastactivity = time(NULL);
    -
    - /* If we don't even have the first 6 bytes then do nothing */
    - conn->header_received += read;
    - if (conn->header_received < 6)
    - return;
    -
    - /* All ODC/OFT frames must start with a magic string */
    - if (memcmp(conn->magic, conn->header, 4))
    - {
    - purple_debug_warning("oscar", "Expecting magic string to "
    - "be %c%c%c%c but received magic string %c%c%c%c. "
    - "Closing connection.\n",
    - conn->magic[0], conn->magic[1], conn->magic[2],
    - conn->magic[3], conn->header[0], conn->header[1],
    - conn->header[2], conn->header[3]);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
    - return;
    - }
    -
    - /* Initialize a new temporary ByteStream for incoming data */
    - conn->buffer_incoming.len = aimutil_get16(&conn->header[4]) - 6;
    - conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len);
    - conn->buffer_incoming.offset = 0;
    - }
    -
    - /* Read data into the temporary buffer until it is complete */
    - read = recv(conn->fd,
    - &conn->buffer_incoming.data[conn->buffer_incoming.offset],
    - conn->buffer_incoming.len - conn->buffer_incoming.offset,
    - 0);
    -
    - /* Check if the remote user closed the connection */
    - if (read == 0)
    - {
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - return;
    - }
    -
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - peer_connection_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
    - return;
    - }
    -
    - conn->lastactivity = time(NULL);
    - conn->buffer_incoming.offset += read;
    - if (conn->buffer_incoming.offset < conn->buffer_incoming.len)
    - /* Waiting for more data to arrive */
    - return;
    -
    - /* We have a complete ODC/OFT frame! Handle it and continue reading */
    - byte_stream_rewind(&conn->buffer_incoming);
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - peer_odc_recv_frame(conn, &conn->buffer_incoming);
    - }
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - peer_oft_recv_frame(conn, &conn->buffer_incoming);
    - }
    -
    - g_free(conn->buffer_incoming.data);
    - conn->buffer_incoming.data = NULL;
    -
    - conn->header_received = 0;
    -}
    -
    -/*******************************************************************/
    -/* End code for receiving data on a peer connection */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin code for sending data on a peer connection */
    -/*******************************************************************/
    -
    -static void
    -send_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - gsize writelen;
    - gssize wrotelen;
    - const gchar *output = NULL;
    -
    - conn = data;
    - writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing);
    -
    - if (writelen == 0)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - /*
    - * The buffer is currently empty, so reset the current input
    - * and output positions to the start of the buffer. We do
    - * this so that the next chunk of data that we put into the
    - * buffer can be read back out of the buffer in one fell swoop.
    - * Otherwise it gets fragmented and we have to read from the
    - * second half of the buffer than go back and read the rest of
    - * the chunk from the first half.
    - *
    - * We're using TCP, which is a stream based protocol, so this
    - * isn't supposed to matter. However, experience has shown
    - * that at least the proxy file transfer code in AIM 6.1.41.2
    - * requires that the entire OFT frame arrive all at once. If
    - * the frame is fragmented then AIM freaks out and aborts the
    - * file transfer. Somebody should teach those guys how to
    - * write good TCP code.
    - */
    - purple_circular_buffer_reset(conn->buffer_outgoing);
    - return;
    - }
    -
    - output = purple_circular_buffer_get_output(conn->buffer_outgoing);
    -
    - wrotelen = send(conn->fd, output, writelen, 0);
    - if (wrotelen <= 0)
    - {
    - if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
    - /* No worries */
    - return;
    -
    - if (conn->ready)
    - {
    - purple_input_remove(conn->watcher_outgoing);
    - conn->watcher_outgoing = 0;
    - close(conn->fd);
    - conn->fd = -1;
    - peer_connection_schedule_destroy(conn,
    - OSCAR_DISCONNECT_LOST_CONNECTION, NULL);
    - }
    - else
    - {
    - /*
    - * This could happen when unable to send a negotiation
    - * frame to a peer proxy server.
    - */
    - peer_connection_trynext(conn);
    - }
    - return;
    - }
    -
    - purple_circular_buffer_mark_read(conn->buffer_outgoing, wrotelen);
    - conn->lastactivity = time(NULL);
    -}
    -
    -/**
    - * This should be called by OFT/ODC code to send a standard OFT or ODC
    - * frame across the peer connection along with some payload data. Or
    - * maybe a file. Anything, really.
    - */
    -void
    -peer_connection_send(PeerConnection *conn, ByteStream *bs)
    -{
    - /* Add everything to our outgoing buffer */
    - purple_circular_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
    -
    - /* If we haven't already started writing stuff, then start the cycle */
    - if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
    - {
    - conn->watcher_outgoing = purple_input_add(conn->fd,
    - PURPLE_INPUT_WRITE, send_cb, conn);
    - send_cb(conn, conn->fd, 0);
    - }
    -}
    -
    -/*******************************************************************/
    -/* End code for sending data on a peer connection */
    -/*******************************************************************/
    -
    -/*******************************************************************/
    -/* Begin code for establishing a peer connection */
    -/*******************************************************************/
    -
    -void
    -peer_connection_finalize_connection(PeerConnection *conn)
    -{
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - /*
    - * If we are connecting to them then send our cookie so they
    - * can verify who we are. Note: This doesn't seem to be
    - * necessary, but it also doesn't seem to hurt.
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
    - peer_odc_send_cookie(conn);
    - }
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - if (purple_xfer_get_xfer_type(conn->xfer) == PURPLE_XFER_TYPE_SEND)
    - {
    - peer_oft_send_prompt(conn);
    - }
    - }
    -
    - /*
    - * Tell the remote user that we're connected (which may also imply
    - * that we've accepted their request).
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
    - aim_im_sendch2_connected(conn);
    -}
    -
    -/**
    - * We tried to make an outgoing connection to a remote user. It
    - * either connected or failed to connect.
    - */
    -static void
    -peer_connection_common_established_cb(gpointer data, gint source, const gchar *error_message, gboolean verified)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - if (verified)
    - conn->verified_connect_data = NULL;
    - else
    - conn->client_connect_data = NULL;
    -
    - if (source < 0)
    - {
    - if ((conn->verified_connect_data == NULL) &&
    - (conn->client_connect_data == NULL))
    - {
    - /* Our parallel connection attemps have both failed. */
    - peer_connection_trynext(conn);
    - }
    - return;
    - }
    -
    - g_source_remove(conn->connect_timeout_timer);
    - conn->connect_timeout_timer = 0;
    -
    - if (conn->client_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->client_connect_data);
    - conn->client_connect_data = NULL;
    - }
    -
    - if (conn->verified_connect_data != NULL)
    - {
    - purple_proxy_connect_cancel(conn->verified_connect_data);
    - conn->verified_connect_data = NULL;
    - }
    -
    - conn->fd = source;
    -
    - peer_connection_finalize_connection(conn);
    -}
    -
    -static void
    -peer_connection_verified_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - peer_connection_common_established_cb(data, source, error_message, TRUE);
    -}
    -
    -static void
    -peer_connection_client_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - peer_connection_common_established_cb(data, source, error_message, FALSE);
    -}
    -
    -/**
    - * This is the watcher callback for any listening socket that is
    - * waiting for a peer to connect. When a peer connects we set the
    - * input watcher to start reading data from the peer.
    - *
    - * To make sure that the connection is with the intended person and
    - * not with a malicious middle man, we don't send anything until we've
    - * received a peer frame from the remote user and have verified that
    - * the cookie in the peer frame matches the cookie that was exchanged
    - * in the channel 2 ICBM.
    - */
    -void
    -peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - struct sockaddr addr;
    - socklen_t addrlen = sizeof(addr);
    -
    - conn = data;
    -
    - purple_debug_info("oscar", "Accepting connection on listener socket.\n");
    -
    - conn->fd = accept(conn->listenerfd, &addr, &addrlen);
    - if (conn->fd < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No connection yet--no worries */
    - /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
    - return;
    -
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
    - {
    - /* Invalid connection type?! Continue waiting. */
    - close(conn->fd);
    - return;
    - }
    -
    - _purple_network_set_common_socket_flags(conn->fd);
    -
    - purple_input_remove(conn->watcher_incoming);
    -
    - peer_connection_finalize_connection(conn);
    -}
    -
    -/**
    - * Converts a dot-decimal IP address to an array of unsigned
    - * chars. For example, converts 192.168.0.1 to a 4 byte
    - * array containing 192, 168, 0 and 1.
    - *
    - * @param ip An IP address in dot-decimal notiation.
    - * @return An array of 4 bytes containing an IP addresses
    - * equivalent to the given parameter, or NULL if
    - * the given IP address is invalid. This value
    - * is statically allocated and should not be
    - * freed.
    - */
    -static const unsigned char *
    -peer_ip_atoi(const char *ip)
    -{
    - static unsigned char ret[4];
    - gchar *delimiter = ".";
    - gchar **split;
    - int i;
    -
    - g_return_val_if_fail(ip != NULL, NULL);
    -
    - split = g_strsplit(ip, delimiter, 4);
    - for (i = 0; split[i] != NULL; i++)
    - ret[i] = atoi(split[i]);
    - g_strfreev(split);
    -
    - /* i should always be 4 */
    - if (i != 4)
    - return NULL;
    -
    - return ret;
    -}
    -
    -/**
    - * We've just opened a listener socket, so we send the remote
    - * user an ICBM and ask them to connect to us.
    - */
    -static void
    -peer_connection_establish_listener_cb(int listenerfd, gpointer data)
    -{
    - PeerConnection *conn;
    - OscarData *od;
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PurpleIMConversation *im;
    - char *tmp;
    - FlapConnection *bos_conn;
    - const char *listener_ip;
    - const guchar *ip_atoi;
    - unsigned short listener_port;
    -
    - conn = data;
    - conn->listen_data = NULL;
    -
    - if (listenerfd < 0)
    - {
    - /* Could not open listener socket */
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - od = conn->od;
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    - conn->listenerfd = listenerfd;
    -
    - /* Watch for new connections on our listener socket */
    - conn->watcher_incoming = purple_input_add(conn->listenerfd,
    - PURPLE_INPUT_READ, peer_connection_listen_cb, conn);
    -
    - /* Send the "please connect to me!" ICBM */
    - bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
    - if (bos_conn == NULL)
    - {
    - /* Not good */
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - if (bos_conn->gsc)
    - listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
    - else
    - listener_ip = purple_network_get_my_ip(bos_conn->fd);
    -
    - ip_atoi = peer_ip_atoi(listener_ip);
    - if (ip_atoi == NULL) {
    - /* Could not convert IP to 4 byte array--weird, but this does
    - happen for some users (#4829, Adium #15839). Maybe they're
    - connecting with IPv6...? Maybe through a proxy? */
    - purple_debug_error("oscar", "Can't ask peer to connect to us "
    - "because peer_ip_atoi(%s) returned NULL. "
    - "fd=%d. is_ssl=%d\n",
    - listener_ip ? listener_ip : "(null)",
    - bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
    - bos_conn->gsc ? 1 : 0);
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - listener_port = purple_network_get_port_from_fd(conn->listenerfd);
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - aim_im_sendch2_odc_requestdirect(od,
    - conn->cookie, conn->bn, ip_atoi,
    - listener_port, ++conn->lastrequestnumber);
    -
    - /* Print a message to a local conversation window */
    - im = purple_im_conversation_new(account, conn->bn);
    - tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
    - "Direct IM."), conn->bn, listener_ip, listener_port);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), tmp, 0);
    - g_free(tmp);
    - }
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - aim_im_sendch2_sendfile_requestdirect(od,
    - conn->cookie, conn->bn,
    - ip_atoi,
    - listener_port, ++conn->lastrequestnumber,
    - (const gchar *)conn->xferdata.name,
    - conn->xferdata.size, conn->xferdata.totfiles);
    - }
    -}
    -
    -/**
    - * This is a callback function used when we're connecting to a peer
    - * using either the client IP or the verified IP and the connection
    - * took longer than 5 seconds to complete. We do this because
    - * waiting for the OS to time out the connection attempt is not
    - * practical--the default timeout on many OSes can be 3 minutes or
    - * more, and users are impatient.
    - *
    - * Worst case scenario: the user is connected to the Internet using
    - * a modem with severe lag. The peer connections fail and Purple falls
    - * back to using a proxied connection. The lower bandwidth
    - * limitations imposed by the proxied connection won't matter because
    - * the user is using a modem.
    - *
    - * I suppose this line of thinking is discriminatory against people
    - * with very high lag but decent throughput who are transferring
    - * large files. But we don't care about those people.
    - *
    - * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is
    - * too long for a user to wait to send a file. I'm also parallelizing
    - * requests when possible. The longest we should have to wait now is 10
    - * seconds. We shouldn't make it shorter than this.
    - */
    -static gboolean
    -peer_connection_tooktoolong(gpointer data)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - purple_debug_info("oscar", "Peer connection timed out after 5 seconds. "
    - "Trying next method...\n");
    -
    - peer_connection_trynext(conn);
    -
    - /* Cancel this timer. It'll be added again, if needed. */
    - return FALSE;
    -}
    -
    -/**
    - * Try to establish the given PeerConnection using a defined
    - * sequence of steps.
    - */
    -void
    -peer_connection_trynext(PeerConnection *conn)
    -{
    - PurpleAccount *account;
    -
    - account = purple_connection_get_account(conn->od->gc);
    -
    - /*
    - * Close any remnants of a previous failed connection attempt.
    - */
    - peer_connection_close(conn);
    -
    - /*
    - * 1. Attempt to connect to the remote user using their verifiedip and clientip.
    - * We try these at the same time and use whichever succeeds first, so we don't
    - * have to wait for a timeout.
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_DIRECT) &&
    - (conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy))
    - {
    - conn->flags |= PEER_CONNECTION_FLAG_TRIED_DIRECT;
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - gchar *tmp;
    - PurpleIMConversation *im;
    - tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
    - conn->verifiedip, conn->port);
    - im = purple_im_conversation_new(account, conn->bn);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), tmp, 0);
    - g_free(tmp);
    - }
    -
    - conn->verified_connect_data = purple_proxy_connect(NULL, account,
    - conn->verifiedip, conn->port,
    - peer_connection_verified_established_cb, conn);
    -
    - if ((conn->verifiedip == NULL) ||
    - !purple_strequal(conn->verifiedip, conn->clientip))
    - {
    - conn->client_connect_data = purple_proxy_connect(NULL, account,
    - conn->clientip, conn->port,
    - peer_connection_client_established_cb, conn);
    - }
    -
    - if ((conn->verified_connect_data != NULL) ||
    - (conn->client_connect_data != NULL))
    - {
    - /* Connecting... */
    - conn->connect_timeout_timer = g_timeout_add_seconds(5,
    - peer_connection_tooktoolong, conn);
    - return;
    - }
    - }
    -
    - /*
    - * 2. Attempt to have the remote user connect to us (using both
    - * our verifiedip and our clientip).
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) &&
    - (!conn->use_proxy))
    - {
    - conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING;
    -
    - /*
    - * Remote user is connecting to us, so we'll need to verify
    - * that the user who connected is our friend.
    - */
    - conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
    -
    - conn->listen_data = purple_network_listen_range(5190, 5290, AF_UNSPEC, SOCK_STREAM, TRUE,
    - peer_connection_establish_listener_cb, conn);
    - if (conn->listen_data != NULL)
    - {
    - /* Opening listener socket... */
    - return;
    - }
    - }
    -
    - /*
    - * 3. Attempt to have both users connect to an intermediate proxy
    - * server.
    - */
    - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY))
    - {
    - conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY;
    -
    - /*
    - * If we initiate the proxy connection, then the remote user
    - * could be anyone, so we need to verify that the user who
    - * connected is our friend.
    - */
    - if (!conn->use_proxy)
    - conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
    -
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - gchar *tmp;
    - PurpleIMConversation *im;
    - tmp = g_strdup(_("Attempting to connect via proxy server."));
    - im = purple_im_conversation_new(account, conn->bn);
    - purple_conversation_write_system_message(
    - PURPLE_CONVERSATION(im), tmp, 0);
    - g_free(tmp);
    - }
    -
    - conn->verified_connect_data = purple_proxy_connect(NULL, account,
    - (conn->proxyip != NULL)
    - ? conn->proxyip
    - : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER),
    - PEER_PROXY_PORT,
    - peer_proxy_connection_established_cb, conn);
    - if (conn->verified_connect_data != NULL)
    - {
    - /* Connecting... */
    - return;
    - }
    - }
    -
    - /* Give up! */
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL);
    -}
    -
    -/**
    - * Initiate a peer connection with someone.
    - */
    -void
    -peer_connection_propose(OscarData *od, guint64 type, const char *bn)
    -{
    - PeerConnection *conn;
    -
    - if (type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - conn = peer_connection_find_by_type(od, bn, type);
    - if (conn != NULL)
    - {
    - if (conn->ready)
    - {
    - PurpleAccount *account;
    - PurpleIMConversation *im;
    -
    - purple_debug_info("oscar", "Already have a direct IM "
    - "session with %s.\n", bn);
    - account = purple_connection_get_account(od->gc);
    - im = purple_conversations_find_im_with_account(bn, account);
    - if (im != NULL)
    - purple_conversation_present(PURPLE_CONVERSATION(im));
    - return;
    - }
    -
    - /* Cancel the old connection and try again */
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_RETRYING, NULL);
    - }
    - }
    -
    - conn = peer_connection_new(od, type, bn);
    - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - aim_icbm_makecookie(conn->cookie);
    -
    - peer_connection_trynext(conn);
    -}
    -
    -/**
    - * Someone else wants to establish a peer connection with us,
    - * and we said yes.
    - */
    -static void
    -peer_connection_got_proposition_yes_cb(gpointer data, gint id)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - peer_connection_trynext(conn);
    -}
    -
    -/**
    - * Someone else wants to establish a peer connection with us,
    - * and we said no.
    - *
    - * "Well, one time my friend asked me if I wanted to play the
    - * piccolo. But I said no."
    - */
    -static void
    -peer_connection_got_proposition_no_cb(gpointer data, gint id)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - aim_im_denytransfer(conn->od, conn->bn, conn->cookie,
    - AIM_TRANSFER_DENY_DECLINE);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
    -}
    -
    -/**
    - * Someone else wants to establish a peer connection with us.
    - */
    -void
    -peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
    -{
    - PurpleConnection *gc;
    - PurpleAccount *account;
    - PeerConnection *conn;
    - gchar *buf;
    -
    - gc = od->gc;
    - account = purple_connection_get_account(gc);
    -
    - /*
    - * If we have a connection with this same cookie then they are
    - * probably just telling us they weren't able to connect to us
    - * and we should try connecting to them, instead. Or they want
    - * to go through a proxy.
    - */
    - conn = peer_connection_find_by_cookie(od, bn, args->cookie);
    - if ((conn != NULL) && (conn->type == args->type))
    - {
    - purple_debug_info("oscar", "Remote user wants to try a "
    - "different connection method\n");
    - g_free(conn->proxyip);
    - g_free(conn->clientip);
    - g_free(conn->verifiedip);
    - if (args->use_proxy)
    - conn->proxyip = g_strdup(args->proxyip);
    - else
    - conn->proxyip = NULL;
    - conn->verifiedip = g_strdup(args->verifiedip);
    - conn->clientip = g_strdup(args->clientip);
    - conn->port = args->port;
    - conn->use_proxy |= args->use_proxy;
    - conn->lastrequestnumber++;
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* If this is a direct IM, then close any existing session */
    - if (args->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - conn = peer_connection_find_by_type(od, bn, args->type);
    - if (conn != NULL)
    - {
    - /* Close the old direct IM and start a new one */
    - purple_debug_info("oscar", "Received new direct IM request "
    - "from %s. Destroying old connection.\n", bn);
    - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
    - }
    - }
    -
    - /* Check for proper arguments */
    - if (args->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - if ((args->info.sendfile.filename == NULL) ||
    - (args->info.sendfile.totsize == 0) ||
    - (args->info.sendfile.totfiles == 0))
    - {
    - purple_debug_warning("oscar",
    - "%s tried to send you a file with incomplete "
    - "information.\n", bn);
    - return;
    - }
    - }
    -
    - conn = peer_connection_new(od, args->type, bn);
    - memcpy(conn->cookie, args->cookie, 8);
    - if (args->use_proxy)
    - conn->proxyip = g_strdup(args->proxyip);
    - conn->clientip = g_strdup(args->clientip);
    - conn->verifiedip = g_strdup(args->verifiedip);
    - conn->port = args->port;
    - conn->use_proxy |= args->use_proxy;
    - conn->lastrequestnumber++;
    -
    - if (args->type == OSCAR_CAPABILITY_DIRECTIM)
    - {
    - buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
    - bn, purple_account_get_username(account));
    -
    - purple_request_action(conn, NULL, buf,
    - _("This requires a direct connection between "
    - "the two computers and is necessary for IM "
    - "Images. Because your IP address will be "
    - "revealed, this may be considered a privacy "
    - "risk."),
    - PURPLE_DEFAULT_ACTION_NONE,
    - purple_request_cpar_from_account(account),
    - conn, 2,
    - _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
    - _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
    - }
    - else if (args->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - gchar *filename;
    -
    - conn->xfer = PURPLE_XFER(g_object_new(
    - OSCAR_TYPE_XFER,
    - "account", account,
    - "type", PURPLE_XFER_TYPE_RECEIVE,
    - "remote-user", bn,
    - "conn", conn,
    - NULL
    - ));
    - purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
    -
    - /* Set the file name */
    - if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
    - filename = g_strdup(args->info.sendfile.filename);
    - else
    - filename = purple_utf8_salvage(args->info.sendfile.filename);
    -
    - if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
    - {
    - /*
    - * If they are sending us a directory then the last character
    - * of the file name will be an asterisk. We don't want to
    - * save stuff to a directory named "*" so we remove the
    - * asterisk from the file name.
    - */
    - char *tmp = strrchr(filename, '\\');
    - if ((tmp != NULL) && (tmp[1] == '*'))
    - tmp[0] = '\0';
    - }
    - purple_xfer_set_filename(conn->xfer, filename);
    - g_free(filename);
    -
    - /*
    - * Set the message, unless this is the dummy message from an
    - * ICQ client or an empty message from an AIM client.
    - * TODO: Maybe we should strip HTML and then see if strlen>0?
    - */
    - if ((message != NULL) &&
    - (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
    - (g_ascii_strcasecmp(message, "<HTML>") != 0))
    - {
    - purple_xfer_set_message(conn->xfer, message);
    - }
    -
    - /* Now perform the request */
    - purple_xfer_request(conn->xfer);
    - }
    -}
    -
    -/*******************************************************************/
    -/* End code for establishing a peer connection */
    -/*******************************************************************/
    -
    -G_DEFINE_DYNAMIC_TYPE(OscarXfer, oscar_xfer, PURPLE_TYPE_XFER);
    -
    -static void
    -oscar_xfer_init_xfer(PurpleXfer *xfer) {
    - PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    -
    - if(type == PURPLE_XFER_TYPE_SEND) {
    - peer_oft_sendcb_init(xfer);
    - } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    - peer_oft_recvcb_init(xfer);
    - }
    -}
    -
    -static void
    -oscar_xfer_ack(PurpleXfer *xfer, const guchar *buffer, size_t size) {
    - PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    -
    - if(type == PURPLE_XFER_TYPE_SEND) {
    - peer_oft_sendcb_ack(xfer, buffer, size);
    - } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    - peer_oft_recvcb_ack_recv(xfer, buffer, size);
    - }
    -
    -}
    -
    -static void
    -oscar_xfer_init(OscarXfer *xfer) {
    -
    -}
    -
    -static void
    -oscar_xfer_class_finalize(OscarXferClass *klass) {
    -
    -}
    -
    -static void
    -oscar_xfer_class_init(OscarXferClass *klass) {
    - PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    -
    - xfer_class->init = oscar_xfer_init_xfer;
    - xfer_class->end = peer_oft_recvcb_end;
    - xfer_class->cancel_send = peer_oft_cb_generic_cancel;
    - xfer_class->cancel_recv = peer_oft_cb_generic_cancel;
    - xfer_class->request_denied = peer_oft_cb_generic_cancel;
    - xfer_class->ack = oscar_xfer_ack;
    -}
    -
    -void
    -oscar_xfer_register(GTypeModule *module) {
    - oscar_xfer_register_type(module);
    -}
    -
    -PeerConnection *
    -oscar_xfer_get_peer_connection(OscarXfer *xfer) {
    - g_return_val_if_fail(OSCAR_IS_XFER(xfer), NULL);
    -
    - return xfer->conn;
    -}
    --- a/libpurple/protocols/oscar/peer.h Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,295 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * OFT and ODC Services
    - */
    -
    -#ifndef PURPLE_OSCAR_PEER_H
    -#define PURPLE_OSCAR_PEER_H
    -
    -#include "xfer.h"
    -#include "network.h"
    -#include "proxy.h"
    -
    -typedef struct _ChecksumData ChecksumData;
    -typedef struct _OdcFrame OdcFrame;
    -typedef struct _OftFrame OftFrame;
    -typedef struct _ProxyFrame ProxyFrame;
    -typedef struct _PeerConnection PeerConnection;
    -
    -#define PEER_CONNECTION_FLAG_INITIATED_BY_ME 0x0001
    -#define PEER_CONNECTION_FLAG_APPROVED 0x0002
    -#define PEER_CONNECTION_FLAG_TRIED_DIRECT 0x0004
    -#define PEER_CONNECTION_FLAG_TRIED_INCOMING 0x0008
    -#define PEER_CONNECTION_FLAG_TRIED_PROXY 0x0010
    -#define PEER_CONNECTION_FLAG_IS_INCOMING 0x0020
    -
    -#define PEER_TYPE_PROMPT 0x0101 /* "I am going to send you this file, is that ok?" */
    -#define PEER_TYPE_RESUMEACCEPT 0x0106 /* We are accepting the resume */
    -#define PEER_TYPE_ACK 0x0202 /* "Yes, it is ok for you to send me that file" */
    -#define PEER_TYPE_DONE 0x0204 /* "I received that file with no problems" or "I already have that file, great!" */
    -#define PEER_TYPE_RESUME 0x0205 /* Resume transferring, sent by whoever receives */
    -#define PEER_TYPE_RESUMEACK 0x0207 /* Our resume accept was ACKed */
    -
    -#define PEER_TYPE_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */
    -#define PEER_TYPE_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */
    -#define PEER_TYPE_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */
    -#define PEER_TYPE_GETFILE_ACKLISTING 0x120b /* "I received the listing.txt file successfully" */
    -#define PEER_TYPE_GETFILE_REQUESTFILE 0x120c /* "Please send me this file" */
    -
    -/*
    - * For peer proxying
    - */
    -#define AIM_PEER_PROXY_SERVER "ars.oscar.aol.com"
    -#define ICQ_PEER_PROXY_SERVER "ars.icq.com"
    -#define PEER_PROXY_PORT 5190 /* The port we should always connect to */
    -#define PEER_PROXY_PACKET_VERSION 0x044a
    -
    -/* Thanks to Keith Lea and the Joust project for documenting these */
    -#define PEER_PROXY_TYPE_ERROR 0x0001
    -#define PEER_PROXY_TYPE_CREATE 0x0002
    -#define PEER_PROXY_TYPE_CREATED 0x0003
    -#define PEER_PROXY_TYPE_JOIN 0x0004
    -#define PEER_PROXY_TYPE_READY 0x0005
    -
    -struct _OdcFrame
    -{
    - /* guchar magic[4]; */ /* 0 */
    - /* guint16 length; */ /* 4 */
    - guint16 type; /* 6 */
    - guint16 subtype; /* 8 */
    - /* Unknown */ /* 10 */
    - guchar cookie[8]; /* 12 */
    - /* Unknown */
    - /* guint32 payloadlength; */ /* 28 */
    - guint16 encoding; /* 32 */
    - /* Unknown */
    - guint16 flags; /* 38 */
    - /* Unknown */
    - guchar bn[32]; /* 44 */
    - /* Unknown */
    - ByteStream payload; /* 76 */
    -};
    -
    -struct _OftFrame
    -{
    - /* guchar magic[4]; */ /* 0 */
    - /* guint16 length; */ /* 4 */
    - guint16 type; /* 6 */
    - guchar cookie[8]; /* 8 */
    - guint16 encrypt; /* 16 */
    - guint16 compress; /* 18 */
    - guint16 totfiles; /* 20 */
    - guint16 filesleft; /* 22 */
    - guint16 totparts; /* 24 */
    - guint16 partsleft; /* 26 */
    - guint32 totsize; /* 28 */
    - guint32 size; /* 32 */
    - guint32 modtime; /* 36 */
    - guint32 checksum; /* 40 */
    - guint32 rfrcsum; /* 44 */
    - guint32 rfsize; /* 48 */
    - guint32 cretime; /* 52 */
    - guint32 rfcsum; /* 56 */
    - guint32 nrecvd; /* 60 */
    - guint32 recvcsum; /* 64 */
    - guchar idstring[32]; /* 68 */
    - guint8 flags; /* 100 */
    - guint8 lnameoffset; /* 101 */
    - guint8 lsizeoffset; /* 102 */
    - guchar dummy[69]; /* 103 */
    - guchar macfileinfo[16]; /* 172 */
    - guint16 nencode; /* 188 */
    - guint16 nlanguage; /* 190 */
    - guchar *name; /* 192 */
    - size_t name_length;
    - /* Payload? */ /* 256 */
    -};
    -
    -struct _ProxyFrame
    -{
    - /* guint16 length; */ /* 0 */
    - guint16 version; /* 2 */
    - guint16 type; /* 4 */
    - guint32 unknown; /* 6 */
    - guint16 flags; /* 10 */
    - ByteStream payload; /* 12 */
    -};
    -
    -struct _PeerConnection
    -{
    - OscarData *od;
    - guint64 type;
    - char *bn;
    - guchar magic[4];
    - guchar cookie[8];
    - guint16 lastrequestnumber;
    -
    - gboolean ready;
    - int flags; /**< Bitmask of PEER_CONNECTION_FLAG_ */
    - time_t lastactivity; /**< Time of last transmit. */
    - guint destroy_timeout;
    - OscarDisconnectReason disconnect_reason;
    - char *error_message;
    -
    - /**
    - * A pointer to either an OdcFrame or an OftFrame.
    - */
    - gpointer frame;
    -
    - /**
    - * This is only used when the peer connection is being established.
    - */
    - PurpleProxyConnectData *client_connect_data;
    - PurpleProxyConnectData *verified_connect_data;
    -
    - /**
    - * This is only used when the peer connection is being established.
    - */
    - PurpleNetworkListenData *listen_data;
    -
    -
    - /**
    - * This is only used when the peer connection is being established.
    - */
    - guint connect_timeout_timer;
    -
    - /**
    - * This is only used while the remote user is attempting to
    - * connect to us.
    - */
    - int listenerfd;
    -
    - int fd;
    - guint8 header[6];
    - gssize header_received;
    - guint8 proxy_header[12];
    - gssize proxy_header_received;
    - ByteStream buffer_incoming;
    - PurpleCircularBuffer *buffer_outgoing;
    - guint watcher_incoming;
    - guint watcher_outgoing;
    -
    - /**
    - * IP address of the proxy server, if applicable.
    - */
    - gchar *proxyip;
    -
    - /**
    - * IP address of the remote user from THEIR point of view.
    - */
    - gchar *clientip;
    -
    - /**
    - * IP address of the remote user from the oscar server's
    - * point of view.
    - */
    - gchar *verifiedip;
    -
    - guint16 port;
    - gboolean use_proxy;
    -
    - /**
    - * Checksumming
    - */
    - ChecksumData *checksum_data;
    -
    - /* TODOFT */
    - PurpleXfer *xfer;
    - OftFrame xferdata;
    - guint sending_data_timer;
    -};
    -
    -/*
    - * For all peer connections
    - */
    -
    -/**
    - * Create a new PeerConnection structure and initialize it with some
    - * sane defaults.
    - *
    - * @param type The type of the peer connection. One of
    - * OSCAR_CAPABILITY_DIRECTIM or OSCAR_CAPABILITY_SENDFILE.
    - */
    -PeerConnection *peer_connection_new(OscarData *od, guint64 type, const char *bn);
    -
    -void peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -void peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
    -PeerConnection *peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type);
    -PeerConnection *peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie);
    -
    -void peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond);
    -void peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond);
    -void peer_connection_send(PeerConnection *conn, ByteStream *bs);
    -
    -void peer_connection_trynext(PeerConnection *conn);
    -void peer_connection_finalize_connection(PeerConnection *conn);
    -void peer_connection_propose(OscarData *od, guint64 type, const char *bn);
    -void peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args);
    -
    -/*
    - * For ODC
    - */
    -void peer_odc_close(PeerConnection *conn);
    -void peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs);
    -void peer_odc_send_cookie(PeerConnection *conn);
    -void peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing);
    -void peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply);
    -
    -/*
    - * For OFT
    - */
    -void peer_oft_close(PeerConnection *conn);
    -void peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs);
    -void peer_oft_send_prompt(PeerConnection *conn);
    -void peer_oft_checksum_destroy(ChecksumData *checksum_data);
    -
    -/* Xfer callbacks for receiving a file */
    -void peer_oft_recvcb_init(PurpleXfer *xfer);
    -void peer_oft_recvcb_end(PurpleXfer *xfer);
    -void peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size);
    -
    -/* Xfer callbacks for sending a file */
    -void peer_oft_sendcb_init(PurpleXfer *xfer);
    -void peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size);
    -
    -/* Xfer callbacks for both sending and receiving */
    -void peer_oft_cb_generic_cancel(PurpleXfer *xfer);
    -
    -/*
    - * For peer proxying
    - */
    -void peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message);
    -
    -/* File transfers */
    -#define OSCAR_TYPE_XFER (oscar_xfer_get_type())
    -G_DECLARE_FINAL_TYPE(OscarXfer, oscar_xfer, OSCAR, XFER, PurpleXfer);
    -
    -struct _OscarXfer {
    - PurpleXfer parent;
    -
    - PeerConnection *conn;
    -};
    -
    -void oscar_xfer_register(GTypeModule *module);
    -PeerConnection *oscar_xfer_get_peer_connection(OscarXfer *xfer);
    -
    -#endif /* PURPLE_OSCAR_PEER_H */
    --- a/libpurple/protocols/oscar/peer_proxy.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,354 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#ifdef HAVE_CONFIG_H
    -#include <config.h>
    -#endif
    -
    -#include "oscar.h"
    -#include "peer.h"
    -
    -static void
    -peer_proxy_send(PeerConnection *conn, ProxyFrame *frame)
    -{
    - size_t length;
    - ByteStream bs;
    -
    - purple_debug_info("oscar", "Outgoing peer proxy frame with "
    - "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
    - "payload length=%" G_GSIZE_FORMAT "\n",
    - frame->type, frame->unknown,
    - frame->flags, frame->payload.len);
    -
    - length = 12 + frame->payload.len;
    - byte_stream_new(&bs, length);
    - byte_stream_put16(&bs, length - 2);
    - byte_stream_put16(&bs, PEER_PROXY_PACKET_VERSION);
    - byte_stream_put16(&bs, frame->type);
    - byte_stream_put32(&bs, frame->unknown);
    - byte_stream_put16(&bs, frame->flags);
    - byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
    -
    - peer_connection_send(conn, &bs);
    -
    - byte_stream_destroy(&bs);
    -}
    -
    -/**
    - * Create a rendezvous "init send" packet and send it on its merry way.
    - * This is the first packet sent to the proxy server by the first client
    - * to indicate that this will be a proxied connection
    - *
    - * @param conn The peer connection.
    - */
    -static void
    -peer_proxy_send_create_new_conn(PeerConnection *conn)
    -{
    - ProxyFrame frame;
    - PurpleAccount *account;
    - const gchar *bn;
    - guint8 bn_length;
    -
    - memset(&frame, 0, sizeof(ProxyFrame));
    - frame.type = PEER_PROXY_TYPE_CREATE;
    - frame.flags = 0x0000;
    -
    - account = purple_connection_get_account(conn->od->gc);
    - bn = purple_account_get_username(account);
    - bn_length = strlen(bn);
    - byte_stream_new(&frame.payload, 1 + bn_length + 8 + 20);
    - byte_stream_put8(&frame.payload, bn_length);
    - byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length);
    - byte_stream_putraw(&frame.payload, conn->cookie, 8);
    -
    - byte_stream_put16(&frame.payload, 0x0001); /* Type */
    - byte_stream_put16(&frame.payload, 16); /* Length */
    - byte_stream_putcaps(&frame.payload, conn->type); /* Value */
    -
    - peer_proxy_send(conn, &frame);
    -
    - byte_stream_destroy(&frame.payload);
    -}
    -
    -/**
    - * Create a rendezvous "init recv" packet and send it on its merry way.
    - * This is the first packet sent to the proxy server by the second client
    - * involved in this rendezvous proxy session.
    - *
    - * @param conn The peer connection.
    - * @param pin The 2 byte PIN sent to us by the other user. This acts
    - * as our passcode when establishing the proxy session.
    - */
    -static void
    -peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin)
    -{
    - ProxyFrame frame;
    - PurpleAccount *account;
    - const gchar *bn;
    - guint8 bn_length;
    -
    - memset(&frame, 0, sizeof(ProxyFrame));
    - frame.type = PEER_PROXY_TYPE_JOIN;
    - frame.flags = 0x0000;
    -
    - account = purple_connection_get_account(conn->od->gc);
    - bn = purple_account_get_username(account);
    - bn_length = strlen(bn);
    - byte_stream_new(&frame.payload, 1 + bn_length + 2 + 8 + 20);
    - byte_stream_put8(&frame.payload, bn_length);
    - byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length);
    - byte_stream_put16(&frame.payload, pin);
    - byte_stream_putraw(&frame.payload, conn->cookie, 8);
    -
    - byte_stream_put16(&frame.payload, 0x0001); /* Type */
    - byte_stream_put16(&frame.payload, 16); /* Length */
    - byte_stream_putcaps(&frame.payload, conn->type); /* Value */
    -
    - peer_proxy_send(conn, &frame);
    -
    - byte_stream_destroy(&frame.payload);
    -}
    -
    -/**
    - * Handle an incoming peer proxy negotiation frame.
    - */
    -static void
    -peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame)
    -{
    - purple_debug_info("oscar", "Incoming peer proxy frame with "
    - "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and "
    - "payload length=%" G_GSIZE_FORMAT "\n", frame->type,
    - frame->unknown, frame->flags, frame->payload.len);
    -
    - if (frame->type == PEER_PROXY_TYPE_CREATED)
    - {
    - /*
    - * Read in 2 byte port then 4 byte IP and tell the
    - * remote user to connect to it by sending an ICBM.
    - */
    - guint16 pin;
    - int i;
    - guint8 ip[4];
    -
    - pin = byte_stream_get16(&frame->payload);
    - for (i = 0; i < 4; i++)
    - ip[i] = byte_stream_get8(&frame->payload);
    - if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
    - aim_im_sendch2_odc_requestproxy(conn->od,
    - conn->cookie,
    - conn->bn, ip, pin, ++conn->lastrequestnumber);
    - else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
    - {
    - aim_im_sendch2_sendfile_requestproxy(conn->od,
    - conn->cookie, conn->bn,
    - ip, pin, ++conn->lastrequestnumber,
    - (const gchar *)conn->xferdata.name,
    - conn->xferdata.size, conn->xferdata.totfiles);
    - }
    - }
    - else if (frame->type == PEER_PROXY_TYPE_READY)
    - {
    - purple_input_remove(conn->watcher_incoming);
    - conn->watcher_incoming = 0;
    -
    - peer_connection_finalize_connection(conn);
    - }
    - else if (frame->type == PEER_PROXY_TYPE_ERROR)
    - {
    - if (byte_stream_bytes_left(&frame->payload) >= 2)
    - {
    - guint16 error;
    - const char *msg;
    - error = byte_stream_get16(&frame->payload);
    - if (error == 0x000d)
    - msg = "bad request";
    - else if (error == 0x0010)
    - msg = "initial request timed out";
    - else if (error == 0x001a)
    - msg ="accept period timed out";
    - else
    - msg = "unknown reason";
    - purple_debug_info("oscar", "Proxy negotiation failed with "
    - "error 0x%04hx: %s\n", error, msg);
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Proxy negotiation failed with "
    - "an unknown error\n");
    - }
    - peer_connection_trynext(conn);
    - }
    - else
    - {
    - purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n",
    - frame->type);
    - }
    -}
    -
    -static void
    -peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    - PeerConnection *conn;
    - gssize read;
    - ProxyFrame *frame;
    -
    - conn = data;
    - frame = conn->frame;
    -
    - /* Start reading a new proxy frame */
    - if (frame == NULL)
    - {
    - /* Read the first 12 bytes (frame length and header) */
    - read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received,
    - 12 - conn->proxy_header_received, 0);
    -
    - /* Check if the proxy server closed the connection */
    - if (read == 0)
    - {
    - purple_debug_info("oscar", "Peer proxy server closed connection\n");
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - purple_debug_info("oscar", "Lost connection with peer proxy server\n");
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - conn->lastactivity = time(NULL);
    -
    - /* If we don't even have the first 12 bytes then do nothing */
    - conn->proxy_header_received += read;
    - if (conn->proxy_header_received < 12)
    - return;
    -
    - /* We only support a specific version of the proxy protocol */
    - if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION)
    - {
    - purple_debug_warning("oscar", "Expected peer proxy protocol "
    - "version %u but received version %u. Closing "
    - "connection.\n", PEER_PROXY_PACKET_VERSION,
    - aimutil_get16(&conn->proxy_header[2]));
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* Initialize a new temporary ProxyFrame for incoming data */
    - frame = g_new0(ProxyFrame, 1);
    - frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10;
    - frame->version = aimutil_get16(&conn->proxy_header[2]);
    - frame->type = aimutil_get16(&conn->proxy_header[4]);
    - frame->unknown = aimutil_get16(&conn->proxy_header[6]);
    - frame->flags = aimutil_get16(&conn->proxy_header[10]);
    - if (frame->payload.len > 0)
    - frame->payload.data = g_new(guint8, frame->payload.len);
    - conn->frame = frame;
    - }
    -
    - /* If this frame has a payload then attempt to read it */
    - if (frame->payload.offset < frame->payload.len) {
    - /* Read data into the temporary buffer until it is complete */
    - read = recv(conn->fd,
    - &frame->payload.data[frame->payload.offset],
    - frame->payload.len - frame->payload.offset,
    - 0);
    -
    - /* Check if the proxy server closed the connection */
    - if (read == 0)
    - {
    - purple_debug_info("oscar", "Peer proxy server closed connection\n");
    - g_free(frame->payload.data);
    - g_free(frame);
    - conn->frame = NULL;
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - /* If there was an error then close the connection */
    - if (read < 0)
    - {
    - if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
    - /* No worries */
    - return;
    -
    - purple_debug_info("oscar", "Lost connection with peer proxy server\n");
    - g_free(frame->payload.data);
    - g_free(frame);
    - conn->frame = NULL;
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - frame->payload.offset += read;
    - }
    -
    - conn->lastactivity = time(NULL);
    - if (frame->payload.offset < frame->payload.len)
    - /* Waiting for more data to arrive */
    - return;
    -
    - /* We have a complete proxy frame! Handle it and continue reading */
    - conn->frame = NULL;
    - byte_stream_rewind(&frame->payload);
    - peer_proxy_recv_frame(conn, frame);
    -
    - g_free(frame->payload.data);
    - g_free(frame);
    -
    - conn->proxy_header_received = 0;
    -}
    -
    -/**
    - * We tried to make an outgoing connection to a proxy server. It
    - * either connected or failed to connect.
    - */
    -void
    -peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message)
    -{
    - PeerConnection *conn;
    -
    - conn = data;
    -
    - conn->verified_connect_data = NULL;
    -
    - if (source < 0)
    - {
    - peer_connection_trynext(conn);
    - return;
    - }
    -
    - conn->fd = source;
    - conn->watcher_incoming = purple_input_add(conn->fd,
    - PURPLE_INPUT_READ, peer_proxy_connection_recv_cb, conn);
    -
    - if (conn->proxyip != NULL)
    - /* Connect to the session created by the remote user */
    - peer_proxy_send_join_existing_conn(conn, conn->port);
    - else
    - /* Create a new session */
    - peer_proxy_send_create_new_conn(conn);
    -}
    --- a/libpurple/protocols/oscar/rxhandlers.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,95 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -#include "peer.h"
    -
    -aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group)
    -{
    - aim_module_t *cur;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    - if (cur->family == group)
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -aim_module_t *aim__findmodule(OscarData *od, const char *name)
    -{
    - aim_module_t *cur;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
    - if (purple_strequal(name, cur->name))
    - return cur;
    - }
    -
    - return NULL;
    -}
    -
    -int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *))
    -{
    - aim_module_t *mod;
    -
    - if (!od || !modfirst)
    - return -1;
    -
    - mod = g_new0(aim_module_t, 1);
    -
    - if (modfirst(od, mod) == -1) {
    - g_free(mod);
    - return -1;
    - }
    -
    - if (aim__findmodule(od, mod->name)) {
    - if (mod->shutdown)
    - mod->shutdown(od, mod);
    - g_free(mod);
    - return -1;
    - }
    -
    - mod->next = (aim_module_t *)od->modlistv;
    - od->modlistv = mod;
    -
    - return 0;
    -}
    -
    -void aim__shutdownmodules(OscarData *od)
    -{
    - aim_module_t *cur;
    -
    - for (cur = (aim_module_t *)od->modlistv; cur; ) {
    - aim_module_t *tmp;
    -
    - tmp = cur->next;
    -
    - if (cur->shutdown)
    - cur->shutdown(od, cur);
    -
    - g_free(cur);
    -
    - cur = tmp;
    - }
    -
    - od->modlistv = NULL;
    -
    - return;
    -}
    --- a/libpurple/protocols/oscar/snac.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,163 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - *
    - * Various SNAC-related dodads...
    - *
    - * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added
    - * whenever a new SNAC is sent and it should remain in the list until the
    - * response for it has been received.
    - *
    - * cleansnacs() should be called periodically by the client in order
    - * to facilitate the aging out of unreplied-to SNACs. This can and does
    - * happen, so it should be handled.
    - *
    - */
    -
    -#include "oscar.h"
    -
    -/*
    - * Called from oscar_session_new() to initialize the hash.
    - */
    -void aim_initsnachash(OscarData *od)
    -{
    - int i;
    -
    - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++)
    - od->snac_hash[i] = NULL;
    -
    - return;
    -}
    -
    -aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen)
    -{
    - aim_snac_t snac;
    -
    - snac.id = od->snacid_next++;
    - snac.family = family;
    - snac.type = type;
    - snac.flags = flags;
    -
    - if (datalen)
    - snac.data = g_memdup(data, datalen);
    - else
    - snac.data = NULL;
    -
    - return aim_newsnac(od, &snac);
    -}
    -
    -/*
    - * Clones the passed snac structure and caches it in the
    - * list/hash.
    - */
    -aim_snacid_t aim_newsnac(OscarData *od, aim_snac_t *newsnac)
    -{
    - aim_snac_t *snac;
    - int index;
    -
    - if (!newsnac)
    - return 0;
    -
    - snac = g_memdup(newsnac, sizeof(aim_snac_t));
    - snac->issuetime = time(NULL);
    -
    - index = snac->id % FAIM_SNAC_HASH_SIZE;
    -
    - snac->next = (aim_snac_t *)od->snac_hash[index];
    - od->snac_hash[index] = (void *)snac;
    -
    - return snac->id;
    -}
    -
    -/*
    - * Finds a snac structure with the passed SNAC ID,
    - * removes it from the list/hash, and returns a pointer to it.
    - *
    - * The returned structure must be freed by the caller.
    - *
    - */
    -aim_snac_t *aim_remsnac(OscarData *od, aim_snacid_t id)
    -{
    - aim_snac_t *cur, **prev;
    - int index;
    -
    - index = id % FAIM_SNAC_HASH_SIZE;
    -
    - for (prev = (aim_snac_t **)&od->snac_hash[index]; (cur = *prev); ) {
    - if (cur->id == id) {
    - *prev = cur->next;
    - if (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) {
    - g_free(cur->data);
    - cur->data = NULL;
    - }
    - return cur;
    - } else
    - prev = &cur->next;
    - }
    -
    - return cur;
    -}
    -
    -/*
    - * This is for cleaning up old SNACs that either don't get replies or
    - * a reply was never received for. Garbage collection. Plain and simple.
    - *
    - * maxage is the _minimum_ age in seconds to keep SNACs.
    - *
    - */
    -void aim_cleansnacs(OscarData *od, int maxage)
    -{
    - int i;
    -
    - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
    - aim_snac_t *cur, **prev;
    - time_t curtime;
    -
    - if (!od->snac_hash[i])
    - continue;
    -
    - curtime = time(NULL); /* done here in case we waited for the lock */
    -
    - for (prev = (aim_snac_t **)&od->snac_hash[i]; (cur = *prev); ) {
    - if ((curtime - cur->issuetime) > maxage) {
    -
    - *prev = cur->next;
    -
    - g_free(cur->data);
    - g_free(cur);
    - } else
    - prev = &cur->next;
    - }
    - }
    -
    - return;
    -}
    -
    -int aim_putsnac(ByteStream *bs, guint16 family, guint16 subtype, aim_snacid_t snacid)
    -{
    -
    - byte_stream_put16(bs, family);
    - byte_stream_put16(bs, subtype);
    - byte_stream_put16(bs, 0x0000);
    - byte_stream_put32(bs, snacid);
    -
    - return 10;
    -}
    --- a/libpurple/protocols/oscar/snactypes.h Sun Oct 20 00:24:28 2019 +0300
    +++ /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
    -*/
    -
    -/*
    - * AIM Callback Types
    - *
    - */
    -
    -#ifndef PURPLE_OSCAR_SNACTYPES_H
    -#define PURPLE_OSCAR_SNACTYPES_H
    -
    -/*
    - * SNAC Families.
    - */
    -#define SNAC_FAMILY_OSERVICE 0x0001
    -#define SNAC_FAMILY_LOCATE 0x0002
    -#define SNAC_FAMILY_BUDDY 0x0003
    -#define SNAC_FAMILY_ICBM 0x0004
    -#define SNAC_FAMILY_ADVERT 0x0005
    -#define SNAC_FAMILY_INVITE 0x0006
    -#define SNAC_FAMILY_ADMIN 0x0007
    -#define SNAC_FAMILY_POPUP 0x0008
    -#define SNAC_FAMILY_BOS 0x0009
    -#define SNAC_FAMILY_USERLOOKUP 0x000a
    -#define SNAC_FAMILY_STATS 0x000b
    -#define SNAC_FAMILY_TRANSLATE 0x000c
    -#define SNAC_FAMILY_CHATNAV 0x000d
    -#define SNAC_FAMILY_CHAT 0x000e
    -#define SNAC_FAMILY_ODIR 0x000f
    -#define SNAC_FAMILY_BART 0x0010
    -#define SNAC_FAMILY_FEEDBAG 0x0013
    -#define SNAC_FAMILY_ICQ 0x0015
    -#define SNAC_FAMILY_AUTH 0x0017
    -#define SNAC_FAMILY_ALERT 0x0018
    -
    -#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
    -
    -/*
    - * SNAC Family: Ack.
    - *
    - * Not really a family, but treating it as one really
    - * helps it fit into the libfaim callback structure better.
    - *
    - */
    -#define AIM_CB_ACK_ACK 0x0001
    -
    -/*
    - * SNAC Family: General.
    - */
    -#define SNAC_SUBTYPE_OSERVICE_ERROR 0x0001
    -#define SNAC_SUBTYPE_OSERVICE_CLIENTREADY 0x0002
    -#define SNAC_SUBTYPE_OSERVICE_SERVERREADY 0x0003
    -#define SNAC_SUBTYPE_OSERVICE_SERVICEREQ 0x0004
    -#define SNAC_SUBTYPE_OSERVICE_REDIRECT 0x0005
    -#define SNAC_SUBTYPE_OSERVICE_RATEINFOREQ 0x0006
    -#define SNAC_SUBTYPE_OSERVICE_RATEINFO 0x0007
    -#define SNAC_SUBTYPE_OSERVICE_RATEINFOACK 0x0008
    -#define SNAC_SUBTYPE_OSERVICE_RATECHANGE 0x000a
    -#define SNAC_SUBTYPE_OSERVICE_SERVERPAUSE 0x000b
    -#define SNAC_SUBTYPE_OSERVICE_SERVERRESUME 0x000d
    -#define SNAC_SUBTYPE_OSERVICE_REQSELFINFO 0x000e
    -#define SNAC_SUBTYPE_OSERVICE_SELFINFO 0x000f
    -#define SNAC_SUBTYPE_OSERVICE_EVIL 0x0010
    -#define SNAC_SUBTYPE_OSERVICE_SETIDLE 0x0011
    -#define SNAC_SUBTYPE_OSERVICE_MIGRATIONREQ 0x0012
    -#define SNAC_SUBTYPE_OSERVICE_MOTD 0x0013
    -#define SNAC_SUBTYPE_OSERVICE_SETPRIVFLAGS 0x0014
    -#define SNAC_SUBTYPE_OSERVICE_WELLKNOWNURL 0x0015
    -#define SNAC_SUBTYPE_OSERVICE_NOP 0x0016
    -#define SNAC_SUBTYPE_OSERVICE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Location Services.
    - */
    -#define SNAC_SUBTYPE_LOCATE_ERROR 0x0001
    -#define SNAC_SUBTYPE_LOCATE_REQRIGHTS 0x0002
    -#define SNAC_SUBTYPE_LOCATE_RIGHTSINFO 0x0003
    -#define SNAC_SUBTYPE_LOCATE_SETUSERINFO 0x0004
    -#define SNAC_SUBTYPE_LOCATE_REQUSERINFO 0x0005
    -#define SNAC_SUBTYPE_LOCATE_USERINFO 0x0006
    -#define SNAC_SUBTYPE_LOCATE_WATCHERSUBREQ 0x0007
    -#define SNAC_SUBTYPE_LOCATE_WATCHERNOT 0x0008
    -#define SNAC_SUBTYPE_LOCATE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Buddy List Management Services.
    - */
    -#define SNAC_SUBTYPE_BUDDY_ERROR 0x0001
    -#define SNAC_SUBTYPE_BUDDY_REQRIGHTS 0x0002
    -#define SNAC_SUBTYPE_BUDDY_RIGHTSINFO 0x0003
    -#define SNAC_SUBTYPE_BUDDY_ADDBUDDY 0x0004
    -#define SNAC_SUBTYPE_BUDDY_REMBUDDY 0x0005
    -#define SNAC_SUBTYPE_BUDDY_REJECT 0x000a
    -#define SNAC_SUBTYPE_BUDDY_ONCOMING 0x000b
    -#define SNAC_SUBTYPE_BUDDY_OFFGOING 0x000c
    -#define SNAC_SUBTYPE_BUDDY_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Messaging Services.
    - */
    -#define SNAC_SUBTYPE_ICBM_ERROR 0x0001
    -#define SNAC_SUBTYPE_ICBM_PARAMINFO 0x0005
    -#define SNAC_SUBTYPE_ICBM_INCOMING 0x0007
    -#define SNAC_SUBTYPE_ICBM_EVIL 0x0009
    -#define SNAC_SUBTYPE_ICBM_MISSEDCALL 0x000a
    -#define SNAC_SUBTYPE_ICBM_CLIENTAUTORESP 0x000b
    -#define SNAC_SUBTYPE_ICBM_ACK 0x000c
    -#define SNAC_SUBTYPE_ICBM_MTN 0x0014
    -#define SNAC_SUBTYPE_ICBM_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Advertisement Services
    - */
    -#define SNAC_SUBTYPE_ADVERT_ERROR 0x0001
    -#define SNAC_SUBTYPE_ADVERT_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Invitation Services.
    - */
    -#define SNAC_SUBTYPE_INVITE_ERROR 0x0001
    -#define SNAC_SUBTYPE_INVITE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Administrative Services.
    - */
    -#define SNAC_SUBTYPE_ADMIN_ERROR 0x0001
    -#define SNAC_SUBTYPE_ADMIN_INFOCHANGE_REPLY 0x0005
    -#define SNAC_SUBTYPE_ADMIN_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Popup Messages
    - */
    -#define SNAC_SUBTYPE_POPUP_ERROR 0x0001
    -#define SNAC_SUBTYPE_POPUP_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Misc BOS Services.
    - */
    -#define SNAC_SUBTYPE_BOS_ERROR 0x0001
    -#define SNAC_SUBTYPE_BOS_RIGHTSQUERY 0x0002
    -#define SNAC_SUBTYPE_BOS_RIGHTS 0x0003
    -#define SNAC_SUBTYPE_BOS_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: User Lookup Services
    - */
    -#define SNAC_SUBTYPE_USERLOOKUP_ERROR 0x0001
    -#define SNAC_SUBTYPE_USERLOOKUP_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: User Status Services
    - */
    -#define SNAC_SUBTYPE_STATS_ERROR 0x0001
    -#define SNAC_SUBTYPE_STATS_SETREPORTINTERVAL 0x0002
    -#define SNAC_SUBTYPE_STATS_REPORTACK 0x0004
    -#define SNAC_SUBTYPE_STATS_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Translation Services
    - */
    -#define SNAC_SUBTYPE_TRANSLATE_ERROR 0x0001
    -#define SNAC_SUBTYPE_TRANSLATE_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Chat Navigation Services
    - */
    -#define SNAC_SUBTYPE_CHATNAV_ERROR 0x0001
    -#define SNAC_SUBTYPE_CHATNAV_CREATE 0x0008
    -#define SNAC_SUBTYPE_CHATNAV_INFO 0x0009
    -#define SNAC_SUBTYPE_CHATNAV_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Chat Services
    - */
    -#define SNAC_SUBTYPE_CHAT_ERROR 0x0001
    -#define SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE 0x0002
    -#define SNAC_SUBTYPE_CHAT_USERJOIN 0x0003
    -#define SNAC_SUBTYPE_CHAT_USERLEAVE 0x0004
    -#define SNAC_SUBTYPE_CHAT_OUTGOINGMSG 0x0005
    -#define SNAC_SUBTYPE_CHAT_INCOMINGMSG 0x0006
    -#define SNAC_SUBTYPE_CHAT_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: "New" Search
    - */
    -#define SNAC_SUBTYPE_ODIR_ERROR 0x0001
    -#define SNAC_SUBTYPE_ODIR_SEARCH 0x0002
    -#define SNAC_SUBTYPE_ODIR_RESULTS 0x0003
    -
    -/*
    - * SNAC Family: Buddy icons
    - */
    -#define SNAC_SUBTYPE_BART_ERROR 0x0001
    -#define SNAC_SUBTYPE_BART_REQUEST 0x0004
    -#define SNAC_SUBTYPE_BART_RESPONSE 0x0005
    -
    -/*
    - * SNAC Family: Server-Stored Buddy Lists
    - */
    -#define SNAC_SUBTYPE_FEEDBAG_ERROR 0x0001
    -#define SNAC_SUBTYPE_FEEDBAG_REQRIGHTS 0x0002
    -#define SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO 0x0003
    -#define SNAC_SUBTYPE_FEEDBAG_REQDATA 0x0004
    -#define SNAC_SUBTYPE_FEEDBAG_REQIFCHANGED 0x0005
    -#define SNAC_SUBTYPE_FEEDBAG_LIST 0x0006
    -#define SNAC_SUBTYPE_FEEDBAG_ACTIVATE 0x0007
    -#define SNAC_SUBTYPE_FEEDBAG_ADD 0x0008
    -#define SNAC_SUBTYPE_FEEDBAG_MOD 0x0009
    -#define SNAC_SUBTYPE_FEEDBAG_DEL 0x000A
    -#define SNAC_SUBTYPE_FEEDBAG_SRVACK 0x000E
    -#define SNAC_SUBTYPE_FEEDBAG_NOLIST 0x000F
    -#define SNAC_SUBTYPE_FEEDBAG_EDITSTART 0x0011
    -#define SNAC_SUBTYPE_FEEDBAG_EDITSTOP 0x0012
    -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTH 0x0014
    -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTH 0x0015
    -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ 0x0018
    -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ 0x0019
    -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP 0x001a
    -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP 0x001b
    -#define SNAC_SUBTYPE_FEEDBAG_ADDED 0x001c
    -
    -/*
    - * SNAC Family: ICQ
    - *
    - * Most of these are actually special.
    - */
    -#define SNAC_SUBTYPE_ICQ_ERROR 0x0001
    -#define SNAC_SUBTYPE_ICQ_OFFLINEMSG 0x00f0
    -#define SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE 0x00f1
    -#define SNAC_SUBTYPE_ICQ_INFO 0x00f2
    -#define SNAC_SUBTYPE_ICQ_ALIAS 0x00f3
    -#define SNAC_SUBTYPE_ICQ_DEFAULT 0xffff
    -
    -/*
    - * SNAC Family: Authorizer
    - *
    - * Used only in protocol versions three and above.
    - */
    -#define SNAC_SUBTYPE_AUTH_ERROR 0x0001
    -#define SNAC_SUBTYPE_AUTH_LOGINREQEST 0x0002
    -#define SNAC_SUBTYPE_AUTH_LOGINRESPONSE 0x0003
    -#define SNAC_SUBTYPE_AUTH_AUTHREQ 0x0006
    -#define SNAC_SUBTYPE_AUTH_AUTHRESPONSE 0x0007
    -#define SNAC_SUBTYPE_AUTH_SECURID_REQUEST 0x000a
    -#define SNAC_SUBTYPE_AUTH_SECURID_RESPONSE 0x000b
    -
    -/*
    - * SNAC Family: Email
    - *
    - * Used for getting information on the email address
    - * associated with your username.
    - */
    -#define SNAC_SUBTYPE_ALERT_ERROR 0x0001
    -#define SNAC_SUBTYPE_ALERT_SENDCOOKIES 0x0006
    -#define SNAC_SUBTYPE_ALERT_MAILSTATUS 0x0007
    -#define SNAC_SUBTYPE_ALERT_INIT 0x0016
    -
    -/*
    - * SNAC Family: Internal Messages
    - *
    - * This isn't truly a SNAC family either, but using
    - * these, we can integrated non-SNAC services into
    - * the SNAC-centered libfaim callback structure.
    - */
    -#define AIM_CB_SPECIAL_CONNERR 0x0003
    -#define AIM_CB_SPECIAL_CONNINITDONE 0x0006
    -
    -/* SNAC flags */
    -#define AIM_SNACFLAGS_DESTRUCTOR 0x0001
    -
    -#endif /* PURPLE_OSCAR_SNACTYPES_H */
    --- a/libpurple/protocols/oscar/tests/meson.build Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,8 +0,0 @@
    -foreach prog : ['util']
    - e = executable(
    - 'test_oscar_' + prog, 'test_oscar_@0@.c'.format(prog),
    - link_with : [oscar_prpl],
    - dependencies : [libpurple_dep, libsoup, glib])
    -
    - test('oscar_' + prog, e)
    -endforeach
    --- a/libpurple/protocols/oscar/tests/test_oscar_util.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,42 +0,0 @@
    -#include <glib.h>
    -
    -#include "../oscar.h"
    -
    -static void
    -test_oscar_util_name_compare(void) {
    - gsize i;
    - const gchar *good[] = {
    - "test",
    - "TEST",
    - "Test",
    - "teSt",
    - " TesT",
    - "test ",
    - " T E s T "
    - };
    - const gchar *bad[] = {
    - "toast",
    - "test@example.com",
    - "test@aim.com"
    - };
    -
    - for(i = 0; i < G_N_ELEMENTS(good); i++) {
    - g_assert_cmpint(0, ==, oscar_util_name_compare("test", good[i]));
    - g_assert_cmpint(0, ==, oscar_util_name_compare(good[i], "test"));
    - }
    -
    - for(i = 0; i < G_N_ELEMENTS(bad); i++) {
    - g_assert_cmpint(0, !=, oscar_util_name_compare("test", bad[i]));
    - g_assert_cmpint(0, !=, oscar_util_name_compare(bad[i], "test"));
    - }
    -}
    -
    -gint
    -main(gint argc, gchar **argv) {
    - g_test_init(&argc, &argv, NULL);
    -
    - g_test_add_func("/oscar/util/name compare",
    - test_oscar_util_name_compare);
    -
    - return g_test_run();
    -}
    --- a/libpurple/protocols/oscar/tlv.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,815 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "oscar.h"
    -
    -static aim_tlv_t *
    -createtlv(guint16 type, guint16 length, guint8 *value)
    -{
    - aim_tlv_t *ret;
    -
    - ret = g_new(aim_tlv_t, 1);
    - ret->type = type;
    - ret->length = length;
    - ret->value = value;
    -
    - return ret;
    -}
    -
    -static void
    -freetlv(aim_tlv_t *oldtlv)
    -{
    - g_free(oldtlv->value);
    - g_free(oldtlv);
    -}
    -
    -static GSList *
    -aim_tlv_read(GSList *list, ByteStream *bs)
    -{
    - guint16 type, length;
    - aim_tlv_t *tlv;
    -
    - type = byte_stream_get16(bs);
    - length = byte_stream_get16(bs);
    -
    - if (length > byte_stream_bytes_left(bs)) {
    - aim_tlvlist_free(list);
    - return NULL;
    - }
    -
    - tlv = createtlv(type, length, NULL);
    - if (tlv->length > 0) {
    - tlv->value = byte_stream_getraw(bs, length);
    - if (!tlv->value) {
    - freetlv(tlv);
    - aim_tlvlist_free(list);
    - return NULL;
    - }
    - }
    -
    - return g_slist_prepend(list, tlv);
    -}
    -
    -/**
    - * Read a TLV chain from a buffer.
    - *
    - * Reads and parses a series of TLV patterns from a data buffer; the
    - * returned structure is manipulatable with the rest of the TLV
    - * routines. When done with a TLV chain, aim_tlvlist_free() should
    - * be called to free the dynamic substructures.
    - *
    - * TODO: There should be a flag setable here to have the tlvlist contain
    - * bstream references, so that at least the ->value portion of each
    - * element doesn't need to be malloc/memcpy'd. This could prove to be
    - * just as efficient as the in-place TLV parsing used in a couple places
    - * in libfaim.
    - *
    - * @param bs Input bstream
    - * @return Return the TLV chain read
    - */
    -GSList *aim_tlvlist_read(ByteStream *bs)
    -{
    - GSList *list = NULL;
    -
    - while (byte_stream_bytes_left(bs) > 0) {
    - list = aim_tlv_read(list, bs);
    - if (list == NULL)
    - return NULL;
    - }
    -
    - return g_slist_reverse(list);
    -}
    -
    -/**
    - * Read a TLV chain from a buffer.
    - *
    - * Reads and parses a series of TLV patterns from a data buffer; the
    - * returned structure is manipulatable with the rest of the TLV
    - * routines. When done with a TLV chain, aim_tlvlist_free() should
    - * be called to free the dynamic substructures.
    - *
    - * TODO: There should be a flag setable here to have the tlvlist contain
    - * bstream references, so that at least the ->value portion of each
    - * element doesn't need to be malloc/memcpy'd. This could prove to be
    - * just as efficient as the in-place TLV parsing used in a couple places
    - * in libfaim.
    - *
    - * @param bs Input bstream
    - * @param num The max number of TLVs that will be read, or -1 if unlimited.
    - * There are a number of places where you want to read in a tlvchain,
    - * but the chain is not at the end of the SNAC, and the chain is
    - * preceded by the number of TLVs. So you can limit that with this.
    - * @return Return the TLV chain read
    - */
    -GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
    -{
    - GSList *list = NULL;
    -
    - while ((byte_stream_bytes_left(bs) > 0) && (num != 0)) {
    - list = aim_tlv_read(list, bs);
    - if (list == NULL)
    - return NULL;
    - num--;
    - }
    -
    - return g_slist_reverse(list);
    -}
    -
    -/**
    - * Read a TLV chain from a buffer.
    - *
    - * Reads and parses a series of TLV patterns from a data buffer; the
    - * returned structure is manipulatable with the rest of the TLV
    - * routines. When done with a TLV chain, aim_tlvlist_free() should
    - * be called to free the dynamic substructures.
    - *
    - * TODO: There should be a flag setable here to have the tlvlist contain
    - * bstream references, so that at least the ->value portion of each
    - * element doesn't need to be malloc/memcpy'd. This could prove to be
    - * just as efficient as the in-place TLV parsing used in a couple places
    - * in libfaim.
    - *
    - * @param bs Input bstream
    - * @param len The max length in bytes that will be read.
    - * There are a number of places where you want to read in a tlvchain,
    - * but the chain is not at the end of the SNAC, and the chain is
    - * preceded by the length of the TLVs. So you can limit that with this.
    - * @return Return the TLV chain read
    - */
    -GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
    -{
    - GSList *list = NULL;
    -
    - while ((byte_stream_bytes_left(bs) > 0) && (len > 0)) {
    - list = aim_tlv_read(list, bs);
    - if (list == NULL)
    - return NULL;
    -
    - len -= 2 + 2 + ((aim_tlv_t *)list->data)->length;
    - }
    -
    - return g_slist_reverse(list);
    -}
    -
    -/**
    - * Duplicate a TLV chain.
    - * This is pretty self explanatory.
    - *
    - * @param orig The TLV chain you want to make a copy of.
    - * @return A newly allocated TLV chain.
    - */
    -GSList *aim_tlvlist_copy(GSList *orig)
    -{
    - GSList *new = NULL;
    - aim_tlv_t *tlv;
    -
    - while (orig != NULL) {
    - tlv = orig->data;
    - aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value);
    - orig = orig->next;
    - }
    -
    - return new;
    -}
    -
    -/*
    - * Compare two TLV lists for equality. This probably is not the most
    - * efficient way to do this.
    - *
    - * @param one One of the TLV chains to compare.
    - * @param two The other TLV chain to compare.
    - * @return Return 0 if the lists are the same, return 1 if they are different.
    - */
    -int aim_tlvlist_cmp(GSList *one, GSList *two)
    -{
    - ByteStream bs1, bs2;
    -
    - if (aim_tlvlist_size(one) != aim_tlvlist_size(two))
    - return 1;
    -
    - byte_stream_new(&bs1, aim_tlvlist_size(one));
    - byte_stream_new(&bs2, aim_tlvlist_size(two));
    -
    - aim_tlvlist_write(&bs1, &one);
    - aim_tlvlist_write(&bs2, &two);
    -
    - if (memcmp(bs1.data, bs2.data, bs1.len)) {
    - byte_stream_destroy(&bs1);
    - byte_stream_destroy(&bs2);
    - return 1;
    - }
    -
    - byte_stream_destroy(&bs1);
    - byte_stream_destroy(&bs2);
    -
    - return 0;
    -}
    -
    -/**
    - * Free a TLV chain structure
    - *
    - * Walks the list of TLVs in the passed TLV chain and
    - * frees each one. Note that any references to this data
    - * should be removed before calling this.
    - *
    - * @param list Chain to be freed
    - */
    -void aim_tlvlist_free(GSList *list)
    -{
    - g_slist_free_full(list, (GDestroyNotify)freetlv);
    -}
    -
    -/**
    - * Count the number of TLVs in a chain.
    - *
    - * @param list Chain to be counted.
    - * @return The number of TLVs stored in the passed chain.
    - */
    -int aim_tlvlist_count(GSList *list)
    -{
    - return (int)g_slist_length(list);
    -}
    -
    -/**
    - * Count the number of bytes in a TLV chain.
    - *
    - * @param list Chain to be sized
    - * @return The number of bytes that would be needed to
    - * write the passed TLV chain to a data buffer.
    - */
    -size_t aim_tlvlist_size(GSList *list)
    -{
    - GSList *cur;
    - size_t size;
    -
    - if (list == NULL)
    - return 0;
    -
    - for (cur = list, size = 0; cur; cur = cur->next)
    - size += (4 + ((aim_tlv_t *)cur->data)->length);
    -
    - return size;
    -}
    -
    -/**
    - * Adds the passed string as a TLV element of the passed type
    - * to the TLV chain.
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param length Length of string to add (not including %NULL).
    - * @param value String to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
    -{
    - aim_tlv_t *tlv;
    -
    - if (list == NULL)
    - return 0;
    -
    - tlv = createtlv(type, length, NULL);
    - if (tlv->length > 0)
    - tlv->value = g_memdup(value, length);
    -
    - *list = g_slist_append(*list, tlv);
    -
    - return tlv->length;
    -}
    -
    -/**
    - * Add a one byte integer to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value)
    -{
    - guint8 v8[1];
    -
    - (void)aimutil_put8(v8, value);
    -
    - return aim_tlvlist_add_raw(list, type, 1, v8);
    -}
    -
    -/**
    - * Add a two byte integer to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value)
    -{
    - guint8 v16[2];
    -
    - (void)aimutil_put16(v16, value);
    -
    - return aim_tlvlist_add_raw(list, type, 2, v16);
    -}
    -
    -/**
    - * Add a four byte integer to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value)
    -{
    - guint8 v32[4];
    -
    - (void)aimutil_put32(v32, value);
    -
    - return aim_tlvlist_add_raw(list, type, 4, v32);
    -}
    -
    -/**
    - * Add a string to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param value Value to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value)
    -{
    - return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
    -}
    -
    -static int
    -count_caps(guint64 caps)
    -{
    - int set_bits = 0;
    - while (caps) {
    - set_bits += caps & 1;
    - caps >>= 1;
    - }
    - return set_bits;
    -}
    -
    -/**
    - * Adds a block of capability blocks to a TLV chain. The bitfield
    - * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
    - *
    - * %OSCAR_CAPABILITY_BUDDYICON Supports Buddy Icons
    - * %OSCAR_CAPABILITY_TALK Supports Voice Chat
    - * %OSCAR_CAPABILITY_IMIMAGE Supports DirectIM/IMImage
    - * %OSCAR_CAPABILITY_CHAT Supports Chat
    - * %OSCAR_CAPABILITY_GETFILE Supports Get File functions
    - * %OSCAR_CAPABILITY_SENDFILE Supports Send File functions
    - *
    - * @param list Destination chain
    - * @param type TLV type to add
    - * @param caps Bitfield of capability flags to send
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood)
    -{
    - int len;
    - ByteStream bs;
    - guint32 bs_size;
    - guint8 *data;
    -
    - if (caps == 0)
    - return 0; /* nothing there anyway */
    -
    - data = icq_get_custom_icon_data(mood);
    - bs_size = 16*(count_caps(caps) + (data != NULL ? 1 : 0));
    -
    - byte_stream_new(&bs, bs_size);
    - byte_stream_putcaps(&bs, caps);
    -
    - /* adding of custom icon GUID */
    - if (data != NULL)
    - byte_stream_putraw(&bs, data, 16);
    -
    - len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
    -
    - byte_stream_destroy(&bs);
    -
    - return len;
    -}
    -
    -/**
    - * Adds the given chatroom info to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param roomname The name of the chat.
    - * @param instance The instance.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
    -{
    - int len;
    - ByteStream bs;
    -
    - byte_stream_new(&bs, 2 + 1 + strlen(roomname) + 2);
    -
    - byte_stream_put16(&bs, exchange);
    - byte_stream_put8(&bs, strlen(roomname));
    - byte_stream_putstr(&bs, roomname);
    - byte_stream_put16(&bs, instance);
    -
    - len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
    -
    - byte_stream_destroy(&bs);
    -
    - return len;
    -}
    -
    -/**
    - * Adds a TLV with a zero length to a TLV chain.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @return The size of the value added.
    - */
    -int aim_tlvlist_add_noval(GSList **list, const guint16 type)
    -{
    - return aim_tlvlist_add_raw(list, type, 0, NULL);
    -}
    -
    -/*
    - * Note that the inner TLV chain will not be modifiable as a tlvchain once
    - * it is written using this. Or rather, it can be, but updates won't be
    - * made to this.
    - *
    - * TODO: Should probably support sublists for real.
    - *
    - * This is so neat.
    - *
    - * @param list Destination chain.
    - * @param type TLV type to add.
    - * @param t1 The TLV chain you want to write.
    - * @return The number of bytes written to the destination TLV chain.
    - * 0 is returned if there was an error or if the destination
    - * TLV chain has length 0.
    - */
    -int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tlvlist)
    -{
    - int buflen;
    - ByteStream bs;
    -
    - buflen = aim_tlvlist_size(*tlvlist);
    -
    - if (buflen <= 0)
    - return 0;
    -
    - byte_stream_new(&bs, buflen);
    -
    - aim_tlvlist_write(&bs, tlvlist);
    -
    - aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
    -
    - byte_stream_destroy(&bs);
    -
    - return buflen;
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param length Length of string to add (not including %NULL).
    - * @param value String to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
    -{
    - GSList *cur;
    - aim_tlv_t *tlv;
    -
    - if (list == NULL)
    - return 0;
    -
    - for (cur = *list; cur != NULL; cur = cur->next)
    - {
    - tlv = cur->data;
    - if (tlv->type == type)
    - break;
    - }
    -
    - if (cur == NULL)
    - /* TLV does not exist, so add a new one */
    - return aim_tlvlist_add_raw(list, type, length, value);
    -
    - g_free(tlv->value);
    - tlv->length = length;
    - if (tlv->length > 0) {
    - tlv->value = g_memdup(value, length);
    - } else
    - tlv->value = NULL;
    -
    - return tlv->length;
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_str().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param str String to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str)
    -{
    - return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str);
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_noval(GSList **list, const guint16 type)
    -{
    - return aim_tlvlist_replace_raw(list, type, 0, NULL);
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param value 8 bit value to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value)
    -{
    - guint8 v8[1];
    -
    - (void)aimutil_put8(v8, value);
    -
    - return aim_tlvlist_replace_raw(list, type, 1, v8);
    -}
    -
    -/**
    - * Substitute a TLV of a given type with a new TLV of the same type. If
    - * you attempt to replace a TLV that does not exist, this function will
    - * just add a new TLV as if you called aim_tlvlist_add_raw().
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - * @param value 32 bit value to add.
    - * @return The length of the TLV.
    - */
    -int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value)
    -{
    - guint8 v32[4];
    -
    - (void)aimutil_put32(v32, value);
    -
    - return aim_tlvlist_replace_raw(list, type, 4, v32);
    -}
    -
    -/**
    - * Remove all TLVs of a given type. If you attempt to remove a TLV
    - * that does not exist, nothing happens.
    - *
    - * @param list Desination chain (%NULL pointer if empty).
    - * @param type TLV type.
    - */
    -void aim_tlvlist_remove(GSList **list, const guint16 type)
    -{
    - GSList *cur, *next;
    - aim_tlv_t *tlv;
    -
    - if (list == NULL || *list == NULL)
    - return;
    -
    - cur = *list;
    - while (cur != NULL)
    - {
    - tlv = cur->data;
    - next = cur->next;
    -
    - if (tlv->type == type)
    - {
    - /* Delete this TLV */
    - *list = g_slist_delete_link(*list, cur);
    - g_free(tlv->value);
    - g_free(tlv);
    - }
    -
    - cur = next;
    - }
    -}
    -
    -/**
    - * Write a TLV chain into a data buffer.
    - *
    - * Copies a TLV chain into a raw data buffer, writing only the number
    - * of bytes specified. This operation does not free the chain;
    - * aim_tlvlist_free() must still be called to free up the memory used
    - * by the chain structures.
    - *
    - * TODO: Clean this up, make better use of bstreams
    - *
    - * @param bs Input bstream
    - * @param list Source TLV chain
    - * @return Return 0 if the destination bstream is too small.
    - */
    -int aim_tlvlist_write(ByteStream *bs, GSList **list)
    -{
    - size_t goodbuflen;
    - GSList *cur;
    - aim_tlv_t *tlv;
    -
    - /* do an initial run to test total length */
    - goodbuflen = aim_tlvlist_size(*list);
    -
    - if (goodbuflen > byte_stream_bytes_left(bs))
    - return 0; /* not enough buffer */
    -
    - /* do the real write-out */
    - for (cur = *list; cur; cur = cur->next) {
    - tlv = cur->data;
    - byte_stream_put16(bs, tlv->type);
    - byte_stream_put16(bs, tlv->length);
    - if (tlv->length > 0)
    - byte_stream_putraw(bs, tlv->value, tlv->length);
    - }
    -
    - return 1; /* TODO: This is a nonsensical return */
    -}
    -
    -
    -/**
    - * Grab the Nth TLV of type type in the TLV list list.
    - *
    - * Returns a pointer to an aim_tlv_t of the specified type;
    - * %NULL on error. The @nth parameter is specified starting at %1.
    - * In most cases, there will be no more than one TLV of any type
    - * in a chain.
    - *
    - * @param list Source chain.
    - * @param type Requested TLV type.
    - * @param nth Index of TLV of type to get.
    - * @return The TLV you were looking for, or NULL if one could not be found.
    - */
    -aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth)
    -{
    - GSList *cur;
    - aim_tlv_t *tlv;
    - int i;
    -
    - for (cur = list, i = 0; cur != NULL; cur = cur->next) {
    - tlv = cur->data;
    - if (tlv->type == type)
    - i++;
    - if (i >= nth)
    - return tlv;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * Get the length of the data of the nth TLV in the given TLV chain.
    - *
    - * @param list Source chain.
    - * @param type Requested TLV type.
    - * @param nth Index of TLV of type to get.
    - * @return The length of the data in this TLV, or -1 if the TLV could not be
    - * found. Unless -1 is returned, this value will be 2 bytes.
    - */
    -int aim_tlv_getlength(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return -1;
    -
    - return tlv->length;
    -}
    -
    -char *
    -aim_tlv_getvalue_as_string(aim_tlv_t *tlv)
    -{
    - char *ret;
    -
    - ret = g_malloc(tlv->length + 1);
    - memcpy(ret, tlv->value, tlv->length);
    - ret[tlv->length] = '\0';
    -
    - return ret;
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as a string.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value of the TLV you were looking for, or NULL if one could
    - * not be found. This is a dynamic buffer and must be freed by the
    - * caller.
    - */
    -char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return NULL;
    -
    - return aim_tlv_getvalue_as_string(tlv);
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as an 8bit
    - * integer.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value the TLV you were looking for, or 0 if one could
    - * not be found.
    - */
    -guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return 0; /* erm */
    -
    - return aimutil_get8(tlv->value);
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as a 16bit
    - * integer.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value the TLV you were looking for, or 0 if one could
    - * not be found.
    - */
    -guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return 0; /* erm */
    -
    - return aimutil_get16(tlv->value);
    -}
    -
    -/**
    - * Retrieve the data from the nth TLV in the given TLV chain as a 32bit
    - * integer.
    - *
    - * @param list Source TLV chain.
    - * @param type TLV type to search for.
    - * @param nth Index of TLV to return.
    - * @return The value the TLV you were looking for, or 0 if one could
    - * not be found.
    - */
    -guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth)
    -{
    - aim_tlv_t *tlv;
    -
    - tlv = aim_tlv_gettlv(list, type, nth);
    - if (tlv == NULL)
    - return 0; /* erm */
    -
    - return aimutil_get32(tlv->value);
    -}
    --- a/libpurple/protocols/oscar/userinfo.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,553 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * Displaying various information about buddies.
    - */
    -
    -#include "encoding.h"
    -#include "oscar.h"
    -
    -static gchar *
    -oscar_caps_to_string(guint64 caps)
    -{
    - GString *str;
    - const gchar *tmp;
    - guint64 bit = 1;
    -
    - if (!caps) {
    - return NULL;
    - }
    -
    - str = g_string_new("");
    - while (bit <= OSCAR_CAPABILITY_LAST) {
    - if (bit & caps) {
    - switch (bit) {
    - case OSCAR_CAPABILITY_BUDDYICON:
    - tmp = _("Buddy Icon");
    - break;
    - case OSCAR_CAPABILITY_TALK:
    - tmp = _("Voice");
    - break;
    - case OSCAR_CAPABILITY_DIRECTIM:
    - tmp = _("AIM Direct IM");
    - break;
    - case OSCAR_CAPABILITY_CHAT:
    - tmp = _("Chat");
    - break;
    - case OSCAR_CAPABILITY_GETFILE:
    - tmp = _("Get File");
    - break;
    - case OSCAR_CAPABILITY_SENDFILE:
    - tmp = _("Send File");
    - break;
    - case OSCAR_CAPABILITY_GAMES:
    - case OSCAR_CAPABILITY_GAMES2:
    - tmp = _("Games");
    - break;
    - case OSCAR_CAPABILITY_XTRAZ:
    - case OSCAR_CAPABILITY_NEWCAPS:
    - tmp = _("ICQ Xtraz");
    - break;
    - case OSCAR_CAPABILITY_ADDINS:
    - tmp = _("Add-Ins");
    - break;
    - case OSCAR_CAPABILITY_SENDBUDDYLIST:
    - tmp = _("Send Buddy List");
    - break;
    - case OSCAR_CAPABILITY_ICQ_DIRECT:
    - tmp = _("ICQ Direct Connect");
    - break;
    - case OSCAR_CAPABILITY_APINFO:
    - tmp = _("AP User");
    - break;
    - case OSCAR_CAPABILITY_ICQRTF:
    - tmp = _("ICQ RTF");
    - break;
    - case OSCAR_CAPABILITY_EMPTY:
    - tmp = _("Nihilist");
    - break;
    - case OSCAR_CAPABILITY_ICQSERVERRELAY:
    - tmp = _("ICQ Server Relay");
    - break;
    - case OSCAR_CAPABILITY_UNICODEOLD:
    - tmp = _("Old ICQ UTF8");
    - break;
    - case OSCAR_CAPABILITY_TRILLIANCRYPT:
    - tmp = _("Trillian Encryption");
    - break;
    - case OSCAR_CAPABILITY_UNICODE:
    - tmp = _("ICQ UTF8");
    - break;
    - case OSCAR_CAPABILITY_HIPTOP:
    - tmp = _("Hiptop");
    - break;
    - case OSCAR_CAPABILITY_SECUREIM:
    - tmp = _("Security Enabled");
    - break;
    - case OSCAR_CAPABILITY_VIDEO:
    - tmp = _("Video Chat");
    - break;
    - /* Not actually sure about this one... WinAIM doesn't show anything */
    - case OSCAR_CAPABILITY_ICHATAV:
    - tmp = _("iChat AV");
    - break;
    - case OSCAR_CAPABILITY_LIVEVIDEO:
    - tmp = _("Live Video");
    - break;
    - case OSCAR_CAPABILITY_CAMERA:
    - tmp = _("Camera");
    - break;
    - case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
    - tmp = _("Screen Sharing");
    - break;
    - default:
    - tmp = NULL;
    - break;
    - }
    - if (tmp)
    - g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
    - }
    - bit <<= 1;
    - }
    -
    - return g_string_free(str, FALSE);
    -}
    -
    -static void
    -oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
    - const char *name, const char *value)
    -{
    - gchar *utf8;
    -
    - if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
    - /* TODO: Check whether it's correct to call add_pair_html,
    - or if we should be using add_pair_plaintext. Will
    - need to check callers of this function. */
    - purple_notify_user_info_add_pair_html(user_info, name, utf8);
    - g_free(utf8);
    - }
    -}
    -
    -static void
    -oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
    - const char *name, const char *value, const char *url_prefix)
    -{
    - gchar *utf8;
    -
    - if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
    - gchar *tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>", url_prefix, utf8, utf8);
    - /* TODO: Check whether it's correct to call add_pair_html,
    - or if we should be using add_pair_plaintext. Will
    - need to check callers of this function. */
    - purple_notify_user_info_add_pair_html(user_info, name, tmp);
    - g_free(utf8);
    - g_free(tmp);
    - }
    -}
    -
    -/**
    - * @brief Append the status information to a user_info struct
    - *
    - * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
    - *
    - * @param gc The PurpleConnection
    - * @param user_info A PurpleNotifyUserInfo object to which status information will be added
    - * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
    - * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
    - * @param use_html_status If TRUE, prefer HTML-formatted away message over plaintext available message.
    - */
    -void
    -oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status)
    -{
    - PurpleAccount *account = purple_connection_get_account(gc);
    - OscarData *od;
    - PurplePresence *presence = NULL;
    - PurpleStatus *status = NULL;
    - gchar *message = NULL, *itmsurl = NULL, *tmp;
    - gboolean escaping_needed = TRUE;
    -
    - od = purple_connection_get_protocol_data(gc);
    -
    - if (b == NULL && userinfo == NULL)
    - return;
    -
    - if (b == NULL)
    - b = purple_blist_find_buddy(purple_connection_get_account(gc), userinfo->bn);
    - else
    - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
    -
    - if (b) {
    - presence = purple_buddy_get_presence(b);
    - status = purple_presence_get_active_status(presence);
    - }
    -
    - /* If we have both b and userinfo we favor userinfo, because if we're
    - viewing someone's profile then we want the HTML away message, and
    - the "message" attribute of the status contains only the plaintext
    - message. */
    - if (userinfo) {
    - if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) {
    - /* Away message */
    - message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len);
    - escaping_needed = FALSE;
    - } else {
    - /*
    - * Available message or non-HTML away message (because that's
    - * all we have right now.
    - */
    - if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
    - message = oscar_encoding_to_utf8(userinfo->status_encoding, userinfo->status, userinfo->status_len);
    - }
    -#if defined (_WIN32) || defined (__APPLE__)
    - if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
    - itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding, userinfo->itmsurl, userinfo->itmsurl_len);
    - }
    -#endif
    - }
    - } else {
    - message = g_strdup(purple_status_get_attr_string(status, "message"));
    - itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
    - }
    -
    - if (message) {
    - tmp = oscar_util_format_string(message, purple_account_get_username(account));
    - g_free(message);
    - message = tmp;
    - if (escaping_needed) {
    - tmp = purple_markup_escape_text(message, -1);
    - g_free(message);
    - message = tmp;
    - }
    - }
    -
    - if (use_html_status && itmsurl) {
    - tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message);
    - g_free(message);
    - message = tmp;
    - }
    -
    - if (b) {
    - if (purple_presence_is_online(presence)) {
    - gboolean is_away = ((status && !purple_status_is_available(status)) || (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
    - if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
    - /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
    - * If the status name and the message are the same, only show one. */
    - const char *status_name = purple_status_get_name(status);
    - if (status_name && message && purple_strequal(status_name, message))
    - status_name = NULL;
    -
    - tmp = g_strdup_printf("%s%s%s",
    - status_name ? status_name : "",
    - ((status_name && message) && *message) ? ": " : "",
    - (message && *message) ? message : "");
    - g_free(message);
    - message = tmp;
    - }
    -
    - } else if (aim_ssi_waitingforauth(&od->ssi.local,
    - aim_ssi_itemlist_findparentname(&od->ssi.local, purple_buddy_get_name(b)),
    - purple_buddy_get_name(b)))
    - {
    - /* Note if an offline buddy is not authorized */
    - tmp = g_strdup_printf("%s%s%s",
    - _("Not Authorized"),
    - (message && *message) ? ": " : "",
    - (message && *message) ? message : "");
    - g_free(message);
    - message = tmp;
    - } else {
    - g_free(message);
    - message = g_strdup(_("Offline"));
    - }
    - }
    -
    - if (presence) {
    - const char *mood;
    - const char *comment;
    - char *description;
    - status = purple_presence_get_status(presence, "mood");
    - mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME));
    - if (mood) {
    - comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
    - if (comment) {
    - char *escaped_comment = purple_markup_escape_text(comment, -1);
    - description = g_strdup_printf("%s (%s)", _(mood), escaped_comment);
    - g_free(escaped_comment);
    - } else {
    - description = g_strdup(_(mood));
    - }
    - purple_notify_user_info_add_pair_html(user_info, _("Mood"), description);
    - g_free(description);
    - }
    - }
    -
    - purple_notify_user_info_add_pair_html(user_info, _("Status"), message);
    - g_free(message);
    - g_free(itmsurl);
    -}
    -
    -void
    -oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
    -{
    - OscarData *od;
    - PurpleAccount *account;
    - PurpleGroup *g = NULL;
    - struct buddyinfo *bi = NULL;
    - char *tmp;
    - const char *bname = NULL, *gname = NULL;
    -
    - od = purple_connection_get_protocol_data(gc);
    - account = purple_connection_get_account(gc);
    -
    - if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
    - return;
    -
    - if (userinfo == NULL)
    - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
    -
    - if (b == NULL)
    - b = purple_blist_find_buddy(account, userinfo->bn);
    -
    - if (b != NULL) {
    - bname = purple_buddy_get_name(b);
    - g = purple_buddy_get_group(b);
    - gname = purple_group_get_name(g);
    - }
    -
    - if (userinfo != NULL)
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
    -
    - if ((bi != NULL) && (bi->ipaddr != 0)) {
    - char tmp2[40];
    - sprintf(tmp2, "%u.%u.%u.%u",
    - 0xFF & ((bi->ipaddr & 0xff000000) >> 24),
    - 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
    - 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
    - 0xFF & (bi->ipaddr & 0x000000ff));
    - purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tmp2);
    - }
    -
    - if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
    - char tmp2[12];
    - sprintf(tmp2, "%d", (int)(userinfo->warnlevel/10.0 + .5));
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Warning Level"), tmp2);
    - }
    -
    - if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
    - tmp = aim_ssi_getcomment(&od->ssi.local, gname, bname);
    - if (tmp != NULL) {
    - char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
    - g_free(tmp);
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
    - g_free(tmp2);
    - }
    - }
    -}
    -
    -void
    -oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy)
    -{
    - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
    - gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason));
    - purple_notify_user_info_add_pair_plaintext(user_info, NULL, buf);
    - purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    - if (!purple_conversation_present_error(buddy,
    - purple_connection_get_account(od->gc), buf))
    - {
    - purple_notify_error(od->gc, NULL, buf, NULL,
    - purple_request_cpar_from_connection(od->gc));
    - }
    - g_free(buf);
    -}
    -
    -void
    -oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleBuddy *buddy;
    - struct buddyinfo *bi;
    - gchar who[16];
    - PurpleNotifyUserInfo *user_info;
    -
    - if (!info->uin)
    - return;
    -
    - user_info = purple_notify_user_info_new();
    -
    - g_snprintf(who, sizeof(who), "%u", info->uin);
    - buddy = purple_blist_find_buddy(account, who);
    - if (buddy != NULL)
    - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
    - else
    - bi = NULL;
    -
    - purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
    - if ((bi != NULL) && (bi->ipaddr != 0)) {
    - char tstr[40];
    - sprintf(tstr, "%u.%u.%u.%u",
    - 0xFF & ((bi->ipaddr & 0xff000000) >> 24),
    - 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
    - 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
    - 0xFF & (bi->ipaddr & 0x000000ff));
    - purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tstr);
    - }
    - oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email, "mailto:");
    - if (info->numaddresses && info->email2) {
    - int i;
    - for (i = 0; i < info->numaddresses; i++) {
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email2[i], "mailto:");
    - }
    - }
    - oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
    -
    - if (info->gender != 0)
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
    -
    - if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
    - /* Initialize the struct properly or strftime() will crash
    - * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
    - time_t t = time(NULL);
    - struct tm *tm = localtime(&t);
    -
    - tm->tm_mday = (int)info->birthday;
    - tm->tm_mon = (int)info->birthmonth - 1;
    - tm->tm_year = (int)info->birthyear - 1900;
    -
    - /* Ignore dst setting of today to avoid timezone shift between
    - * dates in summer and winter time. */
    - tm->tm_isdst = -1;
    -
    - /* To be 100% sure that the fields are re-normalized.
    - * If you're sure strftime() ALWAYS does this EVERYWHERE,
    - * feel free to remove it. --rlaager */
    - mktime(tm);
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
    - }
    - if ((info->age > 0) && (info->age < 255)) {
    - char age[5];
    - snprintf(age, sizeof(age), "%hhd", info->age);
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Age"), age);
    - }
    - /* TODO: Is it correct to pass info->email here...? */
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Personal Web Page"), info->email, "");
    - if (buddy != NULL)
    - oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* use_html_status */ TRUE);
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
    - purple_notify_user_info_add_section_break(user_info);
    -
    - if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
    - purple_notify_user_info_add_section_header(user_info, _("Home Address"));
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
    - oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
    - oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
    - }
    - if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
    - purple_notify_user_info_add_section_header(user_info, _("Work Address"));
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
    - oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
    - oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
    - }
    - if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
    - purple_notify_user_info_add_section_header(user_info, _("Work Information"));
    -
    - oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
    - oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
    - /* TODO: Is it correct to pass info->email here...? */
    - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Web Page"), info->email, "");
    - }
    -
    - purple_notify_userinfo(gc, who, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -}
    -
    -void
    -oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
    -{
    - PurpleConnection *gc = od->gc;
    - PurpleAccount *account = purple_connection_get_account(gc);
    - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
    - gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL;
    -
    - oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* use_html_status */ TRUE);
    -
    - if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) {
    - tmp = purple_str_seconds_to_string(userinfo->idletime*60);
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp);
    - g_free(tmp);
    - }
    -
    - oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
    -
    - if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) {
    - /* An SMS contact is always online; its Online Since value is not useful */
    - time_t t = userinfo->onlinesince;
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
    - }
    -
    - if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
    - time_t t = userinfo->membersince;
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
    - }
    -
    - if (userinfo->capabilities != 0) {
    - tmp = oscar_caps_to_string(userinfo->capabilities);
    - if (tmp && *tmp)
    - purple_notify_user_info_add_pair_plaintext(user_info, _("Capabilities"), tmp);
    - g_free(tmp);
    - }
    -
    - /* Info */
    - if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
    - info_utf8 = oscar_encoding_to_utf8(userinfo->info_encoding, userinfo->info, userinfo->info_len);
    - tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account));
    - purple_notify_user_info_add_section_break(user_info);
    - if (tmp && *tmp) {
    - /* TODO: Check whether it's correct to call add_pair_html,
    - or if we should be using add_pair_plaintext */
    - purple_notify_user_info_add_pair_html(user_info, _("Profile"), tmp);
    - }
    - g_free(tmp);
    - g_free(info_utf8);
    - }
    -
    - purple_notify_user_info_add_section_break(user_info);
    - base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com";
    - tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>",
    - base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile"));
    - purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
    - g_free(tmp);
    -
    - purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -}
    --- a/libpurple/protocols/oscar/util.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,322 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -/*
    - * A little bit of this
    - * A little bit of that
    - * It started with a kiss
    - * Now we're up to bat
    - */
    -
    -#include "oscar.h"
    -
    -#include "core.h"
    -
    -#include <ctype.h>
    -
    -static const char * const msgerrreason[] = {
    - N_("Invalid error"),
    - N_("Invalid SNAC"),
    - N_("Server rate limit exceeded"),
    - N_("Client rate limit exceeded"),
    - N_("Not logged in"),
    - N_("Service unavailable"),
    - N_("Service not defined"),
    - N_("Obsolete SNAC"),
    - N_("Not supported by host"),
    - N_("Not supported by client"),
    - N_("Refused by client"),
    - N_("Reply too big"),
    - N_("Responses lost"),
    - N_("Request denied"),
    - N_("Busted SNAC payload"),
    - N_("Insufficient rights"),
    - N_("In local permit/deny"),
    - N_("Warning level too high (sender)"),
    - N_("Warning level too high (receiver)"),
    - N_("User temporarily unavailable"),
    - N_("No match"),
    - N_("List overflow"),
    - N_("Request ambiguous"),
    - N_("Queue full"),
    - N_("Not while on AOL")
    -};
    -static const gsize msgerrreasonlen = G_N_ELEMENTS(msgerrreason);
    -
    -const char *oscar_get_msgerr_reason(size_t reason)
    -{
    - return (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason");
    -}
    -
    -int oscar_get_ui_info_int(const char *str, int default_value)
    -{
    - GHashTable *ui_info;
    -
    - ui_info = purple_core_get_ui_info();
    - if (ui_info != NULL) {
    - gpointer value;
    - if (g_hash_table_lookup_extended(ui_info, str, NULL, &value))
    - return GPOINTER_TO_INT(value);
    - }
    -
    - return default_value;
    -}
    -
    -const char *oscar_get_ui_info_string(const char *str, const char *default_value)
    -{
    - GHashTable *ui_info;
    - const char *value = NULL;
    -
    - ui_info = purple_core_get_ui_info();
    - if (ui_info != NULL)
    - value = g_hash_table_lookup(ui_info, str);
    - if (value == NULL)
    - value = default_value;
    -
    - return value;
    -}
    -
    -gchar *oscar_get_clientstring(void)
    -{
    - const char *name, *version;
    -
    - name = oscar_get_ui_info_string("name", "Purple");
    - version = oscar_get_ui_info_string("version", VERSION);
    -
    - return g_strdup_printf("%s/%s", name, version);;
    -}
    -
    -/**
    - * Calculate the checksum of a given icon.
    - */
    -guint16
    -aimutil_iconsum(const guint8 *buf, int buflen)
    -{
    - guint32 sum;
    - int i;
    -
    - for (i=0, sum=0; i+1<buflen; i+=2)
    - sum += (buf[i+1] << 8) + buf[i];
    - if (i < buflen)
    - sum += buf[i];
    - sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
    -
    - return sum;
    -}
    -
    -/**
    - * Check if the given name is a valid AIM username.
    - * Example: BobDole
    - * Example: Henry_Ford@mac.com
    - * Example: 1KrazyKat@example.com
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -static gboolean
    -oscar_util_valid_name_aim(const char *name)
    -{
    - int i;
    -
    - if (purple_email_is_valid(name))
    - return TRUE;
    -
    - /* Normal AIM usernames can't start with a number, period or underscore */
    - if (isalnum(name[0]) == 0)
    - return FALSE;
    -
    - for (i = 0; name[i] != '\0'; i++) {
    - if (!isalnum(name[i]) && name[i] != ' ' && name[i] != '.' && name[i] != '_')
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Check if the given name is a valid ICQ username.
    - * Example: 1234567
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -gboolean
    -oscar_util_valid_name_icq(const char *name)
    -{
    - int i;
    -
    - for (i = 0; name[i] != '\0'; i++) {
    - if (!isdigit(name[i]))
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Check if the given name is a valid SMS username.
    - * Example: +19195551234
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -gboolean
    -oscar_util_valid_name_sms(const char *name)
    -{
    - int i;
    -
    - if (name[0] != '+')
    - return FALSE;
    -
    - for (i = 1; name[i] != '\0'; i++) {
    - if (!isdigit(name[i]))
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -/**
    - * Check if the given name is a valid oscar username.
    - *
    - * @return TRUE if the name is valid, FALSE if not.
    - */
    -gboolean
    -oscar_util_valid_name(const char *name)
    -{
    - if ((name == NULL) || (*name == '\0'))
    - return FALSE;
    -
    - return oscar_util_valid_name_icq(name)
    - || oscar_util_valid_name_sms(name)
    - || oscar_util_valid_name_aim(name);
    -}
    -
    -/**
    - * This takes two names and compares them using the rules
    - * on usernames for AIM/AOL. Mainly, this means case and space
    - * insensitivity (all case differences and spacing differences are
    - * ignored, with the exception that usernames can not start with
    - * a space).
    - *
    - * @return 0 if equal, non-0 if different
    - */
    -/* TODO: Do something different for email addresses. */
    -int
    -oscar_util_name_compare(const char *name1, const char *name2)
    -{
    -
    - if ((name1 == NULL) || (name2 == NULL))
    - return -1;
    -
    - do {
    - while (*name2 == ' ')
    - name2++;
    - while (*name1 == ' ')
    - name1++;
    - if (toupper(*name1) != toupper(*name2))
    - return 1;
    - } while ((*name1 != '\0') && name1++ && name2++);
    -
    - return 0;
    -}
    -
    -/**
    - * Looks for %n, %d, or %t in a string, and replaces them with the
    - * specified name, date, and time, respectively.
    - *
    - * @param str The string that may contain the special variables.
    - * @param name The sender name.
    - *
    - * @return A newly allocated string where the special variables are
    - * expanded. This should be g_free'd by the caller.
    - */
    -gchar *
    -oscar_util_format_string(const char *str, const char *name)
    -{
    - char *c;
    - GString *cpy;
    - time_t t;
    - struct tm *tme;
    -
    - g_return_val_if_fail(str != NULL, NULL);
    - g_return_val_if_fail(name != NULL, NULL);
    -
    - /* Create an empty GString that is hopefully big enough for most messages */
    - cpy = g_string_sized_new(1024);
    -
    - t = time(NULL);
    - tme = localtime(&t);
    -
    - c = (char *)str;
    - while (*c) {
    - switch (*c) {
    - case '%':
    - if (*(c + 1)) {
    - switch (*(c + 1)) {
    - case 'n':
    - /* append name */
    - g_string_append(cpy, name);
    - c++;
    - break;
    - case 'd':
    - /* append date */
    - g_string_append(cpy, purple_date_format_short(tme));
    - c++;
    - break;
    - case 't':
    - /* append time */
    - g_string_append(cpy, purple_time_format(tme));
    - c++;
    - break;
    - default:
    - g_string_append_c(cpy, *c);
    - }
    - } else {
    - g_string_append_c(cpy, *c);
    - }
    - break;
    - default:
    - g_string_append_c(cpy, *c);
    - }
    - c++;
    - }
    -
    - return g_string_free(cpy, FALSE);
    -}
    -
    -gchar *
    -oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message)
    -{
    - GSList *cur;
    - GString *result;
    - if (!buddies) {
    - return g_strdup_printf("<i>%s</i>", no_buddies_message);
    - }
    - result = g_string_new("");
    - for (cur = buddies; cur != NULL; cur = cur->next) {
    - PurpleBuddy *buddy = cur->data;
    - const gchar *bname = purple_buddy_get_name(buddy);
    - const gchar *alias = purple_buddy_get_alias_only(buddy);
    - g_string_append(result, bname);
    - if (alias) {
    - g_string_append_printf(result, " (%s)", alias);
    - }
    - g_string_append(result, "<br>");
    - }
    - return g_string_free(result, FALSE);
    -}
    --- a/libpurple/protocols/oscar/visibility.c Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,139 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#include "visibility.h"
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to appear online to the chosen user even when your status is set to
    - Invisible. */
    -#define APPEAR_ONLINE N_("Appear Online")
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to appear offline to the chosen user when your status is set to
    - Invisible (this is the default). */
    -#define DONT_APPEAR_ONLINE N_("Don't Appear Online")
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to always appear offline to the chosen user (even when your status
    - isn't Invisible). */
    -#define APPEAR_OFFLINE N_("Appear Offline")
    -
    -/* Translators: This string is a menu option that, if selected, will cause
    - you to appear offline to the chosen user if you are invisible, and
    - appear online to the chosen user if you are not invisible (this is the
    - default). */
    -#define DONT_APPEAR_OFFLINE N_("Don't Appear Offline")
    -
    -static guint16
    -get_buddy_list_type(OscarData *od)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - return purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE) ? AIM_SSI_TYPE_PERMIT : AIM_SSI_TYPE_DENY;
    -}
    -
    -static gboolean
    -is_buddy_on_list(OscarData *od, const char *bname)
    -{
    - return aim_ssi_itemlist_finditem(&od->ssi.local, NULL, bname, get_buddy_list_type(od)) != NULL;
    -}
    -
    -static void
    -visibility_cb(PurpleBlistNode *node, gpointer whatever)
    -{
    - PurpleBuddy *buddy = PURPLE_BUDDY(node);
    - const char* bname = purple_buddy_get_name(buddy);
    - OscarData *od = purple_connection_get_protocol_data(purple_account_get_connection(purple_buddy_get_account(buddy)));
    - guint16 list_type = get_buddy_list_type(od);
    -
    - if (!is_buddy_on_list(od, bname)) {
    - aim_ssi_add_to_private_list(od, bname, list_type);
    - } else {
    - aim_ssi_del_from_private_list(od, bname, list_type);
    - }
    -}
    -
    -PurpleActionMenu *
    -create_visibility_menu_item(OscarData *od, const char *bname)
    -{
    - PurpleAccount *account = purple_connection_get_account(od->gc);
    - gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
    - gboolean on_list = is_buddy_on_list(od, bname);
    - const gchar *label;
    -
    - if (invisible) {
    - label = on_list ? _(DONT_APPEAR_ONLINE) : _(APPEAR_ONLINE);
    - } else {
    - label = on_list ? _(DONT_APPEAR_OFFLINE) : _(APPEAR_OFFLINE);
    - }
    - return purple_action_menu_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL);
    -}
    -
    -static void
    -show_private_list(PurpleProtocolAction *action, guint16 list_type, const gchar *title, const gchar *list_description, const gchar *menu_action_name)
    -{
    - PurpleConnection *gc = action->connection;
    - OscarData *od = purple_connection_get_protocol_data(gc);
    - PurpleAccount *account = purple_connection_get_account(gc);
    - GSList *buddies, *filtered_buddies, *cur;
    - gchar *text, *secondary;
    -
    - buddies = purple_blist_find_buddies(account, NULL);
    - filtered_buddies = NULL;
    - for (cur = buddies; cur != NULL; cur = cur->next) {
    - PurpleBuddy *buddy;
    - const gchar *bname;
    -
    - buddy = cur->data;
    - bname = purple_buddy_get_name(buddy);
    - if (aim_ssi_itemlist_finditem(&od->ssi.local, NULL, bname, list_type)) {
    - filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
    - }
    - }
    -
    - g_slist_free(buddies);
    -
    - filtered_buddies = g_slist_reverse(filtered_buddies);
    - text = oscar_format_buddies(filtered_buddies, _("you have no buddies on this list"));
    - g_slist_free(filtered_buddies);
    -
    - secondary = g_strdup_printf(_("You can add a buddy to this list "
    - "by right-clicking on them and "
    - "selecting \"%s\""), menu_action_name);
    - purple_notify_formatted(gc, title, list_description, secondary, text, NULL, NULL);
    - g_free(secondary);
    - g_free(text);
    -}
    -
    -void
    -oscar_show_visible_list(PurpleProtocolAction *action)
    -{
    - show_private_list(action, AIM_SSI_TYPE_PERMIT, _("Visible List"),
    - _("These buddies can see your status even when you're "
    - "invisible."),
    - _(APPEAR_ONLINE));
    -}
    -
    -void
    -oscar_show_invisible_list(PurpleProtocolAction *action)
    -{
    - show_private_list(action, AIM_SSI_TYPE_DENY, _("Invisible List"),
    - _("These buddies will always see you as offline."),
    - _(APPEAR_OFFLINE));
    -}
    --- a/libpurple/protocols/oscar/visibility.h Sun Oct 20 00:24:28 2019 +0300
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,32 +0,0 @@
    -/*
    - * Purple's oscar protocol plugin
    - * This file is the legal property of its developers.
    - * Please see the AUTHORS file distributed alongside this file.
    - *
    - * This library is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU Lesser General Public
    - * License as published by the Free Software Foundation; either
    - * version 2 of the License, or (at your option) any later version.
    - *
    - * This library is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this library; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    -*/
    -
    -#ifndef PURPLE_OSCAR_VISIBILITY_H
    -#define PURPLE_OSCAR_VISIBILITY_H
    -
    -#include "oscar.h"
    -#include "plugins.h"
    -#include "action.h"
    -
    -PurpleActionMenu * create_visibility_menu_item(OscarData *od, const char *bname);
    -void oscar_show_visible_list(PurpleProtocolAction *action);
    -void oscar_show_invisible_list(PurpleProtocolAction *action);
    -
    -#endif /* PURPLE_OSCAR_VISIBILITY_H */
    --- a/meson.build Sun Oct 20 00:24:28 2019 +0300
    +++ b/meson.build Fri Oct 25 07:52:44 2019 +0000
    @@ -481,7 +481,7 @@
    DEFAULT_PRPLS = ['bonjour', 'facebook', 'gg', 'irc', 'jabber', 'novell',
    - 'null', 'oscar', 'sametime', 'silc', 'simple', 'zephyr']
    + 'null', 'sametime', 'silc', 'simple', 'zephyr']
    ALL_PRPLS = DEFAULT_PRPLS + ['null']
    dynamic_list = get_option('dynamic-prpls').split(',')
    @@ -514,7 +514,6 @@
    DYNAMIC_JABBER = DYNAMIC_PRPLS.contains('jabber')
    DYNAMIC_NOVELL = DYNAMIC_PRPLS.contains('novell')
    DYNAMIC_NULL = DYNAMIC_PRPLS.contains('null')
    -DYNAMIC_OSCAR = DYNAMIC_PRPLS.contains('oscar') or DYNAMIC_PRPLS.contains('aim') or DYNAMIC_PRPLS.contains('icq')
    DYNAMIC_SAMETIME = DYNAMIC_PRPLS.contains('sametime')
    DYNAMIC_SILC = DYNAMIC_PRPLS.contains('silc')
    DYNAMIC_SIMPLE = DYNAMIC_PRPLS.contains('simple')
    --- a/po/POTFILES.in Sun Oct 20 00:24:28 2019 +0300
    +++ b/po/POTFILES.in Fri Oct 25 07:52:44 2019 +0000
    @@ -26,7 +26,6 @@
    finch/plugins/grouping.c
    finch/plugins/lastlog.c
    libpurple/account.c
    -libpurple/accountopt.c
    libpurple/accounts.c
    libpurple/action.c
    libpurple/attention.c
    @@ -49,7 +48,6 @@
    libpurple/eventloop.c
    libpurple/example/nullclient.c
    libpurple/group.c
    -libpurple/http.c
    libpurple/idle.c
    libpurple/image.c
    libpurple/image-store.c
    @@ -79,7 +77,7 @@
    libpurple/plugins/idle.c
    libpurple/plugins/joinpart.c
    libpurple/plugins/keyrings/internalkeyring.c
    -libpurple/plugins/keyrings/kwallet.cpp
    +libpurple/plugins/keyrings/kwallet/purplekwallet.cpp
    libpurple/plugins/keyrings/secretservice.c
    libpurple/plugins/keyrings/wincred.c
    libpurple/plugins/log_reader.c
    @@ -208,45 +206,6 @@
    libpurple/protocols/novell/nmuserrecord.c
    libpurple/protocols/novell/novell.c
    libpurple/protocols/null/nullprpl.c
    -libpurple/protocols/oscar/aim.c
    -libpurple/protocols/oscar/authorization.c
    -libpurple/protocols/oscar/bstream.c
    -libpurple/protocols/oscar/clientlogin.c
    -libpurple/protocols/oscar/encoding.c
    -libpurple/protocols/oscar/family_admin.c
    -libpurple/protocols/oscar/family_alert.c
    -libpurple/protocols/oscar/family_auth.c
    -libpurple/protocols/oscar/family_bart.c
    -libpurple/protocols/oscar/family_bos.c
    -libpurple/protocols/oscar/family_buddy.c
    -libpurple/protocols/oscar/family_chat.c
    -libpurple/protocols/oscar/family_chatnav.c
    -libpurple/protocols/oscar/family_feedbag.c
    -libpurple/protocols/oscar/family_icbm.c
    -libpurple/protocols/oscar/family_icq.c
    -libpurple/protocols/oscar/family_locate.c
    -libpurple/protocols/oscar/family_oservice.c
    -libpurple/protocols/oscar/family_popup.c
    -libpurple/protocols/oscar/family_stats.c
    -libpurple/protocols/oscar/family_userlookup.c
    -libpurple/protocols/oscar/flap_connection.c
    -libpurple/protocols/oscar/icq.c
    -libpurple/protocols/oscar/kerberos.c
    -libpurple/protocols/oscar/misc.c
    -libpurple/protocols/oscar/msgcookie.c
    -libpurple/protocols/oscar/odc.c
    -libpurple/protocols/oscar/oft.c
    -libpurple/protocols/oscar/oscar.c
    -libpurple/protocols/oscar/oscar_data.c
    -libpurple/protocols/oscar/peer.c
    -libpurple/protocols/oscar/peer_proxy.c
    -libpurple/protocols/oscar/rxhandlers.c
    -libpurple/protocols/oscar/snac.c
    -libpurple/protocols/oscar/tests/test_oscar_util.c
    -libpurple/protocols/oscar/tlv.c
    -libpurple/protocols/oscar/userinfo.c
    -libpurple/protocols/oscar/util.c
    -libpurple/protocols/oscar/visibility.c
    libpurple/protocols/sametime/im_mime.c
    libpurple/protocols/sametime/sametime.c
    libpurple/protocols/sametime/tests/test_sametime_im_mime.c
    @@ -437,7 +396,7 @@
    pidgin/plugins/unity.c
    pidgin/plugins/win32/winprefs/gtkappbar.c
    pidgin/plugins/win32/winprefs/winprefs.c
    -pidgin/plugins/xmppconsole.c
    +pidgin/plugins/xmppconsole/xmppconsole.c
    pidgin/resources/About/about.ui
    pidgin/resources/Conversations/invite_dialog.ui
    pidgin/resources/Debug/debug.ui