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
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 @@
--- 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)
-E: ardentlygnarley a.t users d.o.t sourceforge d.o.t net
-E: thekingant a.t users d.o.t sourceforge d.o.t net
-W: http://www.auk.cx/~mid,http://www.auk.cx/faim
-D: Wrote most of the wap of crap that you see before you.
-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.
-D: Made initial versions cross platform
-W: http://users.n.ml.org/n/
-E: warmenhoven a.t linux d.o.t com
-D: Some OFT info, initial author of the libpurple-side of the oscar protocol plugin
-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.]
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
-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
- 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
- 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
- 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
- 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
- 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
- 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
-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.
- 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
- 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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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 "oscarcommon.h"
-aim_protocol_init(AIMProtocol *self)
- PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
- protocol->id = "prpl-aim";
- protocol->name = "AIM";
- oscar_init_account_options(protocol, FALSE);
-aim_protocol_class_init(AIMProtocolClass *klass)
- PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
- protocol_class->list_icon = oscar_list_icon_aim;
-aim_protocol_class_finalize(G_GNUC_UNUSED AIMProtocolClass *klass)
-aim_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
- client_iface->get_max_message_size = oscar_get_max_message_size;
-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
-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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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
-#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))
- OscarProtocolClass parent_class;
- * Registers the AIMProtocol type in the type system.
-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.
-/* When you ask other people for authorization */
-oscar_auth_sendrequest(PurpleConnection *gc, const char *bname, const char *msg)
- PurpleAccount *account;
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
- buddy = purple_blist_find_buddy(account, bname);
- group = purple_buddy_get_group(buddy);
- gname = purple_group_get_name(group);
- purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
- 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 */
- 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);
-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);
-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);
-oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored)
- 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 */
-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);
- 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.
-int byte_stream_new(ByteStream *bs, size_t len)
- return byte_stream_init(bs, g_malloc(len), 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)
- 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)
-int byte_stream_setpos(ByteStream *bs, size_t off)
- g_return_val_if_fail(off <= bs->len, -1);
-void byte_stream_rewind(ByteStream *bs)
- byte_stream_setpos(bs, 0);
- * N can be negative, which can be used for going backwards
-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);
-guint8 byte_stream_get8(ByteStream *bs)
- g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
-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);
-guint8 *byte_stream_getraw(ByteStream *bs, size_t len)
- g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL);
- byte_stream_getrawbuf_nocheck(bs, ob, len);
-char *byte_stream_getstr(ByteStream *bs, size_t len)
- 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);
-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);
-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);
-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);
-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);
-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);
-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);
-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);
-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);
-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 */
- byte_stream_put8(bs, 0x00); /* No flags */
- byte_stream_put8(bs, 0x00); /* Length */
-void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr)
- size_t len = datastr != NULL ? strlen(datastr) : 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);
- 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.
- * http://dev.aol.com/aim/oscar/#AUTH
- * http://dev.aol.com/authentication_for_clients
-#include "oscarcommon.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 *status_code_node;
- gboolean have_error_code = TRUE;
- status_code_node = purple_xmlnode_get_child(resp, "statusCode");
- if (status_code_node) {
- /* 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
- err = g_strdup_printf(_("Received unexpected response from %s: %s"), url, details);
- /* Translators: %s in this string is a URL */
- err = g_strdup_printf(_("Received unexpected response from %s"), url);
- * @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)
- 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);
- 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);
- signature = hmac_sha256(session_key, signature_base_string);
- g_free(signature_base_string);
-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;
- 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)
- 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);
- /* 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) {
- 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);
- purple_xmlnode_free(response_node);
- /* Make sure the status code was 200 */
- PurpleXmlNode *status_detail_node;
- guint status_detail = 0;
- status_detail_node = purple_xmlnode_get_child(response_node,
- if (status_detail_node) {
- gchar *data = purple_xmlnode_get_data(status_detail_node);
- status_detail = atoi(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 "
- msg = generate_error_message(response_node,
- get_start_oscar_session_url(od));
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
- purple_xmlnode_free(response_node);
- /* Make sure we have everything else */
- if (data_node == NULL || host_node == NULL || port_node == NULL || cookie_node == NULL)
- 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);
- purple_xmlnode_free(response_node);
- 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);
- 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");
- purple_debug_error("oscar", "startOSCARSession was missing tlsCertName: %s\n", response);
- purple_connection_error(
- 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);
- /* 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')
- 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);
- purple_xmlnode_free(response_node);
-start_oscar_session_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
- OscarData *od = user_data;
- char *tls_certname = NULL;
- gsize cookiedata_len = 0;
- g_clear_object(&od->http_conns);
- if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
- /* Translators: The first %s is a URL, the second is a brief error
- tmp = g_strdup_printf(_("Error requesting %s: %d %s"),
- get_start_oscar_session_url(od), msg->status_code,
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- if (!parse_start_oscar_session_response(gc, msg->response_body->data,
- msg->response_body->length, &host,
- &port, &cookie, &tls_certname)) {
- cookiedata = g_base64_decode(cookie, &cookiedata_len);
- oscar_connect_to_bos(gc, od, host, port, cookiedata, cookiedata_len, 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;
- 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"
- "&ts=%" G_GINT64_FORMAT
- 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),
- !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,
- msg = soup_message_new("GET", uri);
- soup_session_queue_message(od->http_conns, msg, start_oscar_session_cb, od);
- * 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;
- /* Parse the response as XML */
- response_node = purple_xmlnode_from_str(response, response_len);
- if (response_node == NULL)
- 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);
- /* 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) {
- 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);
- purple_xmlnode_free(response_node);
- /* Make sure the status code was 200 */
- if (!purple_strequal(tmp, "200"))
- int status_code, status_detail_code = 0;
- status_code = atoi(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);
- 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"));
- msg = generate_error_message(response_node,
- get_client_login_url(od));
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg);
- purple_xmlnode_free(response_node);
- /* Make sure we have everything else */
- if (data_node == NULL || secret_node == NULL ||
- token_node == NULL || tokena_node == NULL)
- 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);
- purple_xmlnode_free(response_node);
- /* 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')
- 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);
- purple_xmlnode_free(response_node);
- *hosttime = strtol(tmp, NULL, 10);
- purple_xmlnode_free(response_node);
-client_login_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
- OscarData *od = user_data;
- char *token, *secret, *session_key;
- if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
- tmp = g_strdup_printf(_("Error requesting %s: %d %s"),
- get_client_login_url(od), msg->status_code,
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- if (!parse_client_login_response(gc, msg->response_body->data,
- msg->response_body->length, &token,
- 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);
- send_start_oscar_session(od, token, session_key, hosttime);
- * 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)
- * 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
- 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,
- soup_session_queue_message(od->http_conns, msg, client_login_cb, od);
--- 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
-encoding_multi_convert_to_utf8(const gchar *text, gssize textlen, const gchar *encodings, GError **error, gboolean fallback)
- 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 */
- purple_debug_error("oscar", "encodings is NULL");
- /* extract next encoding */
- end = strchr(begin, ',');
- curr_encoding_ro = begin;
- } else { /* allocate buffer for encoding */
- curr_encoding = g_strndup(begin, end - begin);
- purple_debug_error("oscar", "Error allocating memory for encoding");
- curr_encoding_ro = curr_encoding;
- if (!g_ascii_strcasecmp(curr_encoding_ro, "utf-8") && g_utf8_validate(text, textlen, NULL)) {
- 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 */
- g_free(curr_encoding); /* free allocated buffer for encoding here */
- if (utf8) /* text was successfully converted */
- { /* "begin" points to last encoding */
- utf8 = g_convert_with_fallback(text, textlen, "UTF-8", begin, "?", NULL, NULL, error);
-encoding_extract(const char *encoding)
- if (encoding == 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);
-oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen)
- 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
- 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.)"));
- utf8 = g_strndup(text, textlen);
- g_free(extracted_encoding);
-oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg)
- const char *charset = NULL;
- if (g_utf8_validate(msg, -1, NULL))
- charset = purple_account_get_string(account, "encoding", NULL);
- if(charset && *charset)
- ret = encoding_multi_convert_to_utf8(msg, -1, charset, NULL, FALSE);
- ret = purple_utf8_try_convert(msg);
-oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback)
- if ((charsetstr == NULL) || (*charsetstr == '\0'))
- if (g_ascii_strcasecmp("UTF-8", charsetstr)) {
- ret = encoding_multi_convert_to_utf8(data, datalen, charsetstr, &err, fallback);
- purple_debug_warning("oscar", "Conversion from %s failed: %s.\n",
- charsetstr, err->message);
- if (g_utf8_validate(data, datalen, NULL))
- ret = g_strndup(data, datalen);
- purple_debug_warning("oscar", "String is not valid UTF-8.\n");
-oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen)
- /* charsetstr1 is always set to what the correct encoding should be. */
- const gchar *charsetstr1, *charsetstr2, *charsetstr3 = NULL;
- if ((datalen == 0) || (data == NULL))
- if (charset == AIM_CHARSET_UNICODE) {
- charsetstr1 = "UTF-16BE";
- } 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);
- charsetstr1 = "ISO-8859-1";
- } else if (charset == AIM_CHARSET_ASCII) {
- /* Should just be "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 */
- charsetstr2 = "ISO-8859-1";
- charsetstr3 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
- /* Unknown, hope for valid 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 (charsetstr3 != NULL) {
- /* Try charsetstr2 without allowing substitutions, then fall through to charsetstr3 if needed */
- ret = oscar_convert_to_utf8(data, datalen, charsetstr2, FALSE);
- ret = oscar_convert_to_utf8(data, datalen, charsetstr3, TRUE);
- /* Try charsetstr2, allowing substitutions */
- ret = oscar_convert_to_utf8(data, datalen, charsetstr2, TRUE);
- char *str, *salvage, *tmp;
- str = g_malloc(datalen + 1);
- strncpy(str, data, datalen);
- 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.)"),
- ret = g_strdup_printf("%s %s", salvage, tmp);
-get_simplest_charset(const char *utf8)
- if ((unsigned char)(*utf8) > 0x7f) {
- return AIM_CHARSET_UNICODE;
- return AIM_CHARSET_ASCII;
-oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr)
- guint16 msg_charset = get_simplest_charset(msg);
- *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 "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,
- * Subtype 0x0002 - Request a bit of account info.
- * Info should be one of the following:
- * 0x0001 - Username formatting
- * 0x0011 - Email address
-aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info)
- 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).
-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)) {
- type = byte_stream_get16(bs);
- length = byte_stream_get16(bs);
- sn = byte_stream_getstr(bs, length);
- url = byte_stream_getstr(bs, length);
- err = byte_stream_get16(bs);
- email = g_strdup("*suppressed");
- email = byte_stream_getstr(bs, length);
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- userfunc(od, conn, frame, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email);
- * Subtype 0x0004 - Set the formatting of username (change spaces and capitalization).
-aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick)
- 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.
-aim_admin_changepasswd(OscarData *od, FlapConnection *conn, const char *newpw, const char *curpw)
- GSList *tlvlist = NULL;
- 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.
-aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail)
- 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.
-aim_admin_reqconfirm(OscarData *od, FlapConnection *conn)
- aim_genericreq_n(od, conn, SNAC_FAMILY_ADMIN, 0x0006);
- * Subtype SNAC_FAMILY_ADMIN - Account confirmation request acknowledgement.
-accountconfirm(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- 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); */
-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);
- } else if (snac->subtype == SNAC_FAMILY_ADMIN)
- return accountconfirm(od, conn, mod, frame, snac, bs);
-int admin_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_ADMIN;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "admin", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
- * 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.
-aim_email_sendcookies(OscarData *od)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ALERT)))
- byte_stream_new(&bs, 2+16+16);
- /* Number of cookies to follow */
- byte_stream_put16(&bs, 0x0002);
- 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);
- 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);
- * 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.
-parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- struct aim_emailinfo *new;
- 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);
- /* Free some of the old info, if it exists */
- /* We don't already have info, so create a new struct for it */
- new = g_new0(struct aim_emailinfo, 1);
- new->next = od->emailinfo;
- new->cookie8 = cookie8;
- new->cookie16 = cookie16;
- tlvlist = aim_tlvlist_readnum(bs, byte_stream_get16(bs));
- tmp = aim_tlv_get16(tlvlist, 0x0080, 1);
- if (new->nummsgs < tmp)
- /* If they don't send a 0x0080 TLV, it means we definitely have new mail */
- /* (ie. this is not just another status update) */
- 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))) {
- 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);
- * 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.
-aim_email_activate(OscarData *od)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ALERT)))
- 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);
-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);
-email_shutdown(OscarData *od, aim_module_t *mod)
- struct aim_emailinfo *tmp = od->emailinfo;
- od->emailinfo = od->emailinfo->next;
-email_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_ALERT;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "alert", sizeof(mod->name));
- mod->snachandler = snachandler;
- mod->shutdown = email_shutdown;
--- 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
-#include "oscarcommon.h"
-/* #define 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.
-aim_encode_password(const char *password, guint8 *encoded)
- guint8 encoding_table[] = {
- 0xf3, 0x26, 0x81, 0xc4,
- 0x39, 0x86, 0xdb, 0x92,
- 0x71, 0xa3, 0xb9, 0xe6,
- for (i = 0; i < strlen(password); i++)
- encoded[i] = (password[i] ^ encoding_table[i]);
-aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
- 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);
-aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
- 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);
- * Part two of the ICQ hack. Note the ignoring of the key.
-goddamnicq2(OscarData *od, FlapConnection *conn, const char *sn, const char *password, ClientInfo *ci)
- GSList *tlvlist = NULL;
- guint8 *password_encoded;
- 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",
- 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);
- gchar *clientstring = oscar_get_clientstring();
- aim_tlvlist_add_str(&tlvlist, 0x0003, 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);
- * 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.
- * 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"
- * AIM for Linux 1.1.112:
- * clientstring = "AOL Instant Messenger (SM)"
- * @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.
-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)
- GSList *tlvlist = NULL;
- if (!ci || !sn || !password)
- /* 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);
- 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)
- aim_encode_password_md5(password, password_len, key, digest);
- distrib = oscar_get_ui_info_int(
- od->icq ? "prpl-icq-distid" : "prpl-aim-distid",
- aim_tlvlist_add_raw(&tlvlist, 0x0025, 16, digest);
- aim_tlvlist_add_noval(&tlvlist, 0x004c);
- if (ci->clientstring != NULL)
- aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
- gchar *clientstring = oscar_get_clientstring();
- aim_tlvlist_add_str(&tlvlist, 0x0003, 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
- 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);
- * 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.
-parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- struct aim_authresp_info *info;
- 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
- 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);
- if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
- info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1);
- * Authorization cookie.
- if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
- tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
- 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.
- * 2 = Limited 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);
- 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);
- * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client
- * This is a bit confusing.
- * Normal SNAC login goes like this:
- * - 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)
- * XOR login (for ICQ) goes like this:
- * - server sends flap version
- * - client sends auth request which contains flap version (aim_send_login)
- * 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
- * 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/.
-goddamnicq(OscarData *od, FlapConnection *conn, const char *sn)
- aim_rxcallback_t userfunc;
- if ((userfunc = aim_callhandler(od, SNAC_FAMILY_AUTH, 0x0007)))
- userfunc(od, conn, &frame, "");
- * 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).
-aim_request_login(OscarData *od, FlapConnection *conn, const char *sn)
- GSList *tlvlist = NULL;
- if (!od || !conn || !sn)
- if (aim_snvalid_icq(sn))
- return goddamnicq(od, conn, sn);
- 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);
- * 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.
-keyparse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- gboolean truncate_pass;
- PurpleAccount *account;
- ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
- ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
- 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"));
- 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);
- aim_tlvlist_free(tlvlist);
- * Receive SecurID request.
-got_securid_request(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame);
- * Send SecurID response.
-aim_auth_securid_send(OscarData *od, const char *securid)
- if (!od || !(conn = flap_connection_getbytype_all(od, SNAC_FAMILY_AUTH)) || !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);
-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);
-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);
-auth_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_AUTH;
- strncpy(mod->name, "auth", sizeof(mod->name));
- mod->snachandler = snachandler;
- mod->shutdown = auth_shutdown;
--- 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.
- * 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.
-aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !icon || !iconlen)
- byte_stream_new(&bs, 2 + 2 + iconlen);
- /* The reference number for the icon */
- byte_stream_put16(&bs, 1);
- 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);
- * Subtype 0x0003 - Acknowledgement for uploading a buddy icon.
- * You get this honky after you upload a buddy icon.
-uploadack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame);
- * 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.
-aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !bn || *bn == '\0' || !iconcsum || !iconcsumlen)
- byte_stream_new(&bs, 1+strlen(bn) + 4 + 1+iconcsumlen);
- 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);
- 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);
- * Subtype 0x0005 - Receive a buddy icon.
- * This is sent in response to a buddy icon request.
-parseicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- 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);
- 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);
-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);
-bart_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_BART;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "bart", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
-/* Subtype 0x0002 - Request BOS rights. */
-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;
- guint16 maxpermits = 0, maxdenies = 0;
- 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);
-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);
-bos_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_BOS;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "bos", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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).
- * Subtype 0x0002 - Request rights.
- * Request Buddy List rights.
-aim_buddylist_reqrights(OscarData *od, FlapConnection *conn)
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_REQRIGHTS);
- * Subtype 0x0003 - Rights.
-rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- guint16 maxbuddies = 0, maxwatchers = 0;
- 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
- if (aim_tlv_gettlv(tlvlist, 0x0002, 1))
- maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1);
- * TLV type 0x0003: Unknown.
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, maxbuddies, maxwatchers);
- aim_tlvlist_free(tlvlist);
- * 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().
-buddychange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- 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);
- 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);
-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);
-buddylist_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_BUDDY;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "buddy", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
-/* Stored in the ->internal of chat connections */
-flap_connection_destroy_chat(OscarData *od, FlapConnection *conn)
- struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal;
-aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo)
- 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);
- * 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:
-infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- guint8 detaillevel = 0;
- struct aim_chat_roominfo roominfo;
- 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);
- 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);
- aim_tlvlist_free(tlvlist);
-/* Subtypes 0x0003 and 0x0004 */
-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)) {
- 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);
- * Subtype 0x0005 - Send a Chat Message.
- * 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
-aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language)
- GSList *tlvlist = NULL, *inner_tlvlist = NULL;
- if (!od || !conn || !msg || (msglen <= 0))
- byte_stream_new(&bs, 1142);
- snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0);
- * 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);
- 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);
- if (!(flags & AIM_CHATFLAGS_NOREFLECT))
- aim_tlvlist_add_noval(&tlvlist, 0x0006);
- 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
- aim_tlvlist_add_str(&inner_tlvlist, 0x0002, encoding);
- * SubTLV: Type 3: Language
- 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);
- * 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:
-incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- aim_userinfo_t userinfo;
- char *encoding = NULL, *language = NULL;
- memset(&userinfo, 0, sizeof(aim_userinfo_t));
- for (i = 0; i < 8; i++)
- cookie[i] = byte_stream_get8(bs);
- if ((ck = aim_uncachecookie(od, cookie, AIM_COOKIETYPE_CHAT))) {
- * 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);
- * Start parsing TLVs right away.
- tlvlist = aim_tlvlist_read(bs);
- * Type 0x0003: Source User Information
- tlv = aim_tlv_gettlv(tlvlist, 0x0003, 1);
- 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);
- 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);
- 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);
- aim_tlvlist_free(tlvlist);
-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);
-chat_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_CHAT;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "chat", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
-error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- guint16 error, chatnav_error;
- snac2 = aim_remsnac(od, snac->id);
- purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08x)\n", snac->id);
- if (snac2->family != SNAC_FAMILY_CHATNAV) {
- purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family);
- * 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",
- purple_notify_error(od->gc, NULL, _("Could not join chat room"),
- chatnav_error == 0x0033 ? _("Invalid chat room name") :
- purple_request_cpar_from_connection(od->gc));
- * conn must be a chatnav connection!
-void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn)
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_CHATNAV, 0x0002);
-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"};
- GSList *tlvlist = NULL;
- byte_stream_new(&bs, 1142);
- snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0);
- 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);
- * Setting this to 0xffff apparently assigns the last instance.
- byte_stream_put16(&bs, 0xffff);
- 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);
- 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);
-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;
- struct aim_chat_exchangeinfo *exchanges = NULL;
- aim_tlv_t *exchangetlv;
- 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))); ) {
- byte_stream_init(&tbs, exchangetlv->value, exchangetlv->length);
- exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
- exchanges[curexchange-1].number = byte_stream_get16(&tbs);
- innerlist = aim_tlvlist_read(&tbs);
- if (aim_tlv_gettlv(innerlist, 0x0002, 1)) {
- classperms = aim_tlv_get16(innerlist, 0x0002, 1);
- purple_debug_misc("oscar", "faim: class permissions %x\n", classperms);
- * 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);
- 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);
- 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);
- 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);
- 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);
- exchanges[curexchange-1].lang2 = NULL;
- aim_tlvlist_free(innerlist);
- 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);
- aim_tlvlist_free(tlvlist);
-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;
- 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);
- 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);
- 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);
- aim_tlvlist_free(innerlist);
- aim_tlvlist_free(tlvlist);
- * 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:
- * 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.
-parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- if (!(snac2 = aim_remsnac(od, snac->id))) {
- purple_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08x)\n", snac->id);
- 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);
- * 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);
- purple_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
-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);
-chatnav_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_CHATNAV;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "chatnav", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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 "oscarcommon.h"
-static int aim_ssi_addmoddel(OscarData *od);
-static void aim_ssi_item_free(struct aim_ssi_item *item)
- aim_tlvlist_free(item->data);
-static void aim_ssi_item_set_name(struct aim_ssi_itemlist *list, struct aim_ssi_item *item, const char *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);
- item->name = g_strdup(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.
-aim_ssi_type_to_string(guint16 type)
- static const struct TypeStringPair type_strings[] = {
- { 0x0002, "Permit/Visible" },
- { 0x0003, "Deny/Invisible" },
- { 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" },
- for (i = 0; i < G_N_ELEMENTS(type_strings); i++) {
- if (type_strings[i].type == type) {
- return type_strings[i].string;
-/** 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.
-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.
-aim_ssi_itemlist_rebuildgroup(struct aim_ssi_itemlist *list, const char *name)
- struct aim_ssi_item *cur, *group;
- if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
- /* Find the length for the new additional data */
- if (group->gid == 0x0000) {
- for (cur=list->data; cur; cur=cur->next)
- if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
- for (cur=list->data; cur; cur=cur->next)
- if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
- /* Build the new TLV list */
- newdata = g_new(guint8, newlen);
- 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);
- 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);
- * 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)
- struct aim_ssi_item *cur, *new;
- new = g_new0(struct aim_ssi_item, 1);
- /* Set the group ID# and buddy ID# */
- if (type == AIM_SSI_TYPE_GROUP) {
- if ((new->gid == 0xFFFF) && name) {
- for (cur = list->data; cur != NULL; cur = cur->next)
- if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) {
- } 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) {
- for (cur = list->data; cur != NULL; cur = cur->next)
- if (cur->bid == new->bid || cur->gid == new->bid) {
- if (new->bid == 0xFFFF) {
- for (cur = list->data; cur != NULL; cur = cur->next)
- if (cur->bid == new->bid && cur->gid == new->gid) {
- /* 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);
- new->data = aim_tlvlist_copy(data);
- /* Add the item to the list in the correct numerical position. Fancy, eh? */
- if ((new->gid < list->data->gid) || ((new->gid == list->data->gid) && (new->bid < list->data->bid))) {
- new->next = list->data;
- 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;
- new->next = list->data;
- * 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)
- if (!(list->data) || !del)
- /* Remove the item from the list */
- if (list->data == del) {
- list->data = list->data->next;
- struct aim_ssi_item *cur;
- for (cur=list->data; (cur->next && (cur->next!=del)); cur=cur->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);
- * 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->data && !cur2->data)
- if (!cur1->data && cur2->data)
- if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
- if (cur1->name && !cur2->name)
- if (!cur1->name && cur2->name)
- if (cur1->name && cur2->name && oscar_util_name_compare(cur1->name, cur2->name))
- if (cur1->gid != cur2->gid)
- if (cur1->bid != cur2->bid)
- if (cur1->type != cur2->type)
-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)
- * 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;
- 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)))
- } 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))
- * 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)
- 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)
- if (!(cur = aim_ssi_itemlist_exists(list, bn)))
- if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
- * 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);
- aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1);
- return aimutil_get8(tlv->value);
- * 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);
- aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1);
- if (tlv && tlv->length)
- return aimutil_get32(tlv->value);
- * 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
-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);
- return aim_ssi_getalias_from_item(item);
-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);
- * 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
-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);
- aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
- if (tlv && tlv->length) {
- return g_strndup((const gchar *)tlv->value, tlv->length);
- * 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 (aim_tlv_gettlv(cur->data, 0x0066, 1))
- * If there are changes, then create temporary items and
- * @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;
- * 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 we're waiting for an ack, we shouldn't do anything else */
- if (od->ssi.waiting_for_ack)
- * 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("");
- 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)) {
- new = g_new(struct aim_ssi_tmp, 1);
- new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
- for (cur=od->ssi.pending; cur->next; cur=cur->next);
- aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
- 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)) {
- new = g_new(struct aim_ssi_tmp, 1);
- new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
- for (cur=od->ssi.pending; cur->next; cur=cur->next);
- aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
- 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))) {
- new = g_new(struct aim_ssi_tmp, 1);
- new->action = SNAC_SUBTYPE_FEEDBAG_MOD;
- for (cur=od->ssi.pending; cur->next; cur=cur->next);
- 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) {
- od->ssi.in_transaction = FALSE;
- /* If this is the first in a series of add/mod/del
- * requests then send the "begin transaction" message. */
- if (!od->ssi.in_transaction)
- 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);;
- * 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.
-aim_ssi_freelist(OscarData *od)
- struct aim_ssi_item *cur, *del;
- struct aim_ssi_tmp *curtmp, *deltmp;
- cur = od->ssi.official.data;
- aim_ssi_item_free(del);
- cur = od->ssi.local.data;
- aim_ssi_item_free(del);
- curtmp = od->ssi.pending;
- 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)
- 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);
- aim_tlvlist_replace_str(&item->data, tlvtype, salvaged);
- aim_tlvlist_remove(&item->data, tlvtype);
- * 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;
- /* 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;
- 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);
- cur = od->ssi.local.data;
- 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 */
- if (cur->type == cur2->type
- && cur->gid == cur2->gid
- && !oscar_util_name_compare(cur->name, cur2->name))
- aim_ssi_itemlist_del(&od->ssi.local, cur2);
- /* 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);
- /* 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)
- 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);
- 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 */
- aim_tlvlist_add_noval(&data, 0x0066);
- aim_tlvlist_add_str(&data, 0x0131, alias);
- aim_tlvlist_add_str(&data, 0x013a, smsnum);
- aim_tlvlist_add_str(&data, 0x013c, comment);
- 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);
-aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type)
- if (!od || !name || !od->ssi.received_data)
- 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);
-aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type)
- struct aim_ssi_item *del;
- if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, name, list_type)))
- 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 (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
- /* 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;
- if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)))
- /* Don't delete the group if it's not empty */
- tlv = aim_tlv_gettlv(del->data, 0x00c8, 1);
- if (tlv && tlv->length > 0)
- /* 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;
- buddy = aim_ssi_itemlist_finditem(&od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
- /* 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);
- * 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
- * @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 (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
- /* Either add or remove the 0x0131 TLV from the TLV chain */
- aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
- 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
- * @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 (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
- /* Either add or remove the 0x0131 TLV from the TLV chain */
- if (comment && *comment)
- aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
- aim_tlvlist_remove(&tmp->data, 0x013c);
- /* Sync our local list with the server list */
- return aim_ssi_sync(od);
- * @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)
- if (!(group = aim_ssi_itemlist_finditem(&od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
- 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:
- * 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)
- /* 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;
- if (!od || !iconsum || !iconsumlen || !od->ssi.received_data)
- /* 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);
- /* 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 */
- * 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)
- /* 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQRIGHTS);
- * Subtype 0x0003 - SSI Rights Information.
-static int parserights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- /* 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);
- 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);
- * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and
-int aim_ssi_reqdata(OscarData *od)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
- /* Free any current data, just in case */
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQDATA);
- * Subtype 0x0006 - SSI Data.
-static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- guint8 fmtver; /* guess */
- guint16 namelen, gid, bid, type;
- 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 */
- while (byte_stream_bytes_left(bs) > 4) { /* last four bytes are timestamp */
- if ((namelen = byte_stream_get16(bs)))
- name = byte_stream_getstr(bs, namelen);
- 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));
- 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 */
- 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);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
- aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, 0x0007);
- * 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)
- struct aim_ssi_tmp *cur;
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !od->ssi.pending || !od->ssi.pending->item)
- /* Calculate total SNAC size */
- for (cur=od->ssi.pending; cur; cur=cur->next) {
- bslen += 10; /* For length, GID, BID, type, and length */
- bslen += strlen(cur->item->name);
- 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);
- 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);
- 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);
- * 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)
- aim_rxcallback_t userfunc;
- guint16 len, gid, bid, type;
- while (byte_stream_bytes_left(bs)) {
- if ((len = byte_stream_get16(bs)))
- name = byte_stream_getstr(bs, len);
- 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);
- 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);
- * Subtype 0x0009 - Incoming SSI mod.
-static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- guint16 len, gid, bid, type;
- struct aim_ssi_item *item;
- while (byte_stream_bytes_left(bs)) {
- if ((len = byte_stream_get16(bs)))
- name = byte_stream_getstr(bs, len);
- 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);
- /* Replace the 2 local items with the given one */
- if ((item = aim_ssi_itemlist_find(&od->ssi.local, gid, bid))) {
- 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))) {
- 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);
- aim_tlvlist_free(data);
- * 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)
- aim_rxcallback_t userfunc;
- 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_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);
- * 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)
- aim_rxcallback_t userfunc;
- struct aim_ssi_tmp *cur, *del;
- /* Read in the success/failure flags from the ack SNAC */
- while (cur && (byte_stream_bytes_left(bs)>0)) {
- cur->ack = byte_stream_get16(bs);
- * 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) {
- /* 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);
- } 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 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);
- /* 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 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 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);
- } /* End if (cur->item) */
- 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 */
- while (cur && (cur->ack != 0xffff)) {
- /* If we're not waiting for any more acks, then send more SNACs */
- if (!od->ssi.pending) {
- od->ssi.waiting_for_ack = FALSE;
- * 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)
- aim_rxcallback_t userfunc;
- od->ssi.received_data = TRUE;
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
- aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTART);
- * Subtype 0x0012 - SSI End Data Modification.
- * Tell the server you're finished modifying data. The marks the end
-int aim_ssi_modend(OscarData *od)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
- aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTOP);
- * 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)
- aim_rxcallback_t userfunc;
- char *bn, *msg, *tmpstr;
- tmp = byte_stream_get8(bs);
- purple_debug_warning("oscar", "Dropping auth grant SNAC "
- "because username was empty\n");
- 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");
- tmp = byte_stream_get16(bs);
- 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);
- tmp = byte_stream_get16(bs);
- purple_debug_warning("oscar", "unknown field missing");
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, bn, msg);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
- byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
- byte_stream_put8(&bs, strlen(bn));
- byte_stream_putstr(&bs, bn);
- /* Message (null terminated) */
- byte_stream_put16(&bs, msg ? strlen(msg) : 0);
- byte_stream_putstr(&bs, msg);
- byte_stream_put8(&bs, 0x00);
- 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);
- * 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)
- aim_rxcallback_t userfunc;
- char *bn, *msg, *tmpstr;
- tmp = byte_stream_get8(bs);
- purple_debug_warning("oscar", "Dropping auth request SNAC "
- "because username was empty\n");
- 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");
- tmp = byte_stream_get16(bs);
- 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);
- tmp = byte_stream_get16(bs);
- purple_debug_warning("oscar", "unknown field missing");
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, bn, msg);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
- byte_stream_new(&bs, 1+strlen(bn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2);
- byte_stream_put8(&bs, strlen(bn));
- byte_stream_putstr(&bs, bn);
- byte_stream_put8(&bs, reply);
- /* Message (null terminated) */
- byte_stream_put16(&bs, msg ? (strlen(msg)+1) : 0);
- byte_stream_putstr(&bs, msg);
- byte_stream_put8(&bs, 0x00);
- 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);
- * 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)
- aim_rxcallback_t userfunc;
- char *bn, *msg, *tmpstr;
- tmp = byte_stream_get8(bs);
- purple_debug_warning("oscar", "Dropping auth reply SNAC "
- "because username was empty\n");
- 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");
- reply = byte_stream_get8(bs);
- tmp = byte_stream_get16(bs);
- 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);
- tmp = byte_stream_get16(bs);
- purple_debug_warning("oscar", "unknown field missing");
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, bn, reply, msg);
- * 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)
- aim_rxcallback_t userfunc;
- tmp = byte_stream_get8(bs);
- purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
- "because username was empty\n");
- 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");
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, bn);
- * 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.
-aim_ssi_getdenyentrytype(OscarData* od)
- return od->icq ? AIM_SSI_TYPE_ICQDENY : AIM_SSI_TYPE_DENY;
-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);
-ssi_shutdown(OscarData *od, aim_module_t *mod)
-ssi_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_FEEDBAG;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "feedbag", sizeof(mod->name));
- mod->snachandler = snachandler;
- mod->shutdown = ssi_shutdown;
--- 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
- * 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.
-static const char * const errcodereason[] = {
- 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
- * @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)
- /* Should be like "21CBF95" and null terminated */
- for (i = 0; i < 7; i++)
- cookie[i] = 0x30 + ((guchar)g_random_int() % 10);
- * Subtype 0x0001 - Error
-error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- guint16 reason, errcode = 0;
- PurpleConnection *gc = od->gc;
- const char *reason_str;
- snac2 = aim_remsnac(od, snac->id);
- purple_debug_misc("oscar", "icbm error: received response from unknown request!\n");
- if (snac2->family != SNAC_FAMILY_ICBM) {
- purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2->family);
- /* Data is assumed to be the destination bn */
- if (!bn || bn[0] == '\0') {
- purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n");
- 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",
- /* 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);
- /* 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]));
- buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
- if (!purple_conversation_present_error(bn, purple_connection_get_account(gc), buf)) {
- if (errcode != 0 && errcode < errcodereasonlen) {
- buf = g_strdup_printf(_("Unable to send message to %s: %s (%s)"),
- bn, reason_str, _(errcodereason[errcode]));
- buf = g_strdup_printf(_("Unable to send message to %s: %s"), bn,
- purple_notify_error(od->gc, NULL, buf, reason_str,
- purple_request_cpar_from_connection(od->gc));
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- 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);
- * Subtype 0x0004 - Request ICBM parameter information.
-int aim_im_reqparams(OscarData *od)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
- * 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, ¶ms);
- * Subtype 0x0006 - Send an ICBM (instant message).
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- if (!args->msg || (args->msglen <= 0))
- if (args->msglen > MAXMSGLEN)
- /* 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);
- 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);
- 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);
- /* 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);
- * 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)
- struct aim_invite_priv *priv;
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- if (!bn || !msg || !roomname)
- 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);
- aim_im_puticbm(&bs, cookie, 0x0002, bn);
- * 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);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
- 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);
- aim_im_puticbm(&bs, cookie, 0x0002, bn);
- * 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);
- byte_stream_put16(&bs, 0x000a);
- byte_stream_put16(&bs, 0x0002);
- byte_stream_put16(&bs, 0x0001);
- byte_stream_put16(&bs, 0x000f);
- byte_stream_put16(&bs, 0x0000);
- 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);
- 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);
- * Cancel a rendezvous invitation. It could be an invitation to
- * establish a direct connection, or a file-send, or a chat invite.
-aim_im_sendch2_cancel(PeerConnection *peer_conn)
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
- byte_stream_new(&bs, 118+strlen(peer_conn->bn));
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
- 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
-aim_im_sendch2_connected(PeerConnection *peer_conn)
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
- byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16);
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
- 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"
-aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber)
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- g_return_if_fail(bn != NULL);
- g_return_if_fail(ip != NULL);
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
- byte_stream_new(&bs, 246+strlen(bn));
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
- 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.
-aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber)
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
- byte_stream_new(&bs, 246+strlen(bn));
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
- 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? */
- 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
-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)
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- g_return_if_fail(bn != NULL);
- g_return_if_fail(ip != NULL);
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
- byte_stream_new(&bs, 1014);
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
- 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 */
- /* 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);
- 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.
-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)
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
- byte_stream_new(&bs, 1014);
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
- 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? */
- aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
- aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
- 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);
- 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);
-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);
- 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);
- byte_stream_advance(message, length);
-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)
- aim_rxcallback_t userfunc;
- struct aim_incomingim_ch1_args args;
- 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);
- endpos = byte_stream_curpos(bs) + length;
- if (type == 0x0002) { /* Message Block */
- 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
- 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);
-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...
- * num of buddies in group
- * num of buddies in group
- while (byte_stream_bytes_left(servdata))
- gnlen = byte_stream_get16(servdata);
- gn = byte_stream_getstr(servdata, gnlen);
- numb = byte_stream_get16(servdata);
- for (i = 0; i < numb; i++) {
- 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);
-incomingim_ch2_buddyicon_free(OscarData *od, IcbmArgsCh2 *args)
- g_free(args->info.icon.icon);
-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;
-incomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args)
- /* XXX - aim_chat_roominfo_free() */
- g_free(args->info.chat.roominfo.name);
-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)
- aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
- args->destructor = (void *)incomingim_ch2_chat_free;
-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.
-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"); \
- byte_stream_advance(servdata, hdrlen);
- 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);
-incomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args)
- g_free(args->info.sendfile.filename);
-/* Someone is sending us a file */
-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)
- 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
- /* 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;
- ByteStream bbs, sdbs, *sdbsptr = NULL;
- 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);
- /* The server sent us ch2 ICBM without ch2 info? Weird. */
- 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");
- memcpy(args.cookie, cookie2, 8);
- * 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);
- if (aim_tlv_gettlv(list2, 0x000d, 1))
- args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
- 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.proxyip = (char *)proxyip;
- 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);
- * 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);
- ((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);
-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)
- aim_rxcallback_t userfunc;
- struct aim_incomingim_ch4_args args;
- * Make a bstream for the meaty part. Yum. Meat.
- if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 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);
- /* There seems to be a problem with the length in SMS msgs from server, this fixed it */
- args.msglen = block->length - 6;
- 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);
- * 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)
- aim_userinfo_t userinfo;
- memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
- cookie = byte_stream_getraw(bs, 8);
- * 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).
- ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie);
- } else if (channel == 2) {
- * 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) {
- tlvlist = aim_tlvlist_read(bs);
- ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
- aim_tlvlist_free(tlvlist);
- purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel);
- aim_info_free(&userinfo);
-static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- 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);
- * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
-int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
- GSList *tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- 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);
- * Send confirmation for a channel 2 message (Miranda wants it by default).
-aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie)
- 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);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b,
- 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)
- aim_rxcallback_t userfunc;
- guint16 channel, reason;
- PurpleAccount *account;
- PurplePresence *presence;
- 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);
- 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");
- char *tmp1, *tmp2, *unescaped_xstatus;
- /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
- xstatus = g_string_new(NULL);
- tmp1 = strstr(xml, "<title>");
- tmp2 = strstr(tmp1, "</title>");
- g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
- tmp1 = strstr(xml, "<desc>");
- tmp2 = strstr(tmp1, "</desc>");
- 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");
- purple_protocol_got_user_status(account, bn,
- PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME),
- PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL);
- g_free(unescaped_xstatus);
- purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
- purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
- } else if (channel == 0x0004) { /* ICQ message */
- case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
- guint8 statusmsgtype, *msg;
- 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) {
- state = AIM_ICQ_STATE_AWAY;
- state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
- state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
- state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
- state = AIM_ICQ_STATE_CHAT;
- 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);
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, channel, bn, reason);
- * 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)
- 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);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
- aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
- * 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)
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
- byte_stream_new(&bs, 11 + strlen(bn) + 2);
- snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
- byte_stream_put32(&bs, 0x00000000);
- byte_stream_put32(&bs, 0x00000000);
- * Channel (should be 0x0001 for mtn)
- byte_stream_put16(&bs, channel);
- 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);
- * Subtype 0x0006 - Send eXtra Status request
-int icq_im_xstatus_request(OscarData *od, const char *sn)
- GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
- ByteStream bs, header, plugindata;
- PurpleAccount *account;
- 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[] = {
- 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)))
- fmt = "<N><QUERY><Q><PluginID>srvMng</PluginID></Q></QUERY><NOTIFY><srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>2</trans><senderId>%s</senderId></req></srv></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
- 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);
-int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
- PurpleAccount *account;
- const char *formatted_msg;
- static const guint8 plugindata[] = {
- 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><ret event='OnRemoteNotification'><srv><id>cAwaySrv</id><val srv_id='cAwaySrv'><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>&l t;uin>%s</uin><index>1</index><title>%s</title><desc>%s</desc></Root></val></srv><srv><id>cRandomizerSrv</id><val srv_id='cRandomizerSrv'>undefined</val></srv></ret></RES></NR>\r\n";
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
- account = purple_connection_get_account(od->gc);
- /* if (purple_strequal(account->username, sn))
- icq_im_xstatus_request(od, sn); */
- status = purple_presence_get_active_status(purple_account_get_presence(account));
- title = purple_status_get_name(status);
- formatted_msg = purple_status_get_attr_string(status, "message");
- msg = purple_markup_strip_html(formatted_msg);
- statxml = g_strdup_printf(fmt, purple_account_get_username(account), title, msg);
- 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);
- byte_stream_destroy(&bs);
- * 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)
- aim_rxcallback_t userfunc;
- 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);
- event = byte_stream_get16(bs);
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, channel, bn, event);
-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);
-msg_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_ICBM;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "messaging", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
-#define AIM_ICQ_INFO_REQUEST 0x04b2
-#define AIM_ICQ_ALIAS_REQUEST 0x04ba
-int compare_icq_infos(gconstpointer a, gconstpointer b)
- const struct aim_icq_info* aa = a;
- return aa->reqid - *bb;
-static void aim_icq_freeinfo(struct aim_icq_info *info) {
- g_free(info->homecity);
- g_free(info->homestate);
- g_free(info->homephone);
- g_free(info->homeaddr);
- g_free(info->personalwebpage);
- for (i = 0; i < info->numaddresses; i++)
- g_free(info->email2[i]);
- g_free(info->workcity);
- g_free(info->workstate);
- g_free(info->workphone);
- g_free(info->workaddr);
- g_free(info->workcompany);
- g_free(info->workdivision);
- g_free(info->workposition);
- g_free(info->workwebpage);
- g_free(info->status_note_title);
- g_free(info->auth_request_reason);
-int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs)
- aim_snac_t *original_snac = aim_remsnac(od, error_snac->id);
- GSList *original_info_ptr;
- struct aim_icq_info *original_info;
- 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");
- 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");
- 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);
- 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);
- purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type);
- aim_icq_freeinfo(original_info);
- od->icq_info = g_slist_remove(od->icq_info, original_info_ptr);
- g_free(original_snac->data);
-aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- 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);
- * Change your ICQ password.
- * @param od The oscar session
- * @param passwd The new password. If this is longer than 8 characters it
- * @return Return 0 if no errors, otherwise return the error number.
-int aim_icq_changepasswd(OscarData *od, const char *passwd)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- 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);
-int aim_icq_getallinfo(OscarData *od, const char *uin)
- struct aim_icq_info *info;
- guint16 request_type = AIM_ICQ_INFO_REQUEST;
- if (!uin || uin[0] < '0' || uin[0] > '9')
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- 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);
- od->icq_info = g_slist_prepend(od->icq_info, info);
-int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason)
- struct aim_icq_info *info;
- guint16 request_type = AIM_ICQ_ALIAS_REQUEST;
- if (!uin || uin[0] < '0' || uin[0] > '9')
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- 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->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);
- * 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:
- * <destination>full_phone_without_leading_+</destination>
- * <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>
- * 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)
- PurpleAccount *account;
- const char *timestr, *username;
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
- if (!name || !msg || !alias)
- account = purple_connection_get_account(od->gc);
- username = purple_account_get_username(account);
- 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>"
- "<codepage>1252</codepage>"
- "<senders_UIN>%s</senders_UIN>"
- "<senders_name>%s</senders_name>"
- "<delivery_receipt>Yes</delivery_receipt>"
- name, stripped, username, alias, timestr);
- 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);
-gotalias(OscarData *od, struct aim_icq_info *info)
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- 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);
- 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);
- * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
-icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs)
- 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");
- 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 */
- 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);
- 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);
- case 0x00a0: { /* hide ip status */
- case 0x00aa: { /* password change status */
- 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);
- /* 1 byte hide email flag? */
- 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 */
- 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));
- case 0x00e6: { /* additional personal information */
- info->info = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)-1);
- case 0x00eb: { /* email address(es) */
- 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 */
- case 0x00f0: { /* personal interests */
- case 0x00fa: { /* past background and current organizations */
- 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? */
- case 0x010e: { /* unknown */
- 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 */
- /* status note title and send request for status note text */
- char *status_note_title = NULL;
- conn = flap_connection_findbygroup(od, 0x0004);
- purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n");
- 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);
- uin = aim_tlv_getvalue_as_string(tlv);
- tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1);
- /* 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(status_note_title);
- if (status_note_title[0] == '\0')
- PurpleAccount *account;
- PurplePresence *presence;
- 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);
- struct aim_icq_info *info;
- 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->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);
- } /* End switch statement */
- if (!(snac->flags & 0x0001)) {
- oscar_user_info_display_icq(od, info);
- if (info->uin && info->nick)
- aim_icq_freeinfo(info);
- od->icq_info = g_slist_remove(od->icq_info, info);
- aim_tlvlist_free(tlvlist);
-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);
-icq_shutdown(OscarData *od, aim_module_t *mod)
- g_slist_free_full(od->icq_info, (GDestroyNotify)aim_icq_freeinfo);
-icq_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_ICQ;
- mod->toolversion = 0x047c;
- strncpy(mod->name, "icq", sizeof(mod->name));
- mod->snachandler = snachandler;
- mod->shutdown = icq_shutdown;
--- 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.
-/* Define to log unknown TLVs */
-/* #define LOG_UNKNOWN_TLV */
- * These are CLSIDs. They should actually be of the form:
- * {0x0946134b, 0x4c7f, 0x11d1,
- * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
- * 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
- {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
- {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}},
- {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. */
-} icq_custom_icons[] = {
- {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60,
- 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}},
- {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08,
- 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}},
- {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. */
- {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1,
- 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}},
- {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0,
- 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}},
- {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a,
- 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}},
- {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42,
- 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}},
- {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4,
- 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}},
- {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60,
- 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}},
- {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70,
- 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}},
- {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27,
- 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}},
- {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c,
- 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}},
- {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76,
- 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}},
- {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff,
- 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}},
- {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65,
- 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}},
- {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9,
- 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}},
- {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48,
- 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}},
- {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78,
- 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}},
- {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d,
- 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}},
- {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66,
- 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}},
- {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4,
- 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}},
- {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3,
- 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}},
- {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5,
- 0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}},
- {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1,
- 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}},
- {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38,
- 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}},
- {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0,
- 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}},
- {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e,
- 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}},
- {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17,
- 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}},
- {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd,
- 0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}},
- {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86,
- 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}},
- {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d,
- 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}},
- {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6,
- 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}},
- {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b,
- 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}},
- {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35,
- 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}},
- {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", 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. */
- * 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.
-aim_locate_adduserinfo(OscarData *od, aim_userinfo_t *userinfo)
- cur = aim_locate_finduserinfo(od, userinfo->bn);
- 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) {
- 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_encoding);
- if (userinfo->info_len > 0) {
- cur->info = (char *)g_malloc(userinfo->info_len);
- memcpy(cur->info, userinfo->info, userinfo->info_len);
- cur->info_encoding = g_strdup(userinfo->info_encoding);
- cur->info_len = userinfo->info_len;
- if (userinfo->status != NULL) {
- 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);
- if (userinfo->status_encoding != NULL)
- cur->status_encoding = g_strdup(userinfo->status_encoding);
- cur->status_encoding = NULL;
- cur->status_len = userinfo->status_len;
- if (userinfo->itmsurl != NULL) {
- 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);
- if (userinfo->itmsurl_encoding != NULL)
- cur->itmsurl_encoding = g_strdup(userinfo->itmsurl_encoding);
- cur->itmsurl_encoding = NULL;
- cur->itmsurl_len = userinfo->itmsurl_len;
- if (userinfo->away != NULL) {
- 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);
- cur->away_encoding = g_strdup(userinfo->away_encoding);
- cur->away_len = userinfo->away_len;
- * We don't have an away message specified in this user_info
- * block, so clear any cached away message now.
- g_free(cur->away_encoding);
- cur->away_encoding = NULL;
-aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn) {
- aim_userinfo_t *cur = NULL;
- cur = od->locate.userinfo;
- if (oscar_util_name_compare(cur->bn, bn) == 0)
-aim_locate_getcaps(OscarData *od, ByteStream *bs, int len)
- for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
- 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;
- break; /* should only match once... */
- 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[10], cap[11], cap[12], cap[13],
-aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
- 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 */
- 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... */
-aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
- for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x02) {
- 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;
- break; /* should only match once... */
- purple_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]);
-byte_stream_putcaps(ByteStream *bs, guint64 caps)
- for (i = 0; byte_stream_bytes_left(bs); i++) {
- if (aim_caps[i].flag == OSCAR_CAPABILITY_LAST)
- if (caps & aim_caps[i].flag)
- byte_stream_putraw(bs, aim_caps[i].data, 0x10);
-dumptlv(OscarData *od, guint16 type, ByteStream *bs, guint8 len)
- if (!od || !bs || !len)
- 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++) {
- purple_debug_misc("oscar", "\nuserinfo: ");
- purple_debug_misc("oscar", "0x%2x ", byte_stream_get8(bs));
- purple_debug_misc("oscar", "\n");
-aim_info_free(aim_userinfo_t *info)
- g_free(info->iconcsum);
- g_free(info->info_encoding);
- g_free(info->status_encoding);
- g_free(info->itmsurl_encoding);
- g_free(info->away_encoding);
- {"icqmood0", "shopping"},
- {"icqmood1", "bathing"},
- {"icqmood2", "sleepy"},
- {"icqmood5", "thinking"},
- {"icqmood8", "meeting"},
- {"icqmood9", "coffee"},
- {"icqmood10", "music"},
- {"icqmood12", "cinema"},
- {"icqmood13", "smile-big"},
- {"icqmood14", "phone"},
- {"icqmood15", "console"},
- {"icqmood16", "studying"},
- {"icqmood18", "sleeping"},
- {"icqmood19", "surfing"},
- {"icqmood20", "internet"},
- {"icqmood21", "working"},
- {"icqmood22", "typing"},
- {"icqmood23", "angry"},
- * AIM is fairly regular about providing user info. This is a generic
- * routine to extract it in its standard form.
-aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
- /* 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++) {
- type = byte_stream_get16(bs);
- length = byte_stream_get16(bs);
- curpos = byte_stream_curpos(bs);
- endpos = curpos + MIN(length, byte_stream_bytes_left(bs));
- * Specified as any of the following ORed together:
- * 0x0001 Unconfirmed account
- * 0x0004 AOL Main Service user
- * 0x0010 Free (AIM) user
- * 0x0040 ICQ user (AIM bit also set)
- * 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
- * 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) {
- * 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) {
- * Number of minutes since the user actively used the
- * Note that the client tells the server when to start
- * counting idle times, so this may or may not be
- outinfo->idletime = byte_stream_get16(bs);
- outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
- } else if (type == 0x0005) {
- * 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's Away/DND/etc "enriched" status. Some decoding
- * of values done by Scott <darkagl@pcnet.com>
- 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) {
- * 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.
- * 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);
- * 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);
- purple_protocol_got_user_status(account, outinfo->bn, "mood",
- PURPLE_MOOD_NAME, mood,
- 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
- * 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) {
- } 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) {
- * 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.
- guint8 number2, length2;
- * 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));
- case 0x0000: { /* This is an official buddy icon? */
- /* This is always 5 bytes of "0x02 01 d2 04 72"? */
- 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;
- case 0x0002: { /* A status/available message */
- g_free(outinfo->status);
- g_free(outinfo->status_encoding);
- 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 */
- outinfo->status_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
- /* No explicit encoding, client should use UTF-8 */
- outinfo->status_encoding = NULL;
- byte_stream_advance(bs, length2);
- outinfo->status_len = 0;
- outinfo->status = g_strdup("");
- outinfo->status_encoding = NULL;
- case 0x0009: { /* An iTunes Music Store link */
- g_free(outinfo->itmsurl);
- g_free(outinfo->itmsurl_encoding);
- 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 */
- outinfo->itmsurl_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
- /* No explicit encoding, client should use UTF-8 */
- outinfo->itmsurl_encoding = NULL;
- byte_stream_advance(bs, length2);
- outinfo->itmsurl_len = 0;
- outinfo->itmsurl = g_strdup("");
- outinfo->itmsurl_encoding = NULL;
- case 0x000e: { /* ICQ mood */
- PurpleAccount *account = purple_connection_get_account(od->gc);
- const char *mood = NULL;
- icqmood = byte_stream_getstr(bs, length2);
- /* icqmood = "" means X-Status
- * with no mood icon. */
- for (i = 0; icqmoods[i].icqmood; i++) {
- if (purple_strequal(icqmood, icqmoods[i].icqmood)) {
- mood = icqmoods[i].mood;
- break; /* should only match once... */
- purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood);
- purple_protocol_got_user_status(account, outinfo->bn, "mood",
- PURPLE_MOOD_NAME, mood,
- purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood");
- 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:
- } 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.
- * 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
- purple_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
- purple_debug_misc("oscar", "userinfo: bn =%s\n", outinfo->bn);
- dumptlv(od, type, bs, length);
- byte_stream_setpos(bs, endpos);
- aim_locate_adduserinfo(od, outinfo);
-error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- snac2 = aim_remsnac(od, snac->id);
- purple_debug_misc("oscar", "locate error: received response from unknown request!\n");
- if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
- purple_debug_misc("oscar", "locate error: received response from invalid request! %d\n", snac2->family);
- purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n");
- reason = byte_stream_get16(bs);
- oscar_user_info_display_error(od, reason, bn);
- * Request Location services rights.
-aim_locate_reqrights(OscarData *od)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_REQRIGHTS);
- * 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?]
-rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- 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);
- * 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:
- * 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
-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)
- GSList *tlvlist = NULL;
- static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
- if (!profile && !awaymsg)
- if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
- /* Build the packet first to get real length */
- 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);
- * 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
- * - If you do not send the type 4 TLV, your status does not change
- * (that is, if you were away, you'll remain away).
- 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);
- 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);
- * Subtype 0x0004 - Set your client's capabilities.
-aim_locate_setcaps(OscarData *od, guint64 caps)
- 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);
- GSList *tlvlist = NULL;
- if (!(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
- 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);
-userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_userinfo_t *userinfo, *userinfo2;
- 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;
- if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
- PurpleAccount *account = purple_connection_get_account(od->gc);
- 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);
- purple_protocol_got_user_status(account, userinfo->bn, "mood",
- PURPLE_MOOD_NAME, mood,
- 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);
- /* Show the info to the user */
- oscar_user_info_display_aim(od, userinfo2);
- * 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.
-aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags)
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
- 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);
-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);
-locate_shutdown(OscarData *od, aim_module_t *mod)
- while (od->locate.userinfo) {
- del = od->locate.userinfo;
- od->locate.userinfo = od->locate.userinfo->next;
-locate_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_LOCATE;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "locate", sizeof(mod->name));
- mod->snachandler = snachandler;
- mod->shutdown = locate_shutdown;
-icq_get_custom_icon_description(const char *mood)
- 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;
-icq_get_custom_icon_data(const char *mood)
- 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;
-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).
- * 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 */
-aim_srv_clientready(OscarData *od, FlapConnection *conn)
- 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)
- 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.
-hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- 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
- aim_srv_setversions(od, conn);
-/* Subtype 0x0004 - Service request */
-aim_srv_requestnew(OscarData *od, guint16 serviceid)
- GSList *tlvlist = NULL;
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
- byte_stream_new(&bs, 6);
- byte_stream_put16(&bs, serviceid);
- /* 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
-aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance)
- GSList *tlvlist = NULL;
- struct chatsnacinfo csi;
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
- if (!conn || !roomname || roomname[0] == '\0')
- 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);
- /* 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);
-/* Subtype 0x0005 - Redirect */
-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;
- aim_snac_t *origsnac = NULL;
- 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);
- 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);
- g_free(origsnac->data);
- aim_tlvlist_free(tlvlist);
-/* Subtype 0x0006 - Request Rate Information. */
-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...
- * - Everything thats not in any of the other classes
- * - Buddy list add/remove
- * - Permit list add/remove
- * - Deny list add/remove
- * - User information requests
- * - A few unknowns: 2/9, 2/b, and f/2
- * - 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)
- for (tmp = rateclasses; tmp != NULL; tmp = tmp->next)
- struct rateclass *rateclass;
- if (rateclass->classid == id)
-/* Subtype 0x0007 - Rate Parameters */
-rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- 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;
- 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);
- 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;
- 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);
- 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);
- g_hash_table_insert(conn->rateclass_members,
- GUINT_TO_POINTER((group << 16) + subtype),
- * 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);
-/* Subtype 0x0008 - Add Rate Parameter */
-aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
- byte_stream_new(&bs, 502);
- for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
- struct rateclass *rateclass;
- 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 */
-ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- struct rateclass *rateclass;
- static const char *codes[5] = {
- gettimeofday(&now, NULL);
- code = byte_stream_get16(bs);
- classid = byte_stream_get16(bs);
- rateclass = rateclass_find(conn->rateclasses, classid);
- /* This should never really happen */
- 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);
- 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");
- * 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 */
-serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame);
-/* Subtype 0x000d - Service Resume */
-serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame);
-/* Subtype 0x000e - Request self-info */
-aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
- aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
-/* Subtype 0x000f - Self User Info */
-selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- 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);
-/* Subtype 0x0010 - Evil Notification */
-evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- 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);
- * 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.
-aim_srv_setidle(OscarData *od, guint32 idletime)
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
- 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.
-migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- * 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
- * Let's play dumb and not support that.
- groupcount = byte_stream_get16(bs);
- for (i = 0; i < groupcount; i++) {
- 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);
-/* Subtype 0x0013 - Message of the Day */
-motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- * 4 Nothing's wrong ("top o the world" -- normal)
- * 5 Lets-break-something.
- id = byte_stream_get16(bs);
- 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);
- aim_tlvlist_free(tlvlist);
- * 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.
-aim_srv_setversions(OscarData *od, FlapConnection *conn)
- 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)
- 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 */
-hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- /* This is frivolous. (Thank you SmarterChild.) */
- vercount = byte_stream_bytes_left(bs)/4;
- /* XXX: vercount probably should be used for reading versions. */
- versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
- aim_srv_reqrates(od, conn);
- * 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
- * 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.
-aim_srv_setextrainfo(OscarData *od,
- gboolean seticqstatus, guint32 icqstatus,
- gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
- GSList *tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
- aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
- AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
- size_t statusmsglen, itmsurllen;
- 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);
-/* 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.
-aim_srv_set_dc_info(OscarData *od)
- 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);
- flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE,
- 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?
-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;
- aim_srv_requestnew(od, SNAC_FAMILY_BART);
- PurpleAccount *account = purple_connection_get_account(od->gc);
- PurpleImage *img = purple_buddy_icons_find_account_icon(account);
- purple_debug_info("oscar",
- "Uploading icon to icon server\n");
- purple_image_get_data(img),
- purple_image_get_data_size(img));
- } else if (flags == 0x81) {
- PurpleAccount *account = purple_connection_get_account(od->gc);
- PurpleImage *img = purple_buddy_icons_find_account_icon(account);
- aim_ssi_seticon(od, md5, length);
-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);
-int service_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_OSERVICE;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "oservice", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
- * This is all there is to it.
- * The message is probably HTML.
-parsepopup(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- 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);
-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);
-popups_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_POPUP;
- mod->toolversion = 0x0001;
- strncpy(mod->name, "popup", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
-reportinterval(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- interval = byte_stream_get16(bs);
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, interval);
-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);
-stats_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_STATS;
- mod->toolversion = 0x0001;
- strncpy(mod->name, "stats", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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()
- * 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)
- aim_rxcallback_t userfunc;
- /* 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);
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, snac2->data /* address */);
-int aim_search_address(OscarData *od, const char *address)
- conn = flap_connection_findbygroup(od, SNAC_FAMILY_USERLOOKUP);
- 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);
-static int reply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- char *cur = NULL, *buf = NULL;
- aim_rxcallback_t userfunc;
- 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);
- * 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);
- aim_tlvlist_free(tlvlist);
- if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
- ret = userfunc(od, conn, frame, searchaddr, j, buf);
-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);
-search_modfirst(OscarData *od, aim_module_t *mod)
- mod->family = SNAC_FAMILY_USERLOOKUP;
- mod->toolversion = 0x0629;
- strncpy(mod->name, "userlookup", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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
- * 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.
-flap_connection_send_version(OscarData *od, FlapConnection *conn)
- 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.
-flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
- 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);
-flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_logins)
- 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);
- gchar *clientstring = oscar_get_clientstring();
- aim_tlvlist_add_str(&tlvlist, 0x0003, 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)
- key = GUINT_TO_POINTER((family << 16) + subtype);
- rateclass = g_hash_table_lookup(conn->rateclass_members, key);
- 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.
-rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now)
- unsigned long timediff; /* In milliseconds */
- /* 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
-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);
- 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. */
- 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_queue_pop_head(queue);
- /* We emptied the queue */
-static gboolean flap_connection_send_queued(gpointer 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),
- 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;
- /* We couldn't send all our SNACs. Keep trying */
- * 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.
-flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority)
- 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);
- byte_stream_rewind(data);
- byte_stream_putbs(&frame->data, data, length);
- if (conn->queued_timeout != 0)
- else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL)
- 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);
- rateclass->current = new_current;
- rateclass->last.tv_sec = now.tv_sec;
- rateclass->last.tv_usec = now.tv_usec;
- /* 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 (!conn->queued_snacs)
- conn->queued_snacs = g_queue_new();
- g_queue_push_tail(conn->queued_snacs, queued_snac);
- 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);
- flap_connection_send(conn, frame);
-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.
-flap_connection_send_close(OscarData *od, FlapConnection *conn)
- 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.
-flap_connection_send_keepalive(OscarData *od, FlapConnection *conn)
- 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.
-flap_connection_new(OscarData *od, int type)
- conn = g_new0(FlapConnection, 1);
- conn->buffer_outgoing = purple_circular_buffer_new(0);
- conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal);
- od->oscar_connections = g_slist_prepend(od->oscar_connections, 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.
-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);
- 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;
- purple_ssl_close(conn->gsc);
- 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;
- * @param frame The frame to free.
-flap_frame_destroy(FlapFrame *frame)
- g_free(frame->data.data);
-flap_connection_destroy_cb(gpointer data)
- PurpleAccount *account;
- aim_rxcallback_t userfunc;
- /* Explicitly added for debugging #5927. Don't re-order this, only
- * consider removing it.
- purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn);
- 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! */
- 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"),
- 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"),
- * We shouldn't print a message for some disconnect_reasons.
- * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
- purple_connection_error(od->gc, reason, tmp);
- flap_connection_close(od, conn);
- g_free(conn->error_message);
- * 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_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_queue_free(conn->queued_lowpriority_snacs);
- if (conn->queued_timeout > 0)
- g_source_remove(conn->queued_timeout);
- * See the comments for the parameters of
- * flap_connection_schedule_destroy().
-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
- * @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().
-flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
- if (conn->destroy_timeout != 0)
- /* Already taken care of */
- 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
- * 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
-flap_connection_findbygroup(OscarData *od, guint16 group)
- for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
- if (g_slist_find(conn->groups, GUINT_TO_POINTER(group)) != NULL) {
- * Locates a connection of the specified type in the
- * 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.
-flap_connection_getbytype(OscarData *od, int type)
- for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
- if ((conn->type == type) && (conn->connected))
-flap_connection_getbytype_all(OscarData *od, int type)
- for (cur = od->oscar_connections; cur; cur = cur->next)
- if (conn->type == type)
- * Allocate a new FLAP frame.
- * @param channel The FLAP channel. This is almost always 2.
-flap_frame_new(OscarData *od, guint16 channel, int datalen)
- frame = g_new0(FlapFrame, 1);
- frame->channel = channel;
- byte_stream_new(&frame->data, datalen);
-parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
- if (byte_stream_bytes_left(&frame->data) < 10)
- 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
- 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
- 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))
- if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
-parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype)
- 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))
- if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
-parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
- if (byte_stream_bytes_left(&frame->data) == 0) {
- /* XXX should do something with this */
- /* An ICQ account is logging in */
- if (conn->type == SNAC_FAMILY_AUTH)
- parse_fakesnac(od, conn, frame, 0x0017, 0x0003);
- 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);
- * Takes a new incoming FLAP frame and sends it to the appropriate
- * handler function to be parsed.
-parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame)
- if (frame->channel == 0x01) {
- guint32 flap_version = byte_stream_get32(&frame->data);
- if (flap_version != 0x00000001)
- purple_debug_warning("oscar", "Expecting FLAP version "
- "0x00000001 but received FLAP version %08x. Closing connection.\n",
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_INVALID_DATA, NULL);
- 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.
-flap_connection_recv(FlapConnection *conn)
- /* Read data until we run out of data and break out of the loop */
- /* 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) */
- read = purple_ssl_read(conn->gsc, buf, buflen);
- read = recv(conn->fd, buf, buflen, 0);
- /* Check if the FLAP server closed the connection */
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
- /* If there was an error then close the connection */
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
- 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)
- /* 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);
- /* 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;
- buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
- /* Read data into the temporary FlapFrame until it is complete */
- read = purple_ssl_read(conn->gsc, buf, buflen);
- read = recv(conn->fd, buf, buflen, 0);
- /* Check if the FLAP server closed the connection */
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
- conn->buffer_incoming.data.offset += read;
- if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len)
- /* Waiting for more data to arrive */
- /* 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;
-flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
- FlapConnection *conn = data;
- flap_connection_recv(conn);
-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.
-send_cb(gpointer data, gint source, PurpleInputCondition cond)
- const gchar *output = NULL;
- writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing);
- output = purple_circular_buffer_get_output(conn->buffer_outgoing);
- purple_input_remove(conn->watcher_outgoing);
- conn->watcher_outgoing = 0;
- ret = purple_ssl_write(conn->gsc, output, writelen);
- ret = send(conn->fd, output, writelen, 0);
- if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
- purple_input_remove(conn->watcher_outgoing);
- conn->watcher_outgoing = 0;
- purple_ssl_close(conn->gsc);
- flap_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
- purple_circular_buffer_mark_read(conn->buffer_outgoing, ret);
-flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count)
- /* 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 */
- /* 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)
- conn->watcher_outgoing = purple_input_add(conn->gsc->fd,
- PURPLE_INPUT_WRITE, send_cb, conn);
- } else if (conn->fd >= 0) {
- conn->watcher_outgoing = purple_input_add(conn->fd,
- PURPLE_INPUT_WRITE, send_cb, conn);
-sendframe_flap(FlapConnection *conn, FlapFrame *frame)
- payloadlen = byte_stream_curpos(&frame->data);
- byte_stream_new(&bs, 6 + payloadlen);
- byte_stream_put8(&bs, 0x2a);
- byte_stream_put8(&bs, frame->channel);
- byte_stream_put16(&bs, frame->seqnum);
- byte_stream_put16(&bs, payloadlen);
- 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);
-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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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 "oscarcommon.h"
-icq_get_account_text_table(PurpleAccount *account)
- table = g_hash_table_new(g_str_hash, g_str_equal);
- g_hash_table_insert(table, "login_label", (gpointer)_("ICQ UIN..."));
-icq_get_max_message_size(PurpleConversation *conv)
- /* XXX: got from pidgin-otr - verify and document it */
-icq_protocol_init(ICQProtocol *self)
- PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
- protocol->id = "prpl-icq";
- protocol->name = "ICQ";
- oscar_init_account_options(protocol, TRUE);
-icq_protocol_class_init(ICQProtocolClass *klass)
- PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
- protocol_class->list_icon = oscar_list_icon_icq;
-icq_protocol_class_finalize(G_GNUC_UNUSED ICQProtocolClass *klass)
-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
-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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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
-#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))
- OscarProtocolClass parent_class;
- * Registers the ICQProtocol type in the type system.
-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 "oscarcommon.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
- gchar *principal1_again;
- aim_xsnac_token_t *tokens;
-static gchar *get_kdc_url(OscarData *od)
- PurpleAccount *account = purple_connection_get_account(od->gc);
- gchar *port_str = NULL;
- server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
- port = purple_account_get_int(account, "port", AIM_DEFAULT_KDC_PORT);
- port_str = g_strdup_printf(":%d", port);
- url = g_strdup_printf("https://%s%s/", server, port_str ? port_str : "");
-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);
-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,
- * 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]);
-aim_xsnac_free(aim_xsnac_t *xsnac)
- 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);
-kerberos_login_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
- OscarData *od = user_data;
- aim_xsnac_t xsnac = {0};
- gchar *tlsCertName = NULL;
- guint32 cookie_len = 0;
- g_clear_object(&od->http_conns);
- if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
- 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);
- 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"));
- if (xsnac.family != 0x50C || xsnac.subtype != 0x0003) {
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Error parsing response from authentication server"));
- 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++) {
- tlv = aim_tlvlist_readnum(&bs, 1);
- xsnac.tokens[i].main_tlv = tlv->data;
- 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")) {
- tlv = aim_tlv_gettlv(xsnac.tokens[i].tlvlist, 0x0003, 1);
- 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);
- cookie_len = tlv->length;
- port = AIM_DEFAULT_KDC_PORT;
- for (i = 0; i < strlen(bosip); i++) {
- port = atoi(&(bosip[i+1]));
- host = g_strndup(bosip, i);
- oscar_connect_to_bos(gc, od, host, port, cookie, cookie_len, tlsCertName);
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unknown error during authentication"));
- aim_xsnac_free(&xsnac);
- * 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
- * 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
-void send_kerberos_login(OscarData *od, const char *username)
- gchar password_xored[MAXAIMPASSLEN];
- const gchar *client_key;
- 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,
- 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,
- const gchar pre_password[] = {
- 0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01,
- 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,
- 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));
- msg = soup_message_new("POST", url);
- soup_message_set_request(msg, "application/x-snac", SOUP_MEMORY_TAKE,
- soup_message_headers_replace(msg->request_headers, "Accept",
- soup_session_queue_message(od->http_conns, msg, kerberos_login_cb, od);
- g_string_free(body, FALSE);
--- 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 @@
- oscar_link_args = ['-Wl,--export-all-symbols']
- oscar_prpl = shared_library('oscar', OSCARSOURCES,
- link_args : oscar_link_args,
- dependencies : [libpurple_dep, glib, libsoup, ws2_32],
- install : true, install_dir : PURPLE_PLUGINDIR)
--- 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.
- * Generic routine for sending commands.
- * I know I can do this in a smarter way...but I'm not thinking straight
- * 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.
-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);
-aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype)
- snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, family, subtype, snacid, NULL);
-aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *longdata)
- aim_genericreq_n(od, conn, family, subtype);
- 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.
-generror(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
- aim_rxcallback_t userfunc;
- 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);
-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);
-misc_modfirst(OscarData *od, aim_module_t *mod)
- mod->flags = AIM_MODFLAG_MULTIFAMILY;
- strncpy(mod->name, "misc", sizeof(mod->name));
- mod->snachandler = snachandler;
--- 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.
- * 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
- * @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)
- newcook = aim_checkcookie(od, cookie->cookie, cookie->type);
- if (newcook == cookie) {
- newcook->addtime = time(NULL);
- aim_cookie_free(od, newcook);
- cookie->addtime = time(NULL);
- cookie->next = od->msgcookies;
- od->msgcookies = cookie;
- * 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)
- for (prev = &od->msgcookies; (cur = *prev); ) {
- if ((cur->type == type) &&
- (memcmp(cur->cookie, cookie, 8) == 0)) {
- * 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
-IcbmCookie *aim_mkcookie(guint8 *c, int type, void *data)
- cookie = g_new0(IcbmCookie, 1);
- memcpy(cookie->cookie, c, 8);
- * 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)
- for (cur = od->msgcookies; cur; cur = cur->next) {
- if ((cur->type == type) &&
- (memcmp(cur->cookie, cookie, 8) == 0))
- * 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;
- for (prev = &od->msgcookies; (cur = *prev); ) {
--- 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 "conversation.h"
-#include "image-store.h"
-#define DIRECTIM_MAX_FILESIZE 52428800
- * Free any ODC related data and print a message to the conversation
- * window based on conn->disconnect_reason.
-peer_odc_close(PeerConnection *conn)
- 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"),
- 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."));
- * We shouldn't print a message for some disconnect_reasons.
- * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
- 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);
- if (conn->frame != NULL)
- g_free(frame->payload.data);
- * Write the given OdcFrame to a ByteStream and send it out
- * on the established PeerConnection.
-peer_odc_send(PeerConnection *conn, OdcFrame *frame)
- PurpleAccount *account;
- 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);
- 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.
-peer_odc_send_cookie(PeerConnection *conn)
- memset(&frame, 0, sizeof(OdcFrame));
- 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.
-peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing)
- memset(&frame, 0, sizeof(OdcFrame));
- frame.subtype = 0x0006;
- if (typing == PURPLE_IM_TYPING)
- frame.flags = 0x0002 | 0x0008;
- else if (typing == PURPLE_IM_TYPED)
- frame.flags = 0x0002 | 0x0004;
- 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.
-peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply)
- g_return_if_fail(msg != NULL);
- g_return_if_fail(len > 0);
- memset(&frame, 0, sizeof(OdcFrame));
- 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);
- * 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">
- * This is a really stupid picture:<BR>
- * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
- * Here is another one:<BR>
- * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">
- * <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
- * <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
-peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply)
- PurpleAccount *account;
- const char *msgend, *binary_start, *dataend;
- const char *tmp, *start, *end, *idstr, *src, *sizestr;
- GHashTable *embedded_datas;
- struct embedded_data *embedded_data;
- gboolean any_images = FALSE;
- PurpleMessageFlags imflags;
- account = purple_connection_get_account(gc);
- * 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)
- /* 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))
- /* Move the binary pointer from ">" to the start of the data */
- idstr = g_datalist_get_data(&attributes, "id");
- g_datalist_clear(&attributes);
- sizestr = g_datalist_get_data(&attributes, "size");
- g_datalist_clear(&attributes);
- g_datalist_clear(&attributes);
- if ((size > 0) && (tmp + size > dataend))
- embedded_data = g_new(struct embedded_data, 1);
- embedded_data->size = size;
- embedded_data->data = (const guint8 *)tmp;
- /* Skip past the closing </data> tag */
- if (g_ascii_strncasecmp(tmp, "</data>", 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("");
- 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))
- embedded_data = g_hash_table_lookup(embedded_datas,
- if ((embedded_data != NULL) && (embedded_data->size == size))
- image = purple_image_new_from_data(
- 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);
- g_string_append(newmsg, utf8);
- img_id = purple_image_store_add_temporary(image);
- /* 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 */
- /* Append any remaining message data */
- utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp);
- g_string_append(newmsg, utf8);
- /* Display the message we received */
- imflags |= PURPLE_MESSAGE_IMAGES;
- 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.
-peer_odc_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
- /* Read data into the temporary buffer until it is complete */
- /* Check if the remote user closed the connection */
- peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- peer_connection_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
- if (bs->offset < bs->len)
- /* Waiting for more data to arrive */
- /* 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);
- 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.
-peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs)
- 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);
- * 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);
- * 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);
- * If they connected to us then close the listener socket
- * and send them our cookie.
- if (conn->listenerfd != -1)
- close(conn->listenerfd);
- /* 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);
- 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);
- 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);
- 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);
- peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
- /* 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;
- purple_input_remove(conn->watcher_incoming);
- conn->watcher_incoming = purple_input_add(conn->fd,
- PURPLE_INPUT_READ, peer_odc_recv_cb, conn);
--- 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
- * 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.
-#define CHECKSUM_BUFFER_SIZE 256 * 1024
- guint8 buffer[CHECKSUM_BUFFER_SIZE];
-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);
- * 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
- * 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
-peer_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevchecksum, int odd)
- guint32 checksum, oldchecksum;
- checksum = (prevchecksum >> 16) & 0xffff;
- * 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.
- for (; i < bufferlen; i++)
- oldchecksum = checksum;
- * The following appears to be necessary.... It happens
- * every once in a while and the checksum doesn't fail.
- if (checksum > oldchecksum)
- checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
- checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
-peer_oft_checksum_file_piece(gpointer data)
- ChecksumData *checksum_data;
- 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);
- checksum_data->checksum = peer_oft_checksum_chunk(checksum_data->buffer, bytes, checksum_data->checksum, checksum_data->total & 1);
- checksum_data->total += bytes;
- 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);
- * 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.
-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);
- checksum_data->timer = g_timeout_add(10,
- peer_oft_checksum_file_piece, checksum_data);
- conn->checksum_data = checksum_data;
-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.
-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) &&
- 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.
-peer_oft_send(PeerConnection *conn, OftFrame *frame)
- 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);
-peer_oft_send_prompt(PeerConnection *conn)
- conn->xferdata.type = PEER_TYPE_PROMPT;
- peer_oft_send(conn, &conn->xferdata);
-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);
-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);
-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.
-start_transfer_when_done_sending_data(gpointer data)
- if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0)
- conn->sending_data_timer = 0;
- purple_xfer_start(conn->xfer, fd, NULL, 0);
- * 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.
-destroy_connection_when_done_sending_data(gpointer 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);
- * 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?
-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.
-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);
- /* 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);
-peer_oft_recv_frame_resume_checksum_calculated_cb(gpointer data)
- ChecksumData *checksum_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;
- /* Accept the change */
- purple_xfer_set_bytes_sent(checksum_data->xfer, conn->xferdata.nrecvd);
- peer_oft_send_resume_accept(conn);
- * 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
-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);
- /* 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,
- * We just sent a file to someone. They said they got it and everything,
- * so we can close our direct connection and what not.
-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
- 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->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.
-peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs)
- 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); */
- peer_oft_recv_frame_prompt(conn, &frame);
- case PEER_TYPE_RESUMEACK:
- peer_oft_recv_frame_ack(conn, &frame);
- peer_oft_recv_frame_resume(conn, &frame);
- peer_oft_recv_frame_done(conn, &frame);
-/*******************************************************************/
-/* Begin PurpleXfer callbacks for use when receiving a file */
-/*******************************************************************/
-peer_oft_recvcb_init(PurpleXfer *xfer)
- conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
- conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
- peer_connection_trynext(conn);
-peer_oft_recvcb_end(PurpleXfer *xfer)
- 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);
-peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size)
- /* 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 */
-/*******************************************************************/
-peer_oft_checksum_calculated_cb(gpointer data)
- ChecksumData *checksum_data;
- conn = checksum_data->conn;
- conn->xferdata.checksum = checksum_data->checksum;
- /* Start the connection process */
- peer_connection_trynext(checksum_data->conn);
-peer_oft_sendcb_init(PurpleXfer *xfer)
- 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);
- peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
- /* 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...
-peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size)
- 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 */
-/*******************************************************************/
-peer_oft_cb_generic_cancel(PurpleXfer *xfer)
- conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
- peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
-/*******************************************************************/
-/* End PurpleXfer callbacks for use when sending and receiving */
-/*******************************************************************/
- * 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
- * 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
- * -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 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.
-peer_oft_dirconvert_tostupid(char *name)
- name[0] = G_DIR_SEPARATOR;
- * Convert the directory separator from ^A (0x01) to / (0x2f)
- * @param name The filename to convert.
-peer_oft_dirconvert_fromstupid(char *name)
- if (name[0] == G_DIR_SEPARATOR)
--- 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 @@
- * 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 "conversation.h"
-#include "image-store.h"
-#include "purpleaccountoption.h"
-#include "oscarcommon.h"
-static PurpleProtocol *aim_protocol = NULL;
-static PurpleProtocol *icq_protocol = NULL;
-static guint64 purple_caps =
- | 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 oscar_ask_directim_data
-/* 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) {
-const char *oscar_get_locale_charset(void) {
- static const char *charset = NULL;
- g_get_charset(&charset);
-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"));
- return g_strdup(_("Online"));
-static char *extract_name(const char *name) {
- x = strchr(x + 1, '-');
- for (i = 0, j = 0; x[i]; i++) {
- strncpy(hex, x + ++i, 2);
- tmp[j++] = strtol(hex, NULL, 16);
-static struct chat_connection *
-find_oscar_chat(PurpleConnection *gc, int id)
- OscarData *od = purple_connection_get_protocol_data(gc);
- struct chat_connection *cc;
- for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
- cc = (struct chat_connection *)cur->data;
-static struct chat_connection *
-find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn)
- OscarData *od = purple_connection_get_protocol_data(gc);
- struct chat_connection *cc;
- for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
- cc = (struct chat_connection *)cur->data;
-static struct chat_connection *
-find_oscar_chat_by_conv(PurpleConnection *gc, PurpleChatConversation *conv)
- OscarData *od = purple_connection_get_protocol_data(gc);
- struct chat_connection *cc;
- for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
- cc = (struct chat_connection *)cur->data;
-oscar_chat_destroy(struct chat_connection *cc)
-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.
-connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
- 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 */
- msg = g_strdup_printf(_("Unable to connect to authentication server: %s"),
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
- else if (conn->type == SNAC_FAMILY_LOCATE)
- msg = g_strdup_printf(_("Unable to connect to BOS server: %s"),
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
- /* 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.
-connection_common_established_cb(FlapConnection *conn)
- PurpleAccount *account;
- account = purple_connection_get_account(gc);
- purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
- if (conn->cookie == NULL)
- flap_connection_send_version(od, conn);
- 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));
- flap_connection_send_version_with_cookie(od, conn,
- conn->cookielen, conn->cookie);
- 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;
-connection_established_cb(gpointer data, gint source, const gchar *error_message)
- conn->connect_data = NULL;
- connection_common_error_cb(conn, error_message);
- conn->watcher_incoming = purple_input_add(conn->fd,
- PURPLE_INPUT_READ, flap_connection_recv_cb, conn);
- connection_common_established_cb(conn);
-ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc,
- PurpleInputCondition cond)
- purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn);
- connection_common_established_cb(conn);
-ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
- if (conn->watcher_outgoing)
- purple_input_remove(conn->watcher_outgoing);
- conn->watcher_outgoing = 0;
- /* sslconn frees the connection on error */
- connection_common_error_cb(conn, purple_ssl_strerror(error));
-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");
- 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_bos_reqrights(od, conn); /* TODO: Don't call this with ssi */
- purple_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS);
-flap_connection_established_admin(OscarData *od, FlapConnection *conn)
- aim_srv_clientready(od, conn);
- purple_debug_info("oscar", "connected to admin\n");
- purple_debug_info("oscar", "changing password\n");
- aim_admin_changepasswd(od, conn, od->newp, od->oldp);
- purple_debug_info("oscar", "formatting username\n");
- aim_admin_setnick(od, conn, od->newformatting);
- g_free(od->newformatting);
- od->newformatting = NULL;
- purple_debug_info("oscar", "confirming account\n");
- aim_admin_reqconfirm(od, conn);
- purple_debug_info("oscar", "requesting email address\n");
- aim_admin_getinfo(od, conn, 0x0011);
- purple_debug_info("oscar", "setting email address\n");
- aim_admin_setemail(od, conn, od->email);
-flap_connection_established_chat(OscarData *od, FlapConnection *conn)
- PurpleConnection *gc = od->gc;
- struct chat_connection *chatcon;
- aim_srv_clientready(od, conn);
- chatcon = find_oscar_chat_by_conn(gc, conn);
- chatcon->conv = purple_serv_got_joined_chat(gc, id++, chatcon->show);
-flap_connection_established_chatnav(OscarData *od, FlapConnection *conn)
- aim_srv_clientready(od, conn);
- aim_chatnav_reqrights(od, conn);
-flap_connection_established_alert(OscarData *od, FlapConnection *conn)
- aim_email_sendcookies(od);
- aim_email_activate(od);
- aim_srv_clientready(od, conn);
-flap_connection_established_bart(OscarData *od, FlapConnection *conn)
- PurpleConnection *gc = od->gc;
- aim_srv_clientready(od, conn);
- od->iconconnecting = FALSE;
- purple_icons_fetch(gc);
-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);
-idle_reporting_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
- od = purple_connection_get_protocol_data(gc);
- report_idle = !purple_strequal((const char *)value, "none");
- presence = aim_ssi_getpresence(&od->ssi.local);
- aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
- 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.
-recent_buddies_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
- od = purple_connection_get_protocol_data(gc);
- presence = aim_ssi_getpresence(&od->ssi.local);
- aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
- 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,
-oscar_get_login_server(gboolean is_icq, gboolean use_ssl)
- return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)];
-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;
-oscar_login(PurpleAccount *account)
- const gchar *encryption_type;
- const gchar *login_type;
- GList *sorted_handlers;
- GString *msg = g_string_new("");
- PurpleConnectionFlags flags;
- gc = purple_account_get_connection(account);
- 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);
- 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))) {
- 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);
- flags = PURPLE_CONNECTION_FLAG_HTML;
- if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
- 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;
- resolver = purple_proxy_get_proxy_resolver(account, &error);
- if (resolver == NULL) {
- purple_debug_error("oscar",
- "Unable to get account proxy resolver: %s",
- purple_connection_g_error(gc, error);
- 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
- * 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)) {
- purple_connection_error(
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("You required Kerberos authentication but encryption is disabled in your account settings."));
- 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));
- FlapConnection *newconn;
- newconn = flap_connection_new(od, SNAC_FAMILY_AUTH);
- 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);
- 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"));
- purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS);
-oscar_close(PurpleConnection *gc)
- 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;
- od->create_rooms = g_slist_remove(od->create_rooms, 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;
- 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.
- conn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
- ssl_connection_established_cb, ssl_connection_error_cb,
- conn->connect_data = purple_proxy_connect(NULL,
- connection_established_cb, conn);
- if (conn->gsc == NULL && conn->connect_data == NULL)
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
- od->default_port = port;
- purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
- * Only used when connecting with the old-style BUCP login.
-purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- FlapConnection *newconn;
- struct aim_authresp_info *info;
- port = purple_account_get_int(account, "port", od->default_port);
- info = va_arg(ap, struct aim_authresp_info *);
- purple_debug_info("oscar",
- "inside auth_resp (Username: %s)\n", info->bn);
- if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
- switch (info->errorcode) {
- /* Unregistered username */
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist"));
- /* 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"));
- /* Suspended account */
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended"));
- /* service temporarily unavailable */
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
- /* 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."));
- 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);
- /* 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."));
- purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason"));
- purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
- purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : "");
- purple_debug_misc("oscar",
- info->regstatus, info->email ? info->email : "null",
- purple_debug_info("oscar", "Closing auth connection...\n");
- flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL);
- for (i = 0; i < strlen(info->bosip); i++) {
- if (info->bosip[i] == ':') {
- port = atoi(&(info->bosip[i+1]));
- 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);
- * 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);
- newconn->connect_data = purple_proxy_connect(NULL, account, host, 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"));
- purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
- * Only used when connecting with the old-style BUCP login.
-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.
-purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value)
- PurpleConnection *gc = user_data;
- 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.
-purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- 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."),
- _("_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),
-purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- char *host, *separator;
- FlapConnection *newconn;
- struct aim_redirect_data *redir;
- redir = va_arg(ap, struct aim_redirect_data *);
- port = od->default_port;
- separator = strchr(redir->ip, ':');
- host = g_strndup(redir->ip, separator - redir->ip);
- port = atoi(separator + 1);
- host = g_strdup(redir->ip);
- 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(
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("You required encryption in your account settings, but one of the servers doesn't support it."));
- * 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);
- 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->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);
- newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
- ssl_connection_established_cb, ssl_connection_error_cb,
- redir->ssl_cert_cn, newconn);
- 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);
-static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleAccount *account;
- PurpleBuddy *buddy = NULL;
- PurpleStatus *previous_status = NULL;
- time_t time_idle = 0, signon = 0;
- gboolean buddy_is_away = FALSE;
- account = purple_connection_get_account(gc);
- info = va_arg(ap, aim_userinfo_t *);
- 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);
- 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
- if (!oscar_util_valid_name_icq(info->bn)) {
- gboolean bn_has_formatting = FALSE;
- for (c = info->bn; *c != '\0'; c++) {
- bn_has_formatting = TRUE;
- 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)
- 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)) {
- 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;
- status_id = OSCAR_STATUS_ID_AVAILABLE;
- if (type & AIM_ICQ_STATE_INVISIBLE)
- status_id = OSCAR_STATUS_ID_INVISIBLE;
- else if (buddy_is_away)
- status_id = OSCAR_STATUS_ID_AWAY;
- 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);
- 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)
- 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);
- 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);
- 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);
- /* 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;
- purple_protocol_got_user_idle(account, info->bn, TRUE, time_idle);
- 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));
- bi = g_new0(struct buddyinfo, 1);
- g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->bn)), bi);
- bi->ico_informed = FALSE;
- bi->ipaddr = info->icqinfo.ipaddr;
- if (info->iconcsumlen) {
- const char *saved_b16 = NULL;
- b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen);
- b = purple_blist_find_buddy(account, info->bn);
- 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);
-static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- info = va_arg(ap, aim_userinfo_t *);
- 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));
-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;
- const char *start, *end;
- purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn);
- bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
- 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)
- 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_len = args->iconlen;
- bi->ico_csum = args->iconsum;
- bi->ico_time = args->iconstamp;
- img = purple_buddy_icons_find_account_icon(account);
- (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",
- aim_im_sendch2_icon(od, userinfo->bn, data, len,
- purple_buddy_icons_get_account_icon_timestamp(account),
- aimutil_iconsum(data, len));
- tmp = g_strdup(args->msg);
- * Convert iChat color tags to normal font tags.
- if (purple_markup_find_tag("body", tmp, &start, &end, &attribs))
- const char *ichattextcolor, *ichatballooncolor;
- const char *slash_body_start, *slash_body_end = NULL; /* </body> */
- * Find the ending </body> so we can strip off the outer <html/>
- 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);
- 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);
- ichatballooncolor = g_datalist_get_data(&attribs, "ichatballooncolor");
- if (ichatballooncolor != NULL)
- tmp2 = g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor, body);
- g_datalist_clear(&attribs);
- tmp2 = g_strdup_printf("%.*s%s%s", len, tmp, body, slash_body_end ? slash_body_end + 1: "</body>");
- * Are there <html/> surrounding tags? If so, strip them out, too.
- if (purple_markup_find_tag("html", tmp, &start, &end, &attribs))
- g_datalist_clear(&attribs);
- tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
- if (purple_markup_find_tag("/html", tmp, &start, &end, &attribs))
- g_datalist_clear(&attribs);
- tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
- purple_serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
-incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, IcbmArgsCh2 *args)
- PurpleAccount *account;
- PurpleMessageFlags flags = 0;
- g_return_val_if_fail(od != NULL, 0);
- g_return_val_if_fail(od->gc != NULL, 0);
- account = purple_connection_get_account(gc);
- od = purple_connection_get_protocol_data(gc);
- 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)
- GHashTable *components;
- if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
- utf8name = oscar_encoding_to_utf8(args->encoding, args->info.chat.roominfo.name, args->info.chat.roominfo.namelen);
- tmp = extract_name(utf8name);
- components = g_hash_table_new_full(g_str_hash, g_str_equal, 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,
- 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 */
- 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.
- 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,
- 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) {
- const char *encoding = args->encoding;
- size_t len = strlen(args->info.rtfmsg.msg);
- 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);
- tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
- purple_serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
- aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie);
- } else if (args->info.rtfmsg.msgtype == 26) {
- purple_debug_info("oscar", "Sending X-Status Reply\n");
- icq_relay_xstatus(od, userinfo->bn, args->cookie);
- purple_debug_error("oscar", "Unknown request class %"
- G_GUINT64_FORMAT "\n", args->type);
-/* When someone sends you buddies */
-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);
-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);
- if (!args->type || !args->msg || !args->uin)
- purple_debug_info("oscar",
- "Received a channel 4 message of type 0x%02hx.",
- * 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
- 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]));
- case 0x01: { /* MacICQ message or basic offline message */
- gchar *uin = g_strdup_printf("%u", args->uin);
- /* 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));
- case 0x04: { /* Someone sent you a URL */
- gchar *uin = g_strdup_printf("%u", args->uin);
- gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>",
- (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]);
- purple_serv_got_im(gc, uin, message, 0, time(NULL));
- case 0x06: { /* Someone requested authorization */
- gchar *bn = g_strdup_printf("%u", args->uin);
- 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",
- aim_icq_getalias(od, bn, TRUE, reason);
- case 0x07: { /* Someone has denied you authorization */
- 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));
- 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));
- case 0x09: { /* Message from the Godly ICQ server itself, I think */
- 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));
- case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
- 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));
- case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
- 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));
- /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
- /* Someone added you to their buddy list? */
- case 0x13: { /* Someone has sent you some ICQ buddies */
- text = g_strsplit(args->msg, "\376", 0);
- /* Read the number of contacts that we were sent */
- num = text[0] ? strtoul(text[0], NULL, 10) : 0;
- if (num > 0 && errno == 0) {
- for (i=0; i<num; i++) {
- struct name_data *data;
- 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);
- 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->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),
- _("_Add"), G_CALLBACK(purple_icq_buddyadd),
- _("_Decline"), G_CALLBACK(oscar_free_name_data));
- gchar *tmp = g_strescape(args->msg, NULL);
- purple_debug_error("oscar", "Unknown syntax parsing "
- "ICQ buddies. args->msg=%s\n", tmp);
- case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */
- 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);
- taglen = byte_stream_getle32(&qbs);
- /* Avoid trying to allocate large amounts of memory, in
- case we get something unexpected. */
- tagstr = byte_stream_getstr(&qbs, taglen);
- byte_stream_advance(&qbs, 3);
- byte_stream_advance(&qbs, 4);
- smslen = byte_stream_getle32(&qbs);
- /* Avoid trying to allocate large amounts of memory, in
- case we get something unexpected. */
- 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);
- xmltmp = purple_xmlnode_get_child(xmlroot, "sender");
- uin = purple_xmlnode_get_data(xmltmp);
- xmltmp = purple_xmlnode_get_child(xmlroot, "text");
- message = purple_xmlnode_get_data(xmltmp);
- if ((uin != NULL) && (message != NULL))
- purple_serv_got_im(gc, uin, message, 0, time(NULL));
- purple_xmlnode_free(xmlroot);
- purple_debug_info("oscar",
- "Received a channel 4 message of unknown type "
- "(type 0x%02x).\n", args->type & 0xFF);
-static int purple_parse_incoming_im(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- aim_userinfo_t *userinfo;
- channel = (guint16)va_arg(ap, unsigned int);
- userinfo = va_arg(ap, aim_userinfo_t *);
- 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);
- case 2: { /* rendezvous */
- args = va_arg(ap, IcbmArgsCh2 *);
- ret = incomingim_chan2(od, conn, userinfo, args);
- struct aim_incomingim_ch4_args *args;
- args = va_arg(ap, struct aim_incomingim_ch4_args *);
- ret = incomingim_chan4(od, conn, userinfo, args);
- purple_debug_warning("oscar",
- "ICBM received on unsupported channel (channel "
-static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- guint16 nummissed, reason;
- aim_userinfo_t *userinfo;
- 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);
- case 0: /* Invalid (0) */
- "You missed %hu message from %s because it was invalid.",
- "You missed %hu messages from %s because they were invalid.",
- case 1: /* Message too large */
- "You missed %hu message from %s because it was too large.",
- "You missed %hu messages from %s because they were too large.",
- case 2: /* Rate exceeded */
- "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.",
- case 3: /* Evil Sender */
- "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.",
- case 4: /* Evil Receiver */
- "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.",
- "You missed %hu message from %s for an unknown reason.",
- "You missed %hu messages from %s for an unknown reason.",
- if (!purple_conversation_present_error(userinfo->bn, account, buf)) {
- purple_notify_error(od->gc, NULL, buf, NULL,
- purple_request_cpar_from_connection(od->gc));
-purple_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, const guchar *cookie)
- /* Rendezvous was refused. */
- conn = peer_connection_find_by_cookie(od, who, cookie);
- purple_debug_info("oscar", "Received a rendezvous cancel message "
- "for a nonexistant connection from %s.\n", who);
- peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_REFUSED, NULL);
- purple_debug_warning("oscar", "Received an unknown rendezvous "
- "message from %s. Type 0x%04hx\n", who, reason);
-static int purple_parse_clientauto_ch4(OscarData *od, const char *who, guint16 reason, guint32 state, char *msg) {
- PurpleConnection *gc = od->gc;
- /* Reply from an ICQ status message request */
- 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));
- purple_notify_userinfo(gc, who, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- purple_debug_warning("oscar",
- "Received an unknown client auto-response from %s. "
- "Type 0x%04hx\n", who, reason);
-static int purple_parse_clientauto(OscarData *od, FlapConnection *conn, FlapFrame *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 */
- if (reason == 0x0003) {
- state = va_arg(ap, guint32);
- msg = va_arg(ap, char *);
- ret = purple_parse_clientauto_ch4(od, who, reason, state, msg);
-static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- reason = (guint16) va_arg(ap, unsigned int);
- purple_debug_error("oscar", "snac threw error (reason 0x%04hx: %s)\n",
- reason, oscar_get_msgerr_reason(reason));
-static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- guint16 channel, event;
- channel = (guint16) va_arg(ap, unsigned int);
- bn = va_arg(ap, char *);
- event = (guint16) va_arg(ap, unsigned int);
- case 0x0000: /* Text has been cleared */
- case 0x000f: /* Closed IM window */
- purple_serv_got_typing_stopped(gc, bn);
- case 0x0001: /* Paused typing */
- purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPED);
- case 0x0002: /* Typing */
- purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPING);
- purple_debug_info("oscar", "Received unknown typing "
- "notification message from %s. Channel is 0x%04x "
- "and event is 0x%04hx.\n", bn, channel, event);
-static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- id = (guint16) va_arg(ap, unsigned int);
- msg = va_arg(ap, char *);
- purple_debug_misc("oscar",
- "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
- purple_notify_warning(od->gc, NULL,
- _("Your AIM connection may be lost."), NULL,
- purple_request_cpar_from_connection(od->gc));
-static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- type = (guint16) va_arg(ap, unsigned int);
- GString *msg = g_string_new("");
- struct aim_chat_exchangeinfo *exchanges;
- 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);
- od->create_rooms = g_slist_remove(od->create_rooms, cr);
- char *fqcn, *name, *ck;
- guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
- 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);
- purple_debug_warning("oscar",
- "chatnav info: unknown type (%04hx)\n", type);
-static int purple_chat_conversation_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- struct chat_connection *c = NULL;
- count = va_arg(ap, int);
- info = va_arg(ap, aim_userinfo_t *);
- c = find_oscar_chat_by_conn(gc, conn);
- for (i = 0; i < count; i++)
- purple_chat_conversation_add_user(c->conv, info[i].bn, NULL, PURPLE_CHAT_USER_NONE, TRUE);
-static int purple_chat_conversation_left(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- struct chat_connection *c = NULL;
- count = va_arg(ap, int);
- info = va_arg(ap, aim_userinfo_t *);
- c = find_oscar_chat_by_conn(gc, conn);
- for (i = 0; i < count; i++)
- purple_chat_conversation_remove_user(c->conv, info[i].bn, NULL);
-static int purple_chat_conversation_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- guint16 maxmsglen, maxvisiblemsglen;
- PurpleConnection *gc = od->gc;
- struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
- maxmsglen = (guint16)va_arg(ap, unsigned int);
- maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
- purple_debug_misc("oscar",
- "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
- maxmsglen, maxvisiblemsglen);
- ccon->maxlen = maxmsglen;
- ccon->maxvis = maxvisiblemsglen;
-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);
- info = va_arg(ap, aim_userinfo_t *);
- msg = va_arg(ap, char *);
- charset = va_arg(ap, char *);
- utf8 = oscar_encoding_to_utf8(charset, msg, len);
- purple_serv_got_chat_in(gc, ccon->id, info->bn,
- PURPLE_MESSAGE_RECV, utf8, time(NULL));
-static int purple_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleAccount *account;
- struct aim_emailinfo *emailinfo;
- char *alertitle, *alerturl;
- account = purple_connection_get_account(gc);
- emailinfo = va_arg(ap, struct aim_emailinfo *);
- havenewmail = va_arg(ap, int);
- alertitle = va_arg(ap, char *);
- alerturl = va_arg(ap, char *);
- 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);
- purple_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : "");
-static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- guint8 *iconcsum, *icon;
- guint16 iconcsumlen, iconlen;
- 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);
- * 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);
-purple_icons_fetch(PurpleConnection *gc)
- OscarData *od = purple_connection_get_protocol_data(gc);
- aim_userinfo_t *userinfo;
- conn = flap_connection_getbytype(od, SNAC_FAMILY_BART);
- if (!od->iconconnecting) {
- aim_srv_requestnew(od, SNAC_FAMILY_BART);
- od->iconconnecting = TRUE;
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleImage *img = purple_buddy_icons_find_account_icon(account);
- purple_debug_info("oscar",
- "Uploading icon to icon server");
- aim_bart_upload(od, purple_image_get_data(img),
- purple_image_get_data_size(img));
- 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, ...) {
- info = va_arg(ap, aim_userinfo_t *);
- purple_connection_set_display_name(od->gc, info->bn);
-static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- code = (guint16)va_arg(ap, int);
- msg = va_arg(ap, char *);
- 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);
- chat = purple_conversations_find_chat(gc, cc->id);
- * 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.
- buf = g_strdup_printf(_("You have been disconnected from chat "
- "room %s."), cc->name);
- purple_conversation_write_system_message(
- PURPLE_CONVERSATION(chat), buf,
- oscar_chat_kill(gc, cc);
-static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- maxsiglen = (guint16) va_arg(ap, int);
- 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));
-static int purple_parse_buddyrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- guint16 maxbuddies, maxwatchers;
- maxbuddies = (guint16) va_arg(ap, unsigned int);
- maxwatchers = (guint16) va_arg(ap, unsigned int);
- 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;
-static void oscar_format_username(PurpleConnection *gc, const char *new_display_name)
- const char *old_display_name, *username;
- 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",
- 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));
- 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, '@');
- 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. */
- g_free(od->newformatting);
- od->newformatting = tmp;
- aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
- aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), tmp);
-static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleAccount *account;
- PurplePresence *presence;
- const char *username, *message, *itmsurl;
- guint16 maxpermits, maxdenies;
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
- maxpermits = (guint16) va_arg(ap, unsigned int);
- maxdenies = (guint16) va_arg(ap, unsigned int);
- 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);
- message = purple_status_get_attr_string(status, "message");
- 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);
- presence = purple_status_get_presence(status);
- aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
- 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
- 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);
-static int purple_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleConnection *gc = od->gc;
- 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 */
- 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);
-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;
- char *email, *usernames;
- email = va_arg(ap, char *);
- usernames = va_arg(ap, char *);
- results = purple_notify_searchresults_new();
- purple_debug_error("oscar", "purple_parse_searchreply: "
- "Unable to display the search results.\n");
- purple_notify_error(gc, NULL, _("Unable to display the search "
- purple_request_cpar_from_connection(gc));
- secondary = g_strdup_printf(
- dngettext(PACKAGE, "The following username is associated with %s",
- "The following usernames are associated with %s",
- column = purple_notify_searchresults_column_new(_("Username"));
- purple_notify_searchresults_column_add(results, column);
- for (i = 0; i < num; i++) {
- 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);
-static int purple_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- email = va_arg(ap, char *);
- 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));
-static int purple_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */
- purple_debug_info("oscar",
- "account confirmation returned status 0x%04x (%s)\n", status,
- status ? "unknown" : "email sent");
- 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));
-static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- char *url, *bn, *email;
- 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 *);
- 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)) {
- 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);
- 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);
- 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));
- 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));
-oscar_keepalive(PurpleConnection *gc)
- od = purple_connection_get_protocol_data(gc);
- for (l = od->oscar_connections; l; l = l->next) {
- flap_connection_send_keepalive(od, l->data);
-oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state)
- 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);
- /* 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);
- aim_im_sendmtn(od, 0x0001, name, 0x0000);
-/* TODO: Move this into odc.c! */
-purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags imflags)
- const char *start, *end, *last;
- msg = g_string_new("<HTML><BODY>");
- data = g_string_new("<BINARY>");
- /* for each valid IMG tag... */
- while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs))
- PurpleImage *image = NULL;
- g_string_append_len(msg, last, start - last);
- src = g_datalist_get_data(&attribs, "src");
- image = purple_image_store_get_from_uri(src);
- /* ... if it refers to a valid purple 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);
- /* ... insert a new img tag with the oscar id ... */
- g_string_append_printf(msg,
- "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">",
- filename, oscar_id, size);
- g_string_append_printf(msg,
- "<IMG ID=\"%d\" DATASIZE=\"%lu\">",
- /* ... and append the data to the binary section ... */
- g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%lu\">",
- 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 */
- /* append any remaining message data */
- 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);
- /* Append any binary data that we may have */
- 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);
-oscar_send_im(PurpleConnection *gc, PurpleMessage *msg)
- PurpleAccount *account;
- 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);
- 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()
- 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);
- 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);
- struct aim_sendimext_args args;
- PurpleIMConversation *im;
- 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."),
- buddy = purple_blist_find_buddy(account, name);
- bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
- bi = g_new0(struct buddyinfo, 1);
- g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi);
- if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy)))
- args.flags |= AIM_IMFLAGS_OFFLINE;
- args.features = features_icq;
- args.featureslen = sizeof(features_icq);
- args.features = features_aim;
- args.featureslen = sizeof(features_aim);
- if (imflags & PURPLE_MESSAGE_AUTO_RESP)
- args.flags |= AIM_IMFLAGS_AWAY;
- purple_debug_info("oscar",
- "Sending buddy icon request with message\n");
- args.flags |= AIM_IMFLAGS_BUDDYREQ;
- img = purple_buddy_icons_find_account_icon(account);
- 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;
- * 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;
- if (oscar_util_valid_name_sms(name)) {
- /* Messaging an SMS (mobile) user--strip HTML */
- tmp2 = purple_markup_strip_html(tmp1);
- /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */
- tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1);
- 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);
- /* re-escape the entities */
- tmp1 = g_markup_escape_text(tmp2, -1);
- tmp2 = purple_strdup_withhtml(tmp1);
- 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);
- * 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);
- 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);
-oscar_set_info(PurpleConnection *gc, const char *rawinfo)
- PurpleAccount *account;
- account = purple_connection_get_account(gc);
- status = purple_account_get_active_status(account);
- oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status);
-oscar_get_extended_status(PurpleConnection *gc)
- PurpleAccount *account;
- 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;
-oscar_set_extended_status(PurpleConnection *gc)
- aim_srv_setextrainfo(purple_connection_get_protocol_data(gc), TRUE, oscar_get_extended_status(gc), FALSE, NULL, NULL);
-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 *away_encoding = NULL;
- char *status_text = NULL;
- const char *itmsurl = NULL;
- status_type = purple_status_get_status_type(status);
- primitive = purple_status_type_get_primitive(status_type);
- 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);
- if (infolen > od->rights.maxsiglen)
- 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));
- 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. */
- /* 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);
- if (awaylen > od->rights.maxawaymsglen)
- 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));
- aim_locate_setprofile(od,
- info_encoding, info, MIN(infolen, od->rights.maxsiglen),
- away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
- 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]);
- itmsurl = purple_status_get_attr_string(status, "itmsurl");
- aim_srv_setextrainfo(od, TRUE, oscar_get_extended_status(gc), TRUE, status_text, itmsurl);
-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);
-oscar_set_status(PurpleAccount *account, PurpleStatus *status)
- 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))
- if (!purple_account_is_connected(account))
- 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);
- 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);
-oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg)
- 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)) {
- 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));
- /* Remove from local list */
- purple_blist_remove_buddy(buddy);
- 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 */
- 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),
- /* Not authorized -- Re-request authorization */
- oscar_auth_sendrequest(gc, bname, msg);
- /* XXX - Should this be done from AIM accounts, as well? */
- 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);
- 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);
- purple_debug_info("oscar",
- "ssi: moved all buddies from group %s to %s\n", old_name, gname);
- 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) {
-static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- reason = (guint16)va_arg(ap, unsigned int);
- purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason);
- if (reason == 0x0005) {
- if (od->getblisttimer > 0)
- g_source_remove(od->getblisttimer);
- /* 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);
-static int purple_ssi_parserights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- numtypes = va_arg(ap, int);
- maxitems = va_arg(ap, guint16 *);
- 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);
- od->rights.maxbuddies = maxitems[0];
- od->rights.maxgroups = maxitems[1];
- od->rights.maxpermits = maxitems[2];
- od->rights.maxdenies = maxitems[3];
-static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleAccount *account;
- GSList *cur, *next, *buddies;
- struct aim_ssi_item *curitem;
- guint16 deny_entry_type = aim_ssi_getdenyentrytype(od);
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
- va_arg(ap, int); /* guint16 fmtver */
- va_arg(ap, int); /* guint16 numitems */
- va_arg(ap, guint32); /* timestamp */
- /* Don't attempt to re-request our buddy list later */
- if (od->getblisttimer != 0) {
- g_source_remove(od->getblisttimer);
- 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 ***/
- for (buddies = purple_blist_find_buddies(account, NULL);
- buddies = g_slist_delete_link(buddies, buddies))
- 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");
- 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);
- 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) */
- next = purple_account_privacy_get_permitted(account);
- 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);
- next = purple_account_privacy_get_denied(account);
- 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;
- idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting");
- report_idle = !purple_strequal(idle_reporting_pref, "none");
- aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
- 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);
- switch (curitem->type) {
- case AIM_SSI_TYPE_BUDDY: { /* Buddy */
- 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);
- 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);
- /* Get server stored alias */
- purple_buddy_set_local_alias(b, alias_utf8);
- 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);
- 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);
- 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);
- 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);
- 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);
- case AIM_SSI_TYPE_PRESENCEPREFS: { /* Presence setting */
- /* We don't want to change Purple's setting because it applies to all accounts */
- } /* End of switch on curitem->type */
- } /* End of for loop */
- /*** End code for adding from server list to local list ***/
- oscar_set_icq_permdeny(account);
- oscar_set_aim_permdeny(gc);
- /* 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");
- * Make sure our server-stored icon is updated correctly in
- * the event that the local user set a new icon while this
- img = purple_buddy_icons_find_account_icon(account);
- oscar_set_icon(gc, 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);
-static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- struct aim_ssi_tmp *retval;
- retval = va_arg(ap, struct aim_ssi_tmp *);
- 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)
- case 0x0000: { /* added successfully */
- case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
- 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));
- case 0x000e: { /* buddy requires authorization */
- if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name))
- oscar_auth_sendrequest(gc, retval->name, NULL);
- default: { /* La la la */
- 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));
-purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- PurpleAccount *account;
- char *gname, *gname_utf8, *alias, *alias_utf8;
- struct aim_ssi_item *ssi_item;
- guint16 snac_subtype, type;
- account = purple_connection_get_account(gc);
- snac_subtype = (guint16)va_arg(ap, int);
- type = (guint16)va_arg(ap, int);
- name = va_arg(ap, char *);
- if ((type != 0x0000) || (name == NULL))
- 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);
- b = purple_blist_find_buddy(account, name);
- * 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 */
- 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);
- purple_debug_error("oscar", "purple_ssi_parseaddmod: "
- "Could not find ssi item for oncoming buddy %s, "
- "group %s\n", name, gname);
-static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- gchar *dialog_msg, *nombre;
- struct name_data *data;
- bn = va_arg(ap, char *);
- va_arg(ap, char *); /* msg */
- 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));
- 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);
- data = g_new(struct name_data, 1);
- 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),
- G_CALLBACK(purple_icq_buddyadd),
- G_CALLBACK(oscar_free_name_data));
-static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
- bn = va_arg(ap, const char *);
- msg = va_arg(ap, char *);
- purple_debug_info("oscar",
- "ssi: received authorization request from %s\n", bn);
- 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);
- aim_icq_getalias(od, bn, TRUE, msg);
-static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- gchar *dialog_msg, *nombre;
- bn = va_arg(ap, char *);
- reply = (guint8)va_arg(ap, int);
- msg = va_arg(ap, char *);
- 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));
- 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));
- 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));
-static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- bn = va_arg(ap, char *);
- 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);
-GList *oscar_chat_info(PurpleConnection *gc) {
- PurpleProtocolChatEntry *pce;
- pce = g_new0(PurpleProtocolChatEntry, 1);
- pce->label = _("_Room:");
- pce->identifier = "room";
- m = g_list_append(m, pce);
- pce = g_new0(PurpleProtocolChatEntry, 1);
- pce->label = _("_Exchange:");
- pce->identifier = "exchange";
- m = g_list_append(m, pce);
-GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
- defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
- g_hash_table_insert(defaults, "room", g_strdup(chat_name));
- g_hash_table_insert(defaults, "exchange", g_strdup("4"));
-oscar_get_chat_name(GHashTable *data)
- return g_strdup(g_hash_table_lookup(data, "room"));
-oscar_join_chat(PurpleConnection *gc, GHashTable *data)
- OscarData *od = purple_connection_get_protocol_data(gc);
- 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);
- 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);
- 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);
-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);
- aim_im_sendch2_chatinvite(od, name, message ? message : "",
- ccon->exchange, ccon->name, 0x0);
-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;
- const gchar *message = purple_message_get_contents(msg);
- if (!(conv = purple_conversations_find_chat(gc, id)))
- if (!(c = find_oscar_chat_by_conv(gc, conv)))
- 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."),
- 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. */
- buf3 = purple_markup_strip_html(buf);
- buf = purple_strdup_withhtml(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);
- purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
- aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
-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))
-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))
-const char *oscar_list_emblem(PurpleBuddy *b)
- PurpleConnection *gc = NULL;
- PurpleAccount *account = NULL;
- PurplePresence *presence;
- aim_userinfo_t *userinfo = NULL;
- account = purple_buddy_get_account(b);
- name = purple_buddy_get_name(b);
- gc = purple_account_get_connection(account);
- od = purple_connection_get_protocol_data(gc);
- userinfo = aim_locate_finduserinfo(od, name);
- presence = purple_buddy_get_presence(b);
- if (purple_presence_is_online(presence) == FALSE) {
- 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)
- if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
- if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM)
- if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
- /* Make the mood icon override anything below this. */
- if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
- if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
-void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
- PurpleAccount *account;
- aim_userinfo_t *userinfo;
- if (!PURPLE_BUDDY_IS_ONLINE(b))
- 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);
- oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
-char *oscar_status_text(PurpleBuddy *b)
- PurpleAccount *account;
- PurplePresence *presence;
- 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"));
- ret = g_strdup(_("Offline"));
- message = purple_status_get_attr_string(status, "message");
- gchar *tmp = oscar_util_format_string(message, purple_account_get_username(account));
- ret = purple_markup_escape_text(tmp, -1);
- else if (purple_status_is_available(status))
- /* Don't show "Available" as status message in case buddy doesn't have a status message */
- ret = g_strdup(purple_status_get_name(status));
-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));
-oscar_status_types(PurpleAccount *account)
- 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,
- _("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,
- _("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,
- 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,
- _("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,
- _("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),
- 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) {
- PurpleAccount *account;
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
- b = purple_blist_find_buddy(account, data->name);
- oscar_free_name_data(data);
- g = purple_buddy_get_group(b);
- oscar_free_name_data(data);
- 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) {
- struct name_data *data;
- PurpleAccount *account;
- 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)))
- 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->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),
-oscar_ask_directim_yes_cb(struct oscar_ask_directim_data *data)
- peer_connection_propose(data->od, OSCAR_CAPABILITY_DIRECTIM, data->who);
-oscar_ask_directim_no_cb(struct oscar_ask_directim_data *data)
-/* This is called from right-click menu on a buddy node. */
-oscar_ask_directim(gpointer object, gpointer ignored)
- struct oscar_ask_directim_data *data;
- PurpleAccount *account;
- 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."),
- purple_request_action(gc, NULL, buf,
- _("Because this reveals your IP address, it "
- "may be considered a security risk. Do you "
- 0, /* Default action is "connect" */
- purple_request_cpar_from_account(account),
- _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
- _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
-oscar_close_directim(gpointer object, gpointer ignored)
- PurpleAccount *account;
- PurpleIMConversation *im;
- 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);
- 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)
- PurpleAccount *account;
- 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);
-oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
- 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);
-oscar_buddy_menu(PurpleBuddy *buddy) {
- 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);
- if (od->icq && oscar_util_valid_name_icq(bname))
- act = purple_action_menu_new(_("Get AIM Info"),
- PURPLE_CALLBACK(oscar_get_aim_info_cb),
- 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),
- menu = g_list_prepend(menu, act);
- act = purple_action_menu_new(_("Get X-Status Msg"),
- PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
- menu = g_list_prepend(menu, act);
- menu = g_list_prepend(menu, create_visibility_menu_item(od, bname));
- oscar_util_name_compare(purple_account_get_username(account), bname) &&
- PURPLE_BUDDY_IS_ONLINE(buddy))
- conn = peer_connection_find_by_type(od, bname, OSCAR_CAPABILITY_DIRECTIM);
- if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
- act = purple_action_menu_new(_("End Direct IM Session"),
- PURPLE_CALLBACK(oscar_close_directim),
- act = purple_action_menu_new(_("Direct IM"),
- PURPLE_CALLBACK(oscar_ask_directim),
- 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.
- 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),
- menu = g_list_prepend(menu, act);
- menu = g_list_reverse(menu);
-GList *oscar_blist_node_menu(PurpleBlistNode *node) {
- if(PURPLE_IS_BUDDY(node)) {
- return oscar_buddy_menu((PurpleBuddy *) node);
-oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields)
- OscarData *od = purple_connection_get_protocol_data(gc);
- PurpleAccount *account = purple_connection_get_account(gc);
- 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);
-oscar_show_icq_privacy_opts(PurpleProtocolAction *action)
- PurpleConnection *gc = action->connection;
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- 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"),
- _("OK"), G_CALLBACK(oscar_icq_privacy_opts),
- purple_request_cpar_from_connection(gc),
-static void oscar_confirm_account(PurpleProtocolAction *action)
- gc = action->connection;
- od = purple_connection_get_protocol_data(gc);
- conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
- aim_admin_reqconfirm(od, conn);
- 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);
- aim_admin_getinfo(od, conn, 0x11);
- 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);
- aim_admin_setemail(od, conn, email);
- 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,
- _("_OK"), G_CALLBACK(oscar_change_email),
- purple_request_cpar_from_connection(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;
- buddies = purple_blist_find_buddies(account, NULL);
- filtered_buddies = NULL;
- for (cur = buddies; cur != NULL; cur = cur->next) {
- const gchar *bname, *gname;
- 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);
- 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);
-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 "
- NULL, FALSE, FALSE, NULL,
- _("_Search"), G_CALLBACK(search_by_email_cb),
- purple_request_cpar_from_connection(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);
-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);
- 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);
- 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.
-oscar_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
- PurpleAccount *account;
- od = purple_connection_get_protocol_data(gc);
- account = purple_connection_get_account(gc);
- 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)))
-oscar_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
- PurpleAccount *account;
- 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;
- "type", PURPLE_XFER_TYPE_SEND,
- 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.
-oscar_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file)
- xfer = oscar_new_xfer(prplxfer, gc, who);
- purple_xfer_request_accepted(xfer, file);
- purple_xfer_request(xfer);
-oscar_get_actions(PurpleConnection *gc)
- OscarData *od = purple_connection_get_protocol_data(gc);
- PurpleProtocolAction *act;
- act = purple_protocol_action_new(_("Set User Info..."),
- menu = g_list_prepend(menu, act);
- 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..."),
- 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)"),
- menu = g_list_prepend(menu, act);
- act = purple_protocol_action_new(_("Configure IM Forwarding (web)"),
- oscar_show_imforwardingurl);
- menu = g_list_prepend(menu, act);
- menu = g_list_prepend(menu, NULL);
- 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);
- 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"),
- 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);
-void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new)
- OscarData *od = purple_connection_get_protocol_data(gc);
- aim_icq_changepasswd(od, new);
- conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
- aim_admin_changepasswd(od, conn, new, old);
- od->oldp = g_strdup(old);
- od->newp = g_strdup(new);
- aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
-oscar_convo_closed(PurpleConnection *gc, const char *who)
- od = purple_connection_get_protocol_data(gc);
- conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM);
- aim_im_sendch2_cancel(conn);
- peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
-oscar_normalize(const PurpleAccount *account, const char *str)
- static char buf[BUF_LEN];
- g_return_val_if_fail(str != NULL, NULL);
- /* copy str to buf and skip all blanks */
- for (j = 0; str[j]; j++) {
- 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));
-oscar_offline_message(const PurpleBuddy *buddy)
-oscar_get_max_message_size(PurpleConversation *conv)
- /* XXX: got from pidgin-otr - verify and document it */
-/* 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 */
- acct = purple_accounts_find(acct_id, protocol);
- if (acct && !purple_account_is_connected(acct))
- } else { /* Otherwise find an active account for the protocol */
- GList *l = purple_accounts_get_all();
- if (purple_strequal(protocol, purple_account_get_protocol_id(l->data))
- && purple_account_is_connected(l->data)) {
-gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params)
- if (g_ascii_strcasecmp(proto, "aim") && g_ascii_strcasecmp(proto, "icq"))
- /* All Oscar URI actions require some parameters eventually */
- purple_debug_warning("oscar",
- "No required params for handling URI");
- acct_id = g_hash_table_lookup(params, "account");
- g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto);
- acct = find_acct(proto, acct_id);
- /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */
- if (!g_ascii_strcasecmp(cmd, "GoIM")) {
- char *bname = g_hash_table_lookup(params, "screenname");
- char *message = g_hash_table_lookup(params, "message");
- PurpleIMConversation *im = purple_conversations_find_im_with_account(
- im = purple_im_conversation_new(acct, bname);
- purple_conversation_present(PURPLE_CONVERSATION(im));
- /* Spaces are encoded as '+' */
- g_strdelimit(message, "+", ' ');
- purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message);
- **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
- /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */
- else if (!g_ascii_strcasecmp(cmd, "GoChat")) {
- char *rname = g_hash_table_lookup(params, "roomname");
- /* 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);
- ** Same as above (except that this would have to be re-written using purple_request_*)
- pidgin_blist_joinchat_show(); */
- /* 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);
-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"),
- static const gchar *encryption_values[] = {
- OSCAR_OPPORTUNISTIC_ENCRYPTION,
- OSCAR_REQUIRE_ENCRYPTION,
- static const gchar *aim_login_keys[] = {
- N_("Use clientLogin authentication"),
- N_("Use Kerberos-based authentication"),
- N_("Use MD5 based authentication"),
- static const gchar *aim_login_values[] = {
- static const gchar *icq_login_keys[] = {
- N_("Use clientLogin authentication"),
- N_("Use MD5 based authentication"),
- static const gchar *icq_login_values[] = {
- const gchar **login_keys;
- const gchar **login_values;
- GList *encryption_options = NULL;
- GList *login_options = NULL;
- 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);
- login_keys = icq_login_keys;
- login_values = icq_login_values;
- 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);
- option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
- protocol->account_options = g_list_append(protocol->account_options, option);
- 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);
-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",
- PURPLE_ICON_SCALE_SEND |
- PURPLE_ICON_SCALE_DISPLAY);
-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;
-oscar_protocol_class_finalize(G_GNUC_UNUSED OscarProtocolClass *klass)
-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;
-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;
-oscar_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
- im_iface->send = oscar_send_im;
- im_iface->send_typing = oscar_send_typing;
-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;
-oscar_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
- privacy_iface->add_deny = oscar_add_deny;
- privacy_iface->rem_deny = oscar_rem_deny;
-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_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(
- "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,
-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);
- icq_protocol = purple_protocols_add(ICQ_TYPE_PROTOCOL, error);
- 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);
- 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");
-plugin_unload(PurplePlugin *plugin, GError **error)
- if (!purple_protocols_remove(icq_protocol, error))
- if (!purple_protocols_remove(aim_protocol, error))
-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"
-#ifndef PURPLE_OSCAR_OSCAR_H
-#define PURPLE_OSCAR_OSCAR_H
-#include "circularbuffer.h"
-#include <libsoup/soup.h>
-#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;
-#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.
- * 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!
- * Maximum size of a Buddy Icon.
-#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.
- const char *clientstring;
- 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
-#define CLIENTINFO_PURPLE_AIM { \
-#define CLIENTINFO_PURPLE_ICQ { \
- 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"
- PurpleProtocolClass parent_class;
- * 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.
- ByteStream data; /* payload stream */
- OscarData *od; /**< Pointer to parent session. */
- time_t lastactivity; /**< Time of last transmit. */
- OscarDisconnectReason disconnect_reason;
- guint16 disconnect_code;
- /* A few variables that are only used when connecting */
- PurpleProxyConnectData *connect_data;
- gpointer new_conn_data;
- PurpleSslConnection *gsc;
- gssize header_received;
- FlapFrame buffer_incoming;
- PurpleCircularBuffer *buffer_outgoing;
- guint watcher_incoming;
- guint watcher_outgoing;
- guint16 seqnum_out; /**< The sequence number of most recently sent packet. */
- guint16 seqnum_in; /**< The sequence number of most recently received packet. */
- 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 */
- void *internal; /* internal conn-specific libfaim data */
- struct _IcbmCookie *next;
-struct aim_ssi_itemlist {
- struct aim_ssi_item *data;
- GHashTable *idx_gid_bid;
- GHashTable *idx_all_named_items;
- * The main client-data interface.
- /* Only used when connecting with clientLogin or Kerberos. */
- SoupSession *http_conns;
- gboolean iconconnecting;
- 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 */
- * 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;
- /** Only used when connecting with the old-style BUCP login. */
- struct aim_authresp_info *authinfo;
- struct aim_emailinfo *emailinfo;
- struct aim_userinfo_s *userinfo;
- /* Server-stored information (ssi) */
- gboolean received_data;
- struct aim_ssi_itemlist official;
- struct aim_ssi_itemlist local;
- struct aim_ssi_tmp *pending;
- gboolean waiting_for_ack;
- gboolean in_transaction;
- /** Contains pointers to handler functions for each family/subtype. */
- GHashTable *handlerlist;
- /** A linked list containing FlapConnections. */
- GSList *oscar_connections;
- /** 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
- * Only used when connecting with the old-style BUCP login.
-struct aim_authresp_info
- struct aim_clientrelease latestrelease;
- struct aim_clientrelease latestbeta;
-/* Callback data for redirect. */
-struct aim_redirect_data
- const char *ssl_cert_cn;
- struct { /* group == SNAC_FAMILY_CHAT */
-int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname);
- * 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);
-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);
-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
-#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
- * 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
- * All incoming IMs are treated as HTML.
-#define AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ 0x00000400
-struct aim_icbmparameters
- guint32 flags; /* AIM_IMPARAM_FLAG_ */
- guint16 maxmsglen; /* message size that you will accept */
- guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */
- guint32 minmsginterval; /* in milliseconds? */
- * TODO: Should probably combine this with struct chat_connection.
-struct aim_chat_roominfo
- char *show; /* AOL did something funny to us */
- PurpleChatConversation *conv;
- * 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_ */
- guint32 flags; /* often 0 */
- /* Only used if AIM_IMFLAGS_HASICON is set */
- * This information is provided in the Incoming ICBM callback for
-struct aim_incomingim_ch1_args
- guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
- time_t timestamp; /* Only set for offline messages */
- /* Only provided if AIM_IMFLAGS_HASICON is set */
-/* Valid values for channel 2 args->status */
-#define AIM_RENDEZVOUS_PROPOSE 0x0000
-#define AIM_RENDEZVOUS_CANCEL 0x0001
-#define AIM_RENDEZVOUS_CONNECTED 0x0002
- guint64 type; /* One of the OSCAR_CAPABILITY_ constants */
- const char *verifiedip;
- const char *msg; /* invite message or file description */
- struct aim_chat_roominfo roominfo;
- void *destructor; /* used internally only */
-struct aim_incomingim_ch4_args
- guint32 uin; /* Of the sender of the ICBM */
- gchar *msg; /* Reason for auth request, deny, or accept */
-/* 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 *next;
-typedef struct aim_userinfo_s
- guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */
- guint16 idletime; /* in seconds */
- guint32 createtime; /* time_t */
- guint32 membersince; /* time_t */
- guint32 onlinesince; /* time_t */
- guint32 sessionlen; /* in seconds */
- guint8 crap[0x25]; /* until we figure it out... */
- char *itmsurl_encoding;
- struct aim_userinfo_s *next;
-#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
-#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 *next;
- 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);
- /* general and "home" information (0x00c8) */
- /* personal (0x00dc) */
- /* additional personal information (0x00e6) */
- 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 *next;
-int aim_email_sendcookies(OscarData *od);
-int aim_email_activate(OscarData *od);
-/* tlv.c - TLV handling */
-typedef struct aim_tlv_s
-/* 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);
-/* 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), \
-#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), \
-#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), \
-#define aimutil_getle8(buf) ( \
-#define aimutil_putle16(buf, data) ( \
- (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \
- (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \
-#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), \
-#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);
-#define AIM_MODULENAME_MAXLEN 16
-#define AIM_MODFLAG_MULTIFAMILY 0x0001
-typedef struct aim_module_s
- 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);
- struct aim_module_s *next;
-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 *);
-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 {
- struct aim_snac_s *next;
-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 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);
- unsigned long ico_me_len;
- unsigned long ico_me_csum;
- unsigned long ico_csum;
-void oscar_free_name_data(struct name_data *data);
-void oscar_init_account_options(PurpleProtocol *protocol, gboolean is_icq);
-#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
-typedef struct _SnacHandler SnacHandler;
- aim_rxcallback_t handler;
- * Allocates a new OscarData and initializes it with default values.
- od = g_new0(OscarData, 1);
- 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);
- aim__registermodule(od, icq_modfirst);
- /* 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(
- "%s (family=0x%04x, version=0x%04x, toolid=0x%04x, toolversion=0x%04x), ",
- purple_debug_misc("oscar", "%s\n", msg->str);
- g_string_free(msg, TRUE);
- * Logoff and deallocate a session.
- * @param od Session to kill
-oscar_data_destroy(OscarData *od)
- aim_cleansnacs(od, -1);
- /* Only used when connecting with clientLogin or Kerberos. */
- soup_session_abort(od->http_conns);
- g_object_unref(od->http_conns);
- g_slist_free_full(od->requesticon, g_free);
- 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);
-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),
-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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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
-#ifndef PURPLE_OSCAR_OSCARCOMMON_H
-#define PURPLE_OSCAR_OSCARCOMMON_H
-#include "purpleaccountoption.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
-#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"
-#define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1"
-#define OSCAR_DEFAULT_CUSTOM_ENCODING oscar_get_locale_charset()
-#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
-const char *oscar_get_locale_charset(void);
-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.)
-/* From the oscar PRPL */
-#include "conversation.h"
-#include <arpa/inet.h> /* for inet_ntoa */
-#include <limits.h> /* for UINT_MAX */
- * 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
-#define PF_INET6 PF_INET
-peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type)
- for (cur = od->peer_connections; cur != NULL; cur = cur->next)
- if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn))
- * @param cookie This must be exactly 8 characters.
-peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie)
- for (cur = od->peer_connections; cur != NULL; cur = cur->next)
- if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn))
-peer_connection_new(OscarData *od, guint64 type, const char *bn)
- PurpleAccount *account;
- account = purple_connection_get_account(od->gc);
- conn = g_new0(PeerConnection, 1);
- conn->bn = g_strdup(bn);
- conn->buffer_outgoing = purple_circular_buffer_new(0);
- 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);
-peer_connection_close(PeerConnection *conn)
- if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
- else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
- 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);
- 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;
-peer_connection_destroy_cb(gpointer 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);
- purple_xfer_cancel_local(conn->xfer);
- g_object_unref(conn->xfer);
- g_free(conn->error_message);
- 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);
-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);
-peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
- if (conn->destroy_timeout != 0)
- /* Already taken care of */
- 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
- * 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.
-peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
- /* 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 */
- peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
- /* If there was an error then close the connection */
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- peer_connection_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
- 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)
- /* 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);
- /* 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 */
- &conn->buffer_incoming.data[conn->buffer_incoming.offset],
- conn->buffer_incoming.len - conn->buffer_incoming.offset,
- /* Check if the remote user closed the connection */
- peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- peer_connection_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
- conn->lastactivity = time(NULL);
- conn->buffer_incoming.offset += read;
- if (conn->buffer_incoming.offset < conn->buffer_incoming.len)
- /* Waiting for more data to arrive */
- /* 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 */
-/*******************************************************************/
-send_cb(gpointer data, gint source, PurpleInputCondition cond)
- const gchar *output = NULL;
- writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing);
- 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
- purple_circular_buffer_reset(conn->buffer_outgoing);
- output = purple_circular_buffer_get_output(conn->buffer_outgoing);
- wrotelen = send(conn->fd, output, writelen, 0);
- if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
- purple_input_remove(conn->watcher_outgoing);
- conn->watcher_outgoing = 0;
- peer_connection_schedule_destroy(conn,
- OSCAR_DISCONNECT_LOST_CONNECTION, NULL);
- * This could happen when unable to send a negotiation
- * frame to a peer proxy server.
- peer_connection_trynext(conn);
- 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.
-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 */
-/*******************************************************************/
-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.
-peer_connection_common_established_cb(gpointer data, gint source, const gchar *error_message, gboolean verified)
- conn->verified_connect_data = NULL;
- conn->client_connect_data = NULL;
- if ((conn->verified_connect_data == NULL) &&
- (conn->client_connect_data == NULL))
- /* Our parallel connection attemps have both failed. */
- peer_connection_trynext(conn);
- 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;
- peer_connection_finalize_connection(conn);
-peer_connection_verified_established_cb(gpointer data, gint source, const gchar *error_message)
- peer_connection_common_established_cb(data, source, error_message, TRUE);
-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.
-peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
- socklen_t addrlen = sizeof(addr);
- purple_debug_info("oscar", "Accepting connection on listener socket.\n");
- conn->fd = accept(conn->listenerfd, &addr, &addrlen);
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- /* No connection yet--no worries */
- /* TODO: Hmm, but they SHOULD be connected if we're here, right? */
- peer_connection_trynext(conn);
- if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
- /* Invalid connection type?! Continue waiting. */
- _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
-static const unsigned char *
-peer_ip_atoi(const char *ip)
- static unsigned char ret[4];
- gchar *delimiter = ".";
- 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]);
- /* i should always be 4 */
- * We've just opened a listener socket, so we send the remote
- * user an ICBM and ask them to connect to us.
-peer_connection_establish_listener_cb(int listenerfd, gpointer data)
- PurpleAccount *account;
- PurpleIMConversation *im;
- FlapConnection *bos_conn;
- const char *listener_ip;
- unsigned short listener_port;
- conn->listen_data = NULL;
- /* Could not open listener socket */
- peer_connection_trynext(conn);
- 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);
- peer_connection_trynext(conn);
- listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
- listener_ip = purple_network_get_my_ip(bos_conn->fd);
- ip_atoi = peer_ip_atoi(listener_ip);
- /* 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. "
- listener_ip ? listener_ip : "(null)",
- bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
- bos_conn->gsc ? 1 : 0);
- peer_connection_trynext(conn);
- 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);
- else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
- aim_im_sendch2_sendfile_requestdirect(od,
- conn->cookie, conn->bn,
- 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.
-peer_connection_tooktoolong(gpointer 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. */
- * Try to establish the given PeerConnection using a defined
-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)
- 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);
- 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))
- conn->connect_timeout_timer = g_timeout_add_seconds(5,
- peer_connection_tooktoolong, conn);
- * 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->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... */
- * 3. Attempt to have both users connect to an intermediate proxy
- 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.
- conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
- if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
- 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);
- conn->verified_connect_data = purple_proxy_connect(NULL, account,
- (conn->proxyip != NULL)
- : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER),
- peer_proxy_connection_established_cb, conn);
- if (conn->verified_connect_data != NULL)
- peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL);
- * Initiate a peer connection with someone.
-peer_connection_propose(OscarData *od, guint64 type, const char *bn)
- if (type == OSCAR_CAPABILITY_DIRECTIM)
- conn = peer_connection_find_by_type(od, bn, type);
- 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);
- purple_conversation_present(PURPLE_CONVERSATION(im));
- /* 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,
-peer_connection_got_proposition_yes_cb(gpointer data, gint id)
- conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
- peer_connection_trynext(conn);
- * Someone else wants to establish a peer connection with us,
- * "Well, one time my friend asked me if I wanted to play the
- * piccolo. But I said no."
-peer_connection_got_proposition_no_cb(gpointer data, gint id)
- 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.
-peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
- PurpleAccount *account;
- 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->clientip);
- g_free(conn->verifiedip);
- conn->proxyip = g_strdup(args->proxyip);
- 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);
- /* 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);
- /* 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 "
- conn = peer_connection_new(od, args->type, bn);
- memcpy(conn->cookie, args->cookie, 8);
- 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 "
- PURPLE_DEFAULT_ACTION_NONE,
- purple_request_cpar_from_account(account),
- _("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)
- conn->xfer = PURPLE_XFER(g_object_new(
- "type", PURPLE_XFER_TYPE_RECEIVE,
- 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);
- 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] == '*'))
- purple_xfer_set_filename(conn->xfer, 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);
-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);
-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);
-oscar_xfer_init(OscarXfer *xfer) {
-oscar_xfer_class_finalize(OscarXferClass *klass) {
-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;
-oscar_xfer_register(GTypeModule *module) {
- oscar_xfer_register_type(module);
-oscar_xfer_get_peer_connection(OscarXfer *xfer) {
- g_return_val_if_fail(OSCAR_IS_XFER(xfer), NULL);
--- 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
-#ifndef PURPLE_OSCAR_PEER_H
-#define PURPLE_OSCAR_PEER_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" */
-#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
- /* guchar magic[4]; */ /* 0 */
- /* guint16 length; */ /* 4 */
- guint16 subtype; /* 8 */
- guchar cookie[8]; /* 12 */
- /* guint32 payloadlength; */ /* 28 */
- guint16 encoding; /* 32 */
- guint16 flags; /* 38 */
- guchar bn[32]; /* 44 */
- ByteStream payload; /* 76 */
- /* guchar magic[4]; */ /* 0 */
- /* guint16 length; */ /* 4 */
- 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 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 */
- /* Payload? */ /* 256 */
- /* guint16 length; */ /* 0 */
- guint16 version; /* 2 */
- guint32 unknown; /* 6 */
- guint16 flags; /* 10 */
- ByteStream payload; /* 12 */
- guint16 lastrequestnumber;
- int flags; /**< Bitmask of PEER_CONNECTION_FLAG_ */
- time_t lastactivity; /**< Time of last transmit. */
- OscarDisconnectReason disconnect_reason;
- * A pointer to either an OdcFrame or an OftFrame.
- * 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
- 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.
- * IP address of the remote user from THEIR point of view.
- * IP address of the remote user from the oscar server's
- ChecksumData *checksum_data;
- guint sending_data_timer;
- * For all peer connections
- * Create a new PeerConnection structure and initialize it with some
- * @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);
-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);
-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);
-void peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message);
-#define OSCAR_TYPE_XFER (oscar_xfer_get_type())
-G_DECLARE_FINAL_TYPE(OscarXfer, oscar_xfer, OSCAR, XFER, PurpleXfer);
-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
-peer_proxy_send(PeerConnection *conn, ProxyFrame *frame)
- 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.
-peer_proxy_send_create_new_conn(PeerConnection *conn)
- PurpleAccount *account;
- memset(&frame, 0, sizeof(ProxyFrame));
- frame.type = PEER_PROXY_TYPE_CREATE;
- 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.
-peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin)
- PurpleAccount *account;
- memset(&frame, 0, sizeof(ProxyFrame));
- frame.type = PEER_PROXY_TYPE_JOIN;
- 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.
-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.
- 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->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)
- error = byte_stream_get16(&frame->payload);
- else if (error == 0x0010)
- msg = "initial request timed out";
- else if (error == 0x001a)
- msg ="accept period timed out";
- msg = "unknown reason";
- purple_debug_info("oscar", "Proxy negotiation failed with "
- "error 0x%04hx: %s\n", error, msg);
- purple_debug_warning("oscar", "Proxy negotiation failed with "
- peer_connection_trynext(conn);
- purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n",
-peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
- /* Start reading a new proxy frame */
- /* 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 */
- purple_debug_info("oscar", "Peer proxy server closed connection\n");
- peer_connection_trynext(conn);
- /* If there was an error then close the connection */
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- purple_debug_info("oscar", "Lost connection with peer proxy server\n");
- peer_connection_trynext(conn);
- 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)
- /* 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);
- /* 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);
- /* 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 */
- &frame->payload.data[frame->payload.offset],
- frame->payload.len - frame->payload.offset,
- /* Check if the proxy server closed the connection */
- purple_debug_info("oscar", "Peer proxy server closed connection\n");
- g_free(frame->payload.data);
- peer_connection_trynext(conn);
- /* If there was an error then close the connection */
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
- purple_debug_info("oscar", "Lost connection with peer proxy server\n");
- g_free(frame->payload.data);
- peer_connection_trynext(conn);
- frame->payload.offset += read;
- conn->lastactivity = time(NULL);
- if (frame->payload.offset < frame->payload.len)
- /* Waiting for more data to arrive */
- /* We have a complete proxy frame! Handle it and continue reading */
- byte_stream_rewind(&frame->payload);
- peer_proxy_recv_frame(conn, frame);
- g_free(frame->payload.data);
- conn->proxy_header_received = 0;
- * We tried to make an outgoing connection to a proxy server. It
- * either connected or failed to connect.
-peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message)
- conn->verified_connect_data = NULL;
- peer_connection_trynext(conn);
- 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);
- /* 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
-aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group)
- for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
- if (cur->family == group)
-aim_module_t *aim__findmodule(OscarData *od, const char *name)
- for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
- if (purple_strequal(name, cur->name))
-int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *))
- mod = g_new0(aim_module_t, 1);
- if (modfirst(od, mod) == -1) {
- if (aim__findmodule(od, mod->name)) {
- mod->shutdown(od, mod);
- mod->next = (aim_module_t *)od->modlistv;
-void aim__shutdownmodules(OscarData *od)
- for (cur = (aim_module_t *)od->modlistv; cur; ) {
- cur->shutdown(od, cur);
--- 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.
- * Called from oscar_session_new() to initialize the hash.
-void aim_initsnachash(OscarData *od)
- for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++)
- od->snac_hash[i] = NULL;
-aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen)
- snac.id = od->snacid_next++;
- snac.data = g_memdup(data, datalen);
- return aim_newsnac(od, &snac);
- * Clones the passed snac structure and caches it in the
-aim_snacid_t aim_newsnac(OscarData *od, aim_snac_t *newsnac)
- 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;
- * 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;
- index = id % FAIM_SNAC_HASH_SIZE;
- for (prev = (aim_snac_t **)&od->snac_hash[index]; (cur = *prev); ) {
- if (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) {
- * 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)
- for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
- aim_snac_t *cur, **prev;
- 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) {
-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);
--- 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
-#ifndef PURPLE_OSCAR_SNACTYPES_H
-#define PURPLE_OSCAR_SNACTYPES_H
-#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 */
- * 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
- * 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
- * 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
-#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 @@
- 'test_oscar_' + prog, 'test_oscar_@0@.c'.format(prog),
- link_with : [oscar_prpl],
- dependencies : [libpurple_dep, libsoup, glib])
- test('oscar_' + prog, e)
--- 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 @@
-test_oscar_util_name_compare(void) {
- const gchar *good[] = {
- 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"));
-main(gint argc, gchar **argv) {
- g_test_init(&argc, &argv, NULL);
- g_test_add_func("/oscar/util/name compare",
- test_oscar_util_name_compare);
--- 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
-createtlv(guint16 type, guint16 length, guint8 *value)
- ret = g_new(aim_tlv_t, 1);
-freetlv(aim_tlv_t *oldtlv)
-aim_tlv_read(GSList *list, ByteStream *bs)
- type = byte_stream_get16(bs);
- length = byte_stream_get16(bs);
- if (length > byte_stream_bytes_left(bs)) {
- aim_tlvlist_free(list);
- tlv = createtlv(type, length, NULL);
- tlv->value = byte_stream_getraw(bs, length);
- aim_tlvlist_free(list);
- 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
- * @param bs Input bstream
- * @return Return the TLV chain read
-GSList *aim_tlvlist_read(ByteStream *bs)
- while (byte_stream_bytes_left(bs) > 0) {
- list = aim_tlv_read(list, bs);
- 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
- * @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)
- while ((byte_stream_bytes_left(bs) > 0) && (num != 0)) {
- list = aim_tlv_read(list, bs);
- 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
- * @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)
- while ((byte_stream_bytes_left(bs) > 0) && (len > 0)) {
- list = aim_tlv_read(list, bs);
- 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)
- aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value);
- * 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)
- if (aim_tlvlist_size(one) != aim_tlvlist_size(two))
- 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);
- byte_stream_destroy(&bs1);
- byte_stream_destroy(&bs2);
- * 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)
- for (cur = list, size = 0; cur; cur = cur->next)
- size += (4 + ((aim_tlv_t *)cur->data)->length);
- * Adds the passed string as a TLV element of the passed type
- * @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)
- tlv = createtlv(type, length, NULL);
- tlv->value = g_memdup(value, length);
- *list = g_slist_append(*list, tlv);
- * 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)
- (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)
- (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)
- (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);
-count_caps(guint64 caps)
- * 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)
- 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 */
- byte_stream_putraw(&bs, data, 16);
- len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
- byte_stream_destroy(&bs);
- * 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)
- 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);
- * 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
- * TODO: Should probably support sublists for real.
- * @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)
- buflen = aim_tlvlist_size(*tlvlist);
- 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);
- * 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)
- for (cur = *list; cur != NULL; cur = cur->next)
- /* TLV does not exist, so add a new one */
- return aim_tlvlist_add_raw(list, type, length, value);
- tlv->value = g_memdup(value, 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)
- (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)
- (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)
- if (list == NULL || *list == NULL)
- *list = g_slist_delete_link(*list, cur);
- * 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)
- /* 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) {
- byte_stream_put16(bs, tlv->type);
- byte_stream_put16(bs, tlv->length);
- 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
- * @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)
- for (cur = list, i = 0; cur != NULL; cur = cur->next) {
- * 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)
- tlv = aim_tlv_gettlv(list, type, nth);
-aim_tlv_getvalue_as_string(aim_tlv_t *tlv)
- ret = g_malloc(tlv->length + 1);
- memcpy(ret, tlv->value, tlv->length);
- ret[tlv->length] = '\0';
- * 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
-char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth)
- tlv = aim_tlv_gettlv(list, type, nth);
- return aim_tlv_getvalue_as_string(tlv);
- * Retrieve the data from the nth TLV in the given TLV chain as an 8bit
- * @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
-guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth)
- tlv = aim_tlv_gettlv(list, type, nth);
- return aimutil_get8(tlv->value);
- * Retrieve the data from the nth TLV in the given TLV chain as a 16bit
- * @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
-guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth)
- tlv = aim_tlv_gettlv(list, type, nth);
- return aimutil_get16(tlv->value);
- * Retrieve the data from the nth TLV in the given TLV chain as a 32bit
- * @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
-guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth)
- tlv = aim_tlv_gettlv(list, type, nth);
- 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.
-oscar_caps_to_string(guint64 caps)
- str = g_string_new("");
- while (bit <= OSCAR_CAPABILITY_LAST) {
- case OSCAR_CAPABILITY_BUDDYICON:
- case OSCAR_CAPABILITY_TALK:
- case OSCAR_CAPABILITY_DIRECTIM:
- tmp = _("AIM Direct IM");
- case OSCAR_CAPABILITY_CHAT:
- case OSCAR_CAPABILITY_GETFILE:
- case OSCAR_CAPABILITY_SENDFILE:
- case OSCAR_CAPABILITY_GAMES:
- case OSCAR_CAPABILITY_GAMES2:
- case OSCAR_CAPABILITY_XTRAZ:
- case OSCAR_CAPABILITY_NEWCAPS:
- case OSCAR_CAPABILITY_ADDINS:
- case OSCAR_CAPABILITY_SENDBUDDYLIST:
- tmp = _("Send Buddy List");
- case OSCAR_CAPABILITY_ICQ_DIRECT:
- tmp = _("ICQ Direct Connect");
- case OSCAR_CAPABILITY_APINFO:
- case OSCAR_CAPABILITY_ICQRTF:
- case OSCAR_CAPABILITY_EMPTY:
- case OSCAR_CAPABILITY_ICQSERVERRELAY:
- tmp = _("ICQ Server Relay");
- case OSCAR_CAPABILITY_UNICODEOLD:
- tmp = _("Old ICQ UTF8");
- case OSCAR_CAPABILITY_TRILLIANCRYPT:
- tmp = _("Trillian Encryption");
- case OSCAR_CAPABILITY_UNICODE:
- case OSCAR_CAPABILITY_HIPTOP:
- case OSCAR_CAPABILITY_SECUREIM:
- tmp = _("Security Enabled");
- case OSCAR_CAPABILITY_VIDEO:
- /* Not actually sure about this one... WinAIM doesn't show anything */
- case OSCAR_CAPABILITY_ICHATAV:
- case OSCAR_CAPABILITY_LIVEVIDEO:
- case OSCAR_CAPABILITY_CAMERA:
- case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
- tmp = _("Screen Sharing");
- g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
- return g_string_free(str, FALSE);
-oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
- const char *name, const char *value)
- 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);
-oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
- const char *name, const char *value, const char *url_prefix)
- 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);
- * @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.
-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);
- 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)
- b = purple_blist_find_buddy(purple_connection_get_account(gc), userinfo->bn);
- userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(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
- if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) {
- message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len);
- escaping_needed = FALSE;
- * 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);
- message = g_strdup(purple_status_get_attr_string(status, "message"));
- itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
- tmp = oscar_util_format_string(message, purple_account_get_username(account));
- tmp = purple_markup_escape_text(message, -1);
- if (use_html_status && itmsurl) {
- tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message);
- 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))
- tmp = g_strdup_printf("%s%s%s",
- status_name ? status_name : "",
- ((status_name && message) && *message) ? ": " : "",
- (message && *message) ? message : "");
- } 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",
- (message && *message) ? ": " : "",
- (message && *message) ? message : "");
- message = g_strdup(_("Offline"));
- status = purple_presence_get_status(presence, "mood");
- mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME));
- comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
- char *escaped_comment = purple_markup_escape_text(comment, -1);
- description = g_strdup_printf("%s (%s)", _(mood), escaped_comment);
- g_free(escaped_comment);
- description = g_strdup(_(mood));
- purple_notify_user_info_add_pair_html(user_info, _("Mood"), description);
- purple_notify_user_info_add_pair_html(user_info, _("Status"), message);
-oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
- PurpleAccount *account;
- struct buddyinfo *bi = NULL;
- 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)))
- userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
- b = purple_blist_find_buddy(account, userinfo->bn);
- bname = purple_buddy_get_name(b);
- g = purple_buddy_get_group(b);
- gname = purple_group_get_name(g);
- bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
- if ((bi != NULL) && (bi->ipaddr != 0)) {
- 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)) {
- 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);
- char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
- oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
-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));
-oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
- PurpleConnection *gc = od->gc;
- PurpleAccount *account = purple_connection_get_account(gc);
- PurpleNotifyUserInfo *user_info;
- user_info = purple_notify_user_info_new();
- g_snprintf(who, sizeof(who), "%u", info->uin);
- buddy = purple_blist_find_buddy(account, who);
- bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
- 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)) {
- 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) {
- 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);
- 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). */
- 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. */
- /* 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 */
- oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
- if ((info->age > 0) && (info->age < 255)) {
- 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, "");
- 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);
-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);
- 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);
- purple_notify_user_info_add_pair_plaintext(user_info, _("Capabilities"), tmp);
- 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);
- /* 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);
- 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);
- 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
- * It started with a kiss
-static const char * const msgerrreason[] = {
- N_("Server rate limit exceeded"),
- N_("Client rate limit exceeded"),
- N_("Service unavailable"),
- N_("Service not defined"),
- N_("Not supported by host"),
- N_("Not supported by client"),
- N_("Refused by client"),
- 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_("Request ambiguous"),
-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)
- ui_info = purple_core_get_ui_info();
- if (g_hash_table_lookup_extended(ui_info, str, NULL, &value))
- return GPOINTER_TO_INT(value);
-const char *oscar_get_ui_info_string(const char *str, const char *default_value)
- const char *value = NULL;
- ui_info = purple_core_get_ui_info();
- value = g_hash_table_lookup(ui_info, str);
-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.
-aimutil_iconsum(const guint8 *buf, int buflen)
- for (i=0, sum=0; i+1<buflen; i+=2)
- sum += (buf[i+1] << 8) + buf[i];
- sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
- * Check if the given name is a valid AIM username.
- * Example: Henry_Ford@mac.com
- * Example: 1KrazyKat@example.com
- * @return TRUE if the name is valid, FALSE if not.
-oscar_util_valid_name_aim(const char *name)
- if (purple_email_is_valid(name))
- /* Normal AIM usernames can't start with a number, period or underscore */
- if (isalnum(name[0]) == 0)
- for (i = 0; name[i] != '\0'; i++) {
- if (!isalnum(name[i]) && name[i] != ' ' && name[i] != '.' && name[i] != '_')
- * Check if the given name is a valid ICQ username.
- * @return TRUE if the name is valid, FALSE if not.
-oscar_util_valid_name_icq(const char *name)
- for (i = 0; name[i] != '\0'; i++) {
- * Check if the given name is a valid SMS username.
- * Example: +19195551234
- * @return TRUE if the name is valid, FALSE if not.
-oscar_util_valid_name_sms(const char *name)
- for (i = 1; name[i] != '\0'; i++) {
- * Check if the given name is a valid oscar username.
- * @return TRUE if the name is valid, FALSE if not.
-oscar_util_valid_name(const char *name)
- if ((name == NULL) || (*name == '\0'))
- 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
- * @return 0 if equal, non-0 if different
-/* TODO: Do something different for email addresses. */
-oscar_util_name_compare(const char *name1, const char *name2)
- if ((name1 == NULL) || (name2 == NULL))
- if (toupper(*name1) != toupper(*name2))
- } while ((*name1 != '\0') && name1++ && name2++);
- * 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.
-oscar_util_format_string(const char *str, const char *name)
- 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);
- g_string_append(cpy, name);
- g_string_append(cpy, purple_date_format_short(tme));
- g_string_append(cpy, purple_time_format(tme));
- g_string_append_c(cpy, *c);
- g_string_append_c(cpy, *c);
- g_string_append_c(cpy, *c);
- return g_string_free(cpy, FALSE);
-oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message)
- 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);
- 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
-/* 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
-#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
-#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
-#define DONT_APPEAR_OFFLINE N_("Don't Appear Offline")
-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;
-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;
-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);
- aim_ssi_del_from_private_list(od, bname, list_type);
-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);
- label = on_list ? _(DONT_APPEAR_ONLINE) : _(APPEAR_ONLINE);
- label = on_list ? _(DONT_APPEAR_OFFLINE) : _(APPEAR_OFFLINE);
- return purple_action_menu_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL);
-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) {
- 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);
- 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);
-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 "
-oscar_show_invisible_list(PurpleProtocolAction *action)
- show_private_list(action, AIM_SSI_TYPE_DENY, _("Invisible List"),
- _("These buddies will always see you as 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
-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 @@
@@ -49,7 +48,6 @@
libpurple/example/nullclient.c
@@ -79,7 +77,7 @@
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/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