Fri, 25 Oct 2019 07:52:44 +0000
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
--- a/libpurple/protocols/meson.build Sun Oct 20 00:24:28 2019 +0300 +++ b/libpurple/protocols/meson.build Fri Oct 25 07:52:44 2019 +0000 @@ -5,7 +5,6 @@ subdir('jabber') subdir('novell') subdir('null') -subdir('oscar') subdir('sametime') subdir('silc') subdir('simple')
--- a/libpurple/protocols/oscar/AUTHORS Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ - -N: ComBOTS Product GmbH (htfv) -T: 2007 -E: foss@combots.com - -N: Jonathan Clark -T: 2005-2006 -E: ardentlygnarley a.t users d.o.t sourceforge d.o.t net - -N: Mark Doliner -T: 2001-2006 -H: markdoliner -E: thekingant a.t users d.o.t sourceforge d.o.t net -W: http://kingant.net/ - -N: Adam Fritzler -T: 1998-2001 -H: mid -E: mid a.t auk d.o.t cx -W: http://www.auk.cx/~mid,http://www.auk.cx/faim -D: Wrote most of the wap of crap that you see before you. - -N: Josh Myer -T: 1998-2001 -E: josh a.t joshisanerd d.o.t com -D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings. - -N: Daniel M. Pomerantz -H: dmprantz -D: Made initial versions cross platform - -N: Daniel Reed -T: 1998-2001 -H: n, linuxkitty -E: n a.t ml d.o.t org -W: http://users.n.ml.org/n/ -D: Fixed aim_snac.c - -N: Eric Warmenhoven -T: 1998-2001 -E: warmenhoven a.t linux d.o.t com -D: Some OFT info, initial author of the libpurple-side of the oscar protocol plugin - -N: Brock Wilcox -T: 1998-2001 -H: awwaiid -E: awwaiid a.t auk d.o.t cx -D: Figured out original password roasting -
--- a/libpurple/protocols/oscar/COPYING Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - -
--- a/libpurple/protocols/oscar/aim.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include "aim.h" - -#include "core.h" -#include "plugins.h" -#include "signals.h" - -#include "oscarcommon.h" - -static void -aim_protocol_init(AIMProtocol *self) -{ - PurpleProtocol *protocol = PURPLE_PROTOCOL(self); - - protocol->id = "prpl-aim"; - protocol->name = "AIM"; - - oscar_init_account_options(protocol, FALSE); -} - -static void -aim_protocol_class_init(AIMProtocolClass *klass) -{ - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass); - - protocol_class->list_icon = oscar_list_icon_aim; -} - -static void -aim_protocol_class_finalize(G_GNUC_UNUSED AIMProtocolClass *klass) -{ -} - -static void -aim_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface) -{ - client_iface->get_max_message_size = oscar_get_max_message_size; -} - -static void -aim_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface) -{ - privacy_iface->add_permit = oscar_add_permit; - privacy_iface->rem_permit = oscar_rem_permit; - privacy_iface->set_permit_deny = oscar_set_aim_permdeny; -} - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - AIMProtocol, aim_protocol, OSCAR_TYPE_PROTOCOL, 0, - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT, - aim_protocol_client_iface_init) - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY, - aim_protocol_privacy_iface_init)); - -/* This exists solely because the above macro makes aim_protocol_register_type - * static. */ -void -aim_protocol_register(PurplePlugin *plugin) -{ - aim_protocol_register_type(G_TYPE_MODULE(plugin)); -}
--- a/libpurple/protocols/oscar/aim.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_OSCAR_AIM_H -#define PURPLE_OSCAR_AIM_H - -#include "oscar.h" - -#define AIM_TYPE_PROTOCOL (aim_protocol_get_type()) -#define AIM_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), AIM_TYPE_PROTOCOL, AIMProtocol)) -#define AIM_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), AIM_TYPE_PROTOCOL, AIMProtocolClass)) -#define AIM_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), AIM_TYPE_PROTOCOL)) -#define AIM_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), AIM_TYPE_PROTOCOL)) -#define AIM_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), AIM_TYPE_PROTOCOL, AIMProtocolClass)) - -typedef struct -{ - OscarProtocol parent; -} AIMProtocol; - -typedef struct -{ - OscarProtocolClass parent_class; -} AIMProtocolClass; - -/** - * Registers the AIMProtocol type in the type system. - */ -G_GNUC_INTERNAL -void aim_protocol_register(PurplePlugin *plugin); - -/** - * Returns the GType for the AIMProtocol object. - */ -G_MODULE_EXPORT GType aim_protocol_get_type(void); - -#endif /* PURPLE_OSCAR_AIM_H */
--- a/libpurple/protocols/oscar/authorization.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Everything related to OSCAR authorization requests. - */ - -#include "oscar.h" -#include "request.h" - -/* When you ask other people for authorization */ -void -oscar_auth_sendrequest(PurpleConnection *gc, const char *bname, const char *msg) -{ - OscarData *od; - PurpleAccount *account; - PurpleBuddy *buddy; - PurpleGroup *group; - const char *gname; - - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - buddy = purple_blist_find_buddy(account, bname); - if (buddy != NULL) - group = purple_buddy_get_group(buddy); - else - group = NULL; - - if (group != NULL) - { - gname = purple_group_get_name(group); - purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n", - bname, gname); - aim_ssi_sendauthrequest(od, bname, msg ? msg : _("Please authorize me so I can add you to my buddy list.")); - if (!aim_ssi_itemlist_finditem(&od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) - { - aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE); - - /* Mobile users should always be online */ - if (bname[0] == '+') { - purple_protocol_got_user_status(account, - purple_buddy_get_name(buddy), - OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_protocol_got_user_status(account, - purple_buddy_get_name(buddy), - OSCAR_STATUS_ID_MOBILE, NULL); - } - } - } -} - -static void -oscar_auth_grant(const char *message, gpointer cbdata) -{ - struct name_data *data = cbdata; - PurpleConnection *gc = data->gc; - OscarData *od = purple_connection_get_protocol_data(gc); - - aim_ssi_sendauthreply(od, data->name, 0x01, NULL); - - oscar_free_name_data(data); -} - -static void -oscar_auth_dontgrant(const char *msg, gpointer cbdata) -{ - struct name_data *data = cbdata; - PurpleConnection *gc = data->gc; - OscarData *od = purple_connection_get_protocol_data(gc); - - aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given.")); - - oscar_free_name_data(data); -} - -void -oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - oscar_auth_sendrequest(gc, purple_buddy_get_name(buddy), NULL); -} - -/* When other people ask you for authorization */ -void -oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason) -{ - PurpleAccount* account = purple_connection_get_account(gc); - struct name_data *data = g_new(struct name_data, 1); - - data->gc = gc; - data->name = name; - data->nick = nick; - - purple_account_request_authorization(account, data->name, NULL, data->nick, - reason, purple_blist_find_buddy(account, data->name) != NULL, - oscar_auth_grant, oscar_auth_dontgrant, data); -}
--- a/libpurple/protocols/oscar/bstream.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,291 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * This file contains all functions needed to use bstreams. - */ - -#include "oscar.h" - -int byte_stream_new(ByteStream *bs, size_t len) -{ - if (bs == NULL) - return -1; - - return byte_stream_init(bs, g_malloc(len), len); -} - -int byte_stream_init(ByteStream *bs, guint8 *data, size_t len) -{ - if (bs == NULL) - return -1; - - bs->data = data; - bs->len = len; - bs->offset = 0; - - return 0; -} - -void byte_stream_destroy(ByteStream *bs) -{ - g_free(bs->data); -} - -size_t byte_stream_bytes_left(ByteStream *bs) -{ - g_return_val_if_fail(bs != NULL, 0); - g_return_val_if_fail(bs->len >= bs->offset, 0); - - return bs->len - bs->offset; -} - -int byte_stream_curpos(ByteStream *bs) -{ - return bs->offset; -} - -int byte_stream_setpos(ByteStream *bs, size_t off) -{ - g_return_val_if_fail(off <= bs->len, -1); - - bs->offset = off; - return off; -} - -void byte_stream_rewind(ByteStream *bs) -{ - byte_stream_setpos(bs, 0); -} - -/* - * N can be negative, which can be used for going backwards - * in a bstream. - */ -int byte_stream_advance(ByteStream *bs, int n) -{ - g_return_val_if_fail(byte_stream_curpos(bs) + n >= 0, 0); - g_return_val_if_fail((gsize)n <= byte_stream_bytes_left(bs), 0); - - bs->offset += n; - return n; -} - -guint8 byte_stream_get8(ByteStream *bs) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0); - - bs->offset++; - return aimutil_get8(bs->data + bs->offset - 1); -} - -guint16 byte_stream_get16(ByteStream *bs) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0); - - bs->offset += 2; - return aimutil_get16(bs->data + bs->offset - 2); -} - -guint32 byte_stream_get32(ByteStream *bs) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0); - - bs->offset += 4; - return aimutil_get32(bs->data + bs->offset - 4); -} - -guint8 byte_stream_getle8(ByteStream *bs) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0); - - bs->offset++; - return aimutil_getle8(bs->data + bs->offset - 1); -} - -guint16 byte_stream_getle16(ByteStream *bs) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0); - - bs->offset += 2; - return aimutil_getle16(bs->data + bs->offset - 2); -} - -guint32 byte_stream_getle32(ByteStream *bs) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0); - - bs->offset += 4; - return aimutil_getle32(bs->data + bs->offset - 4); -} - -static void byte_stream_getrawbuf_nocheck(ByteStream *bs, guint8 *buf, size_t len) -{ - memcpy(buf, bs->data + bs->offset, len); - bs->offset += len; -} - -int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0); - - byte_stream_getrawbuf_nocheck(bs, buf, len); - return len; -} - -guint8 *byte_stream_getraw(ByteStream *bs, size_t len) -{ - guint8 *ob; - - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL); - - ob = g_malloc(len); - byte_stream_getrawbuf_nocheck(bs, ob, len); - return ob; -} - -char *byte_stream_getstr(ByteStream *bs, size_t len) -{ - char *ob; - - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, NULL); - - ob = g_malloc(len + 1); - byte_stream_getrawbuf_nocheck(bs, (guint8 *)ob, len); - ob[len] = '\0'; - return ob; -} - -int byte_stream_put8(ByteStream *bs, guint8 v) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0); - - bs->offset += aimutil_put8(bs->data + bs->offset, v); - return 1; -} - -int byte_stream_put16(ByteStream *bs, guint16 v) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0); - - bs->offset += aimutil_put16(bs->data + bs->offset, v); - return 2; -} - -int byte_stream_put32(ByteStream *bs, guint32 v) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0); - - bs->offset += aimutil_put32(bs->data + bs->offset, v); - return 1; -} - -int byte_stream_putle8(ByteStream *bs, guint8 v) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 1, 0); - - bs->offset += aimutil_putle8(bs->data + bs->offset, v); - return 1; -} - -int byte_stream_putle16(ByteStream *bs, guint16 v) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 2, 0); - - bs->offset += aimutil_putle16(bs->data + bs->offset, v); - return 2; -} - -int byte_stream_putle32(ByteStream *bs, guint32 v) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= 4, 0); - - bs->offset += aimutil_putle32(bs->data + bs->offset, v); - return 1; -} - - -int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len) -{ - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0); - - memcpy(bs->data + bs->offset, v, len); - bs->offset += len; - return len; -} - -int byte_stream_putstr(ByteStream *bs, const char *str) -{ - return byte_stream_putraw(bs, (guint8 *)str, strlen(str)); -} - -int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len) -{ - g_return_val_if_fail(byte_stream_bytes_left(srcbs) >= len, 0); - g_return_val_if_fail(byte_stream_bytes_left(bs) >= len, 0); - - memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len); - bs->offset += len; - srcbs->offset += len; - return len; -} - -int byte_stream_putuid(ByteStream *bs, OscarData *od) -{ - PurpleAccount *account; - - account = purple_connection_get_account(od->gc); - - return byte_stream_putle32(bs, atoi(purple_account_get_username(account))); -} - -void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data) -{ - byte_stream_put16(bs, type); - - if (data != NULL && data->len > 0) { - /* Flags. 0x04 means "this asset has data attached to it" */ - byte_stream_put8(bs, 0x04); /* Flags */ - byte_stream_put8(bs, data->len); /* Length */ - byte_stream_rewind(data); - byte_stream_putbs(bs, data, data->len); /* Data */ - } else { - byte_stream_put8(bs, 0x00); /* No flags */ - byte_stream_put8(bs, 0x00); /* Length */ - /* No data */ - } -} - -void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr) -{ - ByteStream data; - size_t len = datastr != NULL ? strlen(datastr) : 0; - - if (len > 0) { - byte_stream_new(&data, 2 + len + 2); - byte_stream_put16(&data, len); /* Length */ - byte_stream_putstr(&data, datastr); /* String */ - byte_stream_put16(&data, 0x0000); /* Unknown */ - byte_stream_put_bart_asset(bs, type, &data); - byte_stream_destroy(&data); - } else { - byte_stream_put_bart_asset(bs, type, NULL); - } -}
--- a/libpurple/protocols/oscar/clientlogin.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,634 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/** - * This file implements AIM's clientLogin procedure for authenticating - * users. This replaces the older MD5-based and XOR-based - * authentication methods that use SNAC family 0x0017. - * - * This doesn't use SNACs or FLAPs at all. It makes http and https - * POSTs to AOL to validate the user based on the password they - * provided to us. Upon successful authentication we request a - * connection to the BOS server by calling startOSCARsession. The - * AOL server gives us the hostname and port number to use, as well - * as the cookie to use to authenticate to the BOS server. And then - * everything else is the same as with BUCP. - * - * For details, see: - * http://dev.aol.com/aim/oscar/#AUTH - * http://dev.aol.com/authentication_for_clients - */ - -#include "oscar.h" -#include "oscarcommon.h" -#include "core.h" - -#define AIM_LOGIN_HOST "api.screenname.aol.com" -#define ICQ_LOGIN_HOST "api.login.icq.net" - -#define AIM_API_HOST "api.oscar.aol.com" -#define ICQ_API_HOST "api.icq.net" - -#define CLIENT_LOGIN_PAGE "/auth/clientLogin" -#define START_OSCAR_SESSION_PAGE "/aim/startOSCARSession" - -#define HTTPS_FORMAT_URL(host, page) "https://" host page - -static const gchar *client_login_urls[] = { - HTTPS_FORMAT_URL(AIM_LOGIN_HOST, CLIENT_LOGIN_PAGE), - HTTPS_FORMAT_URL(ICQ_LOGIN_HOST, CLIENT_LOGIN_PAGE), -}; - -static const gchar *start_oscar_session_urls[] = { - HTTPS_FORMAT_URL(AIM_API_HOST, START_OSCAR_SESSION_PAGE), - HTTPS_FORMAT_URL(ICQ_API_HOST, START_OSCAR_SESSION_PAGE), -}; - -static const gchar *get_client_login_url(OscarData *od) -{ - return client_login_urls[od->icq ? 1 : 0]; -} - -static const gchar *get_start_oscar_session_url(OscarData *od) -{ - return start_oscar_session_urls[od->icq ? 1 : 0]; -} - -static const char *get_client_key(OscarData *od) -{ - return oscar_get_ui_info_string( - od->icq ? "prpl-icq-clientkey" : "prpl-aim-clientkey", - od->icq ? ICQ_DEFAULT_CLIENT_KEY : AIM_DEFAULT_CLIENT_KEY); -} - -static gchar *generate_error_message(PurpleXmlNode *resp, const char *url) -{ - PurpleXmlNode *text; - PurpleXmlNode *status_code_node; - gboolean have_error_code = TRUE; - gchar *err = NULL; - gchar *details = NULL; - - status_code_node = purple_xmlnode_get_child(resp, "statusCode"); - if (status_code_node) { - gchar *status_code; - - /* We can get 200 OK here if the server omitted something we think it shouldn't have (see #12783). - * No point in showing the "Ok" string to the user. - */ - status_code = purple_xmlnode_get_data_unescaped(status_code_node); - if (purple_strequal(status_code, "200")) { - have_error_code = FALSE; - } - } - if (have_error_code && resp && (text = purple_xmlnode_get_child(resp, "statusText"))) { - details = purple_xmlnode_get_data(text); - } - - if (details && *details) { - /* Translators: The first %s is a URL. The second is a brief error - message. */ - err = g_strdup_printf(_("Received unexpected response from %s: %s"), url, details); - } else { - /* Translators: %s in this string is a URL */ - err = g_strdup_printf(_("Received unexpected response from %s"), url); - } - - g_free(details); - return err; -} - -/** - * @return A null-terminated base64 encoded version of the HMAC - * calculated using the given key and data. - */ -static gchar *hmac_sha256(const char *key, const char *message) -{ - GHmac *hmac; - guchar digest[32]; - gsize digest_len = 32; - - hmac = g_hmac_new(G_CHECKSUM_SHA256, (guchar *)key, strlen(key)); - g_hmac_update(hmac, (guchar *)message, -1); - g_hmac_get_digest(hmac, digest, &digest_len); - g_hmac_unref(hmac); - - return g_base64_encode(digest, sizeof(digest)); -} - -/** - * @return A base-64 encoded HMAC-SHA256 signature created using the - * technique documented at - * http://dev.aol.com/authentication_for_clients#signing - */ -static gchar *generate_signature(const char *method, const char *url, const char *parameters, const char *session_key) -{ - char *encoded_url, *signature_base_string, *signature; - const char *encoded_parameters; - - encoded_url = g_strdup(purple_url_encode(url)); - encoded_parameters = purple_url_encode(parameters); - signature_base_string = g_strdup_printf("%s&%s&%s", - method, encoded_url, encoded_parameters); - g_free(encoded_url); - - signature = hmac_sha256(session_key, signature_base_string); - g_free(signature_base_string); - - return signature; -} - -static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie, char **tls_certname) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleXmlNode *response_node, *tmp_node, *data_node; - PurpleXmlNode *host_node = NULL, *port_node = NULL, *cookie_node = NULL, *tls_node = NULL; - char *tmp; - guint code; - const gchar *encryption_type = purple_account_get_string(purple_connection_get_account(gc), "encryption", OSCAR_DEFAULT_ENCRYPTION); - - /* Parse the response as XML */ - response_node = purple_xmlnode_from_str(response, response_len); - if (response_node == NULL) - { - char *msg; - purple_debug_error("oscar", "startOSCARSession could not parse " - "response as XML: %s\n", response); - msg = generate_error_message(response_node, - get_start_oscar_session_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - return FALSE; - } - - /* Grab the necessary XML nodes */ - tmp_node = purple_xmlnode_get_child(response_node, "statusCode"); - data_node = purple_xmlnode_get_child(response_node, "data"); - if (data_node != NULL) { - host_node = purple_xmlnode_get_child(data_node, "host"); - port_node = purple_xmlnode_get_child(data_node, "port"); - cookie_node = purple_xmlnode_get_child(data_node, "cookie"); - } - - /* Make sure we have a status code */ - if (tmp_node == NULL || (tmp = purple_xmlnode_get_data_unescaped(tmp_node)) == NULL) { - char *msg; - purple_debug_error("oscar", "startOSCARSession response was " - "missing statusCode: %s\n", response); - msg = generate_error_message(response_node, - get_start_oscar_session_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - purple_xmlnode_free(response_node); - return FALSE; - } - - /* Make sure the status code was 200 */ - code = atoi(tmp); - if (code != 200) - { - PurpleXmlNode *status_detail_node; - guint status_detail = 0; - - status_detail_node = purple_xmlnode_get_child(response_node, - "statusDetailCode"); - if (status_detail_node) { - gchar *data = purple_xmlnode_get_data(status_detail_node); - if (data) { - status_detail = atoi(data); - g_free(data); - } - } - - purple_debug_error("oscar", "startOSCARSession response statusCode " - "was %s: %s\n", tmp, response); - - if ((code == 401 && status_detail != 1014) || code == 607) - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("You have been connecting and disconnecting too " - "frequently. Wait ten minutes and try again. If " - "you continue to try, you will need to wait even " - "longer.")); - else { - char *msg; - msg = generate_error_message(response_node, - get_start_oscar_session_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg); - g_free(msg); - } - - g_free(tmp); - purple_xmlnode_free(response_node); - return FALSE; - } - g_free(tmp); - - /* Make sure we have everything else */ - if (data_node == NULL || host_node == NULL || port_node == NULL || cookie_node == NULL) - { - char *msg; - purple_debug_error("oscar", "startOSCARSession response was missing " - "something: %s\n", response); - msg = generate_error_message(response_node, - get_start_oscar_session_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - purple_xmlnode_free(response_node); - return FALSE; - } - - if (!purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION)) { - tls_node = purple_xmlnode_get_child(data_node, "tlsCertName"); - if (tls_node != NULL) { - *tls_certname = purple_xmlnode_get_data_unescaped(tls_node); - } else { - if (purple_strequal(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION)) { - purple_debug_warning("oscar", "We haven't received a tlsCertName to use. We will not do SSL to BOS.\n"); - } else { - purple_debug_error("oscar", "startOSCARSession was missing tlsCertName: %s\n", response); - purple_connection_error( - gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("You required encryption in your account settings, but one of the servers doesn't support it.")); - purple_xmlnode_free(response_node); - return FALSE; - } - } - } - - /* Extract data from the XML */ - *host = purple_xmlnode_get_data_unescaped(host_node); - tmp = purple_xmlnode_get_data_unescaped(port_node); - *cookie = purple_xmlnode_get_data_unescaped(cookie_node); - - if (*host == NULL || **host == '\0' || tmp == NULL || *tmp == '\0' || *cookie == NULL || **cookie == '\0') - { - char *msg; - purple_debug_error("oscar", "startOSCARSession response was missing " - "something: %s\n", response); - msg = generate_error_message(response_node, - get_start_oscar_session_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - g_free(*host); - g_free(tmp); - g_free(*cookie); - purple_xmlnode_free(response_node); - return FALSE; - } - - *port = atoi(tmp); - g_free(tmp); - - return TRUE; -} - -static void -start_oscar_session_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, - gpointer user_data) -{ - OscarData *od = user_data; - PurpleConnection *gc; - char *host, *cookie; - char *tls_certname = NULL; - unsigned short port; - guint8 *cookiedata; - gsize cookiedata_len = 0; - - gc = od->gc; - - g_clear_object(&od->http_conns); - - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { - gchar *tmp; - /* Translators: The first %s is a URL, the second is a brief error - message. */ - tmp = g_strdup_printf(_("Error requesting %s: %d %s"), - get_start_oscar_session_url(od), msg->status_code, - msg->reason_phrase); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - if (!parse_start_oscar_session_response(gc, msg->response_body->data, - msg->response_body->length, &host, - &port, &cookie, &tls_certname)) { - return; - } - - cookiedata = g_base64_decode(cookie, &cookiedata_len); - oscar_connect_to_bos(gc, od, host, port, cookiedata, cookiedata_len, tls_certname); - g_free(cookiedata); - - g_free(host); - g_free(cookie); - g_free(tls_certname); -} - -static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime) -{ - gchar *query_string, *signature, *uri; - SoupMessage *msg; - PurpleAccount *account = purple_connection_get_account(od->gc); - const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); - - /* - * Construct the GET parameters. - */ - query_string = g_strdup_printf("a=%s" - "&distId=%d" - "&f=xml" - "&k=%s" - "&ts=%" G_GINT64_FORMAT - "&useTLS=%d", - purple_url_encode(token), - oscar_get_ui_info_int(od->icq ? "prpl-icq-distid" : "prpl-aim-distid", - od->icq ? ICQ_DEFAULT_DIST_ID : AIM_DEFAULT_DIST_ID), - get_client_key(od), - (gint64)hosttime, - !purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION)); - signature = generate_signature("GET", get_start_oscar_session_url(od), - query_string, session_key); - - uri = g_strdup_printf("%s?%s&sig_sha256=%s", - get_start_oscar_session_url(od), query_string, - signature); - - msg = soup_message_new("GET", uri); - soup_session_queue_message(od->http_conns, msg, start_oscar_session_cb, od); - - g_free(query_string); - g_free(signature); - g_free(uri); -} - -/** - * This function parses the given response from a clientLogin request - * and extracts the useful information. - * - * @param gc The PurpleConnection. If the response data does - * not indicate then purple_connection_error() - * will be called to close this connection. - * @param response The response data from the clientLogin request. - * @param response_len The length of the above response, or -1 if - * @response is NUL terminated. - * @param token If parsing was successful then this will be set to - * a newly allocated string containing the token. The - * caller should g_free this string when it is finished - * with it. On failure this value will be untouched. - * @param secret If parsing was successful then this will be set to - * a newly allocated string containing the secret. The - * caller should g_free this string when it is finished - * with it. On failure this value will be untouched. - * @param hosttime If parsing was successful then this will be set to - * the time on the OpenAuth Server in seconds since the - * Unix epoch. On failure this value will be untouched. - * - * @return TRUE if the request was successful and we were able to - * extract all info we need. Otherwise FALSE. - */ -static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleXmlNode *response_node, *tmp_node, *data_node; - PurpleXmlNode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL; - char *tmp; - - /* Parse the response as XML */ - response_node = purple_xmlnode_from_str(response, response_len); - if (response_node == NULL) - { - char *msg; - purple_debug_error("oscar", "clientLogin could not parse " - "response as XML: %s\n", response); - msg = generate_error_message(response_node, - get_client_login_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - return FALSE; - } - - /* Grab the necessary XML nodes */ - tmp_node = purple_xmlnode_get_child(response_node, "statusCode"); - data_node = purple_xmlnode_get_child(response_node, "data"); - if (data_node != NULL) { - secret_node = purple_xmlnode_get_child(data_node, "sessionSecret"); - hosttime_node = purple_xmlnode_get_child(data_node, "hostTime"); - token_node = purple_xmlnode_get_child(data_node, "token"); - if (token_node != NULL) - tokena_node = purple_xmlnode_get_child(token_node, "a"); - } - - /* Make sure we have a status code */ - if (tmp_node == NULL || (tmp = purple_xmlnode_get_data_unescaped(tmp_node)) == NULL) { - char *msg; - purple_debug_error("oscar", "clientLogin response was " - "missing statusCode: %s\n", response); - msg = generate_error_message(response_node, - get_client_login_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - purple_xmlnode_free(response_node); - return FALSE; - } - - /* Make sure the status code was 200 */ - if (!purple_strequal(tmp, "200")) - { - int status_code, status_detail_code = 0; - - status_code = atoi(tmp); - g_free(tmp); - tmp_node = purple_xmlnode_get_child(response_node, "statusDetailCode"); - if (tmp_node != NULL && (tmp = purple_xmlnode_get_data_unescaped(tmp_node)) != NULL) { - status_detail_code = atoi(tmp); - g_free(tmp); - } - - purple_debug_error("oscar", "clientLogin response statusCode " - "was %d (%d): %s\n", status_code, status_detail_code, response); - - if (status_code == 330 && status_detail_code == 3011) { - PurpleAccount *account = purple_connection_get_account(gc); - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Incorrect password")); - } else if (status_code == 330 && status_detail_code == 3015) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Server requested that you fill out a CAPTCHA in order to " - "sign in, but this client does not currently support CAPTCHAs.")); - } else if (status_code == 401 && status_detail_code == 3019) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("AOL does not allow your screen name to authenticate here")); - } else { - char *msg; - msg = generate_error_message(response_node, - get_client_login_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg); - g_free(msg); - } - - purple_xmlnode_free(response_node); - return FALSE; - } - g_free(tmp); - - /* Make sure we have everything else */ - if (data_node == NULL || secret_node == NULL || - token_node == NULL || tokena_node == NULL) - { - char *msg; - purple_debug_error("oscar", "clientLogin response was missing " - "something: %s\n", response); - msg = generate_error_message(response_node, - get_client_login_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - purple_xmlnode_free(response_node); - return FALSE; - } - - /* Extract data from the XML */ - *token = purple_xmlnode_get_data_unescaped(tokena_node); - *secret = purple_xmlnode_get_data_unescaped(secret_node); - tmp = purple_xmlnode_get_data_unescaped(hosttime_node); - if (*token == NULL || **token == '\0' || *secret == NULL || **secret == '\0' || tmp == NULL || *tmp == '\0') - { - char *msg; - purple_debug_error("oscar", "clientLogin response was missing " - "something: %s\n", response); - msg = generate_error_message(response_node, - get_client_login_url(od)); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - g_free(*token); - g_free(*secret); - g_free(tmp); - purple_xmlnode_free(response_node); - return FALSE; - } - - *hosttime = strtol(tmp, NULL, 10); - g_free(tmp); - - purple_xmlnode_free(response_node); - - return TRUE; -} - -static void -client_login_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, - gpointer user_data) -{ - OscarData *od = user_data; - PurpleConnection *gc; - char *token, *secret, *session_key; - time_t hosttime; - int password_len; - char *password; - - gc = od->gc; - - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { - gchar *tmp; - tmp = g_strdup_printf(_("Error requesting %s: %d %s"), - get_client_login_url(od), msg->status_code, - msg->reason_phrase); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - if (!parse_client_login_response(gc, msg->response_body->data, - msg->response_body->length, &token, - &secret, &hosttime)) { - return; - } - - password_len = strlen(purple_connection_get_password(gc)); - password = g_strdup_printf("%.*s", - od->icq ? MIN(password_len, MAXICQPASSLEN) : password_len, - purple_connection_get_password(gc)); - session_key = hmac_sha256(password, secret); - g_free(password); - g_free(secret); - - send_start_oscar_session(od, token, session_key, hosttime); - - g_free(token); - g_free(session_key); -} - -/** - * This function sends a request to - * https://api.screenname.aol.com/auth/clientLogin with the user's - * username and password and receives the user's session key, which is - * used to request a connection to the BOSS server. - */ -void send_client_login(OscarData *od, const char *username) -{ - PurpleConnection *gc; - SoupMessage *msg; - const char *tmp; - char *password; - int password_len; - - gc = od->gc; - - /* - * We truncate ICQ passwords to 8 characters. There is probably a - * limit for AIM passwords, too, but we really only need to do - * this for ICQ because older ICQ clients let you enter a password - * as long as you wanted and then they truncated it silently. - * - * And we can truncate based on the number of bytes and not the - * number of characters because passwords for AIM and ICQ are - * supposed to be plain ASCII (I don't know if this has always been - * the case, though). - */ - tmp = purple_connection_get_password(gc); - password_len = strlen(tmp); - password = g_strndup(tmp, od->icq ? MIN(password_len, MAXICQPASSLEN) : password_len); - - msg = soup_form_request_new("POST", get_client_login_url(od), "devId", - get_client_key(od), "f", "xml", "pwd", password, - "s", username, NULL); - soup_session_queue_message(od->http_conns, msg, client_login_cb, od); - - g_free(password); -}
--- a/libpurple/protocols/oscar/encoding.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#include "encoding.h" - -static gchar * -encoding_multi_convert_to_utf8(const gchar *text, gssize textlen, const gchar *encodings, GError **error, gboolean fallback) -{ - gchar *utf8 = NULL; - const gchar *begin = encodings; - const gchar *end = NULL; - gchar *curr_encoding = NULL; /* allocated buffer for encoding name */ - const gchar *curr_encoding_ro = NULL; /* read-only encoding name */ - - if (!encodings) { - purple_debug_error("oscar", "encodings is NULL"); - return NULL; - } - - for (;;) - { - /* extract next encoding */ - end = strchr(begin, ','); - if (!end) { - curr_encoding_ro = begin; - } else { /* allocate buffer for encoding */ - curr_encoding = g_strndup(begin, end - begin); - if (!curr_encoding) { - purple_debug_error("oscar", "Error allocating memory for encoding"); - break; - } - curr_encoding_ro = curr_encoding; - } - - if (!g_ascii_strcasecmp(curr_encoding_ro, "utf-8") && g_utf8_validate(text, textlen, NULL)) { - break; - } - - utf8 = g_convert(text, textlen, "UTF-8", curr_encoding_ro, NULL, NULL, NULL); - - if (!end) /* last occurence. do not free curr_encoding: buffer was'nt allocated */ - break; - - g_free(curr_encoding); /* free allocated buffer for encoding here */ - - if (utf8) /* text was successfully converted */ - break; - - begin = end + 1; - } - - if (!utf8 && fallback) - { /* "begin" points to last encoding */ - utf8 = g_convert_with_fallback(text, textlen, "UTF-8", begin, "?", NULL, NULL, error); - } - - return utf8; -} - -static gchar * -encoding_extract(const char *encoding) -{ - char *begin, *end; - - if (encoding == NULL) { - return NULL; - } - - if (!g_str_has_prefix(encoding, "text/aolrtf; charset=") && - !g_str_has_prefix(encoding, "text/x-aolrtf; charset=") && - !g_str_has_prefix(encoding, "text/plain; charset=")) { - return g_strdup(encoding); - } - - begin = strchr(encoding, '"'); - end = strrchr(encoding, '"'); - - if ((begin == NULL) || (end == NULL) || (begin >= end)) { - return g_strdup(encoding); - } - - return g_strndup(begin+1, (end-1) - begin); -} - -gchar * -oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen) -{ - gchar *utf8 = NULL; - const gchar *glib_encoding = NULL; - gchar *extracted_encoding = encoding_extract(encoding); - - if (extracted_encoding == NULL || *extracted_encoding == '\0') { - purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n"); - } else if (!g_ascii_strcasecmp(extracted_encoding, "iso-8859-1")) { - glib_encoding = "iso-8859-1"; - } else if (!g_ascii_strcasecmp(extracted_encoding, "ISO-8859-1-Windows-3.1-Latin-1") || !g_ascii_strcasecmp(extracted_encoding, "us-ascii")) { - glib_encoding = "Windows-1252"; - } else if (!g_ascii_strcasecmp(extracted_encoding, "unicode-2-0")) { - glib_encoding = "UTF-16BE"; - } else if (g_ascii_strcasecmp(extracted_encoding, "utf-8")) { - glib_encoding = extracted_encoding; - } - - if (glib_encoding != NULL) { - utf8 = encoding_multi_convert_to_utf8(text, textlen, glib_encoding, NULL, FALSE); - } - - /* - * If utf8 is still NULL then either the encoding is utf-8 or - * we have been unable to convert the text to utf-8 from the encoding - * that was specified. So we check if the text is valid utf-8 then - * just copy it. - */ - if (utf8 == NULL) { - if (textlen != 0 && *text != '\0' && !g_utf8_validate(text, textlen, NULL)) - utf8 = g_strdup(_("(There was an error receiving this message. The buddy you are speaking with is probably using a different encoding than expected. If you know what encoding he is using, you can specify it in the advanced account options for your AIM/ICQ account.)")); - else - utf8 = g_strndup(text, textlen); - } - - g_free(extracted_encoding); - return utf8; -} - -gchar * -oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg) -{ - const char *charset = NULL; - char *ret = NULL; - - if (msg == NULL) - return NULL; - - if (g_utf8_validate(msg, -1, NULL)) - return g_strdup(msg); - - if (od->icq) - charset = purple_account_get_string(account, "encoding", NULL); - - if(charset && *charset) - ret = encoding_multi_convert_to_utf8(msg, -1, charset, NULL, FALSE); - - if(!ret) - ret = purple_utf8_try_convert(msg); - - return ret; -} - -static gchar * -oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback) -{ - gchar *ret = NULL; - GError *err = NULL; - - if ((charsetstr == NULL) || (*charsetstr == '\0')) - return NULL; - - if (g_ascii_strcasecmp("UTF-8", charsetstr)) { - ret = encoding_multi_convert_to_utf8(data, datalen, charsetstr, &err, fallback); - if (err != NULL) { - purple_debug_warning("oscar", "Conversion from %s failed: %s.\n", - charsetstr, err->message); - g_error_free(err); - } - } else { - if (g_utf8_validate(data, datalen, NULL)) - ret = g_strndup(data, datalen); - else - purple_debug_warning("oscar", "String is not valid UTF-8.\n"); - } - - return ret; -} - -gchar * -oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen) -{ - gchar *ret = NULL; - /* charsetstr1 is always set to what the correct encoding should be. */ - const gchar *charsetstr1, *charsetstr2, *charsetstr3 = NULL; - - if ((datalen == 0) || (data == NULL)) - return NULL; - - if (charset == AIM_CHARSET_UNICODE) { - charsetstr1 = "UTF-16BE"; - charsetstr2 = "UTF-8"; - } else if (charset == AIM_CHARSET_LATIN_1) { - if ((sourcebn != NULL) && oscar_util_valid_name_icq(sourcebn)) - charsetstr1 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - else - charsetstr1 = "ISO-8859-1"; - charsetstr2 = "UTF-8"; - } else if (charset == AIM_CHARSET_ASCII) { - /* Should just be "ASCII" */ - charsetstr1 = "ASCII"; - charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - } else if (charset == 0x000d) { - /* iChat sending unicode over a Direct IM connection = UTF-8 */ - /* Mobile AIM client on multiple devices (including Blackberry Tour, Nokia 3100, and LG VX6000) = ISO-8859-1 */ - charsetstr1 = "UTF-8"; - charsetstr2 = "ISO-8859-1"; - charsetstr3 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - } else { - /* Unknown, hope for valid UTF-8... */ - charsetstr1 = "UTF-8"; - charsetstr2 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - } - - purple_debug_info("oscar", "Parsing IM, charset=0x%04hx, datalen=%" G_GSIZE_FORMAT ", choice1=%s, choice2=%s, choice3=%s\n", - charset, datalen, charsetstr1, charsetstr2, (charsetstr3 ? charsetstr3 : "")); - - ret = oscar_convert_to_utf8(data, datalen, charsetstr1, FALSE); - if (ret == NULL) { - if (charsetstr3 != NULL) { - /* Try charsetstr2 without allowing substitutions, then fall through to charsetstr3 if needed */ - ret = oscar_convert_to_utf8(data, datalen, charsetstr2, FALSE); - if (ret == NULL) - ret = oscar_convert_to_utf8(data, datalen, charsetstr3, TRUE); - } else { - /* Try charsetstr2, allowing substitutions */ - ret = oscar_convert_to_utf8(data, datalen, charsetstr2, TRUE); - } - } - if (ret == NULL) { - char *str, *salvage, *tmp; - - str = g_malloc(datalen + 1); - strncpy(str, data, datalen); - str[datalen] = '\0'; - salvage = purple_utf8_salvage(str); - tmp = g_strdup_printf(_("(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"), - sourcebn, sourcebn); - ret = g_strdup_printf("%s %s", salvage, tmp); - g_free(tmp); - g_free(str); - g_free(salvage); - } - - return ret; -} - -static guint16 -get_simplest_charset(const char *utf8) -{ - while (*utf8) - { - if ((unsigned char)(*utf8) > 0x7f) { - /* not ASCII! */ - return AIM_CHARSET_UNICODE; - } - utf8++; - } - return AIM_CHARSET_ASCII; -} - -gchar * -oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr) -{ - guint16 msg_charset = get_simplest_charset(msg); - if (charset != NULL) { - *charset = msg_charset; - } - if (charsetstr != NULL) { - *charsetstr = msg_charset == AIM_CHARSET_ASCII ? "us-ascii" : "unicode-2-0"; - } - return g_convert(msg, -1, msg_charset == AIM_CHARSET_ASCII ? "ASCII" : "UTF-16BE", "UTF-8", NULL, result_len, NULL); -}
--- a/libpurple/protocols/oscar/encoding.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#ifndef PURPLE_OSCAR_ENCODING_H -#define PURPLE_OSCAR_ENCODING_H - -#include "oscar.h" -#include "oscarcommon.h" - -gchar * oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen); -gchar * oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg); - -/** - * This attemps to decode an incoming IM into a UTF8 string. - * - * We try decoding using two different character sets. The charset - * specified in the IM determines the order in which we attempt to - * decode. We do this because there are lots of broken ICQ clients - * that don't correctly send non-ASCII messages. And if Purple isn't - * able to deal with that crap, then people complain like banshees. - */ -gchar * oscar_decode_im(PurpleAccount *account, const char *sourcebn, guint16 charset, const gchar *data, gsize datalen); - -/** - * Figure out what encoding to use when sending a given outgoing message. - */ -gchar * oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr); - -#endif /* PURPLE_OSCAR_ENCODING_H */
--- a/libpurple/protocols/oscar/family_admin.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,246 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0007 - Account Administration. - * - * Used for stuff like changing the formating of your username, changing your - * email address, requesting an account confirmation email, getting account info, - */ - -#include "oscar.h" - -/** - * Subtype 0x0002 - Request a bit of account info. - * - * Info should be one of the following: - * 0x0001 - Username formatting - * 0x0011 - Email address - * 0x0013 - Unknown - */ -void -aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info) -{ - ByteStream bs; - aim_snacid_t snacid; - - byte_stream_new(&bs, 4); - - byte_stream_put16(&bs, info); - byte_stream_put16(&bs, 0x0000); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtypes 0x0003 and 0x0005 - Parse account info. - * - * Called in reply to both an information request (subtype 0x0002) and - * an information change (subtype 0x0004). - */ -static void -infochange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - char *url=NULL, *sn=NULL, *email=NULL; - guint16 perms, tlvcount, err=0; - - perms = byte_stream_get16(bs); - tlvcount = byte_stream_get16(bs); - - while (tlvcount && byte_stream_bytes_left(bs)) { - guint16 type, length; - - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - - switch (type) { - case 0x0001: { - g_free(sn); - sn = byte_stream_getstr(bs, length); - } break; - - case 0x0004: { - g_free(url); - url = byte_stream_getstr(bs, length); - } break; - - case 0x0008: { - err = byte_stream_get16(bs); - } break; - - case 0x0011: { - g_free(email); - if (length == 0) - email = g_strdup("*suppressed"); - else - email = byte_stream_getstr(bs, length); - } break; - } - - tlvcount--; - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - userfunc(od, conn, frame, (snac->subtype == 0x0005) ? 1 : 0, perms, err, url, sn, email); - - g_free(sn); - g_free(url); - g_free(email); -} - -/** - * Subtype 0x0004 - Set the formatting of username (change spaces and capitalization). - */ -void -aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick) -{ - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - byte_stream_new(&bs, 2+2+strlen(newnick)); - - aim_tlvlist_add_str(&tlvlist, 0x0001, newnick); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0004 - Change password. - */ -void -aim_admin_changepasswd(OscarData *od, FlapConnection *conn, const char *newpw, const char *curpw) -{ - ByteStream bs; - GSList *tlvlist = NULL; - aim_snacid_t snacid; - - byte_stream_new(&bs, 4+strlen(curpw)+4+strlen(newpw)); - - /* new password TLV t(0002) */ - aim_tlvlist_add_str(&tlvlist, 0x0002, newpw); - - /* current password TLV t(0012) */ - aim_tlvlist_add_str(&tlvlist, 0x0012, curpw); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0004 - Change email address. - */ -void -aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail) -{ - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - byte_stream_new(&bs, 2+2+strlen(newemail)); - - aim_tlvlist_add_str(&tlvlist, 0x0011, newemail); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/* - * Subtype 0x0006 - Request account confirmation. - * - * This will cause an email to be sent to the address associated with - * the account. By following the instructions in the mail, you can - * get the TRIAL flag removed from your account. - * - */ -void -aim_admin_reqconfirm(OscarData *od, FlapConnection *conn) -{ - aim_genericreq_n(od, conn, SNAC_FAMILY_ADMIN, 0x0006); -} - -/** - * Subtype SNAC_FAMILY_ADMIN - Account confirmation request acknowledgement. - */ -static int -accountconfirm(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 status; - /* GSList *tlvlist; */ - - status = byte_stream_get16(bs); - /* Status is 0x0013 if unable to confirm at this time */ - - /* tlvlist = aim_tlvlist_read(bs); */ - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, status); - - /* aim_tlvlist_free(tlvlist); */ - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005)) { - infochange(od, conn, mod, frame, snac, bs); - return 1; - } else if (snac->subtype == SNAC_FAMILY_ADMIN) - return accountconfirm(od, conn, mod, frame, snac, bs); - - return 0; -} - -int admin_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_ADMIN; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "admin", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_alert.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0018 - Email notification - * - * Used for being alerted when the email address(es) associated with - * your username get new electronic-m. For normal AIM accounts, you - * get the email address username@netscape.net. AOL accounts have - * username@aol.com, and can also activate a netscape.net account. - * Note: This information might be out of date. - */ - -#include "oscar.h" - -/** - * Subtype 0x0006 - Request information about your email account - * - * @param od The oscar session. - * @param conn The email connection for this session. - * @return Return 0 if no errors, otherwise return the error number. - */ -int -aim_email_sendcookies(OscarData *od) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ALERT))) - return -EINVAL; - - byte_stream_new(&bs, 2+16+16); - - /* Number of cookies to follow */ - byte_stream_put16(&bs, 0x0002); - - /* Cookie */ - byte_stream_put16(&bs, 0x5d5e); - byte_stream_put16(&bs, 0x1708); - byte_stream_put16(&bs, 0x55aa); - byte_stream_put16(&bs, 0x11d3); - byte_stream_put16(&bs, 0xb143); - byte_stream_put16(&bs, 0x0060); - byte_stream_put16(&bs, 0xb0fb); - byte_stream_put16(&bs, 0x1ecb); - - /* Cookie */ - byte_stream_put16(&bs, 0xb380); - byte_stream_put16(&bs, 0x9ad8); - byte_stream_put16(&bs, 0x0dba); - byte_stream_put16(&bs, 0x11d5); - byte_stream_put16(&bs, 0x9f8a); - byte_stream_put16(&bs, 0x0060); - byte_stream_put16(&bs, 0xb0ee); - byte_stream_put16(&bs, 0x0631); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0006, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - - -/** - * Subtype 0x0007 - Receive information about your email account - * - * So I don't even know if you can have multiple 16 byte keys, - * but this is coded so it will handle that, and handle it well. - * This tells you if you have unread mail or not, the URL you - * should use to access that mail, and the domain name for the - * email account (username@domainname.com). If this is the - * first 0x0007 SNAC you've received since you signed on, or if - * this is just a periodic status update, this will also contain - * the number of unread emails that you have. - */ -static int -parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - struct aim_emailinfo *new; - GSList *tlvlist; - guint8 *cookie8, *cookie16; - int tmp, havenewmail = 0; /* Used to tell the client we have _new_ mail */ - - char *alertitle = NULL, *alerturl = NULL; - - cookie8 = byte_stream_getraw(bs, 8); /* Possibly the code used to log you in to mail? */ - cookie16 = byte_stream_getraw(bs, 16); /* Mail cookie sent above */ - - /* See if we already have some info associated with this cookie */ - for (new = od->emailinfo; (new && memcmp(cookie16, new->cookie16, 16)); new = new->next); - if (new) { - /* Free some of the old info, if it exists */ - g_free(new->cookie8); - g_free(new->cookie16); - g_free(new->url); - g_free(new->domain); - } else { - /* We don't already have info, so create a new struct for it */ - new = g_new0(struct aim_emailinfo, 1); - new->next = od->emailinfo; - od->emailinfo = new; - } - - new->cookie8 = cookie8; - new->cookie16 = cookie16; - - tlvlist = aim_tlvlist_readnum(bs, byte_stream_get16(bs)); - - tmp = aim_tlv_get16(tlvlist, 0x0080, 1); - if (tmp) { - if (new->nummsgs < tmp) - havenewmail = 1; - new->nummsgs = tmp; - } else { - /* If they don't send a 0x0080 TLV, it means we definitely have new mail */ - /* (ie. this is not just another status update) */ - havenewmail = 1; - new->nummsgs++; /* We know we have at least 1 new email */ - } - new->url = aim_tlv_getstr(tlvlist, 0x0007, 1); - if (!(new->unread = aim_tlv_get8(tlvlist, 0x0081, 1))) { - havenewmail = 0; - new->nummsgs = 0; - } - new->domain = aim_tlv_getstr(tlvlist, 0x0082, 1); - new->flag = aim_tlv_get16(tlvlist, 0x0084, 1); - - alertitle = aim_tlv_getstr(tlvlist, 0x0005, 1); - alerturl = aim_tlv_getstr(tlvlist, 0x000d, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, new, havenewmail, alertitle, (alerturl ? alerturl + 2 : NULL)); - - aim_tlvlist_free(tlvlist); - - g_free(alertitle); - g_free(alerturl); - - return ret; -} - -/** - * Subtype 0x0016 - Send something or other - * - * @param od The oscar session. - * @param conn The email connection for this session. - * @return Return 0 if no errors, otherwise return the error number. - */ -int -aim_email_activate(OscarData *od) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ALERT))) - return -EINVAL; - - byte_stream_new(&bs, 1+16); - - /* I would guess this tells AIM that you want updates for your mail accounts */ - /* ...but I really have no idea */ - byte_stream_put8(&bs, 0x02); - byte_stream_put32(&bs, 0x04000000); - byte_stream_put32(&bs, 0x04000000); - byte_stream_put32(&bs, 0x04000000); - byte_stream_put32(&bs, 0x00000000); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0016, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0007) - return parseinfo(od, conn, mod, frame, snac, bs); - - return 0; -} - -static void -email_shutdown(OscarData *od, aim_module_t *mod) -{ - while (od->emailinfo) - { - struct aim_emailinfo *tmp = od->emailinfo; - od->emailinfo = od->emailinfo->next; - g_free(tmp->cookie16); - g_free(tmp->cookie8); - g_free(tmp->url); - g_free(tmp->domain); - g_free(tmp); - } - - return; -} - -int -email_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_ALERT; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "alert", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = email_shutdown; - - return 0; -}
--- a/libpurple/protocols/oscar/family_auth.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,647 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0017 - Authentication. - * - * Deals with the authorizer for SNAC-based login, and also old-style - * non-SNAC login. - * - */ - -#include "oscar.h" -#include "oscarcommon.h" - -#include <ctype.h> - -/* #define USE_XOR_FOR_ICQ */ - -#ifdef USE_XOR_FOR_ICQ -/** - * Encode a password using old XOR method - * - * This takes a const pointer to a (null terminated) string - * containing the unencoded password. It also gets passed - * an already allocated buffer to store the encoded password. - * This buffer should be the exact length of the password without - * the null. The encoded password buffer /is not %NULL terminated/. - * - * The encoding_table seems to be a fixed set of values. We'll - * hope it doesn't change over time! - * - * This is only used for the XOR method, not the better MD5 method. - * - * @param password Incoming password. - * @param encoded Buffer to put encoded password. - */ -static int -aim_encode_password(const char *password, guint8 *encoded) -{ - guint8 encoding_table[] = { - 0xf3, 0x26, 0x81, 0xc4, - 0x39, 0x86, 0xdb, 0x92, - 0x71, 0xa3, 0xb9, 0xe6, - 0x53, 0x7a, 0x95, 0x7c - }; - unsigned int i; - - for (i = 0; i < strlen(password); i++) - encoded[i] = (password[i] ^ encoding_table[i]); - - return 0; -} -#endif - -#ifdef USE_OLD_MD5 -static int -aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest) -{ - GChecksum *hash; - gsize digest_len = 16; - - hash = g_checksum_new(G_CHECKSUM_MD5); - g_checksum_update(hash, (const guchar *)key, -1); - g_checksum_update(hash, (const guchar *)password, password_len); - g_checksum_update(hash, (const guchar *)AIM_MD5_STRING, -1); - g_checksum_get_digest(hash, digest, &digest_len); - g_checksum_free(hash); - - return 0; -} -#else -static int -aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest) -{ - GChecksum *hash; - guchar passdigest[16]; - gsize digest_len = 16; - - hash = g_checksum_new(G_CHECKSUM_MD5); - g_checksum_update(hash, (const guchar *)password, password_len); - g_checksum_get_digest(hash, passdigest, &digest_len); - g_checksum_reset(hash); - - g_checksum_update(hash, (const guchar *)key, -1); - g_checksum_update(hash, passdigest, digest_len); - g_checksum_update(hash, (const guchar *)AIM_MD5_STRING, -1); - g_checksum_get_digest(hash, digest, &digest_len); - g_checksum_free(hash); - - return 0; -} -#endif - -#ifdef USE_XOR_FOR_ICQ -/* - * Part two of the ICQ hack. Note the ignoring of the key. - */ -static int -goddamnicq2(OscarData *od, FlapConnection *conn, const char *sn, const char *password, ClientInfo *ci) -{ - FlapFrame *frame; - GSList *tlvlist = NULL; - int passwdlen; - guint8 *password_encoded; - guint32 distrib; - - passwdlen = strlen(password); - password_encoded = (guint8 *)g_malloc(passwdlen+1); - if (passwdlen > MAXICQPASSLEN) - passwdlen = MAXICQPASSLEN; - - frame = flap_frame_new(od, 0x01, 1152); - - aim_encode_password(password, password_encoded); - - distrib = oscar_get_ui_info_int( - od->icq ? "prpl-icq-distid" : "prpl-aim-distid", - ci->distrib); - - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ - aim_tlvlist_add_str(&tlvlist, 0x0001, sn); - aim_tlvlist_add_raw(&tlvlist, 0x0002, passwdlen, password_encoded); - - if (ci->clientstring != NULL) - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring); - else { - gchar *clientstring = oscar_get_clientstring(); - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring); - g_free(clientstring); - } - aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid); - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major); - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor); - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point); - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build); - aim_tlvlist_add_32(&tlvlist, 0x0014, distrib); /* distribution chan */ - aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang); - aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country); - - aim_tlvlist_write(&frame->data, &tlvlist); - - g_free(password_encoded); - aim_tlvlist_free(tlvlist); - - flap_connection_send(conn, frame); - - return 0; -} -#endif - -/* - * Subtype 0x0002 - * - * This is the initial login request packet. - * - * NOTE!! If you want/need to make use of the aim_sendmemblock() function, - * then the client information you send here must exactly match the - * executable that you're pulling the data from. - * - * Java AIM 1.1.19: - * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001" - * clientid = 0x0001 - * major = 0x0001 - * minor = 0x0001 - * point = (not sent) - * build = 0x0013 - * unknown= (not sent) - * - * AIM for Linux 1.1.112: - * clientstring = "AOL Instant Messenger (SM)" - * clientid = 0x1d09 - * major = 0x0001 - * minor = 0x0001 - * point = 0x0001 - * build = 0x0070 - * unknown= 0x0000008b - * serverstore = 0x01 - * - * @param truncate_pass Truncate the password to 8 characters. This - * usually happens for AOL accounts. We are told that we - * should truncate it if the 0x0017/0x0007 SNAC contains - * a TLV of type 0x0026 with data 0x0000. - * @param allow_multiple_logins Allow multiple logins? If TRUE, the AIM - * server will prompt the user when multiple logins occur. If - * FALSE, existing connections (on other clients) will be - * disconnected automatically as we connect. - */ -int -aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins) -{ - FlapFrame *frame; - GSList *tlvlist = NULL; - guint8 digest[16]; - aim_snacid_t snacid; - size_t password_len; - guint32 distrib; - - if (!ci || !sn || !password) - return -EINVAL; - -#ifdef USE_XOR_FOR_ICQ - /* If we're signing on an ICQ account then use the older, XOR login method */ - if (aim_snvalid_icq(sn)) - return goddamnicq2(od, conn, sn, password, ci); -#endif - - frame = flap_frame_new(od, 0x02, 1152); - - snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0002, 0x0000, NULL, 0); - aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0002, snacid); - - aim_tlvlist_add_str(&tlvlist, 0x0001, sn); - - /* Truncate ICQ and AOL passwords, if necessary */ - password_len = strlen(password); - if (oscar_util_valid_name_icq(sn) && (password_len > MAXICQPASSLEN)) - password_len = MAXICQPASSLEN; - else if (truncate_pass && password_len > 8) - password_len = 8; - - aim_encode_password_md5(password, password_len, key, digest); - - distrib = oscar_get_ui_info_int( - od->icq ? "prpl-icq-distid" : "prpl-aim-distid", - ci->distrib); - - aim_tlvlist_add_raw(&tlvlist, 0x0025, 16, digest); - -#ifndef USE_OLD_MD5 - aim_tlvlist_add_noval(&tlvlist, 0x004c); -#endif - - if (ci->clientstring != NULL) - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring); - else { - gchar *clientstring = oscar_get_clientstring(); - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring); - g_free(clientstring); - } - aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid); - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major); - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor); - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point); - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build); - aim_tlvlist_add_32(&tlvlist, 0x0014, distrib); - aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang); - aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country); - - /* - * If set, old-fashioned buddy lists will not work. You will need - * to use SSI. - */ - aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x03)); - - aim_tlvlist_write(&frame->data, &tlvlist); - - aim_tlvlist_free(tlvlist); - - flap_connection_send(conn, frame); - - return 0; -} - -/* - * This is sent back as a general response to the login command. - * It can be either an error or a success, depending on the - * presence of certain TLVs. - * - * The client should check the value passed as errorcode. If - * its nonzero, there was an error. - */ -static int -parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - GSList *tlvlist; - aim_rxcallback_t userfunc; - struct aim_authresp_info *info; - int ret = 0; - - info = g_new0(struct aim_authresp_info, 1); - - /* - * Read block of TLVs. All further data is derived - * from what is parsed here. - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * No matter what, we should have a username. - */ - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) { - info->bn = aim_tlv_getstr(tlvlist, 0x0001, 1); - purple_connection_set_display_name(od->gc, info->bn); - } - - /* - * Check for an error code. If so, we should also - * have an error url. - */ - if (aim_tlv_gettlv(tlvlist, 0x0008, 1)) - info->errorcode = aim_tlv_get16(tlvlist, 0x0008, 1); - if (aim_tlv_gettlv(tlvlist, 0x0004, 1)) - info->errorurl = aim_tlv_getstr(tlvlist, 0x0004, 1); - - /* - * BOS server address. - */ - if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) - info->bosip = aim_tlv_getstr(tlvlist, 0x0005, 1); - - /* - * Authorization cookie. - */ - if (aim_tlv_gettlv(tlvlist, 0x0006, 1)) { - aim_tlv_t *tmptlv; - - tmptlv = aim_tlv_gettlv(tlvlist, 0x0006, 1); - if (tmptlv != NULL) - { - info->cookielen = tmptlv->length; - info->cookie = tmptlv->value; - } - } - - /* - * The email address attached to this account - * Not available for ICQ or @mac.com logins. - * If you receive this TLV, then you are allowed to use - * family 0x0018 to check the status of your email. - * XXX - Not really true! - */ - if (aim_tlv_gettlv(tlvlist, 0x0011, 1)) - info->email = aim_tlv_getstr(tlvlist, 0x0011, 1); - - /* - * The registration status. (Not real sure what it means.) - * Not available for ICQ or @mac.com logins. - * - * 1 = No disclosure - * 2 = Limited disclosure - * 3 = Full disclosure - * - * This has to do with whether your email address is available - * to other users or not. AFAIK, this feature is no longer used. - * - * Means you can use the admin family? (0x0007) - * - */ - if (aim_tlv_gettlv(tlvlist, 0x0013, 1)) - info->regstatus = aim_tlv_get16(tlvlist, 0x0013, 1); - - if (aim_tlv_gettlv(tlvlist, 0x0040, 1)) - info->latestbeta.build = aim_tlv_get32(tlvlist, 0x0040, 1); - if (aim_tlv_gettlv(tlvlist, 0x0041, 1)) - info->latestbeta.url = aim_tlv_getstr(tlvlist, 0x0041, 1); - if (aim_tlv_gettlv(tlvlist, 0x0042, 1)) - info->latestbeta.info = aim_tlv_getstr(tlvlist, 0x0042, 1); - if (aim_tlv_gettlv(tlvlist, 0x0043, 1)) - info->latestbeta.name = aim_tlv_getstr(tlvlist, 0x0043, 1); - - if (aim_tlv_gettlv(tlvlist, 0x0044, 1)) - info->latestrelease.build = aim_tlv_get32(tlvlist, 0x0044, 1); - if (aim_tlv_gettlv(tlvlist, 0x0045, 1)) - info->latestrelease.url = aim_tlv_getstr(tlvlist, 0x0045, 1); - if (aim_tlv_gettlv(tlvlist, 0x0046, 1)) - info->latestrelease.info = aim_tlv_getstr(tlvlist, 0x0046, 1); - if (aim_tlv_gettlv(tlvlist, 0x0047, 1)) - info->latestrelease.name = aim_tlv_getstr(tlvlist, 0x0047, 1); - - /* - * URL to change password. - */ - if (aim_tlv_gettlv(tlvlist, 0x0054, 1)) - info->chpassurl = aim_tlv_getstr(tlvlist, 0x0054, 1); - - od->authinfo = info; - - if ((userfunc = aim_callhandler(od, snac ? snac->family : SNAC_FAMILY_AUTH, snac ? snac->subtype : 0x0003))) - ret = userfunc(od, conn, frame, info); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -#ifdef USE_XOR_FOR_ICQ -/* - * Subtype 0x0007 (kind of) - Send a fake type 0x0007 SNAC to the client - * - * This is a bit confusing. - * - * Normal SNAC login goes like this: - * - connect - * - server sends flap version - * - client sends flap version - * - client sends username (17/6) - * - server sends hash key (17/7) - * - client sends auth request (17/2 -- aim_send_login) - * - server yells - * - * XOR login (for ICQ) goes like this: - * - connect - * - server sends flap version - * - client sends auth request which contains flap version (aim_send_login) - * - server yells - * - * For the client API, we make them implement the most complicated version, - * and for the simpler version, we fake it and make it look like the more - * complicated process. - * - * This is done by giving the client a faked key, just so we can convince - * them to call aim_send_login right away, which will detect the session - * flag that says this is XOR login and ignore the key, sending an ICQ - * login request instead of the normal SNAC one. - * - * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/. - */ -static int -goddamnicq(OscarData *od, FlapConnection *conn, const char *sn) -{ - FlapFrame frame; - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(od, SNAC_FAMILY_AUTH, 0x0007))) - userfunc(od, conn, &frame, ""); - - return 0; -} -#endif - -/* - * Subtype 0x0006 - * - * In AIM 3.5 protocol, the first stage of login is to request login from the - * Authorizer, passing it the username for verification. If the name is - * invalid, a 0017/0003 is spit back, with the standard error contents. If - * valid, a 0017/0007 comes back, which is the signal to send it the main - * login command (0017/0002). - * - */ -int -aim_request_login(OscarData *od, FlapConnection *conn, const char *sn) -{ - FlapFrame *frame; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - if (!od || !conn || !sn) - return -EINVAL; - -#ifdef USE_XOR_FOR_ICQ - if (aim_snvalid_icq(sn)) - return goddamnicq(od, conn, sn); -#endif - - frame = flap_frame_new(od, 0x02, 10+2+2+strlen(sn)+8); - - snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0006, 0x0000, NULL, 0); - aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0006, snacid); - - aim_tlvlist_add_str(&tlvlist, 0x0001, sn); - - /* Tell the server we support SecurID logins. */ - aim_tlvlist_add_noval(&tlvlist, 0x004b); - - /* Unknown. Sent in recent WinAIM clients.*/ - aim_tlvlist_add_noval(&tlvlist, 0x005a); - - aim_tlvlist_write(&frame->data, &tlvlist); - aim_tlvlist_free(tlvlist); - - flap_connection_send(conn, frame); - - return 0; -} - -/* - * Subtype 0x0007 - * - * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed - * by only its length in a two byte word. - * - * Calls the client, which should then use the value to call aim_send_login. - * - */ -static int -keyparse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int keylen; - char *keystr; - GSList *tlvlist; - gboolean truncate_pass; - PurpleConnection *gc; - PurpleAccount *account; - ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM; - ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ; - - gc = od->gc; - account = purple_connection_get_account(gc); - - keylen = byte_stream_get16(bs); - keystr = byte_stream_getstr(bs, keylen); - if (!g_utf8_validate(keystr, -1, NULL)) { - purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with " - "invalid UTF-8 keystr.\n", snac->family, snac->subtype); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Received unexpected response from server")); - g_free(keystr); - return 1; - } - - tlvlist = aim_tlvlist_read(bs); - - /* - * If the truncate_pass TLV exists then we should truncate the - * user's password to 8 characters. This flag is sent to us - * when logging in with an AOL user's username. - */ - truncate_pass = aim_tlv_gettlv(tlvlist, 0x0026, 1) != NULL; - - /* XXX - When GiantGrayPanda signed on AIM I got a thing asking me to register - * for the netscape network. This SNAC had a type 0x0058 TLV with length 10. - * Data is 0x0007 0004 3e19 ae1e 0006 0004 0000 0005 */ - - aim_send_login(od, conn, purple_account_get_username(account), - purple_connection_get_password(gc), truncate_pass, - od->icq ? &icqinfo : &aiminfo, keystr, - purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS)); - - purple_connection_update_progress(gc, - _("Password sent"), 2, OSCAR_CONNECT_STEPS); - - g_free(keystr); - aim_tlvlist_free(tlvlist); - - return 1; -} - -/** - * Subtype 0x000a - * - * Receive SecurID request. - */ -static int -got_securid_request(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame); - - return ret; -} - -/** - * Subtype 0x000b - * - * Send SecurID response. - */ -int -aim_auth_securid_send(OscarData *od, const char *securid) -{ - FlapConnection *conn; - FlapFrame *frame; - int len; - - if (!od || !(conn = flap_connection_getbytype_all(od, SNAC_FAMILY_AUTH)) || !securid) - return -EINVAL; - - len = strlen(securid); - - frame = flap_frame_new(od, 0x02, 10+2+len); - - /* aim_snacid_t snacid = */ aim_cachesnac(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0x0000, NULL, 0); - aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_RESPONSE, 0); - - byte_stream_put16(&frame->data, len); - byte_stream_putstr(&frame->data, securid); - - flap_connection_send(conn, frame); - - return 0; -} - -static void -auth_shutdown(OscarData *od, aim_module_t *mod) -{ - if (od->authinfo != NULL) - { - g_free(od->authinfo->bn); - g_free(od->authinfo->bosip); - g_free(od->authinfo->errorurl); - g_free(od->authinfo->email); - g_free(od->authinfo->chpassurl); - g_free(od->authinfo->latestrelease.name); - g_free(od->authinfo->latestrelease.url); - g_free(od->authinfo->latestrelease.info); - g_free(od->authinfo->latestbeta.name); - g_free(od->authinfo->latestbeta.url); - g_free(od->authinfo->latestbeta.info); - g_free(od->authinfo); - } -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0003) - return parse(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0007) - return keyparse(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000a) - return got_securid_request(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -auth_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_AUTH; - mod->version = 0x0000; - mod->flags = 0; - strncpy(mod->name, "auth", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = auth_shutdown; - - return 0; -}
--- a/libpurple/protocols/oscar/family_bart.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0010 - Server stored buddy art - * - * Used for storing and retrieving your cute little buddy icon - * from the AIM servers. - * - */ - -#include "oscar.h" - -/** - * Subtype 0x0002 - Upload your icon. - * - * @param od The oscar session. - * @param icon The raw data of the icon image file. - * @param iconlen Length of the raw data of the icon image file. - * @return Return 0 if no errors, otherwise return the error number. - */ -int -aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !icon || !iconlen) - return -EINVAL; - - byte_stream_new(&bs, 2 + 2 + iconlen); - - /* The reference number for the icon */ - byte_stream_put16(&bs, 1); - - /* The icon */ - byte_stream_put16(&bs, iconlen); - byte_stream_putraw(&bs, icon, iconlen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0002, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/** - * Subtype 0x0003 - Acknowledgement for uploading a buddy icon. - * - * You get this honky after you upload a buddy icon. - */ -static int -uploadack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - - byte_stream_get16(bs); - byte_stream_get16(bs); - byte_stream_get8(bs); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame); - - return ret; -} - -/** - * Subtype 0x0004 - Request someone's icon. - * - * @param od The oscar session. - * @param bn The name of the buddy whose icon you are requesting. - * @param iconcsum The MD5 checksum of the icon you are requesting. - * @param iconcsumlen Length of the MD5 checksum given above. Should be 10 bytes. - * @return Return 0 if no errors, otherwise return the error number. - */ -int -aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !bn || *bn == '\0' || !iconcsum || !iconcsumlen) - return -EINVAL; - - byte_stream_new(&bs, 1+strlen(bn) + 4 + 1+iconcsumlen); - - /* Buddy name */ - byte_stream_put8(&bs, strlen(bn)); - byte_stream_putstr(&bs, bn); - - /* Some numbers. You like numbers, right? */ - byte_stream_put8(&bs, 0x01); - byte_stream_put16(&bs, 0x0001); - byte_stream_put8(&bs, iconcsumtype); - - /* Icon string */ - byte_stream_put8(&bs, iconcsumlen); - byte_stream_putraw(&bs, iconcsum, iconcsumlen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0004, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/** - * Subtype 0x0005 - Receive a buddy icon. - * - * This is sent in response to a buddy icon request. - */ -static int -parseicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - char *bn; - guint16 iconlen; - guint8 iconcsumtype, iconcsumlen, *iconcsum, *icon; - - bn = byte_stream_getstr(bs, byte_stream_get8(bs)); - if (!g_utf8_validate(bn, -1, NULL)) { - purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with " - "invalid UTF-8 buddy name.\n", snac->family, snac->subtype); - g_free(bn); - return 1; - } - byte_stream_get16(bs); /* flags */ - iconcsumtype = byte_stream_get8(bs); - iconcsumlen = byte_stream_get8(bs); - iconcsum = byte_stream_getraw(bs, iconcsumlen); - iconlen = byte_stream_get16(bs); - icon = byte_stream_getraw(bs, iconlen); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, bn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen); - - g_free(bn); - g_free(iconcsum); - g_free(icon); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0003) - return uploadack(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0005) - return parseicon(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -bart_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_BART; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "bart", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_bos.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0009 - Basic Oscar Service. - * - * The functionality of this family has been replaced by SSI. - */ - -#include "oscar.h" - -#include <string.h> - -/* Subtype 0x0002 - Request BOS rights. */ -void -aim_bos_reqrights(OscarData *od, FlapConnection *conn) -{ - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BOS, 0x0002); -} - -/* Subtype 0x0003 - BOS Rights. */ -static int rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - GSList *tlvlist; - guint16 maxpermits = 0, maxdenies = 0; - int ret = 0; - - /* - * TLVs follow - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * TLV type 0x0001: Maximum number of buddies on permit list. - */ - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) - maxpermits = aim_tlv_get16(tlvlist, 0x0001, 1); - - /* - * TLV type 0x0002: Maximum number of buddies on deny list. - */ - if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) - maxdenies = aim_tlv_get16(tlvlist, 0x0002, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, maxpermits, maxdenies); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0003) - return rights(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -bos_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_BOS; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "bos", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_buddy.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0003 (SNAC_FAMILY_BUDDY) - Old-style Buddylist Management (non-SSI). - * - */ - -#include "oscar.h" - -#include <string.h> - -/* - * Subtype 0x0002 - Request rights. - * - * Request Buddy List rights. - * - */ -void -aim_buddylist_reqrights(OscarData *od, FlapConnection *conn) -{ - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_REQRIGHTS); -} - -/* - * Subtype 0x0003 - Rights. - * - */ -static int -rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - GSList *tlvlist; - guint16 maxbuddies = 0, maxwatchers = 0; - int ret = 0; - - /* - * TLVs follow - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * TLV type 0x0001: Maximum number of buddies. - */ - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) - maxbuddies = aim_tlv_get16(tlvlist, 0x0001, 1); - - /* - * TLV type 0x0002: Maximum number of watchers. - * - * Watchers are other users who have you on their buddy - * list. (This is called the "reverse list" by a certain - * other IM protocol.) - * - */ - if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) - maxwatchers = aim_tlv_get16(tlvlist, 0x0002, 1); - - /* - * TLV type 0x0003: Unknown. - * - * ICQ only? - */ - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, maxbuddies, maxwatchers); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -/* - * Subtypes 0x000b (SNAC_SUBTYPE_BUDDY_ONCOMING) and 0x000c (SNAC_SUBTYPE_BUDDY_OFFGOING) - Change in buddy status - * - * Oncoming Buddy notifications contain a subset of the - * user information structure. It's close enough to run - * through aim_info_extract() however. - * - * Although the offgoing notification contains no information, - * it is still in a format parsable by aim_info_extract(). - * - */ -static int -buddychange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_userinfo_t userinfo; - aim_rxcallback_t userfunc; - - aim_info_extract(od, bs, &userinfo); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, &userinfo); - - if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && - userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) { - PurpleAccount *account = purple_connection_get_account(od->gc); - PurpleBuddy *buddy = purple_blist_find_buddy(account, userinfo.bn); - - if (buddy) { - PurplePresence *presence = purple_buddy_get_presence(buddy); - - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD)) - icq_im_xstatus_request(od, userinfo.bn); - } - } - aim_info_free(&userinfo); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == SNAC_SUBTYPE_BUDDY_RIGHTSINFO) - return rights(od, conn, mod, frame, snac, bs); - else if ((snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING) || (snac->subtype == SNAC_SUBTYPE_BUDDY_OFFGOING)) - return buddychange(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -buddylist_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_BUDDY; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "buddy", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_chat.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,403 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x000e - Routines for the Chat service. - * - */ - -#include "oscar.h" - -#include <string.h> - -/* Stored in the ->internal of chat connections */ -struct chatconnpriv -{ - guint16 exchange; - char *name; - guint16 instance; -}; - -void -flap_connection_destroy_chat(OscarData *od, FlapConnection *conn) -{ - struct chatconnpriv *ccp = (struct chatconnpriv *)conn->internal; - - if (ccp) - g_free(ccp->name); - g_free(ccp); - - return; -} - -int -aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo) -{ - if (!outinfo) - return 0; - - outinfo->exchange = byte_stream_get16(bs); - outinfo->namelen = byte_stream_get8(bs); - outinfo->name = (char *)byte_stream_getraw(bs, outinfo->namelen); - outinfo->instance = byte_stream_get16(bs); - - return 0; -} - -/* - * Subtype 0x0002 - General room information. Lots of stuff. - * - * Values I know are in here but I haven't attached - * them to any of the 'Unknown's: - * - Language (English) - * - */ -static int -infoupdate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - int ret = 0; - guint8 detaillevel = 0; - struct aim_chat_roominfo roominfo; - GSList *tlvlist; - guint16 maxmsglen, maxvisiblemsglen; - - aim_chat_readroominfo(bs, &roominfo); - - detaillevel = byte_stream_get8(bs); - - if (detaillevel != 0x02) { - purple_debug_misc("oscar", "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel); - return 1; - } - - byte_stream_get16(bs); /* skip the TLV count */ - - /* - * Everything else are TLVs. - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * Type 0x00d1: Maximum Message Length - */ - maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1); - - /* - * Type 0x00da: Maximum visible message length - */ - maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) { - ret = userfunc(od, conn, frame, maxmsglen, maxvisiblemsglen); - } - - g_free(roominfo.name); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -/* Subtypes 0x0003 and 0x0004 */ -static int -userlistchange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_userinfo_t *userinfo = NULL; - aim_rxcallback_t userfunc; - int curcount = 0, ret = 0; - - while (byte_stream_bytes_left(bs)) { - curcount++; - userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t)); - aim_info_extract(od, bs, &userinfo[curcount-1]); - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, curcount, userinfo); - - aim_info_free(userinfo); - g_free(userinfo); - - return ret; -} - -/* - * Subtype 0x0005 - Send a Chat Message. - * - * Possible flags: - * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages - * should be sent to their sender. - * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse - * (Note that WinAIM does not honor this, - * and displays the message as normal.) - * - * XXX convert this to use tlvchains - */ -int -aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language) -{ - int i; - ByteStream bs; - IcbmCookie *cookie; - aim_snacid_t snacid; - guint8 ckstr[8]; - GSList *tlvlist = NULL, *inner_tlvlist = NULL; - - if (!od || !conn || !msg || (msglen <= 0)) - return 0; - - byte_stream_new(&bs, 1142); - - snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0); - - /* - * Cookie - * - * XXX mkcookie should generate the cookie and cache it in one - * operation to preserve uniqueness. - */ - for (i = 0; i < 8; i += 4) { - gint32 rnd = g_random_int(); - ckstr[i] = (guint8)((rnd & 0xFF000000) >> 24); - ckstr[i+1] = (guint8)((rnd & 0xFF0000) >> 16); - ckstr[i+2] = (guint8)((rnd & 0xFF00) >> 8); - ckstr[i+3] = (guint8)(rnd & 0xFF); - } - - cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); - cookie->data = NULL; /* XXX store something useful here */ - - aim_cachecookie(od, cookie); - - /* ICBM Header */ - byte_stream_putraw(&bs, ckstr, 8); /* Cookie */ - byte_stream_put16(&bs, 0x0003); /* Channel */ - - /* - * Type 1: Flag meaning this message is destined to the room. - */ - aim_tlvlist_add_noval(&tlvlist, 0x0001); - - /* - * Type 6: Reflect - */ - if (!(flags & AIM_CHATFLAGS_NOREFLECT)) - aim_tlvlist_add_noval(&tlvlist, 0x0006); - - /* - * Type 7: Autoresponse - */ - if (flags & AIM_CHATFLAGS_AWAY) - aim_tlvlist_add_noval(&tlvlist, 0x0007); - - /* - * SubTLV: Type 1: Message - */ - aim_tlvlist_add_raw(&inner_tlvlist, 0x0001, msglen, (guchar *)msg); - - /* - * SubTLV: Type 2: Encoding - */ - if (encoding != NULL) - aim_tlvlist_add_str(&inner_tlvlist, 0x0002, encoding); - - /* - * SubTLV: Type 3: Language - */ - if (language != NULL) - aim_tlvlist_add_str(&inner_tlvlist, 0x0003, language); - - /* - * Type 5: Message block. Contains more TLVs. - * - * This could include other information... We just - * put in a message TLV however. - * - */ - aim_tlvlist_add_frozentlvlist(&tlvlist, 0x0005, &inner_tlvlist); - - aim_tlvlist_write(&bs, &tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0006 - * - * We could probably include this in the normal ICBM parsing - * code as channel 0x0003, however, since only the start - * would be the same, we might as well do it here. - * - * General outline of this SNAC: - * snac - * cookie - * channel id - * tlvlist - * unknown - * source user info - * name - * evility - * userinfo tlvs - * online time - * etc - * message metatlv - * message tlv - * message string - * possibly others - * - */ -static int -incomingim_ch3(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0, i; - aim_rxcallback_t userfunc; - aim_userinfo_t userinfo; - guint8 cookie[8]; - guint16 channel; - GSList *tlvlist; - char *msg = NULL; - int len = 0; - char *encoding = NULL, *language = NULL; - IcbmCookie *ck; - aim_tlv_t *tlv; - ByteStream tbs; - - memset(&userinfo, 0, sizeof(aim_userinfo_t)); - - /* - * Read ICBM Cookie. - */ - for (i = 0; i < 8; i++) - cookie[i] = byte_stream_get8(bs); - - if ((ck = aim_uncachecookie(od, cookie, AIM_COOKIETYPE_CHAT))) { - g_free(ck->data); - g_free(ck); - } - - /* - * Channel ID - * - * Channel 0x0003 is used for chat messages. - * - */ - channel = byte_stream_get16(bs); - - if (channel != 0x0003) { - purple_debug_misc("oscar", "faim: chat_incoming: unknown channel! (0x%04x)\n", channel); - return 0; - } - - /* - * Start parsing TLVs right away. - */ - tlvlist = aim_tlvlist_read(bs); - - /* - * Type 0x0003: Source User Information - */ - tlv = aim_tlv_gettlv(tlvlist, 0x0003, 1); - if (tlv != NULL) - { - byte_stream_init(&tbs, tlv->value, tlv->length); - aim_info_extract(od, &tbs, &userinfo); - } - - /* - * Type 0x0005: Message Block. Conains more TLVs. - */ - tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1); - if (tlv != NULL) - { - GSList *inner_tlvlist; - aim_tlv_t *inner_tlv; - - byte_stream_init(&tbs, tlv->value, tlv->length); - inner_tlvlist = aim_tlvlist_read(&tbs); - - /* - * Type 0x0001: Message. - */ - inner_tlv = aim_tlv_gettlv(inner_tlvlist, 0x0001, 1); - if (inner_tlv != NULL) - { - len = inner_tlv->length; - msg = aim_tlv_getvalue_as_string(inner_tlv); - } - - /* - * Type 0x0002: Encoding. - */ - encoding = aim_tlv_getstr(inner_tlvlist, 0x0002, 1); - - /* - * Type 0x0003: Language. - */ - language = aim_tlv_getstr(inner_tlvlist, 0x0003, 1); - - aim_tlvlist_free(inner_tlvlist); - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, &userinfo, len, msg, encoding, language); - - aim_info_free(&userinfo); - g_free(msg); - g_free(encoding); - g_free(language); - aim_tlvlist_free(tlvlist); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0002) - return infoupdate(od, conn, mod, frame, snac, bs); - else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004)) - return userlistchange(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0006) - return incomingim_ch3(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -chat_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_CHAT; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "chat", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_chatnav.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,446 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x000d - Handle ChatNav. - * - * The ChatNav(igation) service does various things to keep chat - * alive. It provides room information, room searching and creating, - * as well as giving users the right ("permission") to use chat. - * - */ - -#include "oscar.h" - -static int -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_snac_t *snac2; - guint16 error, chatnav_error; - GSList *tlvlist; - - snac2 = aim_remsnac(od, snac->id); - if (!snac2) { - purple_debug_warning("oscar", "chatnav error: received response to unknown request (%08x)\n", snac->id); - return 0; - } - - if (snac2->family != SNAC_FAMILY_CHATNAV) { - purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family); - g_free(snac2->data); - g_free(snac2); - return 0; - } - - /* - * We now know what the original SNAC subtype was. - */ - if (snac2->type == 0x0008) /* create room */ - { - error = byte_stream_get16(bs); - tlvlist = aim_tlvlist_read(bs); - chatnav_error = aim_tlv_get16(tlvlist, 0x0008, 1); - - purple_debug_warning("oscar", - "Could not join room, error=0x%04hx, chatnav_error=0x%04hx\n", - error, chatnav_error); - purple_notify_error(od->gc, NULL, _("Could not join chat room"), - chatnav_error == 0x0033 ? _("Invalid chat room name") : - _("Unknown error"), - purple_request_cpar_from_connection(od->gc)); - - ret = 1; - } - - g_free(snac2->data); - g_free(snac2); - - return ret; -} - -/* - * Subtype 0x0002 - * - * conn must be a chatnav connection! - * - */ -void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn) -{ - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_CHATNAV, 0x0002); -} - -/* - * Subtype 0x0008 - */ -int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange) -{ - static const char ck[] = {"create"}; - static const char lang[] = {"en"}; - static const char charset[] = {"us-ascii"}; - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - byte_stream_new(&bs, 1142); - - snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0); - - /* exchange */ - byte_stream_put16(&bs, exchange); - - /* - * This looks to be a big hack. You'll note that this entire - * SNAC is just a room info structure, but the hard room name, - * here, is set to "create". - * - * Either this goes on the "list of questions concerning - * why-the-hell-did-you-do-that", or this value is completely - * ignored. Without experimental evidence, but a good knowledge of - * AOL style, I'm going to guess that it is the latter, and that - * the value of the room name in create requests is ignored. - */ - byte_stream_put8(&bs, strlen(ck)); - byte_stream_putstr(&bs, ck); - - /* - * instance - * - * Setting this to 0xffff apparently assigns the last instance. - * - */ - byte_stream_put16(&bs, 0xffff); - - /* detail level */ - byte_stream_put8(&bs, 0x01); - - aim_tlvlist_add_str(&tlvlist, 0x00d3, name); - aim_tlvlist_add_str(&tlvlist, 0x00d6, charset); - aim_tlvlist_add_str(&tlvlist, 0x00d7, lang); - - /* tlvcount */ - byte_stream_put16(&bs, aim_tlvlist_count(tlvlist)); - aim_tlvlist_write(&bs, &tlvlist); - - aim_tlvlist_free(tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -static int -parseinfo_perms(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2) -{ - aim_rxcallback_t userfunc; - int ret = 0; - struct aim_chat_exchangeinfo *exchanges = NULL; - int curexchange; - aim_tlv_t *exchangetlv; - guint8 maxrooms = 0; - GSList *tlvlist, *innerlist; - - tlvlist = aim_tlvlist_read(bs); - - /* - * Type 0x0002: Maximum concurrent rooms. - */ - if (aim_tlv_gettlv(tlvlist, 0x0002, 1)) - maxrooms = aim_tlv_get8(tlvlist, 0x0002, 1); - - /* - * Type 0x0003: Exchange information - * - * There can be any number of these, each one - * representing another exchange. - * - */ - for (curexchange = 0; ((exchangetlv = aim_tlv_gettlv(tlvlist, 0x0003, curexchange+1))); ) { - ByteStream tbs; - - byte_stream_init(&tbs, exchangetlv->value, exchangetlv->length); - - curexchange++; - - exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); - - /* exchange number */ - exchanges[curexchange-1].number = byte_stream_get16(&tbs); - innerlist = aim_tlvlist_read(&tbs); - - /* - * Type 0x0002: Unknown - */ - if (aim_tlv_gettlv(innerlist, 0x0002, 1)) { - guint16 classperms; - - classperms = aim_tlv_get16(innerlist, 0x0002, 1); - - purple_debug_misc("oscar", "faim: class permissions %x\n", classperms); - } - - /* - * Type 0x00c9: Flags - * - * 1 Evilable - * 2 Nav Only - * 4 Instancing Allowed - * 8 Occupant Peek Allowed - * - */ - if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) - exchanges[curexchange-1].flags = aim_tlv_get16(innerlist, 0x00c9, 1); - - /* - * Type 0x00d3: Exchange Description - */ - if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) - exchanges[curexchange-1].name = aim_tlv_getstr(innerlist, 0x00d3, 1); - else - exchanges[curexchange-1].name = NULL; - - /* - * Type 0x00d5: Creation Permissions - * - * 0 Creation not allowed - * 1 Room creation allowed - * 2 Exchange creation allowed - * - */ - if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) - aim_tlv_get8(innerlist, 0x00d5, 1); /* createperms */ - - /* - * Type 0x00d6: Character Set (First Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d6, 1)) - exchanges[curexchange-1].charset1 = aim_tlv_getstr(innerlist, 0x00d6, 1); - else - exchanges[curexchange-1].charset1 = NULL; - - /* - * Type 0x00d7: Language (First Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d7, 1)) - exchanges[curexchange-1].lang1 = aim_tlv_getstr(innerlist, 0x00d7, 1); - else - exchanges[curexchange-1].lang1 = NULL; - - /* - * Type 0x00d8: Character Set (Second Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d8, 1)) - exchanges[curexchange-1].charset2 = aim_tlv_getstr(innerlist, 0x00d8, 1); - else - exchanges[curexchange-1].charset2 = NULL; - - /* - * Type 0x00d9: Language (Second Time) - */ - if (aim_tlv_gettlv(innerlist, 0x00d9, 1)) - exchanges[curexchange-1].lang2 = aim_tlv_getstr(innerlist, 0x00d9, 1); - else - exchanges[curexchange-1].lang2 = NULL; - - aim_tlvlist_free(innerlist); - } - - /* - * Call client. - */ - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, snac2->type, maxrooms, curexchange, exchanges); - - for (curexchange--; curexchange >= 0; curexchange--) { - g_free(exchanges[curexchange].name); - g_free(exchanges[curexchange].charset1); - g_free(exchanges[curexchange].lang1); - g_free(exchanges[curexchange].charset2); - g_free(exchanges[curexchange].lang2); - } - g_free(exchanges); - aim_tlvlist_free(tlvlist); - - return ret; -} - -static int -parseinfo_create(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs, aim_snac_t *snac2) -{ - aim_rxcallback_t userfunc; - GSList *tlvlist, *innerlist; - char *ck = NULL, *fqcn = NULL, *name = NULL; - guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; - guint32 createtime = 0; - guint8 createperms = 0, detaillevel; - int cklen; - aim_tlv_t *bigblock; - int ret = 0; - ByteStream bbbs; - - tlvlist = aim_tlvlist_read(bs); - - if (!(bigblock = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { - purple_debug_misc("oscar", "no bigblock in top tlv in create room response\n"); - aim_tlvlist_free(tlvlist); - return 0; - } - - byte_stream_init(&bbbs, bigblock->value, bigblock->length); - - exchange = byte_stream_get16(&bbbs); - cklen = byte_stream_get8(&bbbs); - ck = byte_stream_getstr(&bbbs, cklen); - instance = byte_stream_get16(&bbbs); - detaillevel = byte_stream_get8(&bbbs); - - if (detaillevel != 0x02) { - purple_debug_misc("oscar", "unknown detaillevel in create room response (0x%02x)\n", detaillevel); - aim_tlvlist_free(tlvlist); - g_free(ck); - return 0; - } - - unknown = byte_stream_get16(&bbbs); - - innerlist = aim_tlvlist_read(&bbbs); - - if (aim_tlv_gettlv(innerlist, 0x006a, 1)) - fqcn = aim_tlv_getstr(innerlist, 0x006a, 1); - - if (aim_tlv_gettlv(innerlist, 0x00c9, 1)) - flags = aim_tlv_get16(innerlist, 0x00c9, 1); - - if (aim_tlv_gettlv(innerlist, 0x00ca, 1)) - createtime = aim_tlv_get32(innerlist, 0x00ca, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d1, 1)) - maxmsglen = aim_tlv_get16(innerlist, 0x00d1, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d2, 1)) - maxoccupancy = aim_tlv_get16(innerlist, 0x00d2, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d3, 1)) - name = aim_tlv_getstr(innerlist, 0x00d3, 1); - - if (aim_tlv_gettlv(innerlist, 0x00d5, 1)) - createperms = aim_tlv_get8(innerlist, 0x00d5, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) { - ret = userfunc(od, conn, frame, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); - } - - g_free(ck); - g_free(name); - g_free(fqcn); - aim_tlvlist_free(innerlist); - aim_tlvlist_free(tlvlist); - - return ret; -} - -/* - * Subtype 0x0009 - * - * Since multiple things can trigger this callback, we must lookup the - * snacid to determine the original snac subtype that was called. - * - * XXX This isn't really how this works. But this is: Every d/9 response - * has a 16bit value at the beginning. That matches to: - * Short Desc = 1 - * Full Desc = 2 - * Instance Info = 4 - * Nav Short Desc = 8 - * Nav Instance Info = 16 - * And then everything is really asynchronous. There is no specific - * attachment of a response to a create room request, for example. Creating - * the room yields no different a response than requesting the room's info. - * - */ -static int -parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_snac_t *snac2; - int ret = 0; - - if (!(snac2 = aim_remsnac(od, snac->id))) { - purple_debug_misc("oscar", "faim: chatnav_parse_info: received response to unknown request! (%08x)\n", snac->id); - return 0; - } - - if (snac2->family != SNAC_FAMILY_CHATNAV) { - purple_debug_misc("oscar", "faim: chatnav_parse_info: received response that maps to corrupt request! (fam=%04x)\n", snac2->family); - g_free(snac2->data); - g_free(snac2); - return 0; - } - - /* - * We now know what the original SNAC subtype was. - */ - if (snac2->type == 0x0002) /* request chat rights */ - ret = parseinfo_perms(od, conn, mod, frame, snac, bs, snac2); - else if (snac2->type == 0x0003) /* request exchange info */ - purple_debug_misc("oscar", "chatnav_parse_info: response to exchange info\n"); - else if (snac2->type == 0x0004) /* request room info */ - purple_debug_misc("oscar", "chatnav_parse_info: response to room info\n"); - else if (snac2->type == 0x0005) /* request more room info */ - purple_debug_misc("oscar", "chatnav_parse_info: response to more room info\n"); - else if (snac2->type == 0x0006) /* request occupant list */ - purple_debug_misc("oscar", "chatnav_parse_info: response to occupant info\n"); - else if (snac2->type == 0x0007) /* search for a room */ - purple_debug_misc("oscar", "chatnav_parse_info: search results\n"); - else if (snac2->type == 0x0008) /* create room */ - ret = parseinfo_create(od, conn, mod, frame, snac, bs, snac2); - else - purple_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type); - - g_free(snac2->data); - g_free(snac2); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0001) - return error(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0009) - return parseinfo(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -chatnav_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_CHATNAV; - mod->version = 0x0001; - mod->toolid = 0x0010; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "chatnav", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_feedbag.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2065 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0013 - Server-Side/Stored Information. - * - * Deals with storing certain types of information, such as a user's buddy - * list, permit/deny list, and permit/deny preferences, on the server, so - * that they can be accessed from any client. - * - * We keep 2 copies of SSI data: - * 1) An exact copy of what is stored on the AIM servers. - * 2) A local copy that we make changes to, and then send diffs - * between this and the exact copy to keep them in sync. - * - * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list - * that is given to them (i.e. they don't send SNACs). - * - * The SNAC sending and receiving functions are lower down in the file, and - * they're simpler. They are in the order of the subtypes they deal with, - * starting with the request rights function (subtype 0x0002), then parse - * rights (subtype 0x0003), then--well, you get the idea. - * - * This is entirely too complicated. - * You don't know the half of it. - */ - -#include "oscar.h" -#include "oscarcommon.h" -#include "debug.h" - -static int aim_ssi_addmoddel(OscarData *od); - -static void aim_ssi_item_free(struct aim_ssi_item *item) -{ - g_free(item->name); - aim_tlvlist_free(item->data); - g_free(item); -} - -static void aim_ssi_item_set_name(struct aim_ssi_itemlist *list, struct aim_ssi_item *item, const char *name) -{ - gchar key[3000]; - - if (item->name) { - /* Remove old name from hash table */ - snprintf(key, sizeof(key), "%hx%s", item->type, oscar_normalize(NULL, item->name)); - g_hash_table_remove(list->idx_all_named_items, key); - } - - g_free(item->name); - item->name = g_strdup(name); - - if (name) { - /* Add new name to hash table */ - snprintf(key, sizeof(key), "%hx%s", item->type, oscar_normalize(NULL, item->name)); - g_hash_table_insert(list->idx_all_named_items, g_strdup(key), item); - } -} - -/** - * List types based on http://dev.aol.com/aim/oscar/#FEEDBAG (archive.org) - * and http://iserverd.khstu.ru/oscar/ssi_item.html - * - * @param type The type of a list item as integer number, as provided by an aim_ssi_item struct. - * @return Returns the name of the item type as a character string. - */ -static const gchar* -aim_ssi_type_to_string(guint16 type) -{ - struct TypeStringPair - { - guint16 type; - const gchar *string; - }; - static const struct TypeStringPair type_strings[] = { - { 0x0000, "Buddy" }, - { 0x0001, "Group" }, - { 0x0002, "Permit/Visible" }, - { 0x0003, "Deny/Invisible" }, - { 0x0004, "PDInfo" }, - { 0x0005, "PresencePrefs" }, - { 0x0006, "Non-Buddy Info" }, - { 0x0009, "ClientPrefs" }, - { 0x000e, "ICQDeny/Ignore" }, - { 0x0014, "Buddy Icon" }, - { 0x0015, "Recent Buddies" }, - { 0x0019, "Non-Buddy" }, - { 0x001d, "Vanity Info" }, - { 0x0020, "ICQ-MDir" }, - { 0x0029, "Facebook" }, - }; - size_t i; - for (i = 0; i < G_N_ELEMENTS(type_strings); i++) { - if (type_strings[i].type == type) { - return type_strings[i].string; - } - } - return "unknown"; -} - -/** For debug log output: Appends a line containing information about a given list item to a string. - * - * @param str String to which the line will be appended. - * @param prefix A string which will be prepended to the line. - * @param item List item from which information is extracted. - */ -static void -aim_ssi_item_debug_append(GString *str, char *prefix, struct aim_ssi_item *item) -{ - g_string_append_printf(str, - "%s gid=0x%04hx, bid=0x%04hx, list_type=0x%04hx [%s], name=%s.\n", - prefix, item->gid, item->bid, item->type, aim_ssi_type_to_string(item->type), - item->name ? item->name : "(null)"); -} - -/** - * Locally rebuild the 0x00c8 TLV in the additional data of the given group. - * - * @param list A pointer to a pointer to the current list of items. - * @param name A null terminated string containing the group name, or NULL - * if you want to modify the master group. - * @return Return a pointer to the modified item. - */ -static void -aim_ssi_itemlist_rebuildgroup(struct aim_ssi_itemlist *list, const char *name) -{ - int newlen; - struct aim_ssi_item *cur, *group; - - /* Find the group */ - if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP))) - return; - - /* Find the length for the new additional data */ - newlen = 0; - if (group->gid == 0x0000) { - for (cur=list->data; cur; cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) - newlen += 2; - } else { - for (cur=list->data; cur; cur=cur->next) - if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) - newlen += 2; - } - - /* Build the new TLV list */ - if (newlen > 0) { - guint8 *newdata; - - newdata = g_new(guint8, newlen); - newlen = 0; - if (group->gid == 0x0000) { - for (cur=list->data; cur; cur=cur->next) - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000)) - newlen += aimutil_put16(newdata+newlen, cur->gid); - } else { - for (cur=list->data; cur; cur=cur->next) - if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) - newlen += aimutil_put16(newdata+newlen, cur->bid); - } - aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata); - - g_free(newdata); - } -} - -/** - * Locally add a new item to the given item list. - * - * @param list A pointer to a pointer to the current list of items. - * @param name A null terminated string of the name of the new item, or NULL if the - * item should have no name. - * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something. - * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something. - * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc. - * @param data The additional data for the new item. - * @return A pointer to the newly created item. - */ -static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_itemlist *list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data) -{ - gboolean exists; - struct aim_ssi_item *cur, *new; - - new = g_new0(struct aim_ssi_item, 1); - - /* Set the group ID# and buddy ID# */ - new->gid = gid; - new->bid = bid; - if (type == AIM_SSI_TYPE_GROUP) { - if ((new->gid == 0xFFFF) && name) { - do { - new->gid += 0x0001; - exists = FALSE; - for (cur = list->data; cur != NULL; cur = cur->next) - if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) { - exists = TRUE; - break; - } - } while (exists); - } - } else if (new->gid == 0x0000) { - /* - * This is weird, but apparently items in the root group can't - * have a buddy ID equal to any group ID. You'll get error - * 0x0003 when trying to add, which is "item already exists" - */ - if (new->bid == 0xFFFF) { - do { - new->bid += 0x0001; - exists = FALSE; - for (cur = list->data; cur != NULL; cur = cur->next) - if (cur->bid == new->bid || cur->gid == new->bid) { - exists = TRUE; - break; - } - } while (exists); - } - } else { - if (new->bid == 0xFFFF) { - do { - new->bid += 0x0001; - exists = FALSE; - for (cur = list->data; cur != NULL; cur = cur->next) - if (cur->bid == new->bid && cur->gid == new->gid) { - exists = TRUE; - break; - } - } while (exists); - } - } - - /* Set the type */ - new->type = type; - - /* Add it to the gid+bid hashtable */ - g_hash_table_insert(list->idx_gid_bid, GINT_TO_POINTER((new->gid << 16) + new->bid), new); - - /* Set the name - do this *AFTER* setting the type because type is used for the key */ - aim_ssi_item_set_name(list, new, name); - - /* Set the TLV list */ - new->data = aim_tlvlist_copy(data); - - /* Add the item to the list in the correct numerical position. Fancy, eh? */ - if (list->data) { - if ((new->gid < list->data->gid) || ((new->gid == list->data->gid) && (new->bid < list->data->bid))) { - new->next = list->data; - list->data = new; - } else { - struct aim_ssi_item *prev; - for ((prev=list->data, cur=list->data->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next); - new->next = prev->next; - prev->next = new; - } - } else { - new->next = list->data; - list->data = new; - } - - return new; -} - -/** - * Locally delete an item from the given item list. - * - * @param list A pointer to a pointer to the current list of items. - * @param del A pointer to the item you want to remove from the list. - * @return Return 0 if no errors, otherwise return the error number. - */ -static int aim_ssi_itemlist_del(struct aim_ssi_itemlist *list, struct aim_ssi_item *del) -{ - gchar key[3000]; - - if (!(list->data) || !del) - return -EINVAL; - - /* Remove the item from the list */ - if (list->data == del) { - list->data = list->data->next; - } else { - struct aim_ssi_item *cur; - for (cur=list->data; (cur->next && (cur->next!=del)); cur=cur->next); - if (cur->next) - cur->next = del->next; - } - - /* Remove from the hashtables */ - g_hash_table_remove(list->idx_gid_bid, GINT_TO_POINTER((del->gid << 16) + del->bid)); - - snprintf(key, sizeof(key), "%hx%s", del->type, oscar_normalize(NULL, del->name)); - g_hash_table_remove(list->idx_all_named_items, key); - - /* Free the removed item */ - aim_ssi_item_free(del); - - return 0; -} - -/** - * Compare two items to see if they have the same data. - * - * @param cur1 A pointer to a pointer to the first item. - * @param cur2 A pointer to a pointer to the second item. - * @return Return 0 if no differences, or a number if there are differences. - */ -static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2) -{ - if (!cur1 || !cur2) - return 1; - - if (cur1->data && !cur2->data) - return 2; - - if (!cur1->data && cur2->data) - return 3; - - if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data))) - return 4; - - if (cur1->name && !cur2->name) - return 5; - - if (!cur1->name && cur2->name) - return 6; - - if (cur1->name && cur2->name && oscar_util_name_compare(cur1->name, cur2->name)) - return 7; - - if (cur1->gid != cur2->gid) - return 8; - - if (cur1->bid != cur2->bid) - return 9; - - if (cur1->type != cur2->type) - return 10; - - return 0; -} - -static gboolean aim_ssi_itemlist_valid(struct aim_ssi_itemlist *list, struct aim_ssi_item *item) -{ - struct aim_ssi_item *cur; - for (cur=list->data; cur; cur=cur->next) - if (cur == item) - return TRUE; - return FALSE; -} - -/** - * Locally find an item given a group ID# and a buddy ID#. - * - * @param list A pointer to the current list of items. - * @param gid The group ID# of the desired item. - * @param bid The buddy ID# of the desired item. - * @return Return a pointer to the item if found, else return NULL; - */ -struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_itemlist *list, guint16 gid, guint16 bid) -{ - guint32 id_key = (gid << 16) + bid; - return g_hash_table_lookup(list->idx_gid_bid, GINT_TO_POINTER(id_key)); -} - -/** - * Locally find an item given a group name, buddy name, and type. If group name - * and buddy name are null, then just return the first item of the given type. - * - * @param list A pointer to the current list of items. - * @param gn The group name of the desired item. - * @param bn The buddy name of the desired item. - * @param type The type of the desired item. - * @return Return a pointer to the item if found, else return NULL. - */ -struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_itemlist *list, const char *gn, const char *bn, guint16 type) -{ - struct aim_ssi_item *cur; - gchar key[3000]; - - if (!list->data) - return NULL; - - if (gn && bn) { /* For finding buddies in groups */ - g_return_val_if_fail(type == AIM_SSI_TYPE_BUDDY, NULL); - for (cur=list->data; cur; cur=cur->next) - if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) { - struct aim_ssi_item *curg; - for (curg=list->data; curg; curg=curg->next) - if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(oscar_util_name_compare(curg->name, gn))) - return cur; - } - - } else if (gn || bn) { /* For finding groups, permits, denies and ignores */ - snprintf(key, sizeof(key), "%hx%s", type, oscar_normalize(NULL, gn ? gn : bn)); - return g_hash_table_lookup(list->idx_all_named_items, key); - - /* For stuff without names--permit deny setting, visibility mask, etc. */ - } else for (cur=list->data; cur; cur=cur->next) { - if ((cur->type == type) && (!cur->name)) - return cur; - } - - return NULL; -} - -/** - * Check if the given buddy exists in any group in the buddy list. - * - * @param list A pointer to the current list of items. - * @param bn The group name of the desired item. - * @return Return a pointer to the name of the item if found, else return NULL; - */ -struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_itemlist *list, const char *bn) -{ - if (!bn) - return NULL; - return aim_ssi_itemlist_finditem(list, NULL, bn, AIM_SSI_TYPE_BUDDY); -} - -/** - * Locally find the parent item of the given buddy name. - * - * @param list A pointer to the current list of items. - * @param bn The buddy name of the desired item. - * @return Return a pointer to the name of the item if found, else return NULL; - */ -char *aim_ssi_itemlist_findparentname(struct aim_ssi_itemlist *list, const char *bn) -{ - struct aim_ssi_item *cur, *curg; - if (!list->data || !bn) - return NULL; - if (!(cur = aim_ssi_itemlist_exists(list, bn))) - return NULL; - if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000))) - return NULL; - return curg->name; -} - -/** - * Locally find the permit/deny setting item, and return the setting. - * - * @param list A pointer to the current list of items. - * @return Return the current SSI permit deny setting, or 0 if no setting was found. - */ -int aim_ssi_getpermdeny(struct aim_ssi_itemlist *list) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1); - if (tlv && tlv->value) - return aimutil_get8(tlv->value); - } - return 0; -} - -/** - * Locally find the presence flag item, and return the setting. The returned setting is a - * bitmask of the preferences. See the AIM_SSI_PRESENCE_FLAG_* #defines in oscar.h. - * - * @param list A pointer to the current list of items. - * @return Return the current set of preferences. - */ -guint32 aim_ssi_getpresence(struct aim_ssi_itemlist *list) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1); - if (tlv && tlv->length) - return aimutil_get32(tlv->value); - } - return 0xFFFFFFFF; -} - -/** - * Locally find the alias of the given buddy. - * - * @param list A pointer to the current list of items. - * @param gn The group of the buddy. - * @param bn The name of the buddy. - * @return A pointer to a NULL terminated string that is the buddy's - * alias, or NULL if the buddy has no alias. You should free - * this returned value! - */ -char *aim_ssi_getalias(struct aim_ssi_itemlist *list, const char *gn, const char *bn) -{ - struct aim_ssi_item *item = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY); - if (item) { - return aim_ssi_getalias_from_item(item); - } - return NULL; -} - -char *aim_ssi_getalias_from_item(struct aim_ssi_item *item) -{ - aim_tlv_t *tlv = aim_tlv_gettlv(item->data, 0x0131, 1); - if (tlv && tlv->length) - return g_strndup((const gchar *)tlv->value, tlv->length); - return NULL; -} - -/** - * Locally find the comment of the given buddy. - * - * @param list A pointer to the current list of items. - * @param gn The group of the buddy. - * @param bn The name of the buddy. - * @return A pointer to a NULL terminated string that is the buddy's - * comment, or NULL if the buddy has no comment. You should free - * this returned value! - */ -char *aim_ssi_getcomment(struct aim_ssi_itemlist *list, const char *gn, const char *bn) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY); - if (cur) { - aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1); - if (tlv && tlv->length) { - return g_strndup((const gchar *)tlv->value, tlv->length); - } - } - return NULL; -} - -/** - * Locally find if you are waiting for authorization for a buddy. - * - * @param list A pointer to the current list of items. - * @param gn The group of the buddy. - * @param bn The name of the buddy. - * @return 1 if you are waiting for authorization; 0 if you are not - */ -gboolean aim_ssi_waitingforauth(struct aim_ssi_itemlist *list, const char *gn, const char *bn) -{ - struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY); - if (cur) { - if (aim_tlv_gettlv(cur->data, 0x0066, 1)) - return TRUE; - } - return FALSE; -} - -/** - * If there are changes, then create temporary items and - * call addmoddel. - * - * @param od The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -static int aim_ssi_sync(OscarData *od) -{ - struct aim_ssi_item *cur1, *cur2; - struct aim_ssi_tmp *cur, *new; - int n = 0; - GString *debugstr; - - /* - * The variable "n" is used to limit the number of addmoddel's that - * are performed in a single SNAC. It will hopefully keep the size - * of the SNAC below the maximum SNAC size. - */ - - if (!od) - return -EINVAL; - - /* If we're waiting for an ack, we shouldn't do anything else */ - if (od->ssi.waiting_for_ack) - return 0; - - /* - * Compare the 2 lists and create an aim_ssi_tmp for each difference. - * We should only send either additions, modifications, or deletions - * before waiting for an acknowledgement. So first do deletions, then - * additions, then modifications. Also, both the official and the local - * list should be in ascending numerical order for the group ID#s and the - * buddy ID#s, which makes things more efficient. I think. - */ - debugstr = g_string_new(""); - - /* Deletions */ - if (!od->ssi.pending) { - for (cur1=od->ssi.official.data; cur1 && (n < 15); cur1=cur1->next) { - if (!aim_ssi_itemlist_find(&od->ssi.local, cur1->gid, cur1->bid)) { - n++; - new = g_new(struct aim_ssi_tmp, 1); - new->action = SNAC_SUBTYPE_FEEDBAG_DEL; - new->ack = 0xffff; - new->name = NULL; - new->item = cur1; - new->next = NULL; - if (od->ssi.pending) { - for (cur=od->ssi.pending; cur->next; cur=cur->next); - cur->next = new; - } else { - od->ssi.pending = new; - } - aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1); - } - } - } - - /* Additions */ - if (!od->ssi.pending) { - for (cur1=od->ssi.local.data; cur1 && (n < 15); cur1=cur1->next) { - if (!aim_ssi_itemlist_find(&od->ssi.official, cur1->gid, cur1->bid)) { - n++; - new = g_new(struct aim_ssi_tmp, 1); - new->action = SNAC_SUBTYPE_FEEDBAG_ADD; - new->ack = 0xffff; - new->name = NULL; - new->item = cur1; - new->next = NULL; - if (od->ssi.pending) { - for (cur=od->ssi.pending; cur->next; cur=cur->next); - cur->next = new; - } else { - od->ssi.pending = new; - } - aim_ssi_item_debug_append(debugstr, "Adding item ", cur1); - } - } - } - - /* Modifications */ - if (!od->ssi.pending) { - for (cur1=od->ssi.local.data; cur1 && (n < 15); cur1=cur1->next) { - cur2 = aim_ssi_itemlist_find(&od->ssi.official, cur1->gid, cur1->bid); - if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) { - n++; - new = g_new(struct aim_ssi_tmp, 1); - new->action = SNAC_SUBTYPE_FEEDBAG_MOD; - new->ack = 0xffff; - new->name = NULL; - new->item = cur1; - new->next = NULL; - if (od->ssi.pending) { - for (cur=od->ssi.pending; cur->next; cur=cur->next); - cur->next = new; - } else { - od->ssi.pending = new; - } - aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1); - } - } - } - if (debugstr->len > 0) { - purple_debug_info("oscar", "%s", debugstr->str); - if (purple_debug_is_verbose()) { - PurpleAccount *account = purple_connection_get_account(od->gc); - g_string_truncate(debugstr, 0); - for (cur1 = od->ssi.local.data; cur1; cur1 = cur1->next) { - aim_ssi_item_debug_append(debugstr, "\t", cur1); - } - purple_debug_misc("oscar", "Dumping item list of account %s:\n%s", - purple_account_get_username(account), debugstr->str); - } - } - g_string_free(debugstr, TRUE); - - /* We're out of stuff to do, so tell the AIM servers we're done and exit */ - if (!od->ssi.pending) { - if (od->ssi.in_transaction) { - aim_ssi_modend(od); - od->ssi.in_transaction = FALSE; - } - return 0; - } - - /* If this is the first in a series of add/mod/del - * requests then send the "begin transaction" message. */ - if (!od->ssi.in_transaction) - { - aim_ssi_modbegin(od); - od->ssi.in_transaction = TRUE; - } - - /* Make sure we don't send anything else between now - * and when we receive the ack for the following operation */ - od->ssi.waiting_for_ack = TRUE; - - /* Now go mail off our data and wait 4 to 6 weeks */ - return aim_ssi_addmoddel(od);; -} - -/** - * Free all SSI data. - * - * This doesn't remove it from the server, that's different. - * - * @param od The oscar odion. - * @return Return 0 if no errors, otherwise return the error number. - */ -static void -aim_ssi_freelist(OscarData *od) -{ - struct aim_ssi_item *cur, *del; - struct aim_ssi_tmp *curtmp, *deltmp; - - cur = od->ssi.official.data; - while (cur) { - del = cur; - cur = cur->next; - aim_ssi_item_free(del); - } - - cur = od->ssi.local.data; - while (cur) { - del = cur; - cur = cur->next; - aim_ssi_item_free(del); - } - - curtmp = od->ssi.pending; - while (curtmp) { - deltmp = curtmp; - curtmp = curtmp->next; - g_free(deltmp); - } - - od->ssi.numitems = 0; - od->ssi.official.data = NULL; - od->ssi.local.data = NULL; - od->ssi.pending = NULL; - od->ssi.timestamp = (time_t)0; -} - -/** - * Look up the given TLV type in the item's data. If the value of - * the TLV is not a valid UTF-8 string then use purple_utf8_salvage() - * to replace invalid bytes with question marks. - */ -static void cleanlist_ensure_utf8_data(struct aim_ssi_item *item, guint16 tlvtype) -{ - aim_tlv_t *tlv; - gchar *value, *salvaged; - - tlv = aim_tlv_gettlv(item->data, tlvtype, 1); - if (tlv && tlv->length && !g_utf8_validate((const gchar *)tlv->value, tlv->length, NULL)) { - purple_debug_warning("oscar", "cleanlist found invalid UTF-8 " - "for 0x%04hx field of 0x%04hx item with name %s. " - "Attempting to repair.\n", - tlvtype, item->type, item->name ? item->name : "(null)"); - value = g_strndup((const gchar *)tlv->value, tlv->length); - salvaged = purple_utf8_salvage(value); - g_free(value); - if (*salvaged) - aim_tlvlist_replace_str(&item->data, tlvtype, salvaged); - else - aim_tlvlist_remove(&item->data, tlvtype); - g_free(salvaged); - } -} - -/** - * This "cleans" the ssi list. It does things like: - * - Makes sure all buddies, permits, and denies have names - * - Makes sure all buddies are in a group that exist - * - Makes sure strings are valid UTF-8 - * - * @param od The oscar odion. - * @return Return 0 if no errors, otherwise return the error number. - */ -static int aim_ssi_cleanlist(OscarData *od) -{ - struct aim_ssi_item *cur, *next; - - if (!od) - return -EINVAL; - - /* Delete any buddies, permits, or denies with empty names. */ - /* If there are any buddies directly in the master group, add them to a real group. */ - /* DESTROY any buddies that are directly in the master group. */ - /* Do the same for buddies that are in a non-existant group. */ - /* This will kind of mess up if you hit the item limit, but this function isn't too critical */ - cur = od->ssi.local.data; - while (cur) { - next = cur->next; - if (!cur->name) { - if (cur->type == AIM_SSI_TYPE_BUDDY) - aim_ssi_delbuddy(od, NULL, NULL); - else if (cur->type == AIM_SSI_TYPE_PERMIT || cur->type == AIM_SSI_TYPE_DENY || cur->type == AIM_SSI_TYPE_ICQDENY) - aim_ssi_del_from_private_list(od, NULL, cur->type); - } else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(&od->ssi.local, cur->gid, 0x0000)))) { - char *alias = aim_ssi_getalias(&od->ssi.local, NULL, cur->name); - aim_ssi_addbuddy(od, cur->name, PURPLE_BLIST_DEFAULT_GROUP_NAME, - NULL, alias, NULL, NULL, FALSE); - aim_ssi_delbuddy(od, cur->name, NULL); - g_free(alias); - } - cur = next; - } - - cur = od->ssi.local.data; - while (cur) { - if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY)) - { - struct aim_ssi_item *cur2, *next2; - - /* Make sure there aren't any duplicate permits or denies, or - duplicate buddies within a group */ - cur2 = cur->next; - while (cur2) { - next2 = cur2->next; - if (cur->type == cur2->type - && cur->gid == cur2->gid - && cur->name - && cur2->name - && !oscar_util_name_compare(cur->name, cur2->name)) - { - aim_ssi_itemlist_del(&od->ssi.local, cur2); - } - cur2 = next2; - } - - /* Make sure alias is valid UTF-8 */ - cleanlist_ensure_utf8_data(cur, 0x0131); - - /* Make sure comment is valid UTF-8 */ - cleanlist_ensure_utf8_data(cur, 0x013c); - } - cur = cur->next; - } - - /* If we've made any changes then sync our list with the server's */ - return aim_ssi_sync(od); -} - -/** - * Add a buddy to the list. - * - * @param od The oscar odion. - * @param name The name of the item. - * @param group The group of the item. - * @param data A TLV list to use as the additional data for this item. - * @param alias The alias/nickname of the item, or NULL. - * @param comment The buddy comment for the item, or NULL. - * @param smsnum The locally assigned SMS number, or NULL. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *data, const char *alias, const char *comment, const char *smsnum, gboolean needauth) -{ - struct aim_ssi_item *parent; - - if (!od || !name || !group) - return -EINVAL; - - /* Find the parent */ - if (!(parent = aim_ssi_itemlist_finditem(&od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) { - /* Find the parent's parent (the master group) */ - if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL) - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL); - - /* Add the parent */ - parent = aim_ssi_itemlist_add(&od->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL); - - /* Modify the parent's parent (the master group) */ - aim_ssi_itemlist_rebuildgroup(&od->ssi.local, NULL); - } - - /* Create a TLV list for the new buddy */ - if (needauth) - aim_tlvlist_add_noval(&data, 0x0066); - if (alias != NULL) - aim_tlvlist_add_str(&data, 0x0131, alias); - if (smsnum != NULL) - aim_tlvlist_add_str(&data, 0x013a, smsnum); - if (comment != NULL) - aim_tlvlist_add_str(&data, 0x013c, comment); - - /* Add that bad boy */ - aim_ssi_itemlist_add(&od->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data); - aim_tlvlist_free(data); - - /* Modify the parent group */ - aim_ssi_itemlist_rebuildgroup(&od->ssi.local, group); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -int -aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type) -{ - if (!od || !name || !od->ssi.received_data) - return -EINVAL; - - if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL) - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL); - - aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, list_type, NULL); - return aim_ssi_sync(od); -} - -int -aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type) -{ - struct aim_ssi_item *del; - - if (!od) - return -EINVAL; - - if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, name, list_type))) - return -EINVAL; - - aim_ssi_itemlist_del(&od->ssi.local, del); - return aim_ssi_sync(od); -} - -/** - * Deletes a buddy from the list. - * - * @param od The oscar odion. - * @param name The name of the item, or NULL. - * @param group The group of the item, or NULL. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group) -{ - struct aim_ssi_item *del; - - if (!od) - return -EINVAL; - - /* Find the buddy */ - if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY))) - return -EINVAL; - - /* Remove the item from the list */ - aim_ssi_itemlist_del(&od->ssi.local, del); - - /* Modify the parent group */ - aim_ssi_itemlist_rebuildgroup(&od->ssi.local, group); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/** - * Deletes a group from the list. - * - * @param od The oscar odion. - * @param group The name of the group. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_delgroup(OscarData *od, const char *group) -{ - struct aim_ssi_item *del; - aim_tlv_t *tlv; - - if (!od) - return -EINVAL; - - /* Find the group */ - if (!(del = aim_ssi_itemlist_finditem(&od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) - return -EINVAL; - - /* Don't delete the group if it's not empty */ - tlv = aim_tlv_gettlv(del->data, 0x00c8, 1); - if (tlv && tlv->length > 0) - return -EINVAL; - - /* Remove the item from the list */ - aim_ssi_itemlist_del(&od->ssi.local, del); - - /* Modify the parent group */ - aim_ssi_itemlist_rebuildgroup(&od->ssi.local, NULL); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/** - * Move a buddy from one group to another group. This basically just deletes the - * buddy and re-adds it. - * - * @param od The oscar odion. - * @param oldgn The group that the buddy is currently in. - * @param newgn The group that the buddy should be moved in to. - * @param bn The name of the buddy to be moved. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn) -{ - struct aim_ssi_item *buddy; - GSList *data; - - /* Find the buddy */ - buddy = aim_ssi_itemlist_finditem(&od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY); - if (buddy == NULL) - return -EINVAL; - - /* Make a copy of the buddy's TLV list */ - data = aim_tlvlist_copy(buddy->data); - - /* Delete the old item */ - aim_ssi_delbuddy(od, bn, oldgn); - - /* Add the new item using the EXACT SAME TLV list */ - aim_ssi_addbuddy(od, bn, newgn, data, NULL, NULL, NULL, FALSE); - - return 0; -} - -/** - * Change the alias stored on the server for a given buddy. - * - * @param od The oscar odion. - * @param gn The group that the buddy is currently in. - * @param bn The name of the buddy. - * @param alias The new alias for the buddy, or NULL if you want to remove - * a buddy's comment. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias) -{ - struct aim_ssi_item *tmp; - - if (!od || !gn || !bn) - return -EINVAL; - - if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY))) - return -EINVAL; - - /* Either add or remove the 0x0131 TLV from the TLV chain */ - if (alias && *alias) - aim_tlvlist_replace_str(&tmp->data, 0x0131, alias); - else - aim_tlvlist_remove(&tmp->data, 0x0131); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/** - * Change the comment stored on the server for a given buddy. - * - * @param od The oscar odion. - * @param gn The group that the buddy is currently in. - * @param bn The name of the buddy. - * @param alias The new comment for the buddy, or NULL if you want to remove - * a buddy's comment. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *comment) -{ - struct aim_ssi_item *tmp; - - if (!od || !gn || !bn) - return -EINVAL; - - if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY))) - return -EINVAL; - - /* Either add or remove the 0x0131 TLV from the TLV chain */ - if (comment && *comment) - aim_tlvlist_replace_str(&tmp->data, 0x013c, comment); - else - aim_tlvlist_remove(&tmp->data, 0x013c); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/** - * Rename a group. - * - * @param od The oscar odion. - * @param oldgn The old group name. - * @param newgn The new group name. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn) -{ - struct aim_ssi_item *group; - - if (!od || !oldgn || !newgn) - return -EINVAL; - - if (!(group = aim_ssi_itemlist_finditem(&od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP))) - return -EINVAL; - - aim_ssi_item_set_name(&od->ssi.local, group, newgn); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/** - * Stores your permit/deny setting on the server, and starts using it. - * - * @param od The oscar odion. - * @param permdeny Your permit/deny setting. For ICQ accounts, it actually affects your visibility - * and has nothing to do with blocking. Can be one of the following: - * 1 - Allow all users - * 2 - Block all users - * 3 - Allow only the users below - * 4 - Block only the users below - * 5 - Allow only users on my buddy list - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny) -{ - struct aim_ssi_item *tmp; - - if (!od || !od->ssi.received_data) - return -EINVAL; - - /* Find the PDINFO item, or add it if it does not exist */ - if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) { - /* Make sure the master group exists */ - if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL) - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL); - - tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL); - } - - /* Need to add the 0x00ca TLV to the TLV chain */ - aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/** - * Set buddy icon information - * - * @param od The oscar odion. - * @param iconcsum The MD5 checksum of the icon you are using. - * @param iconcsumlen Length of the MD5 checksum given above. Should be 0x10 bytes. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen) -{ - struct aim_ssi_item *tmp; - guint8 *csumdata; - - if (!od || !iconsum || !iconsumlen || !od->ssi.received_data) - return -EINVAL; - - /* Find the ICONINFO item, or add it if it does not exist */ - if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) { - /* Make sure the master group exists */ - if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL) - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL); - - tmp = aim_ssi_itemlist_add(&od->ssi.local, "1", 0x0000, 0xFFFF, AIM_SSI_TYPE_ICONINFO, NULL); - } - - /* Need to add the 0x00d5 TLV to the TLV chain */ - csumdata = (guint8 *)g_malloc((iconsumlen+2)*sizeof(guint8)); - (void)aimutil_put8(&csumdata[0], 0x00); - (void)aimutil_put8(&csumdata[1], iconsumlen); - memcpy(&csumdata[2], iconsum, iconsumlen); - aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(guint8), csumdata); - g_free(csumdata); - - /* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */ - aim_tlvlist_replace_noval(&tmp->data, 0x0131); - - /* Sync our local list with the server list */ - aim_ssi_sync(od); - return 0; -} - -/** - * Remove a reference to a server stored buddy icon. This will make your - * icon stop showing up to other people. - * - * Really this function just sets the icon to a dummy value. It's weird... - * but I think the dummy value basically means "I don't have an icon!" - * - * @param od The oscar session. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_delicon(OscarData *od) -{ - const guint8 csumdata[] = {0x02, 0x01, 0xd2, 0x04, 0x72}; - - return aim_ssi_seticon(od, csumdata, 5); -} - -/** - * Stores your setting for various SSI settings. Whether you - * should show up as idle or not, etc. - * - * @param od The oscar odion. - * @param presence A bitmask of the first 32 entries [0-31] from - * http://dev.aol.com/aim/oscar/#FEEDBAG__BUDDY_PREFS - * 0x00000002 - Hide "eBuddy group" (whatever that is) - * 0x00000400 - Allow others to see your idle time - * 0x00020000 - Don't show Recent Buddies - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_ssi_setpresence(OscarData *od, guint32 presence) { - struct aim_ssi_item *tmp; - - if (!od || !od->ssi.received_data) - return -EINVAL; - - /* Find the PRESENCEPREFS item, or add it if it does not exist */ - if (!(tmp = aim_ssi_itemlist_finditem(&od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) { - /* Make sure the master group exists */ - if (aim_ssi_itemlist_find(&od->ssi.local, 0x0000, 0x0000) == NULL) - aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL); - - tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL); - } - - /* Need to add the x00c9 TLV to the TLV chain */ - aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence); - - /* Sync our local list with the server list */ - return aim_ssi_sync(od); -} - -/* - * Subtype 0x0002 - Request SSI Rights. - */ -int aim_ssi_reqrights(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG))) - return -EINVAL; - - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQRIGHTS); - - return 0; -} - -/* - * Subtype 0x0003 - SSI Rights Information. - */ -static int parserights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0, i; - aim_rxcallback_t userfunc; - GSList *tlvlist; - aim_tlv_t *tlv; - ByteStream bstream; - guint16 *maxitems; - - /* This SNAC is made up of a bunch of TLVs */ - tlvlist = aim_tlvlist_read(bs); - - /* TLV 0x0004 contains the maximum number of each item */ - if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { - aim_tlvlist_free(tlvlist); - return 0; - } - - byte_stream_init(&bstream, tlv->value, tlv->length); - - maxitems = (guint16 *)g_malloc((tlv->length/2)*sizeof(guint16)); - - for (i=0; i<(tlv->length/2); i++) - maxitems[i] = byte_stream_get16(&bstream); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, tlv->length/2, maxitems); - - aim_tlvlist_free(tlvlist); - g_free(maxitems); - - return ret; -} - -/* - * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and - * revision number. - * - */ -int aim_ssi_reqdata(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG))) - return -EINVAL; - - /* Free any current data, just in case */ - aim_ssi_freelist(od); - - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQDATA); - - return 0; -} - -/* - * Subtype 0x0006 - SSI Data. - */ -static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint8 fmtver; /* guess */ - guint16 namelen, gid, bid, type; - char *name; - GSList *data; - GString *debugstr = g_string_new(""); - - fmtver = byte_stream_get8(bs); /* Version of ssi data. Should be 0x00 */ - od->ssi.numitems += byte_stream_get16(bs); /* # of items in this SSI SNAC */ - - /* Read in the list */ - while (byte_stream_bytes_left(bs) > 4) { /* last four bytes are timestamp */ - if ((namelen = byte_stream_get16(bs))) - name = byte_stream_getstr(bs, namelen); - else - name = NULL; - gid = byte_stream_get16(bs); - bid = byte_stream_get16(bs); - type = byte_stream_get16(bs); - data = aim_tlvlist_readlen(bs, byte_stream_get16(bs)); - aim_ssi_item_debug_append(debugstr, "\t", aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data)); - g_free(name); - aim_tlvlist_free(data); - } - purple_debug_misc("oscar", "Reading items from tlvlist for account %s:\n%s", - purple_account_get_username(purple_connection_get_account(od->gc)), debugstr->str); - g_string_free(debugstr, TRUE); - - /* Read in the timestamp */ - od->ssi.timestamp = byte_stream_get32(bs); - - if (!(snac->flags & 0x0001)) { - /* Make a copy of the list */ - struct aim_ssi_item *cur; - for (cur=od->ssi.official.data; cur; cur=cur->next) - aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data); - - /* Clean the buddy list */ - aim_ssi_cleanlist(od); - - od->ssi.received_data = TRUE; - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, fmtver, od->ssi.numitems, od->ssi.timestamp); - } - - return ret; -} - -/* - * Subtype 0x0007 - SSI Activate Data. - * - * Should be sent after receiving 13/6 or 13/f to tell the server you - * are ready to begin using the list. It will promptly give you the - * presence information for everyone in your list and put your permit/deny - * settings into effect. - * - */ -int aim_ssi_enable(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG))) - return -EINVAL; - - aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, 0x0007); - - return 0; -} - -/* - * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s). - * - * Sends the SNAC to add, modify, or delete items from the server-stored - * information. These 3 SNACs all have an identical structure. The only - * difference is the subtype that is set for the SNAC. - * - */ -static int aim_ssi_addmoddel(OscarData *od) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - struct aim_ssi_tmp *cur; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !od->ssi.pending || !od->ssi.pending->item) - return -EINVAL; - - /* Calculate total SNAC size */ - bslen = 0; - for (cur=od->ssi.pending; cur; cur=cur->next) { - bslen += 10; /* For length, GID, BID, type, and length */ - if (cur->item->name) - bslen += strlen(cur->item->name); - if (cur->item->data) - bslen += aim_tlvlist_size(cur->item->data); - } - - byte_stream_new(&bs, bslen); - - for (cur=od->ssi.pending; cur; cur=cur->next) { - byte_stream_put16(&bs, cur->item->name ? strlen(cur->item->name) : 0); - if (cur->item->name) - byte_stream_putstr(&bs, cur->item->name); - byte_stream_put16(&bs, cur->item->gid); - byte_stream_put16(&bs, cur->item->bid); - byte_stream_put16(&bs, cur->item->type); - byte_stream_put16(&bs, cur->item->data ? aim_tlvlist_size(cur->item->data) : 0); - if (cur->item->data) - aim_tlvlist_write(&bs, &cur->item->data); - } - - snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0008 - Incoming SSI add. - * - * Sent by the server, for example, when someone is added to - * your "Recent Buddies" group. - */ -static int parseadd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - char *name; - guint16 len, gid, bid, type; - GSList *data; - - while (byte_stream_bytes_left(bs)) { - if ((len = byte_stream_get16(bs))) - name = byte_stream_getstr(bs, len); - else - name = NULL; - gid = byte_stream_get16(bs); - bid = byte_stream_get16(bs); - type = byte_stream_get16(bs); - if ((len = byte_stream_get16(bs))) - data = aim_tlvlist_readlen(bs, len); - else - data = NULL; - - aim_ssi_itemlist_add(&od->ssi.local, name, gid, bid, type, data); - aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data); - aim_tlvlist_free(data); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, snac->subtype, type, name); - - g_free(name); - } - - return ret; -} - -/* - * Subtype 0x0009 - Incoming SSI mod. - */ -static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - char *name; - guint16 len, gid, bid, type; - GSList *data; - struct aim_ssi_item *item; - - while (byte_stream_bytes_left(bs)) { - if ((len = byte_stream_get16(bs))) - name = byte_stream_getstr(bs, len); - else - name = NULL; - gid = byte_stream_get16(bs); - bid = byte_stream_get16(bs); - type = byte_stream_get16(bs); - if ((len = byte_stream_get16(bs))) - data = aim_tlvlist_readlen(bs, len); - else - data = NULL; - - /* Replace the 2 local items with the given one */ - if ((item = aim_ssi_itemlist_find(&od->ssi.local, gid, bid))) { - item->type = type; - aim_ssi_item_set_name(&od->ssi.local, item, name); - aim_tlvlist_free(item->data); - item->data = aim_tlvlist_copy(data); - } - - if ((item = aim_ssi_itemlist_find(&od->ssi.official, gid, bid))) { - item->type = type; - aim_ssi_item_set_name(&od->ssi.official, item, name); - aim_tlvlist_free(item->data); - item->data = aim_tlvlist_copy(data); - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, snac->subtype, type, name); - - g_free(name); - aim_tlvlist_free(data); - } - - return ret; -} - -/* - * Subtype 0x000a - Incoming SSI del. - * - * XXX - It would probably be good for the client to actually do something when it gets this. - */ -static int parsedel(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 gid, bid; - struct aim_ssi_item *del; - - while (byte_stream_bytes_left(bs)) { - byte_stream_advance(bs, byte_stream_get16(bs)); - gid = byte_stream_get16(bs); - bid = byte_stream_get16(bs); - byte_stream_get16(bs); - byte_stream_advance(bs, byte_stream_get16(bs)); - - if ((del = aim_ssi_itemlist_find(&od->ssi.local, gid, bid))) - aim_ssi_itemlist_del(&od->ssi.local, del); - if ((del = aim_ssi_itemlist_find(&od->ssi.official, gid, bid))) - aim_ssi_itemlist_del(&od->ssi.official, del); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame); - } - - return ret; -} - -/* - * Subtype 0x000e - SSI Add/Mod/Del Ack. - * - * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel). - * - */ -static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - struct aim_ssi_tmp *cur, *del; - - /* Read in the success/failure flags from the ack SNAC */ - cur = od->ssi.pending; - while (cur && (byte_stream_bytes_left(bs)>0)) { - cur->ack = byte_stream_get16(bs); - cur = cur->next; - } - - /* - * If outcome is 0, then add the item to the item list, or replace the other item, - * or remove the old item. If outcome is non-zero, then remove the item from the - * local list, or unmodify it, or add it. - */ - for (cur=od->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) { - if (cur->item) { - if (cur->ack) { - /* Our action was unsuccessful, so change the local list back to how it was */ - if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) { - /* Remove the item from the local list */ - /* Make sure cur->item is still valid memory */ - /* TODO: "Still valid memory"? That's bad form. */ - if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) { - cur->name = g_strdup(cur->item->name); - aim_ssi_itemlist_del(&od->ssi.local, cur->item); - } - cur->item = NULL; - - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) { - /* Replace the local item with the item from the official list */ - if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) { - struct aim_ssi_item *cur1; - if ((cur1 = aim_ssi_itemlist_find(&od->ssi.official, cur->item->gid, cur->item->bid))) { - aim_ssi_item_set_name(&od->ssi.official, cur->item, cur1->name); - aim_tlvlist_free(cur->item->data); - cur->item->data = aim_tlvlist_copy(cur1->data); - } - } else - cur->item = NULL; - - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) { - /* Add the item back into the local list */ - if (aim_ssi_itemlist_valid(&od->ssi.official, cur->item)) { - aim_ssi_itemlist_add(&od->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); - } else - cur->item = NULL; - } - - } else { - /* Do the exact opposite */ - if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) { - /* Add the local item to the official list */ - if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) { - aim_ssi_itemlist_add(&od->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data); - } else - cur->item = NULL; - - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) { - /* Replace the official item with the item from the local list */ - if (aim_ssi_itemlist_valid(&od->ssi.local, cur->item)) { - struct aim_ssi_item *cur1; - if ((cur1 = aim_ssi_itemlist_find(&od->ssi.official, cur->item->gid, cur->item->bid))) { - aim_ssi_item_set_name(&od->ssi.official, cur1, cur->item->name); - aim_tlvlist_free(cur1->data); - cur1->data = aim_tlvlist_copy(cur->item->data); - } - } else - cur->item = NULL; - - } else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) { - /* Remove the item from the official list */ - if (aim_ssi_itemlist_valid(&od->ssi.official, cur->item)) - aim_ssi_itemlist_del(&od->ssi.official, cur->item); - cur->item = NULL; - } - - } - } /* End if (cur->item) */ - } /* End for loop */ - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, od->ssi.pending); - - /* Free all aim_ssi_tmp's with an outcome */ - cur = od->ssi.pending; - while (cur && (cur->ack != 0xffff)) { - del = cur; - cur = cur->next; - g_free(del->name); - g_free(del); - } - od->ssi.pending = cur; - - /* If we're not waiting for any more acks, then send more SNACs */ - if (!od->ssi.pending) { - od->ssi.waiting_for_ack = FALSE; - aim_ssi_sync(od); - } - - return ret; -} - -/* - * Subtype 0x000f - SSI Data Unchanged. - * - * Response to aim_ssi_reqifchanged() if the server-side data is not newer than - * posted local stamp/revision. - * - */ -static int parsedataunchanged(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - - od->ssi.received_data = TRUE; - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame); - - return ret; -} - -/* - * Subtype 0x0011 - SSI Begin Data Modification. - * - * Tell the server you're going to start modifying data. This marks - * the beginning of a transaction. - */ -int aim_ssi_modbegin(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG))) - return -EINVAL; - - aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTART); - - return 0; -} - -/* - * Subtype 0x0012 - SSI End Data Modification. - * - * Tell the server you're finished modifying data. The marks the end - * of a transaction. - */ -int aim_ssi_modend(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG))) - return -EINVAL; - - aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTOP); - - return 0; -} - -/* - * Subtype 0x0015 - Receive an authorization grant - */ -static int receiveauthgrant(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 tmp; - char *bn, *msg, *tmpstr; - - /* Read buddy name */ - tmp = byte_stream_get8(bs); - if (!tmp) { - purple_debug_warning("oscar", "Dropping auth grant SNAC " - "because username was empty\n"); - return 0; - } - bn = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(bn, -1, NULL)) { - purple_debug_warning("oscar", "Dropping auth grant SNAC " - "because the username was not valid UTF-8\n"); - g_free(bn); - return 0; - } - - /* Read message */ - tmp = byte_stream_get16(bs); - if (tmp) { - msg = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(msg, -1, NULL)) { - /* Ugh, msg isn't UTF8. Let's salvage. */ - purple_debug_warning("oscar", "Got non-UTF8 message in auth " - "grant from %s\n", bn); - tmpstr = purple_utf8_salvage(msg); - g_free(msg); - msg = tmpstr; - } - } else - msg = NULL; - - /* Unknown */ - tmp = byte_stream_get16(bs); - if (!tmp) - purple_debug_warning("oscar", "unknown field missing"); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, bn, msg); - - g_free(bn); - g_free(msg); - - return ret; -} - -/* - * Subtype 0x0018 - Send authorization request - * - * Sends a request for authorization to the given contact. The request will either be - * granted, denied, or dropped. - * - */ -int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn) - return -EINVAL; - - byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2); - - /* Username */ - byte_stream_put8(&bs, strlen(bn)); - byte_stream_putstr(&bs, bn); - - /* Message (null terminated) */ - byte_stream_put16(&bs, msg ? strlen(msg) : 0); - if (msg) { - byte_stream_putstr(&bs, msg); - byte_stream_put8(&bs, 0x00); - } - - /* Unknown */ - byte_stream_put16(&bs, 0x0000); - - snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0019 - Receive an authorization request - */ -static int receiveauthrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 tmp; - char *bn, *msg, *tmpstr; - - /* Read buddy name */ - tmp = byte_stream_get8(bs); - if (!tmp) { - purple_debug_warning("oscar", "Dropping auth request SNAC " - "because username was empty\n"); - return 0; - } - bn = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(bn, -1, NULL)) { - purple_debug_warning("oscar", "Dropping auth request SNAC " - "because the username was not valid UTF-8\n"); - g_free(bn); - return 0; - } - - /* Read message */ - tmp = byte_stream_get16(bs); - if (tmp) { - msg = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(msg, -1, NULL)) { - /* Ugh, msg isn't UTF8. Let's salvage. */ - purple_debug_warning("oscar", "Got non-UTF8 message in auth " - "request from %s\n", bn); - tmpstr = purple_utf8_salvage(msg); - g_free(msg); - msg = tmpstr; - } - } else - msg = NULL; - - /* Unknown */ - tmp = byte_stream_get16(bs); - if (!tmp) - purple_debug_warning("oscar", "unknown field missing"); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, bn, msg); - - g_free(bn); - g_free(msg); - - return ret; -} - -/* - * Subtype 0x001a - Send authorization reply - * - * Sends a reply to a request for authorization. The reply can either - * grant authorization or deny authorization. - * - * if reply=0x00 then deny - * if reply=0x01 then grant - * - */ -int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn) - return -EINVAL; - - byte_stream_new(&bs, 1+strlen(bn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2); - - /* Username */ - byte_stream_put8(&bs, strlen(bn)); - byte_stream_putstr(&bs, bn); - - /* Grant or deny */ - byte_stream_put8(&bs, reply); - - /* Message (null terminated) */ - byte_stream_put16(&bs, msg ? (strlen(msg)+1) : 0); - if (msg) { - byte_stream_putstr(&bs, msg); - byte_stream_put8(&bs, 0x00); - } - - /* Unknown */ - byte_stream_put16(&bs, 0x0000); - - snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x001b - Receive an authorization reply - * - * You get this bad boy when other people respond to the authorization - * request that you have previously sent them. - */ -static int receiveauthreply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 tmp; - guint8 reply; - char *bn, *msg, *tmpstr; - - /* Read buddy name */ - tmp = byte_stream_get8(bs); - if (!tmp) { - purple_debug_warning("oscar", "Dropping auth reply SNAC " - "because username was empty\n"); - return 0; - } - bn = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(bn, -1, NULL)) { - purple_debug_warning("oscar", "Dropping auth reply SNAC " - "because the username was not valid UTF-8\n"); - g_free(bn); - return 0; - } - - /* Read reply */ - reply = byte_stream_get8(bs); - - /* Read message */ - tmp = byte_stream_get16(bs); - if (tmp) { - msg = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(msg, -1, NULL)) { - /* Ugh, msg isn't UTF8. Let's salvage. */ - purple_debug_warning("oscar", "Got non-UTF8 message in auth " - "reply from %s\n", bn); - tmpstr = purple_utf8_salvage(msg); - g_free(msg); - msg = tmpstr; - } - } else - msg = NULL; - - /* Unknown */ - tmp = byte_stream_get16(bs); - if (!tmp) - purple_debug_warning("oscar", "unknown field missing"); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, bn, reply, msg); - - g_free(bn); - g_free(msg); - - return ret; -} - -/* - * Subtype 0x001c - Receive a message telling you someone added you to their list. - */ -static int receiveadded(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 tmp; - char *bn; - - /* Read buddy name */ - tmp = byte_stream_get8(bs); - if (!tmp) { - purple_debug_warning("oscar", "Dropping 'you were added' SNAC " - "because username was empty\n"); - return 0; - } - bn = byte_stream_getstr(bs, tmp); - if (!g_utf8_validate(bn, -1, NULL)) { - purple_debug_warning("oscar", "Dropping 'you were added' SNAC " - "because the username was not valid UTF-8\n"); - g_free(bn); - return 0; - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, bn); - - g_free(bn); - - return ret; -} - -/* - * If we're on ICQ, then AIM_SSI_TYPE_DENY is used for the "permanently invisible" list. - * AIM_SSI_TYPE_ICQDENY is used for blocking users instead. - */ -guint16 -aim_ssi_getdenyentrytype(OscarData* od) -{ - return od->icq ? AIM_SSI_TYPE_ICQDENY : AIM_SSI_TYPE_DENY; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO) - return parserights(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_LIST) - return parsedata(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADD) - return parseadd(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_MOD) - return parsemod(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_DEL) - return parsedel(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_SRVACK) - return parseack(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_NOLIST) - return parsedataunchanged(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTH) - return receiveauthgrant(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ) - return receiveauthrequest(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP) - return receiveauthreply(od, conn, mod, frame, snac, bs); - else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADDED) - return receiveadded(od, conn, mod, frame, snac, bs); - - return 0; -} - -static void -ssi_shutdown(OscarData *od, aim_module_t *mod) -{ - aim_ssi_freelist(od); -} - -int -ssi_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_FEEDBAG; - mod->version = 0x0004; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "feedbag", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = ssi_shutdown; - - return 0; -}
--- a/libpurple/protocols/oscar/family_icbm.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2149 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0004 - Routines for sending/receiving Instant Messages. - * - * Note the term ICBM (Inter-Client Basic Message) which blankets - * all types of generically routed through-server messages. Within - * the ICBM types (family 4), a channel is defined. Each channel - * represents a different type of message. Channel 1 is used for - * what would commonly be called an "instant message". Channel 2 - * is used for negotiating "rendezvous". These transactions end in - * something more complex happening, such as a chat invitation, or - * a file transfer. Channel 3 is used for chat messages (not in - * the same family as these channels). Channel 4 is used for - * various ICQ messages. Examples are normal messages, URLs, and - * old-style authorization. - * - * In addition to the channel, every ICBM contains a cookie. For - * standard IMs, these are only used for error messages. However, - * the more complex rendezvous messages make suitably more complex - * use of this field. - * - * TODO: Split this up into an im.c file an an icbm.c file. It - * will be beautiful, you'll see. - * - * Make sure flap_connection_findbygroup is used by all functions. - */ - -#include "encoding.h" -#include "oscar.h" -#include "peer.h" - -#include "util.h" - -static const char * const errcodereason[] = { - N_("Invalid error"), - N_("Not logged in"), - N_("Cannot receive IM due to parental controls"), - N_("Cannot send SMS without accepting terms"), - N_("Cannot send SMS"), /* SMS_WITHOUT_DISCLAIMER is weird */ - N_("Cannot send SMS to this country"), - N_("Unknown error"), /* Undocumented */ - N_("Unknown error"), /* Undocumented */ - N_("Cannot send SMS to unknown country"), - N_("Bot accounts cannot initiate IMs"), - N_("Bot account cannot IM this user"), - N_("Bot account reached IM limit"), - N_("Bot account reached daily IM limit"), - N_("Bot account reached monthly IM limit"), - N_("Unable to receive offline messages"), - N_("Offline message store full") -}; -static const int errcodereasonlen = G_N_ELEMENTS(errcodereason); - -/** - * Add a standard ICBM header to the given bstream with the given - * information. - * - * @param bs The bstream to write the ICBM header to. - * @param c c is for cookie, and cookie is for me. - * @param channel The ICBM channel (1 through 4). - * @param bn Null-terminated scrizeen nizame. - * @return The number of bytes written. It's really not useful. - */ -static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *bn) -{ - byte_stream_putraw(bs, c, 8); - byte_stream_put16(bs, channel); - byte_stream_put8(bs, strlen(bn)); - byte_stream_putstr(bs, bn); - return 8+2+1+strlen(bn); -} - -/** - * Generates a random ICBM cookie in a character array of length 8 - * and copies it into the variable passed as cookie - * TODO: Maybe we should stop limiting our characters to the visible range? - */ -void aim_icbm_makecookie(guchar *cookie) -{ - int i; - - /* Should be like "21CBF95" and null terminated */ - for (i = 0; i < 7; i++) - cookie[i] = 0x30 + ((guchar)g_random_int() % 10); - cookie[7] = '\0'; -} - -/* - * Subtype 0x0001 - Error - */ -static int -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_snac_t *snac2; - guint16 reason, errcode = 0; - const char *bn; - GSList *tlvlist; - PurpleConnection *gc = od->gc; -#ifdef TODOFT - PurpleXfer *xfer; -#endif - const char *reason_str; - char *buf; - - snac2 = aim_remsnac(od, snac->id); - if (!snac2) { - purple_debug_misc("oscar", "icbm error: received response from unknown request!\n"); - return 1; - } - - if (snac2->family != SNAC_FAMILY_ICBM) { - purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2->family); - g_free(snac2->data); - g_free(snac2); - return 1; - } - - /* Data is assumed to be the destination bn */ - bn = snac2->data; - if (!bn || bn[0] == '\0') { - purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n"); - g_free(snac2->data); - g_free(snac2); - return 1; - } - - reason = byte_stream_get16(bs); - - tlvlist = aim_tlvlist_read(bs); - if (aim_tlv_gettlv(tlvlist, 0x0008, 1)) - errcode = aim_tlv_get16(tlvlist, 0x0008, 1); - aim_tlvlist_free(tlvlist); - - purple_debug_error("oscar", - "Message error with bn %s and reason %hu and errcode %hu\n", - bn, reason, errcode); - -#ifdef TODOFT - /* If this was a file transfer request, bn is a cookie */ - if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, bn))) { - purple_xfer_cancel_remote(xfer); - return 1; - } -#endif - - /* Notify the user that the message wasn't delivered */ - reason_str = oscar_get_msgerr_reason(reason); - if (errcode != 0 && errcode < errcodereasonlen) - buf = g_strdup_printf(_("Unable to send message: %s (%s)"), reason_str, - _(errcodereason[errcode])); - else - buf = g_strdup_printf(_("Unable to send message: %s"), reason_str); - - if (!purple_conversation_present_error(bn, purple_connection_get_account(gc), buf)) { - g_free(buf); - if (errcode != 0 && errcode < errcodereasonlen) { - buf = g_strdup_printf(_("Unable to send message to %s: %s (%s)"), - bn, reason_str, _(errcodereason[errcode])); - } else { - buf = g_strdup_printf(_("Unable to send message to %s: %s"), bn, - reason_str); - } - purple_notify_error(od->gc, NULL, buf, reason_str, - purple_request_cpar_from_connection(od->gc)); - } - g_free(buf); - - g_free(snac2->data); - g_free(snac2); - - return 1; -} - -/** - * Subtype 0x0002 - Set ICBM parameters. - * - * I definitely recommend sending this. If you don't, you'll be stuck - * with the rather unreasonable defaults. - * - */ -int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - if (!params) - return -EINVAL; - - byte_stream_new(&bs, 16); - - /* This is read-only (see Parameter Reply). Must be set to zero here. */ - byte_stream_put16(&bs, 0x0000); - - /* These are all read-write */ - byte_stream_put32(&bs, params->flags); - byte_stream_put16(&bs, params->maxmsglen); - byte_stream_put16(&bs, params->maxsenderwarn); - byte_stream_put16(&bs, params->maxrecverwarn); - byte_stream_put32(&bs, params->minmsginterval); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/** - * Subtype 0x0004 - Request ICBM parameter information. - * - */ -int aim_im_reqparams(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004); - - return 0; -} - -/** - * Subtype 0x0005 - Receive parameter information. - * - */ -static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - struct aim_icbmparameters params; - - params.maxchan = byte_stream_get16(bs); - params.flags = byte_stream_get32(bs); - params.maxmsglen = byte_stream_get16(bs); - params.maxsenderwarn = byte_stream_get16(bs); - params.maxrecverwarn = byte_stream_get16(bs); - params.minmsginterval = byte_stream_get32(bs); - - params.flags = AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED - | AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED - | AIM_IMPARAM_FLAG_EVENTS_ALLOWED - | AIM_IMPARAM_FLAG_SMS_SUPPORTED - | AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED - | AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ; - params.maxmsglen = 8000; - params.minmsginterval = 0; - - aim_im_setparams(od, ¶ms); - - return 0; -} - -/** - * Subtype 0x0006 - Send an ICBM (instant message). - * - * - * Possible flags: - * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse - * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are - * online (probably ICQ only). - * - * Implementation note: Since this is one of the most-used functions - * in all of libfaim, it is written with performance in mind. As such, - * it is not as clear as it could be in respect to how this message is - * supposed to be layed out. Most obviously, tlvlists should be used - * instead of writing out the bytes manually. - */ -int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args) -{ - FlapConnection *conn; - aim_snacid_t snacid; - ByteStream data; - guchar cookie[8]; - int msgtlvlen; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - if (!args) - return -EINVAL; - - if (!args->msg || (args->msglen <= 0)) - return -EINVAL; - - if (args->msglen > MAXMSGLEN) - return -E2BIG; - - /* Painfully calculate the size of the message TLV */ - msgtlvlen = 1 + 1; /* 0501 */ - msgtlvlen += 2 + args->featureslen; - msgtlvlen += 2 /* 0101 */ + 2 /* block len */; - msgtlvlen += 4 /* charset */ + args->msglen; - - byte_stream_new(&data, msgtlvlen + 128); - - /* Generate an ICBM cookie */ - aim_icbm_makecookie(cookie); - - /* ICBM header */ - aim_im_puticbm(&data, cookie, 0x0001, args->destbn); - - /* Message TLV (type 0x0002) */ - byte_stream_put16(&data, 0x0002); - byte_stream_put16(&data, msgtlvlen); - - /* Features TLV (type 0x0501) */ - byte_stream_put16(&data, 0x0501); - byte_stream_put16(&data, args->featureslen); - byte_stream_putraw(&data, args->features, args->featureslen); - - /* Insert message text in a TLV (type 0x0101) */ - byte_stream_put16(&data, 0x0101); - - /* Message block length */ - byte_stream_put16(&data, args->msglen + 0x04); - - /* Character set */ - byte_stream_put16(&data, args->charset); - /* Character subset -- we always use 0 here */ - byte_stream_put16(&data, 0x0); - - /* Message. Not terminated */ - byte_stream_putraw(&data, (guchar *)args->msg, args->msglen); - - /* Set the Autoresponse flag */ - if (args->flags & AIM_IMFLAGS_AWAY) { - byte_stream_put16(&data, 0x0004); - byte_stream_put16(&data, 0x0000); - } else { - /* Set the Request Acknowledge flag */ - byte_stream_put16(&data, 0x0003); - byte_stream_put16(&data, 0x0000); - - if (args->flags & AIM_IMFLAGS_OFFLINE) { - /* Allow this message to be queued as an offline message */ - byte_stream_put16(&data, 0x0006); - byte_stream_put16(&data, 0x0000); - } - } - - /* - * Set the I HAVE A REALLY PURTY ICON flag. - * XXX - This should really only be sent on initial - * IMs and when you change your icon. - */ - if (args->flags & AIM_IMFLAGS_HASICON) { - byte_stream_put16(&data, 0x0008); - byte_stream_put16(&data, 0x000c); - byte_stream_put32(&data, args->iconlen); - byte_stream_put16(&data, 0x0001); - byte_stream_put16(&data, args->iconsum); - byte_stream_put32(&data, args->iconstamp); - } - - /* - * Set the Buddy Icon Requested flag. - * XXX - Every time? Surely not... - */ - if (args->flags & AIM_IMFLAGS_BUDDYREQ) { - byte_stream_put16(&data, 0x0009); - byte_stream_put16(&data, 0x0000); - } - - /* XXX - should be optional */ - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &data); - byte_stream_destroy(&data); - - /* clean out SNACs over 60sec old */ - aim_cleansnacs(od, 60); - - return 0; -} - -/* - * Subtype 0x0006 - Send a chat invitation. - */ -int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - IcbmCookie *msgcookie; - struct aim_invite_priv *priv; - guchar cookie[8]; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream hdrbs; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - if (!bn || !msg || !roomname) - return -EINVAL; - - aim_icbm_makecookie(cookie); - - byte_stream_new(&bs, 1142+strlen(bn)+strlen(roomname)+strlen(msg)); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1); - - /* XXX should be uncached by an unwritten 'invite accept' handler */ - priv = g_new0(struct aim_invite_priv, 1); - priv->bn = g_strdup(bn); - priv->roomname = g_strdup(roomname); - priv->exchange = exchange; - priv->instance = instance; - - if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv))) - aim_cachecookie(od, msgcookie); - else - g_free(priv); - - /* ICBM Header */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - - /* - * TLV t(0005) - * - * Everything else is inside this TLV. - * - * Sigh. AOL was rather inconsistent right here. So we have - * to play some minor tricks. Right inside the type 5 is some - * raw data, followed by a series of TLVs. - * - */ - byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2); - - byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */ - byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */ - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT); - - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001); - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); - aim_tlvlist_add_str(&inner_tlvlist, 0x000c, msg); - aim_tlvlist_add_chatroom(&inner_tlvlist, 0x2711, exchange, roomname, instance); - aim_tlvlist_write(&hdrbs, &inner_tlvlist); - - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data); - byte_stream_destroy(&hdrbs); - - aim_tlvlist_write(&bs, &outer_tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(outer_tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/** - * Subtype 0x0006 - Send your icon to a given user. - * - * This is also performance sensitive. (If you can believe it...) - * - */ -int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - guchar cookie[8]; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN)) - return -EINVAL; - - aim_icbm_makecookie(cookie); - - byte_stream_new(&bs, 8+2+1+strlen(bn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - - /* - * TLV t(0005) - * - * Encompasses everything below. - */ - byte_stream_put16(&bs, 0x0005); - byte_stream_put16(&bs, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT)); - - byte_stream_put16(&bs, 0x0000); - byte_stream_putraw(&bs, cookie, 8); - byte_stream_putcaps(&bs, OSCAR_CAPABILITY_BUDDYICON); - - /* TLV t(000a) */ - byte_stream_put16(&bs, 0x000a); - byte_stream_put16(&bs, 0x0002); - byte_stream_put16(&bs, 0x0001); - - /* TLV t(000f) */ - byte_stream_put16(&bs, 0x000f); - byte_stream_put16(&bs, 0x0000); - - /* TLV t(2711) */ - byte_stream_put16(&bs, 0x2711); - byte_stream_put16(&bs, 4+4+4+iconlen+strlen(AIM_ICONIDENT)); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, iconsum); - byte_stream_put32(&bs, iconlen); - byte_stream_put32(&bs, stamp); - byte_stream_putraw(&bs, icon, iconlen); - byte_stream_putstr(&bs, AIM_ICONIDENT); - - /* TLV t(0003) */ - byte_stream_put16(&bs, 0x0003); - byte_stream_put16(&bs, 0x0000); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/** - * Cancel a rendezvous invitation. It could be an invitation to - * establish a direct connection, or a file-send, or a chat invite. - */ -void -aim_im_sendch2_cancel(PeerConnection *peer_conn) -{ - OscarData *od; - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream hdrbs; - - od = peer_conn->od; - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (conn == NULL) - return; - - byte_stream_new(&bs, 118+strlen(peer_conn->bn)); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn); - - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); - - byte_stream_new(&hdrbs, 64); - - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_CANCEL); - byte_stream_putraw(&hdrbs, peer_conn->cookie, 8); - byte_stream_putcaps(&hdrbs, peer_conn->type); - - /* This TLV means "cancel!" */ - aim_tlvlist_add_16(&inner_tlvlist, 0x000b, 0x0001); - aim_tlvlist_write(&hdrbs, &inner_tlvlist); - - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data); - byte_stream_destroy(&hdrbs); - - aim_tlvlist_write(&bs, &outer_tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(outer_tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0006 - Send an "I accept and I've connected to - * you" message. - */ -void -aim_im_sendch2_connected(PeerConnection *peer_conn) -{ - OscarData *od; - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - od = peer_conn->od; - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (conn == NULL) - return; - - byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn); - - byte_stream_put16(&bs, 0x0005); - byte_stream_put16(&bs, 0x001a); - byte_stream_put16(&bs, AIM_RENDEZVOUS_CONNECTED); - byte_stream_putraw(&bs, peer_conn->cookie, 8); - byte_stream_putcaps(&bs, peer_conn->type); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0006 - Send a direct connect rendezvous ICBM. This - * could have a number of meanings, depending on the content: - * "I want you to connect to me" - * "I want to connect to you" - * "I want to connect through a proxy server" - */ -void -aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream hdrbs; - - g_return_if_fail(bn != NULL); - g_return_if_fail(ip != NULL); - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (conn == NULL) - return; - - byte_stream_new(&bs, 246+strlen(bn)); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); - - byte_stream_new(&hdrbs, 128); - - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE); - byte_stream_putraw(&hdrbs, cookie, 8); - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip); - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip); - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port); - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber); - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); - aim_tlvlist_write(&hdrbs, &inner_tlvlist); - - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data); - byte_stream_destroy(&hdrbs); - - aim_tlvlist_write(&bs, &outer_tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(outer_tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0006 - Send a direct connect rendezvous ICBM asking the - * remote user to connect to us via a proxy server. - */ -void -aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream hdrbs; - guint8 ip_comp[4]; - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (conn == NULL) - return; - - byte_stream_new(&bs, 246+strlen(bn)); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); - - byte_stream_new(&hdrbs, 128); - - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE); - byte_stream_putraw(&hdrbs, cookie, 8); - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip); - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip); - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin); - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber); - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); - aim_tlvlist_add_noval(&inner_tlvlist, 0x0010); - - /* Send the bitwise complement of the port and ip. As a check? */ - ip_comp[0] = ~ip[0]; - ip_comp[1] = ~ip[1]; - ip_comp[2] = ~ip[2]; - ip_comp[3] = ~ip[3]; - aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp); - aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin); - - aim_tlvlist_write(&hdrbs, &inner_tlvlist); - - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data); - byte_stream_destroy(&hdrbs); - - aim_tlvlist_write(&bs, &outer_tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(outer_tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0006 - Send an "I want to send you this file" message - * - */ -void -aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream hdrbs; - - g_return_if_fail(bn != NULL); - g_return_if_fail(ip != NULL); - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (conn == NULL) - return; - - byte_stream_new(&bs, 1014); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); - - byte_stream_new(&hdrbs, 512); - - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE); - byte_stream_putraw(&hdrbs, cookie, 8); - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip); - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip); - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port); - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber); - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); - /* TODO: Send 0x0016 and 0x0017 */ - - if (filename != NULL) - { - ByteStream inner_bs; - - /* Begin TLV t(2711) */ - byte_stream_new(&inner_bs, 2+2+4+strlen(filename)+1); - byte_stream_put16(&inner_bs, (numfiles > 1) ? 0x0002 : 0x0001); - byte_stream_put16(&inner_bs, numfiles); - byte_stream_put32(&inner_bs, size); - - /* Filename - NULL terminated, for some odd reason */ - byte_stream_putstr(&inner_bs, filename); - byte_stream_put8(&inner_bs, 0x00); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, inner_bs.len, inner_bs.data); - byte_stream_destroy(&inner_bs); - /* End TLV t(2711) */ - } - - aim_tlvlist_write(&hdrbs, &inner_tlvlist); - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data); - byte_stream_destroy(&hdrbs); - - aim_tlvlist_write(&bs, &outer_tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(outer_tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Subtype 0x0006 - Send a sendfile connect rendezvous ICBM asking the - * remote user to connect to us via a proxy server. - */ -void -aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream hdrbs; - guint8 ip_comp[4]; - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (conn == NULL) - return; - - byte_stream_new(&bs, 1014); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0); - - /* ICBM header */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); - - byte_stream_new(&hdrbs, 512); - - byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE); - byte_stream_putraw(&hdrbs, cookie, 8); - byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip); - aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip); - aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin); - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber); - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); - aim_tlvlist_add_noval(&inner_tlvlist, 0x0010); - - /* Send the bitwise complement of the port and ip. As a check? */ - ip_comp[0] = ~ip[0]; - ip_comp[1] = ~ip[1]; - ip_comp[2] = ~ip[2]; - ip_comp[3] = ~ip[3]; - aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp); - aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin); - - if (filename != NULL) - { - ByteStream filename_bs; - - /* Begin TLV t(2711) */ - byte_stream_new(&filename_bs, 2+2+4+strlen(filename)+1); - byte_stream_put16(&filename_bs, (numfiles > 1) ? 0x0002 : 0x0001); - byte_stream_put16(&filename_bs, numfiles); - byte_stream_put32(&filename_bs, size); - - /* Filename - NULL terminated, for some odd reason */ - byte_stream_putstr(&filename_bs, filename); - byte_stream_put8(&filename_bs, 0x00); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, filename_bs.len, filename_bs.data); - byte_stream_destroy(&filename_bs); - /* End TLV t(2711) */ - } - - aim_tlvlist_write(&hdrbs, &inner_tlvlist); - - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data); - byte_stream_destroy(&hdrbs); - - aim_tlvlist_write(&bs, &outer_tlvlist); - - aim_tlvlist_free(inner_tlvlist); - aim_tlvlist_free(outer_tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs); - - byte_stream_destroy(&bs); -} - -static void -incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args) -{ - PurpleAccount *account = purple_connection_get_account(od->gc); - /* - * We're interested in the inner TLV 0x101, which contains precious, precious message. - */ - while (byte_stream_bytes_left(message) >= 4) { - guint16 type = byte_stream_get16(message); - guint16 length = byte_stream_get16(message); - if (type == 0x101) { - gchar *msg; - guint16 msglen = length - 4; /* charset + charsubset */ - guint16 charset = byte_stream_get16(message); - byte_stream_advance(message, 2); /* charsubset */ - - msg = byte_stream_getstr(message, msglen); - args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen); - g_free(msg); - } else { - byte_stream_advance(message, length); - } - } -} - -static int -incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie) -{ - guint16 type, length; - aim_rxcallback_t userfunc; - int ret = 0; - struct aim_incomingim_ch1_args args; - unsigned int endpos; - - memset(&args, 0, sizeof(args)); - - /* - * This used to be done using tlvchains. For performance reasons, - * I've changed it to process the TLVs in-place. This avoids lots - * of per-IM memory allocations. - */ - while (byte_stream_bytes_left(bs) >= 4) - { - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - - if (length > byte_stream_bytes_left(bs)) - { - purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s. They are probably trying to do something malicious.\n", userinfo->bn); - break; - } - - endpos = byte_stream_curpos(bs) + length; - - if (type == 0x0002) { /* Message Block */ - ByteStream tlv02; - byte_stream_init(&tlv02, bs->data + bs->offset, length); - incomingim_ch1_parsemsg(od, userinfo, &tlv02, &args); - } else if (type == 0x0003) { /* Server Ack Requested */ - args.icbmflags |= AIM_IMFLAGS_ACK; - } else if (type == 0x0004) { /* Message is Auto Response */ - args.icbmflags |= AIM_IMFLAGS_AWAY; - } else if (type == 0x0006) { /* Message was received offline. */ - /* - * This flag is set on incoming offline messages for both - * AIM and ICQ accounts. - */ - args.icbmflags |= AIM_IMFLAGS_OFFLINE; - } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */ - args.iconlen = byte_stream_get32(bs); - byte_stream_get16(bs); /* 0x0001 */ - args.iconsum = byte_stream_get16(bs); - args.iconstamp = byte_stream_get32(bs); - - /* - * This looks to be a client bug. MacAIM 4.3 will - * send this tag, but with all zero values, in the - * first message of a conversation. This makes no - * sense whatsoever, so I'm going to say its a bug. - * - * You really shouldn't advertise a zero-length icon - * anyway. - * - */ - if (args.iconlen) - args.icbmflags |= AIM_IMFLAGS_HASICON; - } else if (type == 0x0009) { - args.icbmflags |= AIM_IMFLAGS_BUDDYREQ; - } else if (type == 0x000b) { /* Non-direct connect typing notification */ - args.icbmflags |= AIM_IMFLAGS_TYPINGNOT; - } else if (type == 0x0016) { - /* - * UTC timestamp for when the message was sent. Only - * provided for offline messages. - */ - args.timestamp = byte_stream_get32(bs); - } - - /* - * This is here to protect ourselves from ourselves. That - * is, if something above doesn't completely parse its value - * section, or, worse, overparses it, this will set the - * stream where it needs to be in order to land on the next - * TLV when the loop continues. - * - */ - byte_stream_setpos(bs, endpos); - } - - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, userinfo, &args); - - g_free(args.msg); - return ret; -} - -static void -incomingim_ch2_buddylist(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata) -{ - /* - * This goes like this... - * - * group name length - * group name - * num of buddies in group - * buddy name length - * buddy name - * buddy name length - * buddy name - * ... - * group name length - * group name - * num of buddies in group - * buddy name length - * buddy name - * ... - * ... - */ - while (byte_stream_bytes_left(servdata)) - { - guint16 gnlen, numb; - int i; - char *gn; - - gnlen = byte_stream_get16(servdata); - gn = byte_stream_getstr(servdata, gnlen); - numb = byte_stream_get16(servdata); - - for (i = 0; i < numb; i++) { - guint16 bnlen; - char *bn; - - bnlen = byte_stream_get16(servdata); - bn = byte_stream_getstr(servdata, bnlen); - - purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->bn, gn, bn); - - g_free(bn); - } - - g_free(gn); - } - - return; -} - -static void -incomingim_ch2_buddyicon_free(OscarData *od, IcbmArgsCh2 *args) -{ - g_free(args->info.icon.icon); - - return; -} - -static void -incomingim_ch2_buddyicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata) -{ - args->info.icon.checksum = byte_stream_get32(servdata); - args->info.icon.length = byte_stream_get32(servdata); - args->info.icon.timestamp = byte_stream_get32(servdata); - args->info.icon.icon = byte_stream_getraw(servdata, args->info.icon.length); - - args->destructor = (void *)incomingim_ch2_buddyicon_free; - - return; -} - -static void -incomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args) -{ - /* XXX - aim_chat_roominfo_free() */ - g_free(args->info.chat.roominfo.name); - - return; -} - -static void -incomingim_ch2_chat(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata) -{ - /* - * Chat room info. - */ - aim_chat_readroominfo(servdata, &args->info.chat.roominfo); - - args->destructor = (void *)incomingim_ch2_chat_free; -} - -static void -incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args) -{ - g_free((char *)args->info.rtfmsg.msg); -} - -/* - * The relationship between OSCAR_CAPABILITY_ICQSERVERRELAY and OSCAR_CAPABILITY_ICQRTF is - * kind of odd. This sends the client ICQRTF since that is all that I've seen - * SERVERRELAY used for. - * - * Note that this is all little-endian. Cringe. - * - */ -static void -incomingim_ch2_icqserverrelay(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata) -{ - guint16 hdrlen, msglen; - - args->destructor = (void *)incomingim_ch2_icqserverrelay_free; - -#define SKIP_HEADER(expected_hdrlen) \ - hdrlen = byte_stream_getle16(servdata); \ - if (hdrlen != expected_hdrlen) { \ - purple_debug_warning("oscar", "Expected to find a header with length " #expected_hdrlen "; ignoring message"); \ - return; \ - } \ - byte_stream_advance(servdata, hdrlen); - - SKIP_HEADER(0x001b); - SKIP_HEADER(0x000e); - - args->info.rtfmsg.msgtype = byte_stream_get8(servdata); - /* - * Copied from http://iserverd.khstu.ru/oscar/message.html: - * xx byte message flags - * xx xx word (LE) status code - * xx xx word (LE) priority code - * - * We don't need any of these, so just skip them. - */ - byte_stream_advance(servdata, 1 + 2 + 2); - - msglen = byte_stream_getle16(servdata); - args->info.rtfmsg.msg = byte_stream_getstr(servdata, msglen); -} - -static void -incomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args) -{ - g_free(args->info.sendfile.filename); -} - -/* Someone is sending us a file */ -static void -incomingim_ch2_sendfile(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata) -{ - int flen; - - args->destructor = (void *)incomingim_ch2_sendfile_free; - - /* Maybe there is a better way to tell what kind of sendfile - * this is? Maybe TLV t(000a)? */ - - /* subtype is one of AIM_OFT_SUBTYPE_* */ - args->info.sendfile.subtype = byte_stream_get16(servdata); - args->info.sendfile.totfiles = byte_stream_get16(servdata); - args->info.sendfile.totsize = byte_stream_get32(servdata); - - /* - * I hope to God I'm right when I guess that there is a - * 32 char max filename length for single files. I think - * OFT tends to do that. Gotta love inconsistency. I saw - * a 26 byte filename? - */ - /* AAA - create an byte_stream_getnullstr function (don't anymore)(maybe) */ - /* Use an inelegant way of getting the null-terminated filename, - * since there's no easy bstream routine. */ - for (flen = 0; byte_stream_get8(servdata); flen++); - byte_stream_advance(servdata, -flen -1); - args->info.sendfile.filename = byte_stream_getstr(servdata, flen); - - /* There is sometimes more after the null-terminated filename, - * but I'm unsure of its format. */ - /* I don't believe him. */ - /* There is sometimes a null byte inside a unicode filename, - * but as far as I can tell the filename is the last - * piece of data that will be in this message. --Jonathan */ -} - -typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args); - -static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie) -{ - aim_rxcallback_t userfunc; - aim_tlv_t *block1, *servdatatlv; - GSList *list2; - aim_tlv_t *tlv; - IcbmArgsCh2 args; - ByteStream bbs, sdbs, *sdbsptr = NULL; - guint8 *cookie2; - int ret = 0; - - char proxyip[30] = {""}; - char clientip[30] = {""}; - char verifiedip[30] = {""}; - - memset(&args, 0, sizeof(args)); - - /* - * There's another block of TLVs embedded in the type 5 here. - */ - block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1); - if (block1 == NULL) - { - /* The server sent us ch2 ICBM without ch2 info? Weird. */ - return 1; - } - byte_stream_init(&bbs, block1->value, block1->length); - - /* - * First two bytes represent the status of the connection. - * One of the AIM_RENDEZVOUS_ defines. - * - * 0 is a request, 1 is a cancel, 2 is an accept - */ - args.status = byte_stream_get16(&bbs); - - /* - * Next comes the cookie. Should match the ICBM cookie. - */ - cookie2 = byte_stream_getraw(&bbs, 8); - if (memcmp(cookie, cookie2, 8) != 0) - { - purple_debug_warning("oscar", - "Cookies don't match in rendezvous ICBM, bailing out.\n"); - g_free(cookie2); - return 1; - } - memcpy(args.cookie, cookie2, 8); - g_free(cookie2); - - /* - * The next 16bytes are a capability block so we can - * identify what type of rendezvous this is. - */ - args.type = aim_locate_getcaps(od, &bbs, 0x10); - - /* - * What follows may be TLVs or nothing, depending on the - * purpose of the message. - * - * Ack packets for instance have nothing more to them. - */ - list2 = aim_tlvlist_read(&bbs); - - /* - * IP address to proxy the file transfer through. - * - * TODO: I don't like this. Maybe just read in an int? Or inet_ntoa... - */ - tlv = aim_tlv_gettlv(list2, 0x0002, 1); - if ((tlv != NULL) && (tlv->length == 4)) - snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu", - tlv->value[0], tlv->value[1], - tlv->value[2], tlv->value[3]); - - /* - * IP address from the perspective of the client. - */ - tlv = aim_tlv_gettlv(list2, 0x0003, 1); - if ((tlv != NULL) && (tlv->length == 4)) - snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu", - tlv->value[0], tlv->value[1], - tlv->value[2], tlv->value[3]); - - /* - * Verified IP address (from the perspective of Oscar). - * - * This is added by the server. - */ - tlv = aim_tlv_gettlv(list2, 0x0004, 1); - if ((tlv != NULL) && (tlv->length == 4)) - snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu", - tlv->value[0], tlv->value[1], - tlv->value[2], tlv->value[3]); - - /* - * Port number for something. - */ - if (aim_tlv_gettlv(list2, 0x0005, 1)) - args.port = aim_tlv_get16(list2, 0x0005, 1); - - /* - * File transfer "request number": - * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy - * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy) - * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers - */ - if (aim_tlv_gettlv(list2, 0x000a, 1)) - args.requestnumber = aim_tlv_get16(list2, 0x000a, 1); - - /* - * Terminate connection/error code. 0x0001 means the other user - * cancelled the connection. - */ - if (aim_tlv_gettlv(list2, 0x000b, 1)) - args.errorcode = aim_tlv_get16(list2, 0x000b, 1); - - /* - * Invitation message / chat description. - */ - if (aim_tlv_gettlv(list2, 0x000c, 1)) { - args.msg = aim_tlv_getstr(list2, 0x000c, 1); - args.msglen = aim_tlv_getlength(list2, 0x000c, 1); - } - - /* - * Character set. - */ - if (aim_tlv_gettlv(list2, 0x000d, 1)) - args.encoding = aim_tlv_getstr(list2, 0x000d, 1); - - /* - * Language. - */ - if (aim_tlv_gettlv(list2, 0x000e, 1)) - args.language = aim_tlv_getstr(list2, 0x000e, 1); - - /* - * Flag meaning we should proxy the file transfer through an AIM server - */ - if (aim_tlv_gettlv(list2, 0x0010, 1)) - args.use_proxy = TRUE; - - if (*proxyip != '\0') - args.proxyip = (char *)proxyip; - if (*clientip != '\0') - args.clientip = (char *)clientip; - if (*verifiedip != '\0') - args.verifiedip = (char *)verifiedip; - - /* - * This must be present in PROPOSALs, but will probably not - * exist in CANCELs and ACCEPTs. Also exists in ICQ Lite - * Beta 4.0 URLs (OSCAR_CAPABILITY_ICQSERVERRELAY). - * - * Service Data blocks are module-specific in format. - */ - if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) { - - byte_stream_init(&sdbs, servdatatlv->value, servdatatlv->length); - sdbsptr = &sdbs; - - /* - * The rest of the handling depends on what type it is. - * - * Not all of them have special handling (yet). - */ - if (args.type & OSCAR_CAPABILITY_BUDDYICON) - incomingim_ch2_buddyicon(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); - else if (args.type & OSCAR_CAPABILITY_SENDBUDDYLIST) - incomingim_ch2_buddylist(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); - else if (args.type & OSCAR_CAPABILITY_CHAT) - incomingim_ch2_chat(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); - else if (args.type & OSCAR_CAPABILITY_ICQSERVERRELAY) - incomingim_ch2_icqserverrelay(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); - else if (args.type & OSCAR_CAPABILITY_SENDFILE) - incomingim_ch2_sendfile(od, conn, mod, frame, snac, userinfo, &args, sdbsptr); - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, userinfo, &args); - - - if (args.destructor) - ((ch2_args_destructor_t)args.destructor)(od, &args); - - g_free((char *)args.msg); - g_free((char *)args.encoding); - g_free((char *)args.language); - - aim_tlvlist_free(list2); - - return ret; -} - -static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie) -{ - ByteStream meat; - aim_rxcallback_t userfunc; - aim_tlv_t *block; - struct aim_incomingim_ch4_args args; - int ret = 0; - - /* - * Make a bstream for the meaty part. Yum. Meat. - */ - if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1))) - return -1; - byte_stream_init(&meat, block->value, block->length); - - args.uin = byte_stream_getle32(&meat); - args.type = byte_stream_getle8(&meat); - args.flags = byte_stream_getle8(&meat); - if (args.type == 0x1a) - /* There seems to be a problem with the length in SMS msgs from server, this fixed it */ - args.msglen = block->length - 6; - else - args.msglen = byte_stream_getle16(&meat); - args.msg = (gchar *)byte_stream_getraw(&meat, args.msglen); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, userinfo, &args); - - g_free(args.msg); - - return ret; -} - -/* - * Subtype 0x0007 - * - * It can easily be said that parsing ICBMs is THE single - * most difficult thing to do in the in AIM protocol. In - * fact, I think I just did say that. - * - * Below is the best damned solution I've come up with - * over the past sixteen months of battling with it. This - * can parse both away and normal messages from every client - * I have access to. Its not fast, its not clean. But it works. - * - */ -static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - guchar *cookie; - guint16 channel; - aim_userinfo_t userinfo; - - memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); - - /* - * Read ICBM Cookie. - */ - cookie = byte_stream_getraw(bs, 8); - - /* - * Channel ID. - * - * Channel 0x0001 is the message channel. It is - * used to send basic ICBMs. - * - * Channel 0x0002 is the Rendezvous channel, which - * is where Chat Invitiations and various client-client - * connection negotiations come from. - * - * Channel 0x0003 is used for chat messages. - * - * Channel 0x0004 is used for ICQ authorization, or - * possibly any system notice. - * - */ - channel = byte_stream_get16(bs); - - /* - * Extract the standard user info block. - * - * Note that although this contains TLVs that appear contiguous - * with the TLVs read below, they are two different pieces. The - * userinfo block contains the number of TLVs that contain user - * information, the rest are not even though there is no separation. - * You can start reading the message TLVs after aim_info_extract() - * parses out the standard userinfo block. - * - * That also means that TLV types can be duplicated between the - * userinfo block and the rest of the message, however there should - * never be two TLVs of the same type in one block. - * - */ - aim_info_extract(od, bs, &userinfo); - - /* - * From here on, its depends on what channel we're on. - * - * Technically all channels have a TLV list have this, however, - * for the common channel 1 case, in-place parsing is used for - * performance reasons (less memory allocation). - */ - if (channel == 1) { - - ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie); - - } else if (channel == 2) { - GSList *tlvlist; - - /* - * Read block of TLVs (not including the userinfo data). All - * further data is derived from what is parsed here. - */ - tlvlist = aim_tlvlist_read(bs); - - ret = incomingim_ch2(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie); - - aim_tlvlist_free(tlvlist); - - } else if (channel == 4) { - GSList *tlvlist; - - tlvlist = aim_tlvlist_read(bs); - ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie); - aim_tlvlist_free(tlvlist); - - } else { - purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel. Ignoring. (chan = %04x)\n", channel); - } - - aim_info_free(&userinfo); - g_free(cookie); - - return ret; -} - -/* Subtype 0x000a */ -static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 channel, nummissed, reason; - aim_userinfo_t userinfo; - - while (byte_stream_bytes_left(bs)) { - - channel = byte_stream_get16(bs); - aim_info_extract(od, bs, &userinfo); - nummissed = byte_stream_get16(bs); - reason = byte_stream_get16(bs); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, &userinfo, nummissed, reason); - - aim_info_free(&userinfo); - } - - return ret; -} - -/* - * Subtype 0x000b - * - * Possible codes: - * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer" - * - */ -int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - byte_stream_new(&bs, 8+2+1+strlen(bn)+6); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0); - - byte_stream_putraw(&bs, cookie, 8); - - byte_stream_put16(&bs, 0x0002); /* channel */ - byte_stream_put8(&bs, strlen(bn)); - byte_stream_putstr(&bs, bn); - - aim_tlvlist_add_16(&tlvlist, 0x0003, code); - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x000b. - * Send confirmation for a channel 2 message (Miranda wants it by default). - */ -void -aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - guint32 header_size, data_size; - guint16 cookie2 = (guint16)g_random_int(); - - purple_debug_misc("oscar", "Sending message ack to %s\n", bn); - - header_size = 8 + 2 + 1 + strlen(bn) + 2; - data_size = 2 + 1 + 16 + 4*2 + 2*3 + 4*3 + 1*2 + 2*3 + 1; - byte_stream_new(&bs, header_size + data_size); - - /* The message header. */ - aim_im_puticbm(&bs, cookie, 0x0002, bn); - byte_stream_put16(&bs, 0x0003); /* reason */ - - /* The actual message. */ - byte_stream_putle16(&bs, 0x1b); /* subheader #1 length */ - byte_stream_put8(&bs, 0x08); /* protocol version */ - byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY); - byte_stream_put32(&bs, 0x3); /* client features */ - byte_stream_put32(&bs, 0x0004); /* DC type */ - byte_stream_put16(&bs, cookie2); /* a cookie, chosen by fair dice roll */ - byte_stream_putle16(&bs, 0x0e); /* header #2 len? */ - byte_stream_put16(&bs, cookie2); /* the same cookie again */ - byte_stream_put32(&bs, 0); /* unknown */ - byte_stream_put32(&bs, 0); /* unknown */ - byte_stream_put32(&bs, 0); /* unknown */ - byte_stream_put8(&bs, 0x01); /* plain text message */ - byte_stream_put8(&bs, 0x00); /* no message flags */ - byte_stream_put16(&bs, 0x0000); /* no icq status */ - byte_stream_put16(&bs, 0x0100); /* priority */ - byte_stream_putle16(&bs, 1); /* query message len */ - byte_stream_put8(&bs, 0x00); /* empty query message */ - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0); - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - g_warn_if_fail(conn); - if (conn) { - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, - snacid, &bs); - } - byte_stream_destroy(&bs); -} - -/* - * Subtype 0x000b - Receive the response from an ICQ status message - * request (in which case this contains the ICQ status message) or - * a file transfer or direct IM request was declined. - */ -static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 channel, reason; - char *bn; - guchar *cookie; - guint8 bnlen; - char *xml = NULL; - guint16 hdrlen; - int curpos; - guint16 num1, num2; - PurpleAccount *account; - PurpleBuddy *buddy; - PurplePresence *presence; - PurpleStatus *status; - - cookie = byte_stream_getraw(bs, 8); - channel = byte_stream_get16(bs); - bnlen = byte_stream_get8(bs); - bn = byte_stream_getstr(bs, bnlen); - reason = byte_stream_get16(bs); - - if (channel == 0x0002) - { - hdrlen = byte_stream_getle16(bs); - if (hdrlen == 27 && bs->len > (27 + 51)) { - byte_stream_advance(bs, 51); - num1 = byte_stream_getle16(bs); - num2 = byte_stream_getle16(bs); - purple_debug_misc("oscar", "X-Status: num1 %hu, num2 %hu\n", num1, num2); - - if (num1 == 0x4f00 && num2 == 0x3b00) { - byte_stream_advance(bs, 86); - curpos = byte_stream_curpos(bs); - xml = byte_stream_getstr(bs, bs->len - curpos); - purple_debug_misc("oscar", "X-Status: Received XML reply\n"); - if (xml) { - GString *xstatus; - char *tmp1, *tmp2, *unescaped_xstatus; - - /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */ - - xstatus = g_string_new(NULL); - - tmp1 = strstr(xml, "<title>"); - if (tmp1 != NULL) { - tmp1 += 13; - tmp2 = strstr(tmp1, "</title>"); - if (tmp2 != NULL) - g_string_append_len(xstatus, tmp1, tmp2 - tmp1); - } - tmp1 = strstr(xml, "<desc>"); - if (tmp1 != NULL) { - tmp1 += 12; - tmp2 = strstr(tmp1, "</desc>"); - if (tmp2 != NULL) { - if (xstatus->len > 0 && tmp2 > tmp1) - g_string_append(xstatus, " - "); - g_string_append_len(xstatus, tmp1, tmp2 - tmp1); - } - } - unescaped_xstatus = purple_unescape_text(xstatus->str); - g_string_free(xstatus, TRUE); - if (*unescaped_xstatus) { - purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus); - account = purple_connection_get_account(od->gc); - buddy = purple_blist_find_buddy(account, bn); - presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_status(presence, "mood"); - if (status) { - purple_protocol_got_user_status(account, bn, - "mood", - PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME), - PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL); - } - } - g_free(unescaped_xstatus); - } else { - purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n"); - } - } else { - purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n"); - } - - } - - } else if (channel == 0x0004) { /* ICQ message */ - switch (reason) { - case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ - guint8 statusmsgtype, *msg; - guint16 len; - guint32 state; - - len = byte_stream_getle16(bs); /* Should be 0x001b */ - byte_stream_advance(bs, len); /* Unknown */ - - len = byte_stream_getle16(bs); /* Should be 0x000e */ - byte_stream_advance(bs, len); /* Unknown */ - - statusmsgtype = byte_stream_getle8(bs); - switch (statusmsgtype) { - case 0xe8: - state = AIM_ICQ_STATE_AWAY; - break; - case 0xe9: - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; - break; - case 0xea: - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; - break; - case 0xeb: - state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; - break; - case 0xec: - state = AIM_ICQ_STATE_CHAT; - break; - default: - state = 0; - break; - } - - byte_stream_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ - byte_stream_getle16(bs); /* Unknown - 0x0000 */ - byte_stream_getle16(bs); /* Unknown - 0x0000 */ - - len = byte_stream_getle16(bs); - msg = byte_stream_getraw(bs, len); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, bn, reason, state, msg); - - g_free(msg); - } break; - - default: { - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, bn, reason); - } break; - } /* end switch */ - } - - g_free(cookie); - g_free(bn); - g_free(xml); - - return ret; -} - -/* - * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent. - */ -static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - guchar *cookie; - char *bn; - int ret = 0; - - cookie = byte_stream_getraw(bs, 8); - byte_stream_get16(bs); /* ch */ - bn = byte_stream_getstr(bs, byte_stream_get8(bs)); - - purple_debug_info("oscar", "Sent message to %s.\n", bn); - - g_free(bn); - g_free(cookie); - - return ret; -} - -/* - * Subtype 0x0010 - Request any offline messages that are waiting for - * us. This is the "new" way of handling offline messages which is - * used for both AIM and ICQ. The old way is to use the ugly - * aim_icq_reqofflinemsgs() function, but that is no longer necessary. - * - * We set the 0x00000100 flag on the ICBM message parameters, which - * tells the oscar servers that we support offline messages. When we - * set that flag the servers do not automatically send us offline - * messages. Instead we must request them using this function. This - * should happen after sending the 0x0001/0x0002 "client online" SNAC. - */ -int aim_im_reqofflinemsgs(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002))) - return -EINVAL; - - aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010); - - return 0; -} - -/* - * Subtype 0x0014 - Send a mini typing notification (mtn) packet. - * - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, - * and Purple 0.60 and newer. - * - */ -int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, 0x0002))) - return -EINVAL; - - if (!bn) - return -EINVAL; - - byte_stream_new(&bs, 11 + strlen(bn) + 2); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0); - - /* ICBM cookie */ - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000000); - - /* - * Channel (should be 0x0001 for mtn) - */ - byte_stream_put16(&bs, channel); - - /* - * Dest buddy name - */ - byte_stream_put8(&bs, strlen(bn)); - byte_stream_putstr(&bs, bn); - - /* - * Event (should be 0x0000, 0x0001, or 0x0002 for mtn) - */ - byte_stream_put16(&bs, event); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0006 - Send eXtra Status request - */ -int icq_im_xstatus_request(OscarData *od, const char *sn) -{ - FlapConnection *conn; - aim_snacid_t snacid; - guchar cookie[8]; - GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; - ByteStream bs, header, plugindata; - PurpleAccount *account; - const char *fmt; - char *statxml; - int xmllen; - - static const guint8 pluginid[] = { - 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00 - }; - - static const guint8 c_plugindata[] = { - 0x1B, 0x00, 0x0A, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C, - 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, - 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, - 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00 - }; - - if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) - return -EINVAL; - - if (!sn) - return -EINVAL; - - fmt = "<N><QUERY><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 - + 2 + 2); - - snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0); - aim_im_puticbm(&bs, cookie, 0x0002, sn); - - byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */ - byte_stream_put16(&header, 0x0000); /* Message Type: Request */ - byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */ - byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */ - - aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001); - aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); - - /* Add Plugin Specific Data */ - byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen)); - byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */ - byte_stream_putraw(&plugindata, (const guint8*)statxml, xmllen); - - aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data); - - aim_tlvlist_write(&header, &inner_tlvlist); - aim_tlvlist_free(inner_tlvlist); - - aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data); - aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */ - - aim_tlvlist_write(&bs, &outer_tlvlist); - - purple_debug_misc("oscar", "X-Status Request\n"); - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, TRUE); - - aim_tlvlist_free(outer_tlvlist); - byte_stream_destroy(&header); - byte_stream_destroy(&plugindata); - byte_stream_destroy(&bs); - g_free(statxml); - - return 0; -} - -int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - PurpleAccount *account; - PurpleStatus *status; - const char *fmt; - const char *formatted_msg; - char *msg; - char *statxml; - const char *title; - int len; - - static const guint8 plugindata[] = { - 0x1B, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F, - 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, - 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, - 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75, - 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, - 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00 - }; - - fmt = "<NR><RES><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))) - return -EINVAL; - - if (!sn) - return -EINVAL; - - account = purple_connection_get_account(od->gc); - if (!account) - return -EINVAL; - - /* if (purple_strequal(account->username, sn)) - icq_im_xstatus_request(od, sn); */ - - status = purple_presence_get_active_status(purple_account_get_presence(account)); - if (!status) - return -EINVAL; - - title = purple_status_get_name(status); - if (!title) - return -EINVAL; - - formatted_msg = purple_status_get_attr_string(status, "message"); - if (!formatted_msg) - return -EINVAL; - - msg = purple_markup_strip_html(formatted_msg); - if (!msg) - return -EINVAL; - - statxml = g_strdup_printf(fmt, purple_account_get_username(account), title, msg); - len = strlen(statxml); - - purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg); - - byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + len); /* 16 extra */ - - snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0); - aim_im_puticbm(&bs, cookie, 0x0002, sn); - byte_stream_put16(&bs, 0x0003); - byte_stream_putraw(&bs, plugindata, sizeof(plugindata)); - byte_stream_putraw(&bs, (const guint8*)statxml, len); - - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, snacid, &bs, TRUE); - - g_free(statxml); - g_free(msg); - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. - * - * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, - * and Purple 0.60 and newer. - * - */ -static int mtn_receive(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - char *bn; - guint8 bnlen; - guint16 channel, event; - - byte_stream_advance(bs, 8); /* ICBM cookie */ - channel = byte_stream_get16(bs); - bnlen = byte_stream_get8(bs); - bn = byte_stream_getstr(bs, bnlen); - if (!g_utf8_validate(bn, -1, NULL)) { - purple_debug_warning("oscar", "Received SNAC %04hx/%04hx with " - "invalid UTF-8 buddy name.\n", snac->family, snac->subtype); - g_free(bn); - return 1; - } - event = byte_stream_get16(bs); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, bn, event); - - g_free(bn); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0001) - return error(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0005) - return aim_im_paraminfo(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0007) - return incomingim(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000a) - return missedcall(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000b) - return clientautoresp(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000c) - return msgack(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0014) - return mtn_receive(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -msg_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_ICBM; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "messaging", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_icq.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,792 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0015 - Encapsulated ICQ. - * - */ - -#include "glibcompat.h" - -#include "encoding.h" -#include "oscar.h" - -#define AIM_ICQ_INFO_REQUEST 0x04b2 -#define AIM_ICQ_ALIAS_REQUEST 0x04ba - -static -int compare_icq_infos(gconstpointer a, gconstpointer b) -{ - const struct aim_icq_info* aa = a; - const guint16* bb = b; - return aa->reqid - *bb; -} - -static void aim_icq_freeinfo(struct aim_icq_info *info) { - int i; - - if (!info) - return; - g_free(info->nick); - g_free(info->first); - g_free(info->last); - g_free(info->email); - g_free(info->homecity); - g_free(info->homestate); - g_free(info->homephone); - g_free(info->homefax); - g_free(info->homeaddr); - g_free(info->mobile); - g_free(info->homezip); - g_free(info->personalwebpage); - if (info->email2) - for (i = 0; i < info->numaddresses; i++) - g_free(info->email2[i]); - g_free(info->email2); - g_free(info->workcity); - g_free(info->workstate); - g_free(info->workphone); - g_free(info->workfax); - g_free(info->workaddr); - g_free(info->workzip); - g_free(info->workcompany); - g_free(info->workdivision); - g_free(info->workposition); - g_free(info->workwebpage); - g_free(info->info); - g_free(info->status_note_title); - g_free(info->auth_request_reason); -} - -static -int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs) -{ - aim_snac_t *original_snac = aim_remsnac(od, error_snac->id); - guint16 *request_type; - GSList *original_info_ptr; - struct aim_icq_info *original_info; - guint16 reason; - gchar *uin; - - if (!original_snac || (original_snac->family != SNAC_FAMILY_ICQ) || !original_snac->data) { - purple_debug_misc("oscar", "icq: the original snac for the error packet was not found"); - g_free(original_snac); - return 0; - } - - request_type = original_snac->data; - original_info_ptr = g_slist_find_custom(od->icq_info, &original_snac->id, compare_icq_infos); - - if (!original_info_ptr) { - purple_debug_misc("oscar", "icq: the request info for the error packet was not found"); - g_free(original_snac); - return 0; - } - - original_info = original_info_ptr->data; - - reason = byte_stream_get16(bs); - uin = g_strdup_printf("%u", original_info->uin); - switch (*request_type) { - case AIM_ICQ_INFO_REQUEST: - oscar_user_info_display_error(od, reason, uin); - break; - case AIM_ICQ_ALIAS_REQUEST: - /* Couldn't retrieve an alias for the buddy requesting authorization; have to make do with UIN only. */ - if (original_info->for_auth_request) - oscar_auth_recvrequest(od->gc, uin, NULL, original_info->auth_request_reason); - break; - default: - purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type); - break; - } - - aim_icq_freeinfo(original_info); - od->icq_info = g_slist_remove(od->icq_info, original_info_ptr); - g_free(original_snac->data); - g_free(original_snac); - return 1; -} - -int -aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x0c3a); /* shrug. */ - byte_stream_putle16(&bs, 0x030c); - byte_stream_putle16(&bs, 0x0001); - byte_stream_putle8(&bs, webaware); - byte_stream_putle8(&bs, 0xf8); - byte_stream_putle8(&bs, 0x02); - byte_stream_putle8(&bs, 0x01); - byte_stream_putle8(&bs, 0x00); - byte_stream_putle8(&bs, !auth_required); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/** - * Change your ICQ password. - * - * @param od The oscar session - * @param passwd The new password. If this is longer than 8 characters it - * will be truncated. - * @return Return 0 if no errors, otherwise return the error number. - */ -int aim_icq_changepasswd(OscarData *od, const char *passwd) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen, passwdlen; - - if (!passwd) - return -EINVAL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - passwdlen = strlen(passwd); - if (passwdlen > MAXICQPASSLEN) - passwdlen = MAXICQPASSLEN; - bslen = 2+4+2+2+2+2+passwdlen+1; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x042e); /* shrug. */ - byte_stream_putle16(&bs, passwdlen+1); - byte_stream_putraw(&bs, (const guint8 *)passwd, passwdlen); - byte_stream_putle8(&bs, '\0'); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -int aim_icq_getallinfo(OscarData *od, const char *uin) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - struct aim_icq_info *info; - guint16 request_type = AIM_ICQ_INFO_REQUEST; - - if (!uin || uin[0] < '0' || uin[0] > '9') - return -EINVAL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - bslen = 2 + 4 + 2 + 2 + 2 + 4; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type)); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, request_type); /* shrug. */ - byte_stream_putle32(&bs, atoi(uin)); - - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE); - - byte_stream_destroy(&bs); - - /* Keep track of this request and the ICQ number and request ID */ - info = g_new0(struct aim_icq_info, 1); - info->reqid = snacid; - info->uin = atoi(uin); - od->icq_info = g_slist_prepend(od->icq_info, info); - - return 0; -} - -int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - struct aim_icq_info *info; - guint16 request_type = AIM_ICQ_ALIAS_REQUEST; - - if (!uin || uin[0] < '0' || uin[0] > '9') - return -EINVAL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - purple_debug_info("oscar", "Requesting ICQ alias for %s\n", uin); - - bslen = 2 + 4 + 2 + 2 + 2 + 4; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type)); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, request_type); /* shrug. */ - byte_stream_putle32(&bs, atoi(uin)); - - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs, FALSE); - - byte_stream_destroy(&bs); - - /* Keep track of this request and the ICQ number and request ID */ - info = g_new0(struct aim_icq_info, 1); - info->reqid = snacid; - info->uin = atoi(uin); - info->for_auth_request = for_auth_request; - info->auth_request_reason = g_strdup(auth_request_reason); - od->icq_info = g_slist_prepend(od->icq_info, info); - - return 0; -} - -/* - * Send an SMS message. This is the non-US way. The US-way is to IM - * their cell phone number (+19195551234). - * - * We basically construct and send an XML message. The format is: - * <icq_sms_message> - * <destination>full_phone_without_leading_+</destination> - * <text>message</text> - * <codepage>1252</codepage> - * <senders_UIN>self_uin</senders_UIN> - * <senders_name>self_name</senders_name> - * <delivery_receipt>Yes|No</delivery_receipt> - * <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time> - * </icq_sms_message> - * - * Yeah hi Peter, whaaaat's happening. If there's any way to use - * a codepage other than 1252 that would be great. Thaaaanks. - */ -int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias) -{ - FlapConnection *conn; - PurpleAccount *account; - ByteStream bs; - aim_snacid_t snacid; - int bslen, xmllen; - char *xml; - const char *timestr, *username; - time_t t; - struct tm *tm; - gchar *stripped; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - return -EINVAL; - - if (!name || !msg || !alias) - return -EINVAL; - - account = purple_connection_get_account(od->gc); - username = purple_account_get_username(account); - - time(&t); - tm = gmtime(&t); - timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm); - - stripped = purple_markup_strip_html(msg); - - /* The length of xml included the null terminating character */ - xmllen = 209 + strlen(name) + strlen(stripped) + strlen(username) + strlen(alias) + strlen(timestr) + 1; - - xml = g_new(char, xmllen); - snprintf(xml, xmllen, "<icq_sms_message>" - "<destination>%s</destination>" - "<text>%s</text>" - "<codepage>1252</codepage>" - "<senders_UIN>%s</senders_UIN>" - "<senders_name>%s</senders_name>" - "<delivery_receipt>Yes</delivery_receipt>" - "<time>%s</time>" - "</icq_sms_message>", - name, stripped, username, alias, timestr); - - bslen = 36 + xmllen; - - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - /* For simplicity, don't bother using a tlvlist */ - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - - /* From libicq200-0.3.2/src/SNAC-SRV.cpp */ - byte_stream_putle16(&bs, 0x1482); - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, 0x0016); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000000); - - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, xmllen); - byte_stream_putstr(&bs, xml); - byte_stream_put8(&bs, 0x00); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); - - g_free(xml); - g_free(stripped); - - return 0; -} - -static void -gotalias(OscarData *od, struct aim_icq_info *info) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleBuddy *b; - gchar *utf8 = oscar_utf8_try_convert(account, od, info->nick); - - if (info->for_auth_request) { - oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason); - } else { - if (utf8 && *utf8) { - gchar who[16]; - g_snprintf(who, sizeof(who), "%u", info->uin); - purple_serv_got_alias(gc, who, utf8); - if ((b = purple_blist_find_buddy(account, who))) { - purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8); - } - } - g_free(utf8); - } -} - -/** - * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet. - */ -static int -icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs) -{ - GSList *tlvlist; - aim_tlv_t *datatlv; - ByteStream qbs; - guint32 ouruin; - guint16 cmdlen, cmd, reqid; - - if (!(tlvlist = aim_tlvlist_read(bs)) || !(datatlv = aim_tlv_gettlv(tlvlist, 0x0001, 1))) { - aim_tlvlist_free(tlvlist); - purple_debug_misc("oscar", "corrupt ICQ response\n"); - return 0; - } - - byte_stream_init(&qbs, datatlv->value, datatlv->length); - - cmdlen = byte_stream_getle16(&qbs); - ouruin = byte_stream_getle32(&qbs); - cmd = byte_stream_getle16(&qbs); - reqid = byte_stream_getle16(&qbs); - - purple_debug_misc("oscar", "icq response: %d bytes, %u, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); - - if (cmd == 0x07da) { /* information */ - guint16 subtype; - GSList *info_ptr; - struct aim_icq_info *info; - - subtype = byte_stream_getle16(&qbs); - byte_stream_advance(&qbs, 1); /* 0x0a */ - - /* find other data from the same request */ - info_ptr = g_slist_find_custom(od->icq_info, &reqid, compare_icq_infos); - if (!info_ptr) { - struct aim_icq_info *new_info = g_new0(struct aim_icq_info, 1); - new_info->reqid = reqid; - info_ptr = od->icq_info = g_slist_prepend(od->icq_info, new_info); - } - - info = info_ptr->data; - switch (subtype) { - case 0x00a0: { /* hide ip status */ - /* nothing */ - } break; - - case 0x00aa: { /* password change status */ - /* nothing */ - } break; - - case 0x00c8: { /* general and "home" information */ - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homecity = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homestate = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homephone = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homefax = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homeaddr = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->mobile = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homezip = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->homecountry = byte_stream_getle16(&qbs); - /* 0x0a 00 02 00 */ - /* 1 byte timezone? */ - /* 1 byte hide email flag? */ - } break; - - case 0x00dc: { /* personal information */ - info->age = byte_stream_getle8(&qbs); - info->unknown = byte_stream_getle8(&qbs); - info->gender = byte_stream_getle8(&qbs); /* Not specified=0x00, Female=0x01, Male=0x02 */ - info->personalwebpage = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->birthyear = byte_stream_getle16(&qbs); - info->birthmonth = byte_stream_getle8(&qbs); - info->birthday = byte_stream_getle8(&qbs); - info->language1 = byte_stream_getle8(&qbs); - info->language2 = byte_stream_getle8(&qbs); - info->language3 = byte_stream_getle8(&qbs); - /* 0x00 00 01 00 00 01 00 00 00 00 00 */ - } break; - - case 0x00d2: { /* work information */ - info->workcity = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workstate = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workphone = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workfax = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workaddr = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workzip = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workcountry = byte_stream_getle16(&qbs); - info->workcompany = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workdivision = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->workposition = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - byte_stream_advance(&qbs, 2); /* 0x01 00 */ - info->workwebpage = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - } break; - - case 0x00e6: { /* additional personal information */ - info->info = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)-1); - } break; - - case 0x00eb: { /* email address(es) */ - int i; - info->numaddresses = byte_stream_getle16(&qbs); - info->email2 = g_new0(char *, info->numaddresses); - for (i = 0; i < info->numaddresses; i++) { - info->email2[i] = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - if (i+1 != info->numaddresses) - byte_stream_advance(&qbs, 1); /* 0x00 */ - } - } break; - - case 0x00f0: { /* personal interests */ - } break; - - case 0x00fa: { /* past background and current organizations */ - } break; - - case 0x0104: { /* alias info */ - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - byte_stream_advance(&qbs, byte_stream_getle16(&qbs)); /* email address? */ - /* Then 0x00 02 00 */ - } break; - - case 0x010e: { /* unknown */ - /* 0x00 00 */ - } break; - - case 0x019a: { /* simple info */ - byte_stream_advance(&qbs, 2); - info->uin = byte_stream_getle32(&qbs); - info->nick = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->first = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->last = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs)); - /* Then 0x00 02 00 00 00 00 00 */ - } break; - - /* status note title and send request for status note text */ - case 0x0fb4: { - GSList *tlvlist; - aim_tlv_t *tlv; - FlapConnection *conn; - char *uin = NULL; - char *status_note_title = NULL; - - conn = flap_connection_findbygroup(od, 0x0004); - if (conn == NULL) - { - purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n"); - break; - } - - byte_stream_advance(&qbs, 0x02); /* length */ - byte_stream_advance(&qbs, 0x2f); /* unknown stuff */ - - tlvlist = aim_tlvlist_read(&qbs); - - tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1); - if (tlv != NULL) - /* Get user number */ - uin = aim_tlv_getvalue_as_string(tlv); - - tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1); - if (tlv != NULL) - /* Get status note title */ - status_note_title = aim_tlv_getvalue_as_string(tlv); - - aim_tlvlist_free(tlvlist); - - if (uin == NULL || status_note_title == NULL) - { - purple_debug_misc("oscar", "icq/0x0fb4: uin or " - "status_note_title was not found\n"); - g_free(uin); - g_free(status_note_title); - break; - } - - if (status_note_title[0] == '\0') - { - PurpleAccount *account; - PurpleBuddy *buddy; - PurplePresence *presence; - PurpleStatus *status; - - account = purple_connection_get_account(od->gc); - buddy = purple_blist_find_buddy(account, uin); - presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_active_status(presence); - - purple_protocol_got_user_status(account, uin, - purple_status_get_id(status), - "message", NULL, NULL); - - g_free(status_note_title); - } - else - { - struct aim_icq_info *info; - ByteStream bs; - guint32 bslen; - aim_snacid_t snacid; - guchar cookie[8]; - - info = g_new0(struct aim_icq_info, 1); - - bslen = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4; - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0); - - aim_icbm_makecookie(cookie); - - byte_stream_putraw(&bs, cookie, 8); /* ICBM cookie */ - byte_stream_put16(&bs, 0x0002); /* message channel */ - byte_stream_put8(&bs, strlen(uin)); /* uin */ - byte_stream_putstr(&bs, uin); - - byte_stream_put16(&bs, 0x0005); /* rendez vous data */ - byte_stream_put16(&bs, 0x00b2); - byte_stream_put16(&bs, 0x0000); /* request */ - byte_stream_putraw(&bs, cookie, 8); /* ICBM cookie */ - byte_stream_put32(&bs, 0x09461349); /* ICQ server relaying */ - byte_stream_put16(&bs, 0x4c7f); - byte_stream_put16(&bs, 0x11d1); - byte_stream_put32(&bs, 0x82224445); - byte_stream_put32(&bs, 0x53540000); - - byte_stream_put16(&bs, 0x000a); /* unknown TLV */ - byte_stream_put16(&bs, 0x0002); - byte_stream_put16(&bs, 0x0001); - - byte_stream_put16(&bs, 0x000f); /* unknown TLV */ - byte_stream_put16(&bs, 0x0000); - - byte_stream_put16(&bs, 0x2711); /* extended data */ - byte_stream_put16(&bs, 0x008a); - byte_stream_putle16(&bs, 0x001b); /* length */ - byte_stream_putle16(&bs, 0x0009); /* version */ - byte_stream_putle32(&bs, 0x00000000); /* plugin: none */ - byte_stream_putle32(&bs, 0x00000000); - byte_stream_putle32(&bs, 0x00000000); - byte_stream_putle32(&bs, 0x00000000); - byte_stream_putle16(&bs, 0x0000); /* unknown */ - byte_stream_putle32(&bs, 0x00000000); /* client capabilities flags */ - byte_stream_put8(&bs, 0x00); /* unknown */ - byte_stream_putle16(&bs, 0x0064); /* downcounter? */ - byte_stream_putle16(&bs, 0x000e); /* length */ - byte_stream_putle16(&bs, 0x0064); /* downcounter? */ - byte_stream_putle32(&bs, 0x00000000); /* unknown */ - byte_stream_putle32(&bs, 0x00000000); - byte_stream_putle32(&bs, 0x00000000); - byte_stream_put8(&bs, 0x1a); /* message type: plugin message descibed by text string */ - byte_stream_put8(&bs, 0x00); /* message flags */ - byte_stream_putle16(&bs, 0x0000); /* status code */ - byte_stream_putle16(&bs, 0x0001); /* priority code */ - byte_stream_putle16(&bs, 0x0000); /* text length */ - - byte_stream_put8(&bs, 0x3a); /* message dump */ - byte_stream_put32(&bs, 0x00811a18); - byte_stream_put32(&bs, 0xbc0e6c18); - byte_stream_put32(&bs, 0x47a5916f); - byte_stream_put32(&bs, 0x18dcc76f); - byte_stream_put32(&bs, 0x1a010013); - byte_stream_put32(&bs, 0x00000041); - byte_stream_put32(&bs, 0x77617920); - byte_stream_put32(&bs, 0x53746174); - byte_stream_put32(&bs, 0x7573204d); - byte_stream_put32(&bs, 0x65737361); - byte_stream_put32(&bs, 0x67650100); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x00000015); - byte_stream_put32(&bs, 0x00000000); - byte_stream_put32(&bs, 0x0000000d); - byte_stream_put32(&bs, 0x00000074); - byte_stream_put32(&bs, 0x6578742f); - byte_stream_put32(&bs, 0x782d616f); - byte_stream_put32(&bs, 0x6c727466); - - byte_stream_put16(&bs, 0x0003); /* server ACK requested */ - byte_stream_put16(&bs, 0x0000); - - info->uin = atoi(uin); - info->status_note_title = status_note_title; - - memcpy(&info->icbm_cookie, cookie, 8); - - od->icq_info = g_slist_prepend(od->icq_info, info); - - flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, FALSE); - - byte_stream_destroy(&bs); - } - - g_free(uin); - - } break; - - } /* End switch statement */ - - if (!(snac->flags & 0x0001)) { - if (subtype != 0x0104) - oscar_user_info_display_icq(od, info); - - if (info->uin && info->nick) - gotalias(od, info); - - aim_icq_freeinfo(info); - od->icq_info = g_slist_remove(od->icq_info, info); - } - } - - aim_tlvlist_free(tlvlist); - - return 1; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0001) - return error(od, snac, bs); - else if (snac->subtype == 0x0003) - return icqresponse(od, snac, bs); - - return 0; -} - -static void -icq_shutdown(OscarData *od, aim_module_t *mod) -{ - g_slist_free_full(od->icq_info, (GDestroyNotify)aim_icq_freeinfo); -} - -int -icq_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_ICQ; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x047c; - mod->flags = 0; - strncpy(mod->name, "icq", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = icq_shutdown; - - return 0; -}
--- a/libpurple/protocols/oscar/family_locate.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1546 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0002 - Locate. - * - * The functions here are responsible for requesting and parsing information- - * gathering SNACs. Or something like that. This family contains the SNACs - * for getting and setting info, away messages, directory profile thingy, etc. - */ - -#include "oscar.h" - -/* Define to log unknown TLVs */ -/* #define LOG_UNKNOWN_TLV */ - -/* - * Capability blocks. - * - * These are CLSIDs. They should actually be of the form: - * - * {0x0946134b, 0x4c7f, 0x11d1, - * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}}, - * - * But, eh. - */ -static const struct { - guint64 flag; - guint8 data[16]; -} aim_caps[] = { - - /* - * These are in ascending numerical order. - */ - - /* Client understands short caps, a UUID of the form - * 0946XXYY-4C7F-11D1-8222-444553540000 where XXYY is the short cap. */ - {OSCAR_CAPABILITY_SHORTCAPS, - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_SECUREIM, - {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* OSCAR_CAPABILITY_XHTML_IM */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_VIDEO, - {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */ - {OSCAR_CAPABILITY_LIVEVIDEO, - {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* "Camera" support in Windows AIM 5.5.3501 and newer */ - {OSCAR_CAPABILITY_CAMERA, - {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* "Microphone" support in Windows AIM 5.5.3501 and newer */ - /* OSCAR_CAPABILITY_MICROPHONE */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* Supports RTC Audio */ - /* OSCAR_CAPABILITY_RTCAUDIO */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0x01, 0x04, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* In iChatAV (version numbers...?) */ - {OSCAR_CAPABILITY_ICHATAV, - {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}}, - - /* Supports "new status message features" (Who advertises this one?) */ - /* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0x01, 0x0a, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* Supports "see as I type" (Who advertises this one?) */ - /* OSCAR_CAPABILITY_SEE_AS_I_TYPE */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0x01, 0x0b, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* Client only asserts caps for services in which it is participating */ - /* OSCAR_CAPABILITY_SMARTCAPS */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_HIPTOP, - {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_TALK, - {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_SENDFILE, - {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_ICQ_DIRECT, - {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_DIRECTIM, - {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_BUDDYICON, - {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_ADDINS, - {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_GETFILE, - {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_ICQSERVERRELAY, - {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* - * Indeed, there are two of these. The former appears to be correct, - * but in some versions of winaim, the second one is set. Either they - * forgot to fix endianness, or they made a typo. It really doesn't - * matter which. - */ - {OSCAR_CAPABILITY_GAMES, - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - {OSCAR_CAPABILITY_GAMES2, - {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, - 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* New format of caps (xtraz icons) */ - {OSCAR_CAPABILITY_NEWCAPS, - {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* Support xtraz statuses */ - {OSCAR_CAPABILITY_XTRAZ, - {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5, - 0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}}, - - {OSCAR_CAPABILITY_SENDBUDDYLIST, - {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* - * Setting this lets AIM users receive messages from ICQ users, and ICQ - * users receive messages from AIM users. It also lets ICQ users show - * up in buddy lists for AIM users, and AIM users show up in buddy lists - * for ICQ users. And ICQ privacy/invisibility acts like AIM privacy, - * in that if you add a user to your deny list, you will not be able to - * see them as online (previous you could still see them, but they - * couldn't see you. - */ - {OSCAR_CAPABILITY_INTEROPERATE, - {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_UNICODE, - {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_ICHAT_SCREENSHARE, - {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_UNICODEOLD, - {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, - 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}}, - - {OSCAR_CAPABILITY_TYPING, - {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd, - 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}}, - - /* - * Chat is oddball. - */ - {OSCAR_CAPABILITY_CHAT, - {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, - 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - - /* This is added by the servers and it only shows up for ourselves... */ - {OSCAR_CAPABILITY_GENERICUNKNOWN, - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}}, - - {OSCAR_CAPABILITY_ICQRTF, - {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, - 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}}, - - {OSCAR_CAPABILITY_APINFO, - {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6, - 0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}}, - - {OSCAR_CAPABILITY_TRILLIANCRYPT, - {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, - 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_EMPTY, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - - {OSCAR_CAPABILITY_HTML_MSGS, - {0x01, 0x38, 0xca, 0x7b, 0x76, 0x9a, 0x49, 0x15, - 0x88, 0xf2, 0x13, 0xfc, 0x00, 0x97, 0x9e, 0xa8}}, - - {OSCAR_CAPABILITY_LAST, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, -}; - -/* Keep this array synchronized with icq_purple_moods. */ -static const struct { - const char *mood; - guint8 data[16]; -} icq_custom_icons[] = { - - {"thinking", - {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60, - 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}}, - - {"busy", - {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08, - 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}}, - - {"shopping", - {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff, - 0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}}, - - /* This was in the original patch, but isn't what the official client - * (ICQ 6) sets when you choose its typewriter icon. */ - {"typing", - {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1, - 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}}, - - {"question", - {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0, - 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}}, - - {"angry", - {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a, - 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}}, - - {"plate", - {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42, - 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}}, - - {"cinema", - {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4, - 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}}, - - {"sick", - {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60, - 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}}, - - {"typing", - {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70, - 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}}, - - {"suit", - {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27, - 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}}, - - {"bathing", - {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c, - 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}}, - - {"tv", - {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76, - 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}}, - - {"excited", - {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff, - 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}}, - - {"sleeping", - {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65, - 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}}, - - {"hiptop", - {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9, - 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}}, - - {"in_love", - {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48, - 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}}, - - {"sleepy", - {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78, - 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}}, - - {"meeting", - {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d, - 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}}, - - {"phone", - {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66, - 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}}, - - {"surfing", - {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4, - 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}}, - - {"mobile", - {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3, - 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}}, - - {"search", - {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5, - 0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}}, - - {"party", - {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1, - 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}}, - - {"coffee", - {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38, - 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}}, - - {"console", - {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0, - 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}}, - - {"internet", - {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e, - 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}}, - - {"cigarette", - {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17, - 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}}, - - {"writing", - {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd, - 0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}}, - - {"beer", - {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86, - 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}}, - - {"music", - {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d, - 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}}, - - {"studying", - {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6, - 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}}, - - {"working", - {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b, - 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}}, - - {"restroom", - {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35, - 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}}, - - {NULL, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} -}; - -/* Keep this array synchronized with icq_custom_icons. */ -static PurpleMood icq_purple_moods[] = { - {"thinking", N_("Thinking"), NULL}, - {"busy", N_("Busy"), NULL}, - {"shopping", N_("Shopping"), NULL}, - /* This was in the original patch, but isn't what the official client - * (ICQ 6) sets when you choose its typewriter icon. */ - {"typing", NULL, NULL}, - {"question", N_("Questioning"), NULL}, - {"angry", N_("Angry"), NULL}, - {"plate", N_("Eating"), NULL}, - {"cinema", N_("Watching a movie"), NULL}, - {"sick", N_("Sick"), NULL}, - {"typing", N_("Typing"), NULL}, - {"suit", N_("At the office"), NULL}, - {"bathing", N_("Taking a bath"), NULL}, - {"tv", N_("Watching TV"), NULL}, - {"excited", N_("Having fun"), NULL}, - {"sleeping", N_("Sleeping"), NULL}, - {"hiptop", N_("Using a PDA"), NULL}, - {"in_love", N_("In love"), NULL}, - /* Sleepy / Tired */ - {"sleepy", N_("Sleepy"), NULL}, - {"meeting", N_("Meeting friends"), NULL}, - {"phone", N_("On the phone"), NULL}, - {"surfing", N_("Surfing"), NULL}, - /* "I am mobile." / "John is mobile." */ - {"mobile", N_("Mobile"), NULL}, - {"search", N_("Searching the web"), NULL}, - {"party", N_("At a party"), NULL}, - {"coffee", N_("Having Coffee"), NULL}, - /* Playing video games */ - {"console", N_("Gaming"), NULL}, - {"internet", N_("Browsing the web"), NULL}, - {"cigarette", N_("Smoking"), NULL}, - {"writing", N_("Writing"), NULL}, - /* Drinking [Alcohol] */ - {"beer", N_("Drinking"), NULL}, - {"music", N_("Listening to music"), NULL}, - {"studying", N_("Studying"), NULL}, - {"working", N_("Working"), NULL}, - {"restroom", N_("In the restroom"), NULL}, - /* Mark the last record. */ - {NULL, NULL, NULL}, -}; - - -/* - * Add the userinfo to our linked list. If we already have userinfo - * for this buddy, then just overwrite parts of the old data. - * - * @param userinfo Contains the new information for the buddy. - */ -static void -aim_locate_adduserinfo(OscarData *od, aim_userinfo_t *userinfo) -{ - aim_userinfo_t *cur; - - cur = aim_locate_finduserinfo(od, userinfo->bn); - - if (cur == NULL) { - cur = (aim_userinfo_t *)g_new0(aim_userinfo_t, 1); - cur->bn = g_strdup(userinfo->bn); - cur->next = od->locate.userinfo; - od->locate.userinfo = cur; - } - - cur->warnlevel = userinfo->warnlevel; - cur->idletime = userinfo->idletime; - if (userinfo->flags != 0) - cur->flags = userinfo->flags; - if (userinfo->createtime != 0) - cur->createtime = userinfo->createtime; - if (userinfo->membersince != 0) - cur->membersince = userinfo->membersince; - if (userinfo->onlinesince != 0) - cur->onlinesince = userinfo->onlinesince; - if (userinfo->sessionlen != 0) - cur->sessionlen = userinfo->sessionlen; - if (userinfo->capabilities != 0) - cur->capabilities = userinfo->capabilities; - - cur->present |= userinfo->present; - - if (userinfo->iconcsumlen > 0) { - g_free(cur->iconcsum); - cur->iconcsum = (guint8 *)g_malloc(userinfo->iconcsumlen); - memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen); - cur->iconcsumlen = userinfo->iconcsumlen; - } - - if (userinfo->info != NULL) { - g_free(cur->info); - g_free(cur->info_encoding); - if (userinfo->info_len > 0) { - cur->info = (char *)g_malloc(userinfo->info_len); - memcpy(cur->info, userinfo->info, userinfo->info_len); - } else - cur->info = NULL; - cur->info_encoding = g_strdup(userinfo->info_encoding); - cur->info_len = userinfo->info_len; - } - - if (userinfo->status != NULL) { - g_free(cur->status); - g_free(cur->status_encoding); - if (userinfo->status_len > 0) { - cur->status = (char *)g_malloc(userinfo->status_len); - memcpy(cur->status, userinfo->status, userinfo->status_len); - } else - cur->status = NULL; - if (userinfo->status_encoding != NULL) - cur->status_encoding = g_strdup(userinfo->status_encoding); - else - cur->status_encoding = NULL; - cur->status_len = userinfo->status_len; - } - - if (userinfo->itmsurl != NULL) { - g_free(cur->itmsurl); - g_free(cur->itmsurl_encoding); - if (userinfo->itmsurl_len > 0) { - cur->itmsurl = (char *)g_malloc(userinfo->itmsurl_len); - memcpy(cur->itmsurl, userinfo->itmsurl, userinfo->itmsurl_len); - } else - cur->itmsurl = NULL; - if (userinfo->itmsurl_encoding != NULL) - cur->itmsurl_encoding = g_strdup(userinfo->itmsurl_encoding); - else - cur->itmsurl_encoding = NULL; - cur->itmsurl_len = userinfo->itmsurl_len; - } - - if (userinfo->away != NULL) { - g_free(cur->away); - g_free(cur->away_encoding); - if (userinfo->away_len > 0) { - cur->away = (char *)g_malloc(userinfo->away_len); - memcpy(cur->away, userinfo->away, userinfo->away_len); - } else - cur->away = NULL; - cur->away_encoding = g_strdup(userinfo->away_encoding); - cur->away_len = userinfo->away_len; - - } else { - /* - * We don't have an away message specified in this user_info - * block, so clear any cached away message now. - */ - g_free(cur->away); - cur->away = NULL; - g_free(cur->away_encoding); - cur->away_encoding = NULL; - cur->away_len = 0; - } -} - -aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn) { - aim_userinfo_t *cur = NULL; - - if (bn == NULL) - return NULL; - - cur = od->locate.userinfo; - - while (cur != NULL) { - if (oscar_util_name_compare(cur->bn, bn) == 0) - return cur; - cur = cur->next; - } - - return NULL; -} - -guint64 -aim_locate_getcaps(OscarData *od, ByteStream *bs, int len) -{ - guint64 flags = 0; - int offset; - - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) { - guint8 *cap; - int i, identified; - - cap = byte_stream_getraw(bs, 0x10); - - for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) { - if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { - flags |= aim_caps[i].flag; - identified++; - break; /* should only match once... */ - } - } - - if (!identified) - purple_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", - cap[0], cap[1], cap[2], cap[3], - cap[4], cap[5], - cap[6], cap[7], - cap[8], cap[9], - cap[10], cap[11], cap[12], cap[13], - cap[14], cap[15]); - g_free(cap); - } - - return flags; -} - -static const char * -aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len) -{ - int offset; - const char *result = NULL; - - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) { - /* check wheather this capability is a custom user icon */ - guint8 *cap; - int i; - - cap = byte_stream_getraw(bs, 0x10); - - for (i = 0; icq_custom_icons[i].mood; i++) { - if (memcmp(&icq_custom_icons[i].data, cap, 0x10) == 0) { - purple_debug_misc("oscar", "Custom status icon: %s\n", icq_purple_moods[i].description); - result = icq_custom_icons[i].mood; - break; /* should only match once... */ - } - } - g_free(cap); - } - - return result; -} - -guint64 -aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len) -{ - guint64 flags = 0; - int offset; - - for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x02) { - guint8 *cap; - int i, identified; - - cap = byte_stream_getraw(bs, 0x02); - - for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) { - if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) { - flags |= aim_caps[i].flag; - identified++; - break; /* should only match once... */ - } - } - - if (!identified) - purple_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]); - - g_free(cap); - } - - return flags; -} - -int -byte_stream_putcaps(ByteStream *bs, guint64 caps) -{ - int i; - - if (!bs) - return -EINVAL; - - for (i = 0; byte_stream_bytes_left(bs); i++) { - if (aim_caps[i].flag == OSCAR_CAPABILITY_LAST) - break; - - if (caps & aim_caps[i].flag) - byte_stream_putraw(bs, aim_caps[i].data, 0x10); - } - return 0; -} - -#ifdef LOG_UNKNOWN_TLV -static void -dumptlv(OscarData *od, guint16 type, ByteStream *bs, guint8 len) -{ - int i; - - if (!od || !bs || !len) - return; - - purple_debug_misc("oscar", "userinfo: type =0x%04x\n", type); - purple_debug_misc("oscar", "userinfo: length=0x%04x\n", len); - purple_debug_misc("oscar", "userinfo: value:\n"); - - for (i = 0; i < len; i++) { - if ((i % 8) == 0) - purple_debug_misc("oscar", "\nuserinfo: "); - purple_debug_misc("oscar", "0x%2x ", byte_stream_get8(bs)); - } - - purple_debug_misc("oscar", "\n"); - - return; -} -#endif - -void -aim_info_free(aim_userinfo_t *info) -{ - g_free(info->bn); - g_free(info->iconcsum); - g_free(info->info); - g_free(info->info_encoding); - g_free(info->status); - g_free(info->status_encoding); - g_free(info->itmsurl); - g_free(info->itmsurl_encoding); - g_free(info->away); - g_free(info->away_encoding); -} - -static const struct { - char *icqmood; - const char *mood; -} icqmoods[] = { - {"icqmood0", "shopping"}, - {"icqmood1", "bathing"}, - {"icqmood2", "sleepy"}, - {"icqmood3", "party"}, - {"icqmood4", "beer"}, - {"icqmood5", "thinking"}, - {"icqmood6", "plate"}, - {"icqmood7", "tv"}, - {"icqmood8", "meeting"}, - {"icqmood9", "coffee"}, - {"icqmood10", "music"}, - {"icqmood11", "suit"}, - {"icqmood12", "cinema"}, - {"icqmood13", "smile-big"}, - {"icqmood14", "phone"}, - {"icqmood15", "console"}, - {"icqmood16", "studying"}, - {"icqmood17", "sick"}, - {"icqmood18", "sleeping"}, - {"icqmood19", "surfing"}, - {"icqmood20", "internet"}, - {"icqmood21", "working"}, - {"icqmood22", "typing"}, - {"icqmood23", "angry"}, - {NULL, 0} - -}; - -/* - * AIM is fairly regular about providing user info. This is a generic - * routine to extract it in its standard form. - */ -int -aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo) -{ - int curtlv, tlvcnt; - guint8 bnlen; - - if (!bs || !outinfo) - return -EINVAL; - - /* Clear out old data first */ - memset(outinfo, 0x00, sizeof(aim_userinfo_t)); - - /* - * Username. Stored as an unterminated string prepended with a - * byte containing its length. - */ - bnlen = byte_stream_get8(bs); - outinfo->bn = byte_stream_getstr(bs, bnlen); - - /* - * Warning Level. Stored as an unsigned short. - */ - outinfo->warnlevel = byte_stream_get16(bs); - - /* - * TLV Count. Unsigned short representing the number of - * Type-Length-Value triples that follow. - */ - tlvcnt = byte_stream_get16(bs); - - /* - * Parse out the Type-Length-Value triples as they're found. - */ - for (curtlv = 0; curtlv < tlvcnt; curtlv++) { - guint16 type, length; - int endpos; - int curpos; - - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - curpos = byte_stream_curpos(bs); - endpos = curpos + MIN(length, byte_stream_bytes_left(bs)); - - if (type == 0x0001) { - /* - * User flags - * - * Specified as any of the following ORed together: - * 0x0001 Unconfirmed account - * 0x0002 Unknown bit 2 - * 0x0004 AOL Main Service user - * 0x0008 Unknown bit 4 - * 0x0010 Free (AIM) user - * 0x0020 Away - * 0x0040 ICQ user (AIM bit also set) - * 0x0080 Mobile device - * 0x0400 Bot (like ActiveBuddy) - */ - outinfo->flags = byte_stream_get16(bs); - outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; - - } else if (type == 0x0002) { - /* - * Account creation time - * - * The time/date that the user originally registered for - * the service, stored in time_t format. - * - * I'm not sure how this differs from type 5 ("member - * since"). - * - * Note: This is the field formerly known as "member - * since". All these years and I finally found out - * that I got the name wrong. - */ - outinfo->createtime = byte_stream_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME; - - } else if (type == 0x0003) { - /* - * On-Since date - * - * The time/date that the user started their current - * session, stored in time_t format. - */ - outinfo->onlinesince = byte_stream_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; - - } else if (type == 0x0004) { - /* - * Idle time - * - * Number of minutes since the user actively used the - * service. - * - * Note that the client tells the server when to start - * counting idle times, so this may or may not be - * related to reality. - */ - outinfo->idletime = byte_stream_get16(bs); - outinfo->present |= AIM_USERINFO_PRESENT_IDLE; - - } else if (type == 0x0005) { - /* - * Member since date - * - * The time/date that the user originally registered for - * the service, stored in time_t format. - * - * This is sometimes sent instead of type 2 ("account - * creation time"), particularly in the self-info. - * And particularly for ICQ? - */ - outinfo->membersince = byte_stream_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; - - } else if (type == 0x0006) { - /* - * ICQ Online Status - * - * ICQ's Away/DND/etc "enriched" status. Some decoding - * of values done by Scott <darkagl@pcnet.com> - */ - byte_stream_get16(bs); - outinfo->icqinfo.status = byte_stream_get16(bs); - outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; - - } else if (type == 0x0008) { - /* - * Client type, or some such. - */ - - } else if (type == 0x000a) { - /* - * ICQ User IP Address - * - * Ahh, the joy of ICQ security. - */ - outinfo->icqinfo.ipaddr = byte_stream_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; - - } else if (type == 0x000c) { - /* - * Random crap containing the IP address, - * apparently a port number, and some Other Stuff. - * - * Format is: - * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43 - */ - byte_stream_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); - outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; - - } else if (type == 0x000d) { - PurpleAccount *account = purple_connection_get_account(od->gc); - const char *mood; - - /* - * OSCAR Capability information - */ - outinfo->capabilities |= aim_locate_getcaps(od, bs, length); - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; - byte_stream_setpos(bs, curpos); - - mood = aim_receive_custom_icon(od, bs, length); - if (mood) - purple_protocol_got_user_status(account, outinfo->bn, "mood", - PURPLE_MOOD_NAME, mood, - NULL); - else - purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood"); - - } else if (type == 0x000e) { - /* - * AOL capability information - */ - - } else if ((type == 0x000f) || (type == 0x0010)) { - /* - * Type = 0x000f: Session Length. (AIM) - * Type = 0x0010: Session Length. (AOL) - * - * The duration, in seconds, of the user's current - * session. - * - * Which TLV type this comes in depends on the - * service the user is using (AIM or AOL). - */ - outinfo->sessionlen = byte_stream_get32(bs); - outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; - - } else if (type == 0x0014) { - /* - * My instance number. - */ - byte_stream_get8(bs); - - } else if (type == 0x0019) { - /* - * OSCAR short capability information. A shortened - * form of the normal capabilities. - */ - outinfo->capabilities |= aim_locate_getcaps_short(od, bs, length); - outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; - - } else if (type == 0x001a) { - /* - * Type = 0x001a - * - * AOL short capability information. A shortened - * form of the normal capabilities. - */ - - } else if (type == 0x001b) { - /* - * Encryption certification MD5 checksum. - */ - - } else if (type == 0x001d) { - /* - * Buddy icon information and status/available messages. - * - * This almost seems like the AIM protocol guys gave - * the iChat guys a Type, and the iChat guys tried to - * cram as much cool shit into it as possible. Then - * the Windows AIM guys were like, "hey, that's - * pretty neat, let's copy those prawns." - * - * In that spirit, this can contain a custom message, - * kind of like an away message, but you're not away - * (it's called an "available" message). Or it can - * contain information about the buddy icon the user - * has stored on the server. - */ - guint16 type2; - guint8 number2, length2; - int endpos2; - - /* - * Continue looping as long as we're able to read type2, - * number2, and length2. - */ - while (byte_stream_curpos(bs) + 4 <= endpos) { - type2 = byte_stream_get16(bs); - number2 = byte_stream_get8(bs); - length2 = byte_stream_get8(bs); - - endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_bytes_left(bs)); - - switch (type2) { - case 0x0000: { /* This is an official buddy icon? */ - /* This is always 5 bytes of "0x02 01 d2 04 72"? */ - } break; - - case 0x0001: { /* A buddy icon checksum */ - if ((length2 > 0) && ((number2 == 0x00) || (number2 == 0x01))) { - g_free(outinfo->iconcsum); - outinfo->iconcsumtype = number2; - outinfo->iconcsum = byte_stream_getraw(bs, length2); - outinfo->iconcsumlen = length2; - } - } break; - - case 0x0002: { /* A status/available message */ - g_free(outinfo->status); - g_free(outinfo->status_encoding); - if (length2 >= 4) { - outinfo->status_len = byte_stream_get16(bs); - outinfo->status = byte_stream_getstr(bs, outinfo->status_len); - if (byte_stream_get16(bs) == 0x0001) { /* We have an encoding */ - byte_stream_get16(bs); - outinfo->status_encoding = byte_stream_getstr(bs, byte_stream_get16(bs)); - } else { - /* No explicit encoding, client should use UTF-8 */ - outinfo->status_encoding = NULL; - } - } else { - byte_stream_advance(bs, length2); - outinfo->status_len = 0; - outinfo->status = g_strdup(""); - outinfo->status_encoding = NULL; - } - } break; - - case 0x0009: { /* An iTunes Music Store link */ - g_free(outinfo->itmsurl); - g_free(outinfo->itmsurl_encoding); - if (length2 >= 4) { - outinfo->itmsurl_len = byte_stream_get16(bs); - outinfo->itmsurl = byte_stream_getstr(bs, outinfo->itmsurl_len); - if (byte_stream_get16(bs) == 0x0001) { - /* We have an encoding */ - byte_stream_get16(bs); - outinfo->itmsurl_encoding = byte_stream_getstr(bs, byte_stream_get16(bs)); - } else { - /* No explicit encoding, client should use UTF-8 */ - outinfo->itmsurl_encoding = NULL; - } - } else { - byte_stream_advance(bs, length2); - outinfo->itmsurl_len = 0; - outinfo->itmsurl = g_strdup(""); - outinfo->itmsurl_encoding = NULL; - } - } break; - - case 0x000e: { /* ICQ mood */ - PurpleAccount *account = purple_connection_get_account(od->gc); - char *icqmood; - gint32 i; - const char *mood = NULL; - - icqmood = byte_stream_getstr(bs, length2); - - /* icqmood = "" means X-Status - * with no mood icon. */ - if (*icqmood) { - for (i = 0; icqmoods[i].icqmood; i++) { - if (purple_strequal(icqmood, icqmoods[i].icqmood)) { - mood = icqmoods[i].mood; - break; /* should only match once... */ - } - } - - if (!mood) - purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood); - } - g_free(icqmood); - - if (mood) - purple_protocol_got_user_status(account, outinfo->bn, "mood", - PURPLE_MOOD_NAME, mood, - NULL); - else - purple_protocol_got_user_status_deactive(account, outinfo->bn, "mood"); - } break; - } - - /* Save ourselves. */ - byte_stream_setpos(bs, endpos2); - } - - } else if (type == 0x001e) { - /* - * Always four bytes, but it doesn't look like an int. - */ - - } else if (type == 0x001f) { - /* - * Upper bytes of user flags. Can be any size - * - * Seen on a buddy using DeadAIM. Data was 4 bytes: - * 0x00 00 00 10 - */ - - } else if (type == 0x0023) { - /* - * Last Buddy Feed update time, in seconds since the epoch. - */ - - } else if (type == 0x0026) { - /* - * Time that the profile was set, in seconds since the epoch. - */ - - } else if (type == 0x0027) { - /* - * Time that the away message was set, in seconds since the epoch. - */ - - } else if (type == 0x002a) { - /* - * Country code based on GeoIP data. - */ - - } else { - - /* - * Reaching here indicates that either AOL has - * added yet another TLV for us to deal with, - * or the parsing has gone Terribly Wrong. - * - * Either way, inform the owner and attempt - * recovery. - * - */ -#ifdef LOG_UNKNOWN_TLV - purple_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n"); - purple_debug_misc("oscar", "userinfo: bn =%s\n", outinfo->bn); - dumptlv(od, type, bs, length); -#endif - } - - /* Save ourselves. */ - byte_stream_setpos(bs, endpos); - } - - aim_locate_adduserinfo(od, outinfo); - - return 0; -} - -/* - * Subtype 0x0001 - */ -static int -error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_snac_t *snac2; - guint16 reason; - char *bn; - - snac2 = aim_remsnac(od, snac->id); - if (!snac2) { - purple_debug_misc("oscar", "locate error: received response from unknown request!\n"); - return 0; - } - - if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) { - purple_debug_misc("oscar", "locate error: received response from invalid request! %d\n", snac2->family); - g_free(snac2->data); - g_free(snac2); - return 0; - } - - bn = snac2->data; - if (!bn) { - purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n"); - g_free(snac2); - return 0; - } - - reason = byte_stream_get16(bs); - - oscar_user_info_display_error(od, reason, bn); - - g_free(snac2->data); - g_free(snac2); - - return 1; -} - -/* - * Subtype 0x0002 - * - * Request Location services rights. - * - */ -int -aim_locate_reqrights(OscarData *od) -{ - FlapConnection *conn; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) - return -EINVAL; - - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_REQRIGHTS); - - return 0; -} - -/* - * Subtype 0x0003 - * - * Normally contains: - * t(0001) - short containing max profile length (value = 1024) - * t(0002) - short - unknown (value = 16) [max MIME type length?] - * t(0003) - short - unknown (value = 10) - * t(0004) - short - unknown (value = 2048) [ICQ only?] - */ -static int -rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - GSList *tlvlist; - aim_rxcallback_t userfunc; - int ret = 0; - guint16 maxsiglen = 0; - - tlvlist = aim_tlvlist_read(bs); - - if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) - maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, maxsiglen); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -/* - * Subtype 0x0004 - * - * Gives BOS your profile. - * - * profile_encoding and awaymsg_encoding MUST be set if profile or - * away are set, respectively, and their value may or may not be - * restricted to a few choices. I am currently aware of: - * - * us-ascii Just that - * unicode-2-0 UTF-16BE - * - * profile_len and awaymsg_len MUST be set similarly, and they MUST - * be the length of their respective strings in bytes. - * - * To get the previous behavior of awaymsg == "" un-setting the away - * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the - * obvious equivalent). - * - */ -int -aim_locate_setprofile(OscarData *od, - const char *profile_encoding, const gchar *profile, const int profile_len, - const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - char *encoding; - static const char defencoding[] = {"text/aolrtf; charset=\"%s\""}; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) - return -EINVAL; - - if (!profile && !awaymsg) - return -EINVAL; - - if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) { - return -EINVAL; - } - - /* Build the packet first to get real length */ - if (profile) { - encoding = g_strdup_printf(defencoding, profile_encoding); - aim_tlvlist_add_str(&tlvlist, 0x0001, encoding); - aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile); - g_free(encoding); - } - - /* - * So here's how this works: - * - You are away when you have a non-zero-length type 4 TLV stored. - * - You become unaway when you clear the TLV with a zero-length - * type 4 TLV. - * - If you do not send the type 4 TLV, your status does not change - * (that is, if you were away, you'll remain away). - */ - if (awaymsg) { - if (awaymsg_len) { - encoding = g_strdup_printf(defencoding, awaymsg_encoding); - aim_tlvlist_add_str(&tlvlist, 0x0003, encoding); - aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg); - g_free(encoding); - } else - aim_tlvlist_add_noval(&tlvlist, 0x0004); - } - - byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); - - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0004 - Set your client's capabilities. - */ -int -aim_locate_setcaps(OscarData *od, guint64 caps) -{ - FlapConnection *conn; - PurpleAccount *account = purple_connection_get_account(od->gc); - PurplePresence *presence = purple_account_get_presence(account); - PurpleStatus *status = purple_presence_get_status(presence, "mood"); - const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - if (!(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) - return -EINVAL; - - aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood); - - byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); - - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* Subtype 0x0006 */ -static int -userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_userinfo_t *userinfo, *userinfo2; - GSList *tlvlist; - aim_tlv_t *tlv = NULL; - - userinfo = g_new0(aim_userinfo_t, 1); - aim_info_extract(od, bs, userinfo); - tlvlist = aim_tlvlist_read(bs); - - /* Profile will be 1 and 2 */ - userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1); - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) { - userinfo->info = (char *)g_malloc(tlv->length); - memcpy(userinfo->info, tlv->value, tlv->length); - userinfo->info_len = tlv->length; - } - - /* Away message will be 3 and 4 */ - userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1); - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) { - userinfo->away = (char *)g_malloc(tlv->length); - memcpy(userinfo->away, tlv->value, tlv->length); - userinfo->away_len = tlv->length; - } - - /* Caps will be 5 */ - if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { - ByteStream cbs; - PurpleAccount *account = purple_connection_get_account(od->gc); - const char *mood; - - byte_stream_init(&cbs, tlv->value, tlv->length); - userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); - byte_stream_rewind(&cbs); - userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; - - mood = aim_receive_custom_icon(od, &cbs, tlv->length); - if (mood) - purple_protocol_got_user_status(account, userinfo->bn, "mood", - PURPLE_MOOD_NAME, mood, - NULL); - else - purple_protocol_got_user_status_deactive(account, userinfo->bn, "mood"); - } - aim_tlvlist_free(tlvlist); - - aim_locate_adduserinfo(od, userinfo); - userinfo2 = aim_locate_finduserinfo(od, userinfo->bn); - aim_info_free(userinfo); - g_free(userinfo); - - /* Show the info to the user */ - oscar_user_info_display_aim(od, userinfo2); - - return ret; -} - -/* - * Subtype 0x0015 - Request the info of a user using the short method. This is - * what iChat uses. It normally is VERY leniently rate limited. - * - * @param bn The buddy name whose info you wish to request. - * @param flags The bitmask which specifies the type of info you wish to request. - * 0x00000001 - Info/profile. - * 0x00000002 - Away message. - * 0x00000004 - Capabilities. - * 0x00000008 - Certification. - * @return Return 0 if no errors, otherwise return the error number. - */ -int -aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn) - return -EINVAL; - - byte_stream_new(&bs, 4 + 1 + strlen(bn)); - byte_stream_put32(&bs, flags); - byte_stream_put8(&bs, strlen(bn)); - byte_stream_putstr(&bs, bn); - - snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1); - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, snacid, &bs, FALSE); - - byte_stream_destroy(&bs); - - return 0; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0001) - return error(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0003) - return rights(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0006) - return userinfo(od, conn, mod, frame, snac, bs); - - return 0; -} - -static void -locate_shutdown(OscarData *od, aim_module_t *mod) -{ - aim_userinfo_t *del; - - while (od->locate.userinfo) { - del = od->locate.userinfo; - od->locate.userinfo = od->locate.userinfo->next; - aim_info_free(del); - g_free(del); - } -} - -int -locate_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_LOCATE; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "locate", sizeof(mod->name)); - mod->snachandler = snachandler; - mod->shutdown = locate_shutdown; - - return 0; -} - -const char* -icq_get_custom_icon_description(const char *mood) -{ - int i; - - if (!(mood && *mood)) - return NULL; - - for (i = 0; icq_custom_icons[i].mood; i++) { - /* We check that description is not NULL to exclude - * duplicates, like the typing duplicate. */ - if (icq_purple_moods[i].description && - purple_strequal(mood, icq_custom_icons[i].mood)) { - return icq_purple_moods[i].description; - } - } - - return NULL; -} - -guint8* -icq_get_custom_icon_data(const char *mood) -{ - int i; - - if (!(mood && *mood)) - return NULL; - - for (i = 0; icq_custom_icons[i].mood; i++) { - /* We check that description is not NULL to exclude - * duplicates, like the typing duplicate. */ - if (icq_purple_moods[i].description && - purple_strequal(mood, icq_custom_icons[i].mood)) { - return (guint8 *)icq_custom_icons[i].data; - } - } - return NULL; -} - -PurpleMood* -icq_get_purple_moods(PurpleAccount *account) -{ - return icq_purple_moods; -}
--- a/libpurple/protocols/oscar/family_oservice.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,994 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0001 - This is a very special group. All connections support - * this group, as it does some particularly good things (like rate limiting). - */ - -#include "oscar.h" - -/* - * Each time we make a FLAP connection to an oscar server the server gives - * us a list of rate classes. Each rate class has different properties for - * how frequently we can send SNACs in that rate class before we become - * throttled or disconnected. - * - * The server also gives us a list of every available SNAC and tells us which - * rate class it's in. There are a lot of different SNACs, so this list can be - * fairly large. One important characteristic of these rate classes is that - * currently (and since at least 2004) most SNACs are in the same rate class. - * - * One optimization we can do to save memory is to only keep track of SNACs - * that are in classes other than this default rate class. So if we try to - * look up a SNAC and it's not in our hash table then we can assume that it's - * in the default rate class. - */ -#define OSCAR_DEFAULT_RATECLASS 1 - -/* Subtype 0x0002 - Client Online */ -void -aim_srv_clientready(OscarData *od, FlapConnection *conn) -{ - ByteStream bs; - aim_snacid_t snacid; - GSList *cur; - - byte_stream_new(&bs, 1142); - - /* - * Send only the tool versions that the server cares about (that it - * marked as supporting in the server ready SNAC). - */ - for (cur = conn->groups; cur != NULL; cur = cur->next) - { - aim_module_t *mod; - - if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data)))) - { - byte_stream_put16(&bs, mod->family); - byte_stream_put16(&bs, mod->version); - byte_stream_put16(&bs, mod->toolid); - byte_stream_put16(&bs, mod->toolversion); - } - } - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/* - * Subtype 0x0003 - Host Online - * - * See comments in conn.c about how the group associations are supposed - * to work, and how they really work. - * - * This info probably doesn't even need to make it to the client. - * - * We don't actually call the client here. This starts off the connection - * initialization routine required by all AIM connections. The next time - * the client is called is the CONNINITDONE callback, which should be - * shortly after the rate information is acknowledged. - * - */ -static int -hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int group; - - while (byte_stream_bytes_left(bs)) - { - group = byte_stream_get16(bs); - conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group)); - } - - /* - * Next step is in the Host Versions handler. - * - * Note that we must send this before we request rates, since - * the format of the rate information depends on the versions we - * give it. - * - */ - aim_srv_setversions(od, conn); - - return 1; -} - -/* Subtype 0x0004 - Service request */ -void -aim_srv_requestnew(OscarData *od, guint16 serviceid) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS); - if(!conn) - return; - - byte_stream_new(&bs, 6); - - byte_stream_put16(&bs, serviceid); - - if (od->use_ssl) - /* Request SSL Connection */ - aim_tlvlist_add_noval(&tlvlist, 0x008c); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/* - * Join a room of name roomname. This is the first step to joining an - * already created room. It's basically a Service Request for - * family 0x000e, with a little added on to specify the exchange and room - * name. - */ -int -aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - struct chatsnacinfo csi; - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS); - if (!conn || !roomname || roomname[0] == '\0') - return -EINVAL; - - byte_stream_new(&bs, 506); - - memset(&csi, 0, sizeof(csi)); - csi.exchange = exchange; - g_strlcpy(csi.name, roomname, sizeof(csi.name)); - csi.instance = instance; - - /* - * Requesting service chat (0x000e) - */ - byte_stream_put16(&bs, 0x000e); - - aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance); - - if (od->use_ssl) - /* Request SSL Connection */ - aim_tlvlist_add_noval(&tlvlist, 0x008c); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi)); - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* Subtype 0x0005 - Redirect */ -static int -redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - struct aim_redirect_data redir; - aim_rxcallback_t userfunc; - GSList *tlvlist; - aim_snac_t *origsnac = NULL; - int ret = 0; - - memset(&redir, 0, sizeof(redir)); - - tlvlist = aim_tlvlist_read(bs); - - if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) || - !aim_tlv_gettlv(tlvlist, 0x0005, 1) || - !aim_tlv_gettlv(tlvlist, 0x0006, 1)) { - aim_tlvlist_free(tlvlist); - return 0; - } - - redir.group = aim_tlv_get16(tlvlist, 0x000d, 1); - redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1); - redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length; - redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1); - redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1); - redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1); - - /* Fetch original SNAC so we can get csi if needed */ - origsnac = aim_remsnac(od, snac->id); - - if ((redir.group == SNAC_FAMILY_CHAT) && origsnac) { - struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data; - - redir.chat.exchange = csi->exchange; - redir.chat.room = csi->name; - redir.chat.instance = csi->instance; - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, &redir); - - g_free((void *)redir.ip); - g_free((void *)redir.cookie); - g_free((void *)redir.ssl_cert_cn); - - if (origsnac) - g_free(origsnac->data); - g_free(origsnac); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -/* Subtype 0x0006 - Request Rate Information. */ -void -aim_srv_reqrates(OscarData *od, FlapConnection *conn) -{ - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006); -} - -/* - * OSCAR defines several 'rate classes'. Each class has separate - * rate limiting properties (limit level, alert level, disconnect - * level, etc), and a set of SNAC family/type pairs associated with - * it. The rate classes, their limiting properties, and the definitions - * of which SNACs belong to which class are defined in the - * Rate Response packet at login to each host. - * - * Logically, all rate offenses within one class count against further - * offenses for other SNACs in the same class (ie, sending messages - * too fast will limit the number of user info requests you can send, - * since those two SNACs are in the same rate class). - * - * Since the rate classes are defined dynamically at login, the values - * below may change. But they seem to be fairly constant. - * - * Currently, BOS defines five rate classes, with the commonly used - * members as follows... - * - * Rate class 0x0001: - * - Everything thats not in any of the other classes - * - * Rate class 0x0002: - * - Buddy list add/remove - * - Permit list add/remove - * - Deny list add/remove - * - * Rate class 0x0003: - * - User information requests - * - Outgoing ICBMs - * - * Rate class 0x0004: - * - A few unknowns: 2/9, 2/b, and f/2 - * - * Rate class 0x0005: - * - Chat room create - * - Outgoing chat ICBMs - * - * The only other thing of note is that class 5 (chat) has slightly looser - * limiting properties than class 3 (normal messages). But thats just a - * small bit of trivia for you. - * - * The last thing that needs to be learned about the rate limiting - * system is how the actual numbers relate to the passing of time. This - * seems to be a big mystery. - * - * See joscar's javadoc for the RateClassInfo class for a great - * explanation. You might be able to find it at - * http://dscoder.com/RateClassInfo.html - */ - -static struct rateclass * -rateclass_find(GSList *rateclasses, guint16 id) -{ - GSList *tmp; - - for (tmp = rateclasses; tmp != NULL; tmp = tmp->next) - { - struct rateclass *rateclass; - rateclass = tmp->data; - if (rateclass->classid == id) - return rateclass; - } - - return NULL; -} - -/* Subtype 0x0007 - Rate Parameters */ -static int -rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - guint16 numclasses, i; - aim_rxcallback_t userfunc; - - /* - * First are the parameters for each rate class. - */ - numclasses = byte_stream_get16(bs); - for (i = 0; i < numclasses; i++) - { - struct rateclass *rateclass; - guint32 delta; - struct timeval now; - - gettimeofday(&now, NULL); - rateclass = g_new(struct rateclass, 1); - - rateclass->classid = byte_stream_get16(bs); - rateclass->windowsize = byte_stream_get32(bs); - rateclass->clear = byte_stream_get32(bs); - rateclass->alert = byte_stream_get32(bs); - rateclass->limit = byte_stream_get32(bs); - rateclass->disconnect = byte_stream_get32(bs); - rateclass->current = byte_stream_get32(bs); - rateclass->max = byte_stream_get32(bs); - if (mod->version >= 3) { - delta = byte_stream_get32(bs); - rateclass->dropping_snacs = byte_stream_get8(bs); - } else { - delta = 0; - rateclass->dropping_snacs = 0; - } - - rateclass->last.tv_sec = now.tv_sec - delta / 1000; - rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000; - - conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass); - - if (rateclass->classid == OSCAR_DEFAULT_RATECLASS) - conn->default_rateclass = rateclass; - } - conn->rateclasses = g_slist_reverse(conn->rateclasses); - - /* - * Then the members of each class. - */ - for (i = 0; i < numclasses; i++) - { - guint16 classid, count; - struct rateclass *rateclass; - int j; - - classid = byte_stream_get16(bs); - count = byte_stream_get16(bs); - - if (classid == OSCAR_DEFAULT_RATECLASS) { - /* - * Don't bother adding these SNACs to the hash table. See the - * comment for OSCAR_DEFAULT_RATECLASS at the top of this file. - */ - byte_stream_advance(bs, 4 * count); - continue; - } - - rateclass = rateclass_find(conn->rateclasses, classid); - - for (j = 0; j < count; j++) - { - guint16 group, subtype; - - group = byte_stream_get16(bs); - subtype = byte_stream_get16(bs); - - if (rateclass != NULL) - g_hash_table_insert(conn->rateclass_members, - GUINT_TO_POINTER((group << 16) + subtype), - rateclass); - } - } - - /* - * We don't pass the rate information up to the client, as it really - * doesn't care. The information is stored in the connection, however - * so that we can do rate limiting management when sending SNACs. - */ - - /* - * Subscribe to rate change information for all rate classes. - */ - aim_srv_rates_addparam(od, conn); - - /* - * Finally, tell the client it's ready to go... - */ - if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE))) - userfunc(od, conn, frame); - - return 1; -} - -/* Subtype 0x0008 - Add Rate Parameter */ -void -aim_srv_rates_addparam(OscarData *od, FlapConnection *conn) -{ - ByteStream bs; - aim_snacid_t snacid; - GSList *tmp; - - byte_stream_new(&bs, 502); - - for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next) - { - struct rateclass *rateclass; - rateclass = tmp->data; - byte_stream_put16(&bs, rateclass->classid); - } - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/* Subtype 0x000a - Rate Change */ -static int -ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - guint16 code, classid; - struct rateclass *rateclass; - guint32 delta; - struct timeval now; - static const char *codes[5] = { - "invalid", - "change", - "warning", - "limit", - "limit cleared", - }; - - gettimeofday(&now, NULL); - code = byte_stream_get16(bs); - classid = byte_stream_get16(bs); - - rateclass = rateclass_find(conn->rateclasses, classid); - if (rateclass == NULL) - /* This should never really happen */ - return 0; - - rateclass->windowsize = byte_stream_get32(bs); - rateclass->clear = byte_stream_get32(bs); - rateclass->alert = byte_stream_get32(bs); - rateclass->limit = byte_stream_get32(bs); - rateclass->disconnect = byte_stream_get32(bs); - rateclass->current = byte_stream_get32(bs); - rateclass->max = byte_stream_get32(bs); - if (mod->version >= 3) { - delta = byte_stream_get32(bs); - rateclass->dropping_snacs = byte_stream_get8(bs); - } else { - delta = 0; - rateclass->dropping_snacs = 0; - } - - rateclass->last.tv_sec = now.tv_sec - delta / 1000; - rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000; - - purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, " - "maxavg = %u, alert at %u, clear warning at %u, limit at %u, " - "disconnect at %u, delta is %u, dropping is %u (window size = %u)\n", - (code < 5) ? codes[code] : codes[0], rateclass->classid, - rateclass->current, rateclass->max, rateclass->alert, - rateclass->clear, rateclass->limit, rateclass->disconnect, - delta, rateclass->dropping_snacs, rateclass->windowsize); - - if (code == AIM_RATE_CODE_LIMIT) { - purple_debug_warning("oscar", "The last action you attempted " - "could not be performed because you are over the rate " - "limit. Please wait 10 seconds and try again.\n"); - } - - return 1; -} - -/* - * How Migrations work. - * - * The server sends a Server Pause message, which the client should respond to - * with a Server Pause Ack, which contains the families it needs on this - * connection. The server will send a Migration Notice with an IP address, and - * then disconnect. Next the client should open the connection and send the - * cookie. Repeat the normal login process and pretend this never happened. - * - * The Server Pause contains no data. - * - */ - -/* Subtype 0x000b - Service Pause */ -static int -serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame); - - return ret; -} - -/* Subtype 0x000d - Service Resume */ -static int -serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame); - - return ret; -} - -/* Subtype 0x000e - Request self-info */ -void -aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn) -{ - aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e); -} - -/* Subtype 0x000f - Self User Info */ -static int -selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - aim_userinfo_t userinfo; - - aim_info_extract(od, bs, &userinfo); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, &userinfo); - - aim_info_free(&userinfo); - - return ret; -} - -/* Subtype 0x0010 - Evil Notification */ -static int -evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 newevil; - aim_userinfo_t userinfo; - - memset(&userinfo, 0, sizeof(aim_userinfo_t)); - - newevil = byte_stream_get16(bs); - - if (byte_stream_bytes_left(bs)) - aim_info_extract(od, bs, &userinfo); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, newevil, &userinfo); - - aim_info_free(&userinfo); - - return ret; -} - -/* - * Subtype 0x0011 - Idle Notification - * - * Should set your current idle time in seconds. Note that this should - * never be called consecutively with a non-zero idle time. That makes - * OSCAR do funny things. Instead, just set it once you go idle, and then - * call it again with zero when you're back. - * - */ -void -aim_srv_setidle(OscarData *od, guint32 idletime) -{ - FlapConnection *conn; - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS); - if(!conn) - return; - - aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime); -} - -/* - * Subtype 0x0012 - Service Migrate - * - * This is the final SNAC sent on the original connection during a migration. - * It contains the IP and cookie used to connect to the new server, and - * optionally a list of the SNAC groups being migrated. - * - */ -static int -migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - int ret = 0; - guint16 groupcount, i; - GSList *tlvlist; - char *ip = NULL; - aim_tlv_t *cktlv; - - /* - * Apparently there's some fun stuff that can happen right here. The - * migration can actually be quite selective about what groups it - * moves to the new server. When not all the groups for a connection - * are migrated, or they are all migrated but some groups are moved - * to a different server than others, it is called a bifurcated - * migration. - * - * Let's play dumb and not support that. - * - */ - groupcount = byte_stream_get16(bs); - for (i = 0; i < groupcount; i++) { - guint16 group; - - group = byte_stream_get16(bs); - - purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group); - } - - tlvlist = aim_tlvlist_read(bs); - - if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) - ip = aim_tlv_getstr(tlvlist, 0x0005, 1); - - cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL); - - aim_tlvlist_free(tlvlist); - g_free(ip); - - return ret; -} - -/* Subtype 0x0013 - Message of the Day */ -static int -motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - char *msg = NULL; - int ret = 0; - GSList *tlvlist; - guint16 id; - - /* - * Code. - * - * Valid values: - * 1 Mandatory upgrade - * 2 Advisory upgrade - * 3 System bulletin - * 4 Nothing's wrong ("top o the world" -- normal) - * 5 Lets-break-something. - * - */ - id = byte_stream_get16(bs); - - /* - * TLVs follow - */ - tlvlist = aim_tlvlist_read(bs); - - msg = aim_tlv_getstr(tlvlist, 0x000b, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, id, msg); - - g_free(msg); - - aim_tlvlist_free(tlvlist); - - return ret; -} - -/* - * Subtype 0x0017 - Set client versions - * - * If you've seen the clientonline/clientready SNAC you're probably - * wondering what the point of this one is. And that point seems to be - * that the versions in the client online SNAC are sent too late for the - * server to be able to use them to change the protocol for the earlier - * login packets (client versions are sent right after Host Online is - * received, but client online versions aren't sent until quite a bit later). - * We can see them already making use of this by changing the format of - * the rate information based on what version of group 1 we advertise here. - * - */ -void -aim_srv_setversions(OscarData *od, FlapConnection *conn) -{ - ByteStream bs; - aim_snacid_t snacid; - GSList *cur; - - byte_stream_new(&bs, 1142); - - /* - * Send only the versions that the server cares about (that it - * marked as supporting in the server ready SNAC). - */ - for (cur = conn->groups; cur != NULL; cur = cur->next) - { - aim_module_t *mod; - - if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data)))) - { - byte_stream_put16(&bs, mod->family); - byte_stream_put16(&bs, mod->version); - } - } - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/* Subtype 0x0018 - Host versions */ -static int -hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int vercount; - guint8 *versions; - - /* This is frivolous. (Thank you SmarterChild.) */ - vercount = byte_stream_bytes_left(bs)/4; - - /* XXX: vercount probably should be used for reading versions. */ - (void)vercount; - versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs)); - g_free(versions); - - /* - * Now request rates. - */ - aim_srv_reqrates(od, conn); - - return 1; -} - -/** - * Subtype 0x001e - Extended Status/Extra Info. - * - * These settings are transient, not server-stored (i.e. they only - * apply to this session, and must be re-set the next time you sign - * on). - * - * You can set your ICQ status (available, away, do not disturb, - * etc.), or whether your IP address should be hidden or not, or - * if your status is visible on ICQ web sites, and you can set - * your IP address info and what not. - * - * You can also set your "available" message. This is currently - * only supported by iChat, Purple and other 3rd party clients. - * - * These are the same TLVs seen in user info. You can - * also set 0x0008 and 0x000c. - */ -int -aim_srv_setextrainfo(OscarData *od, - gboolean seticqstatus, guint32 icqstatus, - gboolean setstatusmsg, const char *statusmsg, const char *itmsurl) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM))) - return -EINVAL; - - if (seticqstatus) - { - aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus | - AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH); - } - - if (setstatusmsg) - { - size_t statusmsglen, itmsurllen; - ByteStream tmpbs; - - statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0; - itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0; - - byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8); - byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg); - byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl); - - aim_tlvlist_add_raw(&tlvlist, 0x001d, - byte_stream_curpos(&tmpbs), tmpbs.data); - byte_stream_destroy(&tmpbs); - } - - byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); - - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0); - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* Send dummy DC (direct connect) information to the server. - * Direct connect is ICQ's counterpart for AIM's DirectIM, - * as far as I can tell. Anyway, we don't support it; - * the reason to send this packet is that some clients - * (Miranda, QIP) won't send us channel 2 ICBM messages - * unless we specify DC version >= 8. - * - * See #12044 for more information. - */ -void -aim_srv_set_dc_info(OscarData *od) -{ - FlapConnection *conn; - - ByteStream bs, tlv0c; - aim_snacid_t snacid; - GSList *tlvlist = NULL; - - /* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv. - * Kopete sends a dummy DC info, too, so I just copied the values from them. - */ - byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2); - byte_stream_put32(&tlv0c, 0x0); - byte_stream_put32(&tlv0c, 0x0); - byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */ - byte_stream_put16(&tlv0c, 8); /* DC version */ - byte_stream_put32(&tlv0c, 0x0); - byte_stream_put32(&tlv0c, 0x50); - byte_stream_put32(&tlv0c, 0x3); - byte_stream_put32(&tlv0c, 0x0); - byte_stream_put32(&tlv0c, 0x0); - byte_stream_put32(&tlv0c, 0x0); - byte_stream_put16(&tlv0c, 0x0); - aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data); - byte_stream_destroy(&tlv0c); - - byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); - aim_tlvlist_write(&bs, &tlvlist); - aim_tlvlist_free(tlvlist); - - snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0); - conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - g_warn_if_fail(conn != NULL); - if (conn) { - flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, - 0x001e, snacid, &bs); - } - - byte_stream_destroy(&bs); -} - -/* - * Subtype 0x0021 - Receive our extended status - * - * This is used for iChat's "available" messages, and maybe ICQ extended - * status messages? It's also used to tell the client whether or not it - * needs to upload an SSI buddy icon... who engineers this stuff, anyway? - */ -static int -aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - guint16 type = byte_stream_get16(bs); - if (type == 0x0000 || type == 0x0001) { - /* buddy icon checksum */ - /* not sure what the difference between 1 and 0 is */ - guint8 flags = byte_stream_get8(bs); - guint8 length = byte_stream_get8(bs); - guint8 *md5 = byte_stream_getraw(bs, length); - - if ((flags == 0x00) || (flags == 0x41)) { - if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) { - od->iconconnecting = TRUE; - od->set_icon = TRUE; - aim_srv_requestnew(od, SNAC_FAMILY_BART); - } else { - PurpleAccount *account = purple_connection_get_account(od->gc); - PurpleImage *img = purple_buddy_icons_find_account_icon(account); - if (img == NULL) { - aim_ssi_delicon(od); - } else { - - purple_debug_info("oscar", - "Uploading icon to icon server\n"); - aim_bart_upload(od, - purple_image_get_data(img), - purple_image_get_data_size(img)); - g_object_unref(img); - } - } - } else if (flags == 0x81) { - PurpleAccount *account = purple_connection_get_account(od->gc); - PurpleImage *img = purple_buddy_icons_find_account_icon(account); - if (img == NULL) - aim_ssi_delicon(od); - else { - aim_ssi_seticon(od, md5, length); - g_object_unref(img); - } - } - - g_free(md5); - } - - return 0; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0003) - return hostonline(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0005) - return redirect(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0007) - return rateresp(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000a) - return ratechange(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000b) - return serverpause(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000d) - return serverresume(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x000f) - return selfinfo(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0010) - return evilnotify(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0012) - return migrate(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0013) - return motd(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0018) - return hostversions(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0021) - return aim_parse_extstatus(od, conn, mod, frame, snac, bs); - - return 0; -} - -int service_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_OSERVICE; - mod->version = 0x0003; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "oservice", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_popup.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x0008 - Popups. - * - * Popups are just what it sounds like. They're a way for the server to - * open up an informative box on the client's screen. - */ - -#include <oscar.h> - -/* - * This is all there is to it. - * - * The message is probably HTML. - * - */ -static int -parsepopup(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - aim_rxcallback_t userfunc; - GSList *tlvlist; - int ret = 0; - char *msg, *url; - guint16 width, height, delay; - - tlvlist = aim_tlvlist_read(bs); - - msg = aim_tlv_getstr(tlvlist, 0x0001, 1); - url = aim_tlv_getstr(tlvlist, 0x0002, 1); - width = aim_tlv_get16(tlvlist, 0x0003, 1); - height = aim_tlv_get16(tlvlist, 0x0004, 1); - delay = aim_tlv_get16(tlvlist, 0x0005, 1); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, msg, url, width, height, delay); - - aim_tlvlist_free(tlvlist); - g_free(msg); - g_free(url); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0002) - return parsepopup(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -popups_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_POPUP; - mod->version = 0x0001; - mod->toolid = 0x0104; - mod->toolversion = 0x0001; - mod->flags = 0; - strncpy(mod->name, "popup", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_stats.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x000b - Statistics. - * - */ - -#include <oscar.h> - -static int -reportinterval(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - guint16 interval; - - interval = byte_stream_get16(bs); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, interval); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0002) - return reportinterval(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -stats_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_STATS; - mod->version = 0x0001; - mod->toolid = 0x0104; - mod->toolversion = 0x0001; - mod->flags = 0; - strncpy(mod->name, "stats", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/family_userlookup.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Family 0x000a - User Search. - * - * TODO: Add aim_usersearch_name() - * - */ - -#include "oscar.h" - -/* - * Subtype 0x0001 - * - * XXX can this be integrated with the rest of the error handling? - */ -static int error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - aim_rxcallback_t userfunc; - aim_snac_t *snac2; - - /* XXX the modules interface should have already retrieved this for us */ - if (!(snac2 = aim_remsnac(od, snac->id))) { - purple_debug_misc("oscar", "search error: couldn't get a snac for 0x%08x\n", snac->id); - return 0; - } - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, snac2->data /* address */); - - /* XXX freesnac()? */ - g_free(snac2->data); - g_free(snac2); - - return ret; -} - -/* - * Subtype 0x0002 - * - */ -int aim_search_address(OscarData *od, const char *address) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - - conn = flap_connection_findbygroup(od, SNAC_FAMILY_USERLOOKUP); - - if (!conn || !address) - return -EINVAL; - - byte_stream_new(&bs, strlen(address)); - - byte_stream_putstr(&bs, address); - - snacid = aim_cachesnac(od, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, address, strlen(address)+1); - flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, snacid, &bs); - - byte_stream_destroy(&bs); - - return 0; -} - -/* - * Subtype 0x0003 - * - */ -static int reply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int j = 0, m, ret = 0; - GSList *tlvlist; - char *cur = NULL, *buf = NULL; - aim_rxcallback_t userfunc; - aim_snac_t *snac2; - const char *searchaddr = NULL; - - if ((snac2 = aim_remsnac(od, snac->id))) - searchaddr = (const char *)snac2->data; - - tlvlist = aim_tlvlist_read(bs); - m = aim_tlvlist_count(tlvlist); - - /* XXX uhm. - * This is the only place that uses something other than 1 for the 3rd - * parameter to aim_tlv_gettlv_whatever(). - */ - while ((cur = aim_tlv_getstr(tlvlist, 0x0001, j+1)) && j < m) - { - buf = g_realloc(buf, (j+1) * (MAXSNLEN+1)); - - strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); - g_free(cur); - - j++; - } - g_free(cur); - - aim_tlvlist_free(tlvlist); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, searchaddr, j, buf); - - /* XXX freesnac()? */ - if (snac2) - g_free(snac2->data); - g_free(snac2); - - g_free(buf); - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0001) - return error(od, conn, mod, frame, snac, bs); - else if (snac->subtype == 0x0003) - return reply(od, conn, mod, frame, snac, bs); - - return 0; -} - -int -search_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = SNAC_FAMILY_USERLOOKUP; - mod->version = 0x0001; - mod->toolid = 0x0110; - mod->toolversion = 0x0629; - mod->flags = 0; - strncpy(mod->name, "userlookup", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/flap_connection.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1117 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#include "oscar.h" - -#include "eventloop.h" -#include "proxy.h" - -#ifndef _WIN32 -#include <netdb.h> -#include <sys/socket.h> -#include <netinet/in.h> -#endif - -/** - * This sends a channel 1 SNAC containing the FLAP version. - * The FLAP version is sent by itself at the beginning of every - * connection to a FLAP server. It is always the very first - * packet sent by both the server and the client after the SYN, - * SYN/ACK, ACK handshake. - */ -void -flap_connection_send_version(OscarData *od, FlapConnection *conn) -{ - FlapFrame *frame; - - frame = flap_frame_new(od, 0x01, 4); - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ - flap_connection_send(conn, frame); -} - -/** - * This sends a channel 1 FLAP containing the FLAP version and - * the authentication cookie. This is sent when connecting to - * any FLAP server after the initial connection to the auth - * server. It is always the very first packet sent by both the - * server and the client after the SYN, SYN/ACK, ACK handshake. - */ -void -flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy) -{ - FlapFrame *frame; - GSList *tlvlist = NULL; - - frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length); - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ - aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy); - aim_tlvlist_write(&frame->data, &tlvlist); - aim_tlvlist_free(tlvlist); - - flap_connection_send(conn, frame); -} - -void -flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_logins) -{ - FlapFrame *frame; - GSList *tlvlist = NULL; - - frame = flap_frame_new(od, 0x01, 1152 + length); - - byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ - aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy); - - if (ci->clientstring != NULL) - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring); - else { - gchar *clientstring = oscar_get_clientstring(); - aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring); - g_free(clientstring); - } - aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major); - aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor); - aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point); - aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build); - aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x03)); - - aim_tlvlist_write(&frame->data, &tlvlist); - - aim_tlvlist_free(tlvlist); - - flap_connection_send(conn, frame); -} - -static struct rateclass * -flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype) -{ - gconstpointer key; - gpointer rateclass; - - key = GUINT_TO_POINTER((family << 16) + subtype); - rateclass = g_hash_table_lookup(conn->rateclass_members, key); - if (rateclass != NULL) - return rateclass; - - return conn->default_rateclass; -} - -/* - * Attempt to calculate what our new current average would be if we - * were to send a SNAC in this rateclass at the given time. - */ -static guint32 -rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now) -{ - unsigned long timediff; /* In milliseconds */ - guint32 current; - - /* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */ - timediff = (now->tv_sec - rateclass->last.tv_sec) * 1000 + (now->tv_usec - rateclass->last.tv_usec) / 1000; - current = ((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize; - - return MIN(current, rateclass->max); -} - -/* - * Attempt to send the contents of a given queue - * - * @return TRUE if the queue was completely emptied or was initially - * empty; FALSE if rate limiting prevented it from being - * emptied. - */ -static gboolean flap_connection_send_snac_queue(FlapConnection *conn, struct timeval now, GQueue *queue) -{ - while (!g_queue_is_empty(queue)) - { - QueuedSnac *queued_snac; - struct rateclass *rateclass; - - queued_snac = g_queue_peek_head(queue); - - rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype); - if (rateclass != NULL) - { - guint32 new_current; - - new_current = rateclass_get_new_current(conn, rateclass, &now); - - if (rateclass->dropping_snacs || new_current <= rateclass->alert) - /* Not ready to send this SNAC yet--keep waiting. */ - return FALSE; - - rateclass->current = new_current; - rateclass->last.tv_sec = now.tv_sec; - rateclass->last.tv_usec = now.tv_usec; - } - - flap_connection_send(conn, queued_snac->frame); - g_free(queued_snac); - g_queue_pop_head(queue); - } - - /* We emptied the queue */ - return TRUE; -} - -static gboolean flap_connection_send_queued(gpointer data) -{ - FlapConnection *conn; - struct timeval now; - - conn = data; - gettimeofday(&now, NULL); - - purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n", - (conn->queued_snacs ? conn->queued_snacs->length : 0), - (conn->queued_lowpriority_snacs ? conn->queued_lowpriority_snacs->length : 0), - conn); - if (!conn->queued_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_snacs)) { - if (!conn->queued_lowpriority_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_lowpriority_snacs)) { - /* Both queues emptied. */ - conn->queued_timeout = 0; - return FALSE; - } - } - - /* We couldn't send all our SNACs. Keep trying */ - return TRUE; -} - -/** - * This sends a channel 2 FLAP containing a SNAC. The SNAC family and - * subtype are looked up in the rate info for this connection, and if - * sending this SNAC will induce rate limiting then we delay sending - * of the SNAC by putting it into an outgoing holding queue. - * - * @param data The optional bytestream that makes up the data portion - * of this SNAC. For empty SNACs this should be NULL. - * @param high_priority If TRUE, the SNAC will be queued normally if - * needed. If FALSE, it will be queued separately, to be sent - * only if all high priority SNACs have been sent. - */ -void -flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority) -{ - FlapFrame *frame; - guint32 length; - gboolean enqueue = FALSE; - struct rateclass *rateclass; - - length = data != NULL ? data->offset : 0; - - frame = flap_frame_new(od, 0x02, 10 + length); - aim_putsnac(&frame->data, family, subtype, snacid); - - if (length > 0) - { - byte_stream_rewind(data); - byte_stream_putbs(&frame->data, data, length); - } - - if (conn->queued_timeout != 0) - enqueue = TRUE; - else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL) - { - struct timeval now; - guint32 new_current; - - gettimeofday(&now, NULL); - new_current = rateclass_get_new_current(conn, rateclass, &now); - - if (rateclass->dropping_snacs || new_current <= rateclass->alert) - { - purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn, new_current, rateclass->alert); - - enqueue = TRUE; - } - else - { - rateclass->current = new_current; - rateclass->last.tv_sec = now.tv_sec; - rateclass->last.tv_usec = now.tv_usec; - } - } - - if (enqueue) - { - /* We've been sending too fast, so delay this message */ - QueuedSnac *queued_snac; - - queued_snac = g_new(QueuedSnac, 1); - queued_snac->family = family; - queued_snac->subtype = subtype; - queued_snac->frame = frame; - - if (high_priority) { - if (!conn->queued_snacs) - conn->queued_snacs = g_queue_new(); - g_queue_push_tail(conn->queued_snacs, queued_snac); - } else { - if (!conn->queued_lowpriority_snacs) - conn->queued_lowpriority_snacs = g_queue_new(); - g_queue_push_tail(conn->queued_lowpriority_snacs, queued_snac); - } - - if (conn->queued_timeout == 0) - conn->queued_timeout = g_timeout_add(500, flap_connection_send_queued, conn); - - return; - } - - flap_connection_send(conn, frame); -} - -void -flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data) -{ - flap_connection_send_snac_with_priority(od, conn, family, subtype, snacid, data, TRUE); -} - -/** - * This sends an empty channel 4 FLAP. This is sent to signify - * that we're logging off. This shouldn't really be necessary-- - * usually the AIM server will detect that the TCP connection has - * been destroyed--but it's good practice. - */ -static void -flap_connection_send_close(OscarData *od, FlapConnection *conn) -{ - FlapFrame *frame; - - frame = flap_frame_new(od, 0x04, 0); - flap_connection_send(conn, frame); -} - -/** - * This sends an empty channel 5 FLAP. This is used as a keepalive - * packet in FLAP connections. WinAIM 4.x and higher send these - * _every minute_ to keep the connection alive. - */ -void -flap_connection_send_keepalive(OscarData *od, FlapConnection *conn) -{ - FlapFrame *frame; - - frame = flap_frame_new(od, 0x05, 0); - flap_connection_send(conn, frame); - - /* clean out SNACs over 60sec old */ - aim_cleansnacs(od, 60); -} - -/** - * Allocate a new empty connection structure. - * - * @param od The oscar session associated with this connection. - * @param type Type of connection to create - * - * @return Returns the new connection structure. - */ -FlapConnection * -flap_connection_new(OscarData *od, int type) -{ - FlapConnection *conn; - - conn = g_new0(FlapConnection, 1); - conn->od = od; - conn->buffer_outgoing = purple_circular_buffer_new(0); - conn->fd = -1; - conn->subtype = -1; - conn->type = type; - conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal); - - od->oscar_connections = g_slist_prepend(od->oscar_connections, conn); - - return conn; -} - -/** - * Close (but not free) a connection. - * - * This cancels any currently pending connection attempt, - * closes any open fd and frees the auth cookie. - * - * @param conn The connection to close. - */ -void -flap_connection_close(OscarData *od, FlapConnection *conn) -{ - if (conn->connect_data != NULL) - { - purple_proxy_connect_cancel(conn->connect_data); - conn->connect_data = NULL; - } - - if (conn->gsc != NULL && conn->gsc->connect_data != NULL) - { - purple_ssl_close(conn->gsc); - conn->gsc = NULL; - } - - if (conn->new_conn_data != NULL) - { - if (conn->type == SNAC_FAMILY_CHAT) - { - oscar_chat_destroy(conn->new_conn_data); - conn->new_conn_data = NULL; - } - } - - if ((conn->fd >= 0 || conn->gsc != NULL) - && conn->type == SNAC_FAMILY_LOCATE) - flap_connection_send_close(od, conn); - - if (conn->watcher_incoming != 0) - { - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = 0; - } - - if (conn->watcher_outgoing != 0) - { - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - } - - if (conn->fd >= 0) - { - close(conn->fd); - conn->fd = -1; - } - - if (conn->gsc != NULL) - { - purple_ssl_close(conn->gsc); - conn->gsc = NULL; - } - - g_free(conn->buffer_incoming.data.data); - conn->buffer_incoming.data.data = NULL; - - g_object_unref(G_OBJECT(conn->buffer_outgoing)); - conn->buffer_outgoing = NULL; -} - -/** - * Free a FlapFrame - * - * @param frame The frame to free. - */ -static void -flap_frame_destroy(FlapFrame *frame) -{ - g_free(frame->data.data); - g_free(frame); -} - -static gboolean -flap_connection_destroy_cb(gpointer data) -{ - FlapConnection *conn; - OscarData *od; - PurpleAccount *account; - aim_rxcallback_t userfunc; - - conn = data; - /* Explicitly added for debugging #5927. Don't re-order this, only - * consider removing it. - */ - purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn); - - od = conn->od; - account = purple_connection_get_account(od->gc); - - purple_debug_info("oscar", "Destroying oscar connection (%p) of " - "type 0x%04hx. Disconnect reason is %d\n", conn, - conn->type, conn->disconnect_reason); - - od->oscar_connections = g_slist_remove(od->oscar_connections, conn); - - if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) - userfunc(od, conn, NULL, conn->disconnect_code, conn->error_message); - - /* - * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then - * we should try to request one instead of disconnecting. - */ - if (!purple_account_is_disconnecting(account) && ((od->oscar_connections == NULL) - || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE)))) - { - /* No more FLAP connections! Sign off this PurpleConnection! */ - gchar *tmp; - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - - if (conn->disconnect_code == 0x0001) { - reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; - tmp = g_strdup(_("You have signed on from another location")); - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - } else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) - tmp = g_strdup(_("Server closed the connection")); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION) - tmp = g_strdup_printf(_("Lost connection with server: %s"), - conn->error_message); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA) - tmp = g_strdup(_("Received invalid data on connection with server")); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT) - tmp = g_strdup_printf(_("Unable to connect: %s"), - conn->error_message); - else - /* - * We shouldn't print a message for some disconnect_reasons. - * Like OSCAR_DISCONNECT_LOCAL_CLOSED. - */ - tmp = NULL; - - if (tmp != NULL) - { - purple_connection_error(od->gc, reason, tmp); - g_free(tmp); - } - } - - flap_connection_close(od, conn); - - g_free(conn->error_message); - g_free(conn->cookie); - - /* - * Free conn->internal, if necessary - */ - if (conn->type == SNAC_FAMILY_CHAT) - flap_connection_destroy_chat(od, conn); - - g_slist_free(conn->groups); - g_slist_free_full(conn->rateclasses, g_free); - - g_hash_table_destroy(conn->rateclass_members); - - if (conn->queued_snacs) { - while (!g_queue_is_empty(conn->queued_snacs)) - { - QueuedSnac *queued_snac; - queued_snac = g_queue_pop_head(conn->queued_snacs); - flap_frame_destroy(queued_snac->frame); - g_free(queued_snac); - } - g_queue_free(conn->queued_snacs); - } - - if (conn->queued_lowpriority_snacs) { - while (!g_queue_is_empty(conn->queued_lowpriority_snacs)) - { - QueuedSnac *queued_snac; - queued_snac = g_queue_pop_head(conn->queued_lowpriority_snacs); - flap_frame_destroy(queued_snac->frame); - g_free(queued_snac); - } - g_queue_free(conn->queued_lowpriority_snacs); - } - - if (conn->queued_timeout > 0) - g_source_remove(conn->queued_timeout); - - g_free(conn); - - return FALSE; -} - -/** - * See the comments for the parameters of - * flap_connection_schedule_destroy(). - */ -void -flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message) -{ - if (conn->destroy_timeout != 0) - g_source_remove(conn->destroy_timeout); - conn->disconnect_reason = reason; - g_free(conn->error_message); - conn->error_message = g_strdup(error_message); - flap_connection_destroy_cb(conn); -} - -/** - * Schedule Purple to destroy the given FlapConnection as soon as we - * return control back to the program's main loop. We must do this - * if we want to destroy the connection but we are still using it - * for some reason. - * - * @param reason The reason for the disconnection. - * @param error_message A brief error message that gives more detail - * regarding the reason for the disconnecting. This should - * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION, - * in which case it should contain the value of g_strerror(errno), - * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it - * should contain the error_message passed back from the call - * to purple_proxy_connect(). - */ -void -flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message) -{ - if (conn->destroy_timeout != 0) - /* Already taken care of */ - return; - - purple_debug_info("oscar", "Scheduling destruction of FLAP " - "connection %p of type 0x%04hx\n", conn, conn->type); - conn->disconnect_reason = reason; - g_free(conn->error_message); - conn->error_message = g_strdup(error_message); - conn->destroy_timeout = g_timeout_add(0, flap_connection_destroy_cb, conn); -} - -/** - * In OSCAR, every connection has a set of SNAC groups associated - * with it. These are the groups that you can send over this connection - * without being guaranteed a "Not supported" SNAC error. - * - * The grand theory of things says that these associations transcend - * what libfaim calls "connection types" (conn->type). You can probably - * see the elegance here, but since I want to revel in it for a bit, you - * get to hear it all spelled out. - * - * So let us say that you have your core BOS connection running. One - * of your modules has just given you a SNAC of the group 0x0004 to send - * you. Maybe an IM destined for some twit in Greenland. So you start - * at the top of your connection list, looking for a connection that - * claims to support group 0x0004. You find one. Why, that neat BOS - * connection of yours can do that. So you send it on its way. - * - * Now, say, that fellow from Greenland has friends and they all want to - * meet up with you in a lame chat room. This has landed you a SNAC - * in the family 0x000e and you have to admit you're a bit lost. You've - * searched your connection list for someone who wants to make your life - * easy and deliver this SNAC for you, but there isn't one there. - * - * Here comes the good bit. Without even letting anyone know, particularly - * the module that decided to send this SNAC, and definitely not that twit - * in Greenland, you send out a service request. In this request, you have - * marked the need for a connection supporting group 0x000e. A few seconds - * later, you receive a service redirect with an IP address and a cookie in - * it. Great, you say. Now I have something to do. Off you go, making - * that connection. One of the first things you get from this new server - * is a message saying that indeed it does support the group you were looking - * for. So you continue and send rate confirmation and all that. - * - * Then you remember you had that SNAC to send, and now you have a means to - * do it, and you do, and everyone is happy. Except the Greenlander, who is - * still stuck in the bitter cold. - * - * Oh, and this is useful for building the Migration SNACs, too. In the - * future, this may help convince me to implement rate limit mitigation - * for real. We'll see. - * - * Just to make me look better, I'll say that I've known about this great - * scheme for quite some time now. But I still haven't convinced myself - * to make libfaim work that way. It would take a fair amount of effort, - * and probably some client API changes as well. (Whenever I don't want - * to do something, I just say it would change the client API. Then I - * instantly have a couple of supporters of not doing it.) - * - * Generally, addgroup is only called by the internal handling of the - * server ready SNAC. So if you want to do something before that, you'll - * have to be more creative. That is done rather early, though, so I don't - * think you have to worry about it. Unless you're me. I care deeply - * about such inane things. - * - */ - -/** - * Find a FlapConnection that supports the given oscar - * family. - */ -FlapConnection * -flap_connection_findbygroup(OscarData *od, guint16 group) -{ - GSList *cur; - - for (cur = od->oscar_connections; cur != NULL; cur = cur->next) - { - FlapConnection *conn; - - conn = cur->data; - - if (g_slist_find(conn->groups, GUINT_TO_POINTER(group)) != NULL) { - return conn; - } - } - - return NULL; -} - -/** - * Locates a connection of the specified type in the - * specified session. - * - * TODO: Use flap_connection_findbygroup everywhere and get rid of this. - * - * @param od The session to search. - * @param type The type of connection to look for. - * - * @return Returns the first connection found of the given target type, - * or NULL if none could be found. - */ -FlapConnection * -flap_connection_getbytype(OscarData *od, int type) -{ - GSList *cur; - - for (cur = od->oscar_connections; cur != NULL; cur = cur->next) - { - FlapConnection *conn; - conn = cur->data; - if ((conn->type == type) && (conn->connected)) - return conn; - } - - return NULL; -} - -FlapConnection * -flap_connection_getbytype_all(OscarData *od, int type) -{ - GSList *cur; - - for (cur = od->oscar_connections; cur; cur = cur->next) - { - FlapConnection *conn; - conn = cur->data; - if (conn->type == type) - return conn; - } - - return NULL; -} - -/** - * Allocate a new FLAP frame. - * - * @param channel The FLAP channel. This is almost always 2. - */ -FlapFrame * -flap_frame_new(OscarData *od, guint16 channel, int datalen) -{ - FlapFrame *frame; - - frame = g_new0(FlapFrame, 1); - frame->channel = channel; - - if (datalen > 0) - byte_stream_new(&frame->data, datalen); - - return frame; -} - -static void -parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame) -{ - aim_module_t *cur; - aim_modsnac_t snac; - - if (byte_stream_bytes_left(&frame->data) < 10) - return; - - snac.family = byte_stream_get16(&frame->data); - snac.subtype = byte_stream_get16(&frame->data); - snac.flags = byte_stream_get16(&frame->data); - snac.id = byte_stream_get32(&frame->data); - - /* SNAC flags are apparently uniform across all SNACs, so we handle them here */ - if (snac.flags & 0x0001) { - /* - * This means the SNAC will be followed by another SNAC with - * related information. We don't need to do anything about - * this here. - */ - } - if (snac.flags & 0x8000) { - /* - * This packet contains the version of the family that this SNAC is - * in. You get this when your SSI module is version 2 or higher. - * For now we have no need for this, but you could always save - * it as a part of aim_modnsac_t, or something. The format is... - * 2 byte length of total mini-header (which is 6 bytes), then TLV - * of type 0x0001, length 0x0002, value is the 2 byte version - * number - */ - byte_stream_advance(&frame->data, byte_stream_get16(&frame->data)); - } - - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) { - - if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && - (cur->family != snac.family)) - continue; - - if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data)) - return; - } -} - -static void -parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype) -{ - aim_module_t *cur; - aim_modsnac_t snac; - - snac.family = family; - snac.subtype = subtype; - snac.flags = snac.id = 0; - - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) { - - if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && - (cur->family != snac.family)) - continue; - - if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data)) - return; - } -} - -static void -parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame) -{ - GSList *tlvlist; - char *msg = NULL; - - if (byte_stream_bytes_left(&frame->data) == 0) { - /* XXX should do something with this */ - return; - } - - /* An ICQ account is logging in */ - if (conn->type == SNAC_FAMILY_AUTH) - { - parse_fakesnac(od, conn, frame, 0x0017, 0x0003); - return; - } - - tlvlist = aim_tlvlist_read(&frame->data); - - if (aim_tlv_gettlv(tlvlist, 0x0009, 1)) - conn->disconnect_code = aim_tlv_get16(tlvlist, 0x0009, 1); - - if (aim_tlv_gettlv(tlvlist, 0x000b, 1)) - msg = aim_tlv_getstr(tlvlist, 0x000b, 1); - - /* - * The server ended this FLAP connnection, so let's be nice and - * close the physical TCP connection - */ - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_REMOTE_CLOSED, msg); - - aim_tlvlist_free(tlvlist); - - g_free(msg); -} - -/** - * Takes a new incoming FLAP frame and sends it to the appropriate - * handler function to be parsed. - */ -static void -parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame) -{ - if (frame->channel == 0x01) { - guint32 flap_version = byte_stream_get32(&frame->data); - if (flap_version != 0x00000001) - { - /* Error! */ - purple_debug_warning("oscar", "Expecting FLAP version " - "0x00000001 but received FLAP version %08x. Closing connection.\n", - flap_version); - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_INVALID_DATA, NULL); - } - else - conn->connected = TRUE; - - } else if (frame->channel == 0x02) { - parse_snac(od, conn, frame); - - } else if (frame->channel == 0x04) { - parse_flap_ch4(od, conn, frame); - - } else if (frame->channel == 0x05) { - /* TODO: Reset our keepalive watchdog? */ - - } -} - -/** - * Read in all available data on the socket for a given connection. - * All complete FLAPs handled immedate after they're received. - * Incomplete FLAP data is stored locally and appended to the next - * time this callback is triggered. - * - * This is called by flap_connection_recv_cb and - * flap_connection_recv_cb_ssl for unencrypted/encrypted connections. - */ -static void -flap_connection_recv(FlapConnection *conn) -{ - gpointer buf; - gsize buflen; - gssize read; - - /* Read data until we run out of data and break out of the loop */ - while (TRUE) - { - /* Start reading a new FLAP */ - if (conn->buffer_incoming.data.data == NULL) - { - buf = conn->header + conn->header_received; - buflen = 6 - conn->header_received; - - /* Read the first 6 bytes (the FLAP header) */ - if (conn->gsc) - read = purple_ssl_read(conn->gsc, buf, buflen); - else - read = recv(conn->fd, buf, buflen, 0); - - /* Check if the FLAP server closed the connection */ - if (read == 0) - { - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - break; - } - - /* If there was an error then close the connection */ - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - break; - - /* Error! */ - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); - break; - } - purple_connection_update_last_received(conn->od->gc); - - /* If we don't even have a complete FLAP header then do nothing */ - conn->header_received += read; - if (conn->header_received < 6) - break; - - /* All FLAP frames must start with the byte 0x2a */ - if (aimutil_get8(&conn->header[0]) != 0x2a) - { - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_INVALID_DATA, NULL); - break; - } - - /* Initialize a new temporary FlapFrame for incoming data */ - conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]); - conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]); - conn->buffer_incoming.data.len = aimutil_get16(&conn->header[4]); - conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len); - conn->buffer_incoming.data.offset = 0; - } - - buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset; - if (buflen) - { - buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset]; - /* Read data into the temporary FlapFrame until it is complete */ - if (conn->gsc) - read = purple_ssl_read(conn->gsc, buf, buflen); - else - read = recv(conn->fd, buf, buflen, 0); - - /* Check if the FLAP server closed the connection */ - if (read == 0) - { - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - break; - } - - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - break; - - /* Error! */ - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); - break; - } - - conn->buffer_incoming.data.offset += read; - if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len) - /* Waiting for more data to arrive */ - break; - } - - /* We have a complete FLAP! Handle it and continue reading */ - byte_stream_rewind(&conn->buffer_incoming.data); - parse_flap(conn->od, conn, &conn->buffer_incoming); - conn->lastactivity = time(NULL); - - g_free(conn->buffer_incoming.data.data); - conn->buffer_incoming.data.data = NULL; - - conn->header_received = 0; - } -} - -void -flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - FlapConnection *conn = data; - - flap_connection_recv(conn); -} - -void -flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond) -{ - FlapConnection *conn = data; - - flap_connection_recv(conn); -} - -/** - * @param source When this function is called as a callback source is - * set to the fd that triggered the callback. But this function - * is also called directly from flap_connection_send_byte_stream(), - * in which case source will be -1. So don't use source--use - * conn->gsc or conn->fd instead. - */ -static void -send_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - FlapConnection *conn; - int writelen, ret; - const gchar *output = NULL; - - conn = data; - writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing); - output = purple_circular_buffer_get_output(conn->buffer_outgoing); - - if (writelen == 0) - { - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - return; - } - - if (conn->gsc) - ret = purple_ssl_write(conn->gsc, output, writelen); - else - ret = send(conn->fd, output, writelen, 0); - if (ret <= 0) - { - if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) - /* No worries */ - return; - - /* Error! */ - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - if (conn->gsc) { - purple_ssl_close(conn->gsc); - conn->gsc = NULL; - } else { - close(conn->fd); - conn->fd = -1; - } - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); - return; - } - - purple_circular_buffer_mark_read(conn->buffer_outgoing, ret); -} - -static void -flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count) -{ - if (conn == NULL) - return; - - /* Make sure we don't send past the end of the bs */ - if (count > byte_stream_bytes_left(bs)) - count = byte_stream_bytes_left(bs); /* truncate to remaining space */ - - if (count == 0) - return; - - /* Add everything to our outgoing buffer */ - purple_circular_buffer_append(conn->buffer_outgoing, bs->data, count); - - /* If we haven't already started writing stuff, then start the cycle */ - if (conn->watcher_outgoing == 0) - { - if (conn->gsc) { - conn->watcher_outgoing = purple_input_add(conn->gsc->fd, - PURPLE_INPUT_WRITE, send_cb, conn); - send_cb(conn, -1, 0); - } else if (conn->fd >= 0) { - conn->watcher_outgoing = purple_input_add(conn->fd, - PURPLE_INPUT_WRITE, send_cb, conn); - send_cb(conn, -1, 0); - } - } -} - -static void -sendframe_flap(FlapConnection *conn, FlapFrame *frame) -{ - ByteStream bs; - int payloadlen, bslen; - - payloadlen = byte_stream_curpos(&frame->data); - - byte_stream_new(&bs, 6 + payloadlen); - - /* FLAP header */ - byte_stream_put8(&bs, 0x2a); - byte_stream_put8(&bs, frame->channel); - byte_stream_put16(&bs, frame->seqnum); - byte_stream_put16(&bs, payloadlen); - - /* Payload */ - byte_stream_rewind(&frame->data); - byte_stream_putbs(&bs, &frame->data, payloadlen); - - bslen = byte_stream_curpos(&bs); - byte_stream_rewind(&bs); - flap_connection_send_byte_stream(&bs, conn, bslen); - - byte_stream_destroy(&bs); -} - -void -flap_connection_send(FlapConnection *conn, FlapFrame *frame) -{ - frame->seqnum = ++(conn->seqnum_out); - sendframe_flap(conn, frame); - flap_frame_destroy(frame); -}
--- a/libpurple/protocols/oscar/icq.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include "icq.h" - -#include "core.h" -#include "plugins.h" -#include "signals.h" - -#include "oscarcommon.h" - -static GHashTable * -icq_get_account_text_table(PurpleAccount *account) -{ - GHashTable *table; - table = g_hash_table_new(g_str_hash, g_str_equal); - g_hash_table_insert(table, "login_label", (gpointer)_("ICQ UIN...")); - return table; -} - -static gssize -icq_get_max_message_size(PurpleConversation *conv) -{ - /* XXX: got from pidgin-otr - verify and document it */ - return 2346; -} - -static void -icq_protocol_init(ICQProtocol *self) -{ - PurpleProtocol *protocol = PURPLE_PROTOCOL(self); - - protocol->id = "prpl-icq"; - protocol->name = "ICQ"; - - oscar_init_account_options(protocol, TRUE); -} - -static void -icq_protocol_class_init(ICQProtocolClass *klass) -{ - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass); - - protocol_class->list_icon = oscar_list_icon_icq; -} - -static void -icq_protocol_class_finalize(G_GNUC_UNUSED ICQProtocolClass *klass) -{ -} - -static void -icq_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface) -{ - client_iface->get_account_text_table = icq_get_account_text_table; - client_iface->get_moods = oscar_get_purple_moods; - client_iface->get_max_message_size = icq_get_max_message_size; -} - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - ICQProtocol, icq_protocol, OSCAR_TYPE_PROTOCOL, 0, - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT, - icq_protocol_client_iface_init)); - -/* This exists solely because the above macro makes icq_protocol_register_type - * static. */ -void -icq_protocol_register(PurplePlugin *plugin) -{ - icq_protocol_register_type(G_TYPE_MODULE(plugin)); -}
--- a/libpurple/protocols/oscar/icq.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifndef PURPLE_OSCAR_ICQ_H -#define PURPLE_OSCAR_ICQ_H - -#include "oscar.h" - -#define ICQ_TYPE_PROTOCOL (icq_protocol_get_type()) -#define ICQ_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ICQ_TYPE_PROTOCOL, ICQProtocol)) -#define ICQ_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ICQ_TYPE_PROTOCOL, ICQProtocolClass)) -#define ICQ_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ICQ_TYPE_PROTOCOL)) -#define ICQ_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ICQ_TYPE_PROTOCOL)) -#define ICQ_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ICQ_TYPE_PROTOCOL, ICQProtocolClass)) - -typedef struct -{ - OscarProtocol parent; -} ICQProtocol; - -typedef struct -{ - OscarProtocolClass parent_class; -} ICQProtocolClass; - -/** - * Registers the ICQProtocol type in the type system. - */ -G_GNUC_INTERNAL -void icq_protocol_register(PurplePlugin *plugin); - -/** - * Returns the GType for the ICQProtocol object. - */ -G_MODULE_EXPORT GType icq_protocol_get_type(void); - -#endif /* PURPLE_OSCAR_ICQ_H */
--- a/libpurple/protocols/oscar/kerberos.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,425 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/** - * This file implements AIM's kerberos procedure for authenticating - * users. This replaces the older MD5-based and XOR-based - * authentication methods that use SNAC family 0x0017. - * - * This doesn't use SNACs or FLAPs at all. It makes https - * POSTs to AOL KDC server to validate the user based on the password they - * provided to us. Upon successful authentication we receive two tokens - * in the response. One is assumed to be the kerberos ticket for authentication - * on the various AOL websites, while the other contains BOSS information, such - * as the hostname and port number to use, the TLS certificate name as well as - * the cookie to use to authenticate to the BOS server. - * And then everything else is the same as with BUCP. - * - */ - -#include "oscar.h" -#include "oscarcommon.h" -#include "core.h" - -#define MAXAIMPASSLEN 16 - -/* - * Incomplete X-SNAC format taken from reverse engineering doen by digsby: - * https://github.com/ifwe/digsby/blob/master/digsby/src/oscar/login2.py - */ -typedef struct { - aim_tlv_t *main_tlv; - gchar *principal1; - gchar *service; - gchar *principal1_again; - gchar *principal2; - gchar unknown; - guint8 *footer; - struct { - guint32 unknown1; - guint32 unknown2; - guint32 epoch_now; - guint32 epoch_valid; - guint32 epoch_renew; - guint32 epoch_expire; - guint32 unknown3; - guint32 unknown4; - guint32 unknown5; - } dates; - GSList *tlvlist; -} aim_xsnac_token_t; - -typedef struct { - guint16 family; - guint16 subtype; - guint8 flags[8]; - guint16 request_id; - guint32 epoch; - guint32 unknown; - gchar *principal1; - gchar *principal2; - guint16 num_tokens; - aim_xsnac_token_t *tokens; - GSList *tlvlist; -} aim_xsnac_t; - -static gchar *get_kdc_url(OscarData *od) -{ - PurpleAccount *account = purple_connection_get_account(od->gc); - const gchar *server; - gchar *url; - gchar *port_str = NULL; - gint port; - - server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER); - port = purple_account_get_int(account, "port", AIM_DEFAULT_KDC_PORT); - if (port != 443) - port_str = g_strdup_printf(":%d", port); - url = g_strdup_printf("https://%s%s/", server, port_str ? port_str : ""); - g_free(port_str); - - return url; -} - -static const char *get_client_key(OscarData *od) -{ - return oscar_get_ui_info_string( - od->icq ? "prpl-icq-clientkey" : "prpl-aim-clientkey", - od->icq ? ICQ_DEFAULT_CLIENT_KEY : AIM_DEFAULT_CLIENT_KEY); -} - -static void -aim_encode_password(const char *password, gchar *encoded) -{ - guint8 encoding_table[] = { - 0x76, 0x91, 0xc5, 0xe7, - 0xd0, 0xd9, 0x95, 0xdd, - 0x9e, 0x2F, 0xea, 0xd8, - 0x6B, 0x21, 0xc2, 0xbc, - - }; - guint i; - - /* - * We truncate AIM passwords to 16 characters since that's what - * the official client does as well. - */ - for (i = 0; i < strlen(password) && i < MAXAIMPASSLEN; i++) - encoded[i] = (password[i] ^ encoding_table[i]); -} - -static void -aim_xsnac_free(aim_xsnac_t *xsnac) -{ - gint i; - - g_free(xsnac->principal1); - g_free(xsnac->principal2); - aim_tlvlist_free(xsnac->tlvlist); - - for (i = 0; i < xsnac->num_tokens; i++) { - g_free(xsnac->tokens[i].main_tlv->value); - g_free(xsnac->tokens[i].main_tlv); - g_free(xsnac->tokens[i].principal1); - g_free(xsnac->tokens[i].service); - g_free(xsnac->tokens[i].principal1_again); - g_free(xsnac->tokens[i].principal2); - g_free(xsnac->tokens[i].footer); - aim_tlvlist_free(xsnac->tokens[i].tlvlist); - } - g_free(xsnac->tokens); -} - -static void -kerberos_login_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, - gpointer user_data) -{ - OscarData *od = user_data; - PurpleConnection *gc; - ByteStream bs; - aim_xsnac_t xsnac = {0}; - guint16 len; - gchar *bosip = NULL; - gchar *tlsCertName = NULL; - guint8 *cookie = NULL; - guint32 cookie_len = 0; - char *host; int port; - gsize i; - - gc = od->gc; - - g_clear_object(&od->http_conns); - - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { - gchar *tmp; - gchar *url; - - url = get_kdc_url(od); - tmp = g_strdup_printf(_("Error requesting %s: %d %s"), url, - msg->status_code, msg->reason_phrase); - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - g_free(url); - return; - } - - purple_debug_info("oscar", - "Received kerberos login HTTP response %" G_GOFFSET_FORMAT - " : ", - msg->response_body->length); - - byte_stream_init(&bs, (guint8 *)msg->response_body->data, - msg->response_body->length); - - xsnac.family = byte_stream_get16(&bs); - xsnac.subtype = byte_stream_get16(&bs); - byte_stream_getrawbuf(&bs, (guint8 *) xsnac.flags, 8); - - if (xsnac.family == 0x50C && xsnac.subtype == 0x0005) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Incorrect password")); - return; - } - if (xsnac.family != 0x50C || xsnac.subtype != 0x0003) { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Error parsing response from authentication server")); - return; - } - xsnac.request_id = byte_stream_get16(&bs); - xsnac.epoch = byte_stream_get32(&bs); - xsnac.unknown = byte_stream_get32(&bs); - len = byte_stream_get16(&bs); - xsnac.principal1 = byte_stream_getstr(&bs, len); - len = byte_stream_get16(&bs); - xsnac.principal2 = byte_stream_getstr(&bs, len); - xsnac.num_tokens = byte_stream_get16(&bs); - - purple_debug_info("oscar", "KDC: %d tokens between '%s' and '%s'\n", - xsnac.num_tokens, xsnac.principal1, xsnac.principal2); - xsnac.tokens = g_new0(aim_xsnac_token_t, xsnac.num_tokens); - for (i = 0; i < xsnac.num_tokens; i++) { - GSList *tlv; - - tlv = aim_tlvlist_readnum(&bs, 1); - if (tlv) - xsnac.tokens[i].main_tlv = tlv->data; - g_slist_free(tlv); - - len = byte_stream_get16(&bs); - xsnac.tokens[i].principal1 = byte_stream_getstr(&bs, len); - len = byte_stream_get16(&bs); - xsnac.tokens[i].service = byte_stream_getstr(&bs, len); - len = byte_stream_get16(&bs); - xsnac.tokens[i].principal1_again = byte_stream_getstr(&bs, len); - len = byte_stream_get16(&bs); - xsnac.tokens[i].principal2 = byte_stream_getstr(&bs, len); - xsnac.tokens[i].unknown = byte_stream_get8(&bs); - len = byte_stream_get16(&bs); - xsnac.tokens[i].footer = byte_stream_getraw(&bs, len); - - xsnac.tokens[i].dates.unknown1 = byte_stream_get32(&bs); - xsnac.tokens[i].dates.unknown2 = byte_stream_get32(&bs); - xsnac.tokens[i].dates.epoch_now = byte_stream_get32(&bs); - xsnac.tokens[i].dates.epoch_valid = byte_stream_get32(&bs); - xsnac.tokens[i].dates.epoch_renew = byte_stream_get32(&bs); - xsnac.tokens[i].dates.epoch_expire = byte_stream_get32(&bs); - xsnac.tokens[i].dates.unknown3 = byte_stream_get32(&bs); - xsnac.tokens[i].dates.unknown4 = byte_stream_get32(&bs); - xsnac.tokens[i].dates.unknown5 = byte_stream_get32(&bs); - - len = byte_stream_get16(&bs); - xsnac.tokens[i].tlvlist = aim_tlvlist_readnum(&bs, len); - - purple_debug_info("oscar", - "Token %" G_GSIZE_FORMAT - " has %d TLVs for service '%s'\n", - i, len, xsnac.tokens[i].service); - } - len = byte_stream_get16(&bs); - xsnac.tlvlist = aim_tlvlist_readnum(&bs, len); - - for (i = 0; i < xsnac.num_tokens; i++) { - if (purple_strequal(xsnac.tokens[i].service, "im/boss")) { - aim_tlv_t *tlv; - GSList *tlvlist; - ByteStream tbs; - - tlv = aim_tlv_gettlv(xsnac.tokens[i].tlvlist, 0x0003, 1); - if (tlv != NULL) { - byte_stream_init(&tbs, tlv->value, tlv->length); - byte_stream_get32(&tbs); - tlvlist = aim_tlvlist_read(&tbs); - if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) - bosip = aim_tlv_getstr(tlvlist, 0x0005, 1); - if (aim_tlv_gettlv(tlvlist, 0x0005, 1)) - tlsCertName = aim_tlv_getstr(tlvlist, 0x008D, 1); - tlv = aim_tlv_gettlv(tlvlist, 0x0006, 1); - if (tlv) { - cookie_len = tlv->length; - cookie = tlv->value; - } - } - break; - } - } - if (bosip && cookie) { - port = AIM_DEFAULT_KDC_PORT; - for (i = 0; i < strlen(bosip); i++) { - if (bosip[i] == ':') { - port = atoi(&(bosip[i+1])); - break; - } - } - host = g_strndup(bosip, i); - oscar_connect_to_bos(gc, od, host, port, cookie, cookie_len, tlsCertName); - g_free(host); - } else { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unknown error during authentication")); - } - aim_xsnac_free(&xsnac); - g_free(tlsCertName); - g_free(bosip); -} - -/** - * This function sends a binary blob request to the Kerberos KDC server - * https://kdc.uas.aol.com with the user's username and password and - * receives the IM cookie, which is used to request a connection to the - * BOSS server. - * The binary data below is what AIM 8.0.8.1 sends in order to authenticate - * to the KDC server. It is an 'X-SNAC' packet, which is relatively similar - * to SNAC packets but somehow different. - * The header starts with the 0x50C family follow by 0x0002 subtype, then - * some fixed length data and TLVs. The string "COOL" appears in there for - * some reason followed by the 'US' and 'en' strings. - * Then the 'imApp key=<client key>' comes after that, and then the username - * and the string "im/boss" which seems to represent the service we are - * requesting the authentication for. Changing that will lead to a - * 'unknown service' error. The client key is then added again (without the - * 'imApp key' string prepended to it) then a XOR-ed version of the password. - * The meaning of the header/footer/in-between bytes is not known but never - * seems to change so there is no need to reverse engineer their meaning at - * this point. - */ -void send_kerberos_login(OscarData *od, const char *username) -{ - PurpleConnection *gc; - SoupMessage *msg; - gchar *url; - const gchar *password; - gchar password_xored[MAXAIMPASSLEN]; - const gchar *client_key; - gchar *imapp_key; - GString *body; - guint16 len_be; - guint16 reqid; - const gchar header[] = { - 0x05, 0x0C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x18, 0x99, - 0x00, 0x05, 0x00, 0x04, 0x43, 0x4F, 0x4F, 0x4C, - 0x00, 0x0A, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0B, - 0x00, 0x04, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x55, 0x53, 0x00, 0x02, 0x65, 0x6E, 0x00, 0x04, - 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0D, - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x05}; - const gchar pre_username[] = { - 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x01, 0x8B, - 0x01, 0x00, 0x00, 0x00, 0x00}; - const gchar post_username[] = { - 0x00, 0x07, 0x69, 0x6D, 0x2F, 0x62, 0x6F, 0x73, - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x02}; - const gchar pre_password[] = { - 0x40, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, - 0x00, 0x00}; - const gchar post_password[] = {0x00, 0x00, 0x00, 0x1D}; - const gchar footer[] = { - 0x00, 0x21, 0x00, 0x32, 0x00, 0x01, 0x10, 0x03, - 0x00, 0x2C, 0x00, 0x07, 0x00, 0x14, 0x00, 0x04, - 0x00, 0x00, 0x01, 0x8B, 0x00, 0x16, 0x00, 0x02, - 0x00, 0x26, 0x00, 0x17, 0x00, 0x02, 0x00, 0x07, - 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x19, - 0x00, 0x02, 0x00, 0x0D, 0x00, 0x1A, 0x00, 0x02, - 0x00, 0x04, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x28, - 0x00, 0x00}; - - gc = od->gc; - - password = purple_connection_get_password(gc); - aim_encode_password(password, password_xored); - - client_key = get_client_key(od); - imapp_key = g_strdup_printf("imApp key=%s", client_key); - - /* Construct the body of the HTTP POST request */ - body = g_string_new(NULL); - g_string_append_len(body, header, sizeof(header)); - reqid = (guint16) g_random_int(); - g_string_overwrite_len(body, 0xC, (void *)&reqid, sizeof(guint16)); - - len_be = GUINT16_TO_BE(strlen(imapp_key)); - g_string_append_len(body, (void *)&len_be, sizeof(guint16)); - g_string_append(body, imapp_key); - - len_be = GUINT16_TO_BE(strlen(username)); - g_string_append_len(body, pre_username, sizeof(pre_username)); - g_string_append_len(body, (void *)&len_be, sizeof(guint16)); - g_string_append(body, username); - g_string_append_len(body, post_username, sizeof(post_username)); - - len_be = GUINT16_TO_BE(strlen(password) + 0x10); - g_string_append_len(body, (void *)&len_be, sizeof(guint16)); - g_string_append_len(body, pre_password, sizeof(pre_password)); - len_be = GUINT16_TO_BE(strlen(password) + 4); - g_string_append_len(body, (void *)&len_be, sizeof(guint16)); - len_be = GUINT16_TO_BE(strlen(password)); - g_string_append_len(body, (void *)&len_be, sizeof(guint16)); - g_string_append_len(body, password_xored, strlen(password)); - g_string_append_len(body, post_password, sizeof(post_password)); - - len_be = GUINT16_TO_BE(strlen(client_key)); - g_string_append_len(body, (void *)&len_be, sizeof(guint16)); - g_string_append(body, client_key); - g_string_append_len(body, footer, sizeof(footer)); - - g_free(imapp_key); - - url = get_kdc_url(od); - msg = soup_message_new("POST", url); - soup_message_set_request(msg, "application/x-snac", SOUP_MEMORY_TAKE, - body->str, body->len); - soup_message_headers_replace(msg->request_headers, "Accept", - "application/x-snac"); - soup_session_queue_message(od->http_conns, msg, kerberos_login_cb, od); - - g_string_free(body, FALSE); - g_free(url); -}
--- a/libpurple/protocols/oscar/meson.build Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -OSCARSOURCES = [ - 'authorization.c', - 'aim.c', - 'aim.h', - 'bstream.c', - 'clientlogin.c', - 'kerberos.c', - 'encoding.c', - 'encoding.h', - 'family_admin.c', - 'family_alert.c', - 'family_auth.c', - 'family_bart.c', - 'family_bos.c', - 'family_buddy.c', - 'family_chat.c', - 'family_chatnav.c', - 'family_icq.c', - 'family_icbm.c', - 'family_locate.c', - 'family_oservice.c', - 'family_popup.c', - 'family_feedbag.c', - 'family_stats.c', - 'family_userlookup.c', - 'flap_connection.c', - 'icq.c', - 'icq.h', - 'misc.c', - 'msgcookie.c', - 'odc.c', - 'oft.c', - 'oscar.c', - 'oscar.h', - 'oscarcommon.h', - 'oscar_data.c', - 'peer.c', - 'peer.h', - 'peer_proxy.c', - 'rxhandlers.c', - 'snac.c', - 'snactypes.h', - 'tlv.c', - 'userinfo.c', - 'util.c', - 'visibility.c', - 'visibility.h' -] - -if IS_WIN32 - oscar_link_args = ['-Wl,--export-all-symbols'] -else - oscar_link_args = [] -endif - -if DYNAMIC_OSCAR - oscar_prpl = shared_library('oscar', OSCARSOURCES, - link_args : oscar_link_args, - dependencies : [libpurple_dep, glib, libsoup, ws2_32], - install : true, install_dir : PURPLE_PLUGINDIR) - - subdir('tests') -endif
--- a/libpurple/protocols/oscar/misc.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Random stuff. Basically just a few functions for sending - * simple SNACs, and then the generic error handler. - */ - -#include "oscar.h" - -/* - * Generic routine for sending commands. - * - * I know I can do this in a smarter way...but I'm not thinking straight - * right now... - * - * I had one big function that handled all three cases, but then it broke - * and I split it up into three. But then I fixed it. I just never went - * back to the single. I don't see any advantage to doing it either way. - * - */ -void -aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype) -{ - aim_snacid_t snacid = 0x00000000; - - flap_connection_send_snac(od, conn, family, subtype, snacid, NULL); -} - -void -aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype) -{ - aim_snacid_t snacid; - - snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0); - - flap_connection_send_snac(od, conn, family, subtype, snacid, NULL); -} - -void -aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *longdata) -{ - ByteStream bs; - aim_snacid_t snacid; - - if (!longdata) - { - aim_genericreq_n(od, conn, family, subtype); - return; - } - - byte_stream_new(&bs, 4); - - snacid = aim_cachesnac(od, family, subtype, 0x0000, NULL, 0); - - byte_stream_put32(&bs, *longdata); - - flap_connection_send_snac(od, conn, family, subtype, snacid, &bs); - - byte_stream_destroy(&bs); -} - -/* - * Should be generic enough to handle the errors for all groups. - * - */ -static int -generror(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - int ret = 0; - int error = 0; - aim_rxcallback_t userfunc; - aim_snac_t *snac2; - - snac2 = aim_remsnac(od, snac->id); - - if (byte_stream_bytes_left(bs)) - error = byte_stream_get16(bs); - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, error, snac2 ? snac2->data : NULL); - - if (snac2) { - g_free(snac2->data); - g_free(snac2); - } - - return ret; -} - -static int -snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) -{ - if (snac->subtype == 0x0001) - return generror(od, conn, mod, frame, snac, bs); - else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) { - aim_rxcallback_t userfunc; - - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - return userfunc(od, conn, frame); - } - - return 0; -} - -int -misc_modfirst(OscarData *od, aim_module_t *mod) -{ - mod->family = 0xffff; - mod->version = 0x0000; - mod->flags = AIM_MODFLAG_MULTIFAMILY; - strncpy(mod->name, "misc", sizeof(mod->name)); - mod->snachandler = snachandler; - - return 0; -}
--- a/libpurple/protocols/oscar/msgcookie.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Cookie Caching stuff. Adam wrote this, apparently just some - * derivatives of n's SNAC work. I cleaned it up, added comments. - * - */ - -/* - * I'm assuming that cookies are type-specific. that is, we can have - * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we - * lose some error checking. if we assume cookies are not type-specific and are - * wrong, we get quirky behavior when cookies step on each others' toes. - */ - -#include "oscar.h" - -/** - * aim_cachecookie - appends a cookie to the cookie list - * - * if cookie->cookie for type cookie->type is found, updates the - * ->addtime of the found structure; otherwise adds the given cookie - * to the cache - * - * @param od session to add to - * @param cookie pointer to struct to append - * @return returns -1 on error, 0 on append, 1 on update. the cookie you pass - * in may be free'd, so don't count on its value after calling this! - */ -int aim_cachecookie(OscarData *od, IcbmCookie *cookie) -{ - IcbmCookie *newcook; - - if (!od || !cookie) - return -EINVAL; - - newcook = aim_checkcookie(od, cookie->cookie, cookie->type); - - if (newcook == cookie) { - newcook->addtime = time(NULL); - return 1; - } else if (newcook) - aim_cookie_free(od, newcook); - - cookie->addtime = time(NULL); - - cookie->next = od->msgcookies; - od->msgcookies = cookie; - - return 0; -} - -/** - * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list) - * - * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process. - * - * @param od session to grab cookie from - * @param cookie cookie string to look for - * @param type cookie type to look for - * @return if found, returns the struct; if none found (or on error), returns NULL: - */ -IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type) -{ - IcbmCookie *cur, **prev; - - if (!cookie || !od->msgcookies) - return NULL; - - for (prev = &od->msgcookies; (cur = *prev); ) { - if ((cur->type == type) && - (memcmp(cur->cookie, cookie, 8) == 0)) { - *prev = cur->next; - return cur; - } - prev = &cur->next; - } - - return NULL; -} - -/** - * aim_mkcookie - generate an IcbmCookie *struct from a cookie string, a type, and a data pointer. - * - * @param c pointer to the cookie string array - * @param type cookie type to use - * @param data data to be cached with the cookie - * @return returns NULL on error, a pointer to the newly-allocated - * cookie on success. - */ -IcbmCookie *aim_mkcookie(guint8 *c, int type, void *data) -{ - IcbmCookie *cookie; - - if (!c) - return NULL; - - cookie = g_new0(IcbmCookie, 1); - - cookie->data = data; - cookie->type = type; - memcpy(cookie->cookie, c, 8); - - return cookie; -} - -/** - * aim_checkcookie - check to see if a cookietuple has been cached - * - * @param od session to check for the cookie in - * @param cookie pointer to the cookie string array - * @param type type of the cookie to look for - * @return returns a pointer to the cookie struct (still in the list) - * on success; returns NULL on error/not found - */ - -IcbmCookie *aim_checkcookie(OscarData *od, const guint8 *cookie, const int type) -{ - IcbmCookie *cur; - - for (cur = od->msgcookies; cur; cur = cur->next) { - if ((cur->type == type) && - (memcmp(cur->cookie, cookie, 8) == 0)) - return cur; - } - - return NULL; -} - -/** - * aim_cookie_free - free an IcbmCookie struct - * - * this function removes the cookie *cookie from the list of cookies - * in od, and then frees all memory associated with it. including - * its data! if you want to use the private data after calling this, - * make sure you copy it first. - * - * @param od session to remove the cookie from - * @param cookie the address of a pointer to the cookie struct to remove - * @return returns -1 on error, 0 on success. - * - */ -int aim_cookie_free(OscarData *od, IcbmCookie *cookie) -{ - IcbmCookie *cur, **prev; - - if (!od || !cookie) - return -EINVAL; - - for (prev = &od->msgcookies; (cur = *prev); ) { - if (cur == cookie) - *prev = cur->next; - else - prev = &cur->next; - } - - g_free(cookie->data); - g_free(cookie); - - return 0; -}
--- a/libpurple/protocols/oscar/odc.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,626 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* From the oscar PRPL */ -#include "encoding.h" -#include "oscar.h" -#include "peer.h" - -/* From Purple */ -#include "conversation.h" -#include "image-store.h" -#include "util.h" - -#define DIRECTIM_MAX_FILESIZE 52428800 - -/** - * Free any ODC related data and print a message to the conversation - * window based on conn->disconnect_reason. - */ -void -peer_odc_close(PeerConnection *conn) -{ - gchar *tmp; - - if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) - tmp = g_strdup(_("The remote user has closed the connection.")); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED) - tmp = g_strdup(_("The remote user has declined your request.")); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION) - tmp = g_strdup_printf(_("Lost connection with the remote user:<br>%s"), - conn->error_message); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA) - tmp = g_strdup(_("Received invalid data on connection with remote user.")); - else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT) - tmp = g_strdup(_("Unable to establish a connection with the remote user.")); - else - /* - * We shouldn't print a message for some disconnect_reasons. - * Like OSCAR_DISCONNECT_LOCAL_CLOSED. - */ - tmp = NULL; - - if (tmp != NULL) - { - PurpleAccount *account; - PurpleIMConversation *im; - - account = purple_connection_get_account(conn->od->gc); - im = purple_im_conversation_new(account, conn->bn); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), tmp, 0); - g_free(tmp); - } - - if (conn->frame != NULL) - { - OdcFrame *frame; - frame = conn->frame; - g_free(frame->payload.data); - g_free(frame); - } -} - -/** - * Write the given OdcFrame to a ByteStream and send it out - * on the established PeerConnection. - */ -static void -peer_odc_send(PeerConnection *conn, OdcFrame *frame) -{ - PurpleAccount *account; - const char *username; - size_t length; - ByteStream bs; - - purple_debug_info("oscar", "Outgoing ODC frame to %s with " - "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n", - conn->bn, frame->type, frame->flags, frame->payload.len); - - account = purple_connection_get_account(conn->od->gc); - username = purple_account_get_username(account); - memcpy(frame->bn, username, strlen(username)); - memcpy(frame->cookie, conn->cookie, 8); - - length = 76; - byte_stream_new(&bs, length + frame->payload.len); - byte_stream_putraw(&bs, conn->magic, 4); - byte_stream_put16(&bs, length); - byte_stream_put16(&bs, frame->type); - byte_stream_put16(&bs, frame->subtype); - byte_stream_put16(&bs, 0x0000); - byte_stream_putraw(&bs, frame->cookie, 8); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, 0x0000); - byte_stream_put32(&bs, frame->payload.len); - byte_stream_put16(&bs, frame->encoding); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, frame->flags); - byte_stream_put16(&bs, 0x0000); - byte_stream_put16(&bs, 0x0000); - byte_stream_putraw(&bs, frame->bn, 32); - byte_stream_putraw(&bs, frame->payload.data, frame->payload.len); - - peer_connection_send(conn, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Send a very basic ODC frame (which contains the cookie) so that the - * remote user can verify that we are the person they were expecting. - * If we made an outgoing connection to then remote user, then we send - * this immediately. If the remote user connected to us, then we wait - * for the other person to send this to us, then we send one to them. - */ -void -peer_odc_send_cookie(PeerConnection *conn) -{ - OdcFrame frame; - - memset(&frame, 0, sizeof(OdcFrame)); - frame.type = 0x0001; - frame.subtype = 0x0006; - frame.flags = 0x0060; /* Maybe this means "we're sending the cookie"? */ - - peer_odc_send(conn, &frame); -} - -/** - * Send client-to-client typing notification over an established direct connection. - */ -void -peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing) -{ - OdcFrame frame; - - memset(&frame, 0, sizeof(OdcFrame)); - frame.type = 0x0001; - frame.subtype = 0x0006; - if (typing == PURPLE_IM_TYPING) - frame.flags = 0x0002 | 0x0008; - else if (typing == PURPLE_IM_TYPED) - frame.flags = 0x0002 | 0x0004; - else - frame.flags = 0x0002; - - peer_odc_send(conn, &frame); -} - -/** - * Send client-to-client IM over an established direct connection. - * To send a direct IM, call this just like you would aim_send_im. - * - * @param conn The already-connected ODC connection. - * @param msg Null-terminated string to send. - * @param len The length of the message to send, including binary data. - * @param encoding See the AIM_CHARSET_* defines in oscar.h - * @param autoreply TRUE if this is any auto-reply. - */ -void -peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply) -{ - OdcFrame frame; - - g_return_if_fail(msg != NULL); - g_return_if_fail(len > 0); - - memset(&frame, 0, sizeof(OdcFrame)); - frame.type = 0x0001; - frame.subtype = 0x0006; - frame.payload.len = len; - frame.encoding = encoding; - frame.flags = autoreply; - byte_stream_new(&frame.payload, len); - byte_stream_putraw(&frame.payload, (guint8 *)msg, len); - - peer_odc_send(conn, &frame); - - g_free(frame.payload.data); -} - -struct embedded_data -{ - size_t size; - const guint8 *data; -}; - -/** - * This is called after a direct IM has been received in its entirety. This - * function is passed a long chunk of data which contains the IM with any - * data chunks (images) appended to it. - * - * This function rips out all the data chunks and creates a PurpleImage (?) for - * each one. In order to do this, it first goes through the IM and takes - * out all the IMG tags. When doing so, it rewrites the original IMG tag - * with one compatible with the PurpleImage (?) code. For each one, we - * then read in chunks of data from the end of the message and actually - * create the img store using the given data. - * - * For somewhat easy reference, here's a sample message - * (with added whitespace): - * - * <HTML><BODY BGCOLOR="#ffffff"> - * <FONT LANG="0"> - * This is a really stupid picture:<BR> - * <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR> - * Yeah it is<BR> - * Here is another one:<BR> - * <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978"> - * </FONT> - * </BODY></HTML> - * <BINARY> - * <DATA ID="1" SIZE="9894">datadatadatadata</DATA> - * <DATA ID="2" SIZE="65978">datadatadatadata</DATA> - * </BINARY> - */ -static void -peer_odc_handle_payload(PeerConnection *conn, const char *msg, size_t len, int encoding, gboolean autoreply) -{ - PurpleConnection *gc; - PurpleAccount *account; - const char *msgend, *binary_start, *dataend; - const char *tmp, *start, *end, *idstr, *src, *sizestr; - GData *attributes; - GHashTable *embedded_datas; - struct embedded_data *embedded_data; - gboolean any_images = FALSE; - gchar *utf8; - GString *newmsg; - PurpleMessageFlags imflags; - - gc = conn->od->gc; - account = purple_connection_get_account(gc); - - dataend = msg + len; - - /* - * Create a hash table containing references to each embedded - * data chunk. The key is the "ID" and the value is an - * embedded_data struct. - */ - embedded_datas = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, g_free); - - /* - * Create an index of any binary chunks. If we run into any - * problems while parsing the binary data section then we stop - * parsing it, and the local user will see broken image icons. - */ - binary_start = purple_strcasestr(msg, "<binary>"); - if (binary_start == NULL) - msgend = dataend; - else - { - msgend = binary_start; - - /* Move our pointer to immediately after the <binary> tag */ - tmp = binary_start + 8; - - /* The embedded binary markup has a mimimum length of 29 bytes */ - while ((tmp + 29 <= dataend) && - purple_markup_find_tag("data", tmp, &start, &tmp, &attributes)) - { - unsigned int id; - size_t size; - - /* Move the binary pointer from ">" to the start of the data */ - tmp++; - - /* Get the ID */ - idstr = g_datalist_get_data(&attributes, "id"); - if (idstr == NULL) - { - g_datalist_clear(&attributes); - break; - } - id = atoi(idstr); - - /* Get the size */ - sizestr = g_datalist_get_data(&attributes, "size"); - if (sizestr == NULL) - { - g_datalist_clear(&attributes); - break; - } - size = atol(sizestr); - - g_datalist_clear(&attributes); - - if ((size > 0) && (tmp + size > dataend)) - break; - - embedded_data = g_new(struct embedded_data, 1); - embedded_data->size = size; - embedded_data->data = (const guint8 *)tmp; - tmp += size; - - /* Skip past the closing </data> tag */ - if (g_ascii_strncasecmp(tmp, "</data>", 7)) - { - g_free(embedded_data); - break; - } - tmp += 7; - - g_hash_table_insert(embedded_datas, - GINT_TO_POINTER(id), embedded_data); - } - } - - /* - * Loop through the message, replacing OSCAR img tags with the - * equivalent Purple img tag. - */ - newmsg = g_string_new(""); - tmp = msg; - while (purple_markup_find_tag("img", tmp, &start, &end, &attributes)) - { - PurpleImage *image = NULL; - - idstr = g_datalist_get_data(&attributes, "id"); - src = g_datalist_get_data(&attributes, "src"); - sizestr = g_datalist_get_data(&attributes, "datasize"); - - if ((idstr != NULL) && (src != NULL) && (sizestr!= NULL)) - { - unsigned int id; - size_t size; - - id = atoi(idstr); - size = atol(sizestr); - embedded_data = g_hash_table_lookup(embedded_datas, - GINT_TO_POINTER(id)); - - if ((embedded_data != NULL) && (embedded_data->size == size)) - { - image = purple_image_new_from_data( - embedded_data->data, - size); - purple_image_set_friendly_filename(image, src); - } - } - - /* Delete the attribute list */ - g_datalist_clear(&attributes); - - /* Append the message up to the tag */ - utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, start - tmp); - if (utf8 != NULL) { - g_string_append(newmsg, utf8); - g_free(utf8); - } - - if (image) - { - guint img_id; - - img_id = purple_image_store_add_temporary(image); - g_object_unref(image); - any_images = TRUE; - - /* Write the new image tag */ - g_string_append_printf(newmsg, "<img src=\"" - PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id); - } - - /* Continue from the end of the tag */ - tmp = end + 1; - } - - /* Append any remaining message data */ - if (tmp <= msgend) - { - utf8 = oscar_decode_im(account, conn->bn, encoding, tmp, msgend - tmp); - if (utf8 != NULL) { - g_string_append(newmsg, utf8); - g_free(utf8); - } - } - - /* Display the message we received */ - imflags = 0; - if (any_images) - imflags |= PURPLE_MESSAGE_IMAGES; - if (autoreply) - imflags |= PURPLE_MESSAGE_AUTO_RESP; - purple_serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL)); - g_string_free(newmsg, TRUE); - - /* Delete our list of pointers to embedded images */ - g_hash_table_destroy(embedded_datas); -} - -/** - * This is a purple_input_add() watcher callback function for reading - * direct IM payload data. "Payload data" is always an IM and - * maybe some embedded images or files or something. The actual - * ODC frame is read using peer_connection_recv_cb(). We temporarily - * switch to this watcher callback ONLY to read the payload, and we - * switch back once we're done. - */ -static void -peer_odc_recv_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - PeerConnection *conn; - OdcFrame *frame; - ByteStream *bs; - gssize read; - - conn = data; - frame = conn->frame; - bs = &frame->payload; - - /* Read data into the temporary buffer until it is complete */ - read = recv(conn->fd, - &bs->data[bs->offset], - bs->len - bs->offset, - 0); - - /* Check if the remote user closed the connection */ - if (read == 0) - { - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - return; - } - - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - return; - - peer_connection_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); - return; - } - - bs->offset += read; - if (bs->offset < bs->len) - /* Waiting for more data to arrive */ - return; - /* TODO: Instead of null-terminating this, it would be better if we just - respected the length of the buffer when parsing it. But it doesn't - really matter and this is easy. */ - bs->data[bs->len] = '\0'; - - /* We have a complete ODC/OFT frame! Handle it and continue reading */ - byte_stream_rewind(bs); - peer_odc_handle_payload(conn, (const char *)bs->data, - bs->len, frame->encoding, frame->flags & 0x0001); - g_free(bs->data); - bs->data = NULL; - g_free(frame); - conn->frame = NULL; - - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, peer_connection_recv_cb, conn); -} - -/** - * Handle an incoming OdcFrame. If there is a payload associated - * with this frame, then we remove the old watcher and add the - * ODC watcher to read in the payload. - */ -void -peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs) -{ - PurpleConnection *gc; - OdcFrame *frame; - - gc = conn->od->gc; - - frame = g_new0(OdcFrame, 1); - frame->type = byte_stream_get16(bs); - frame->subtype = byte_stream_get16(bs); - byte_stream_advance(bs, 2); - byte_stream_getrawbuf(bs, frame->cookie, 8); - byte_stream_advance(bs, 8); - frame->payload.len = byte_stream_get32(bs); - frame->encoding = byte_stream_get16(bs); - byte_stream_advance(bs, 4); - frame->flags = byte_stream_get16(bs); - byte_stream_advance(bs, 4); - byte_stream_getrawbuf(bs, frame->bn, 32); - - purple_debug_info("oscar", "Incoming ODC frame from %s with " - "type=0x%04x, flags=0x%04x, payload length=%" G_GSIZE_FORMAT "\n", - frame->bn, frame->type, frame->flags, frame->payload.len); - - if (!conn->ready) - { - /* - * We need to verify the cookie so that we know we are - * connected to our friend and not a malicious middle man. - */ - - PurpleAccount *account; - PurpleIMConversation *im; - - if (conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING) - { - if (memcmp(conn->cookie, frame->cookie, 8)) - { - /* - * Oh no! The user that connected to us did not send - * the correct cookie! They are not our friend. Go try - * to accept another connection? - */ - purple_debug_info("oscar", "Received an incorrect cookie. " - "Closing connection.\n"); - peer_connection_destroy(conn, - OSCAR_DISCONNECT_INVALID_DATA, NULL); - g_free(frame); - return; - } - - /* - * Ok, we know they are legit. Now be courteous and - * send them our cookie. Note: This doesn't seem - * to be necessary, but it also doesn't seem to hurt. - */ - peer_odc_send_cookie(conn); - } - - conn->ready = TRUE; - - /* - * If they connected to us then close the listener socket - * and send them our cookie. - */ - if (conn->listenerfd != -1) - { - close(conn->listenerfd); - conn->listenerfd = -1; - } - - /* Tell the local user that we are connected */ - account = purple_connection_get_account(gc); - im = purple_im_conversation_new(account, conn->bn); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), _("Direct IM established"), 0); - } - - if ((frame->type != 0x0001) && (frame->subtype != 0x0006)) - { - purple_debug_info("oscar", "Unknown ODC frame type 0x%04hx, " - "subtype 0x%04hx.\n", frame->type, frame->subtype); - g_free(frame); - return; - } - - if (frame->flags & 0x0008) - { - /* I had to leave this. It's just too funny. It reminds me of my sister. */ - purple_debug_info("oscar", "ohmigod! %s has started typing " - "(DirectIM). He's going to send you a message! " - "*squeal*\n", conn->bn); - purple_serv_got_typing(gc, conn->bn, 0, PURPLE_IM_TYPING); - } - else if (frame->flags & 0x0004) - { - purple_serv_got_typing(gc, conn->bn, 0, PURPLE_IM_TYPED); - } - else - { - purple_serv_got_typing_stopped(gc, conn->bn); - } - - if (frame->payload.len > 0) - { - if (frame->payload.len > DIRECTIM_MAX_FILESIZE) - { - gchar *tmp, *size1, *size2; - PurpleAccount *account; - PurpleIMConversation *im; - - size1 = g_format_size(frame->payload.len); - size2 = g_format_size(DIRECTIM_MAX_FILESIZE); - tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM. Try using file transfer instead.\n"), conn->bn, size1, size2); - g_free(size1); - g_free(size2); - - account = purple_connection_get_account(conn->od->gc); - im = purple_im_conversation_new(account, conn->bn); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), tmp, 0); - g_free(tmp); - - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); - g_free(frame); - return; - } - - /* We have payload data! Switch to the ODC watcher to read it. */ - frame->payload.data = g_new(guint8, frame->payload.len + 1); - frame->payload.offset = 0; - conn->frame = frame; - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, peer_odc_recv_cb, conn); - return; - } - - g_free(frame); -}
--- a/libpurple/protocols/oscar/oft.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,823 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * I feel like this is a good place to explain OFT, so I'm going to - * do just that. Each OFT packet has a header type. I guess this - * is pretty similar to the subtype of a SNAC packet. The type - * basically tells the other client the meaning of the OFT packet. - * There are two distinct types of file transfer, which I usually - * call "sendfile" and "getfile." Sendfile is when you send a file - * to another AIM user. Getfile is when you share a group of files, - * and other users request that you send them the files. - * - * A typical sendfile file transfer goes like this: - * 1) Sender sends a channel 2 ICBM telling the other user that - * we want to send them a file. At the same time, we open a - * listener socket (this should be done before sending the - * ICBM) on some port, and wait for them to connect to us. - * The ICBM we sent should contain our IP address and the port - * number that we're listening on. - * 2) The receiver connects to the sender on the given IP address - * and port. After the connection is established, the receiver - * sends an ICBM signifying that we are ready and waiting. - * 3) The sender sends an OFT PROMPT message over the OFT - * connection. - * 4) The receiver of the file sends back an exact copy of this - * OFT packet, except the cookie is filled in with the cookie - * from the ICBM. I think this might be an attempt to verify - * that the user that is connected is actually the guy that - * we sent the ICBM to. Oh, I've been calling this the ACK. - * 5) The sender starts sending raw data across the connection - * until the entire file has been sent. - * 6) The receiver knows the file is finished because the sender - * sent the file size in an earlier OFT packet. So then the - * receiver sends the DONE thingy (after filling in the - * "received" checksum and size) and closes the connection. - */ - -#include "oscar.h" -#include "peer.h" - -#include "util.h" - -#define CHECKSUM_BUFFER_SIZE 256 * 1024 - -struct _ChecksumData -{ - PeerConnection *conn; - PurpleXfer *xfer; - GSourceFunc callback; - size_t size; - guint32 checksum; - size_t total; - FILE *file; - guint8 buffer[CHECKSUM_BUFFER_SIZE]; - guint timer; -}; - -void -peer_oft_checksum_destroy(ChecksumData *checksum_data) -{ - checksum_data->conn->checksum_data = NULL; - fclose(checksum_data->file); - if (checksum_data->timer > 0) - g_source_remove(checksum_data->timer); - g_free(checksum_data); -} - -/** - * Calculate oft checksum of buffer - * - * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The - * checksum is kind of a rolling checksum thing, so each time you get bytes - * of a file you just call this puppy and it updates the checksum. You can - * calculate the checksum of an entire file by calling this in a while or a - * for loop, or something. - * - * Thanks to Graham Booker for providing this improved checksum routine, - * which is simpler and should be more accurate than Josh Myer's original - * code. -- wtm - * - * This algorithm works every time I have tried it. The other fails - * sometimes. So, AOL who thought this up? It has got to be the weirdest - * checksum I have ever seen. - * - * @param buffer Buffer of data to checksum. Man I'd like to buff her... - * @param bufsize Size of buffer. - * @param prevchecksum Previous checksum. - * @param odd Whether an odd number of bytes have been processed before this call - */ -static guint32 -peer_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevchecksum, int odd) -{ - guint32 checksum, oldchecksum; - int i = 0; - unsigned short val; - - checksum = (prevchecksum >> 16) & 0xffff; - if (odd) - { - /* - * This is one hell of a hack, but it should always work. - * Essentially, I am reindexing the array so that index 1 - * is the first element. Since the odd and even bytes are - * detected by the index number. - */ - i = 1; - bufferlen++; - buffer--; - } - for (; i < bufferlen; i++) - { - oldchecksum = checksum; - if (i & 1) - val = buffer[i]; - else - val = buffer[i] << 8; - checksum -= val; - /* - * The following appears to be necessary.... It happens - * every once in a while and the checksum doesn't fail. - */ - if (checksum > oldchecksum) - checksum--; - } - checksum = ((checksum & 0x0000ffff) + (checksum >> 16)); - checksum = ((checksum & 0x0000ffff) + (checksum >> 16)); - return checksum << 16; -} - -static gboolean -peer_oft_checksum_file_piece(gpointer data) -{ - ChecksumData *checksum_data; - gboolean repeat; - - checksum_data = data; - repeat = FALSE; - - if (checksum_data->total < checksum_data->size) - { - size_t bytes = MIN(CHECKSUM_BUFFER_SIZE, - checksum_data->size - checksum_data->total); - - bytes = fread(checksum_data->buffer, 1, bytes, checksum_data->file); - if (bytes != 0) - { - checksum_data->checksum = peer_oft_checksum_chunk(checksum_data->buffer, bytes, checksum_data->checksum, checksum_data->total & 1); - checksum_data->total += bytes; - repeat = TRUE; - } - } - - if (!repeat) - { - purple_debug_info("oscar", "Checksum of %s calculated\n", - purple_xfer_get_local_filename(checksum_data->xfer)); - if (checksum_data->callback != NULL) - checksum_data->callback(checksum_data); - peer_oft_checksum_destroy(checksum_data); - } - - return repeat; -} - -/** - * Calculate oft checksum of a file in a series of calls to - * peer_oft_checksum_file_piece(). We do it this way because - * calculating the checksum on large files can take a long time, - * and we want to return control to the UI so that the application - * doesn't appear completely frozen. - * - * @param conn The connection used for this file transfer. - * @param xfer The file transfer needing this checksum. - * @param callback The function to call upon calculation of the checksum. - * @param size The maximum size to check. - */ - -static void -peer_oft_checksum_file(PeerConnection *conn, PurpleXfer *xfer, GSourceFunc callback, size_t size) -{ - ChecksumData *checksum_data; - - purple_debug_info("oscar", "Calculating checksum of %s\n", - purple_xfer_get_local_filename(xfer)); - - checksum_data = g_malloc0(sizeof(ChecksumData)); - checksum_data->conn = conn; - checksum_data->xfer = xfer; - checksum_data->callback = callback; - checksum_data->size = size; - checksum_data->checksum = 0xffff0000; - checksum_data->file = g_fopen(purple_xfer_get_local_filename(xfer), "rb"); - - if (checksum_data->file == NULL) - { - purple_debug_error("oscar", "Unable to open %s for checksumming: %s\n", - purple_xfer_get_local_filename(xfer), g_strerror(errno)); - callback(checksum_data); - g_free(checksum_data); - } - else - { - checksum_data->timer = g_timeout_add(10, - peer_oft_checksum_file_piece, checksum_data); - conn->checksum_data = checksum_data; - } -} - -static void -peer_oft_copy_xfer_data(PeerConnection *conn, OftFrame *frame) -{ - g_free(conn->xferdata.name); - - memcpy(&(conn->xferdata), frame, sizeof(OftFrame)); - conn->xferdata.name = g_memdup(frame->name, frame->name_length); -} - -/** - * Free any OFT related data. - */ -void -peer_oft_close(PeerConnection *conn) -{ - /* - * If cancelled by local user, and we're receiving a file, and - * we're not connected/ready then send an ICBM cancel message. - */ - if ((purple_xfer_get_status(conn->xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) && - !conn->ready) - { - aim_im_sendch2_cancel(conn); - } - - if (conn->sending_data_timer != 0) - { - g_source_remove(conn->sending_data_timer); - conn->sending_data_timer = 0; - } -} - -/** - * Write the given OftFrame to a ByteStream and send it out - * on the established PeerConnection. - */ -static void -peer_oft_send(PeerConnection *conn, OftFrame *frame) -{ - size_t length; - ByteStream bs; - - length = 192 + frame->name_length; - byte_stream_new(&bs, length); - byte_stream_putraw(&bs, conn->magic, 4); - byte_stream_put16(&bs, length); - byte_stream_put16(&bs, frame->type); - byte_stream_putraw(&bs, frame->cookie, 8); - byte_stream_put16(&bs, frame->encrypt); - byte_stream_put16(&bs, frame->compress); - byte_stream_put16(&bs, frame->totfiles); - byte_stream_put16(&bs, frame->filesleft); - byte_stream_put16(&bs, frame->totparts); - byte_stream_put16(&bs, frame->partsleft); - byte_stream_put32(&bs, frame->totsize); - byte_stream_put32(&bs, frame->size); - byte_stream_put32(&bs, frame->modtime); - byte_stream_put32(&bs, frame->checksum); - byte_stream_put32(&bs, frame->rfrcsum); - byte_stream_put32(&bs, frame->rfsize); - byte_stream_put32(&bs, frame->cretime); - byte_stream_put32(&bs, frame->rfcsum); - byte_stream_put32(&bs, frame->nrecvd); - byte_stream_put32(&bs, frame->recvcsum); - byte_stream_putraw(&bs, frame->idstring, 32); - byte_stream_put8(&bs, frame->flags); - byte_stream_put8(&bs, frame->lnameoffset); - byte_stream_put8(&bs, frame->lsizeoffset); - byte_stream_putraw(&bs, frame->dummy, 69); - byte_stream_putraw(&bs, frame->macfileinfo, 16); - byte_stream_put16(&bs, frame->nencode); - byte_stream_put16(&bs, frame->nlanguage); - /* - * The name can be more than 64 characters, but if it is less than - * 64 characters it is padded with NULLs. - */ - byte_stream_putraw(&bs, frame->name, frame->name_length); - - peer_connection_send(conn, &bs); - - byte_stream_destroy(&bs); -} - -void -peer_oft_send_prompt(PeerConnection *conn) -{ - conn->xferdata.type = PEER_TYPE_PROMPT; - peer_oft_send(conn, &conn->xferdata); -} - -static void -peer_oft_send_ack(PeerConnection *conn) -{ - conn->xferdata.type = PEER_TYPE_ACK; - - /* Fill in the cookie */ - memcpy(conn->xferdata.cookie, conn->cookie, 8); - - peer_oft_send(conn, &conn->xferdata); -} - -static void -peer_oft_send_resume_accept(PeerConnection *conn) -{ - conn->xferdata.type = PEER_TYPE_RESUMEACCEPT; - - /* Fill in the cookie */ - memcpy(conn->xferdata.cookie, conn->cookie, 8); - - peer_oft_send(conn, &conn->xferdata); -} - -static void -peer_oft_send_done(PeerConnection *conn) -{ - conn->xferdata.type = PEER_TYPE_DONE; - conn->xferdata.rfrcsum = 0xffff0000; - conn->xferdata.nrecvd = purple_xfer_get_bytes_sent(conn->xfer); - peer_oft_send(conn, &conn->xferdata); -} - -/** - * This function exists so that we don't remove the outgoing - * data watcher while we're still sending data. In most cases - * any data we're sending will be instantly wisked away to a TCP - * buffer maintained by our operating system... but we want to - * make sure the core doesn't start sending file data while - * we're still sending OFT frame data. That would be bad. - */ -static gboolean -start_transfer_when_done_sending_data(gpointer data) -{ - PeerConnection *conn; - - conn = data; - - if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0) - { - int fd = conn->fd; - conn->sending_data_timer = 0; - conn->fd = -1; - purple_xfer_start(conn->xfer, fd, NULL, 0); - return FALSE; - } - - return TRUE; -} - -/** - * This function is similar to the above function, except instead - * of starting the xfer it will destroy the connection. This is - * used when you want to send one final message across the peer - * connection, and then close everything. - */ -static gboolean -destroy_connection_when_done_sending_data(gpointer data) -{ - PeerConnection *conn; - - conn = data; - - if (purple_circular_buffer_get_max_read(conn->buffer_outgoing) == 0) - { - conn->sending_data_timer = 0; - peer_connection_destroy(conn, conn->disconnect_reason, NULL); - return FALSE; - } - - return TRUE; -} - -/* - * This is called when a buddy sends us some file info. This happens when they - * are sending a file to you, and you have just established a connection to them. - * You should send them the exact same info except use the real cookie. We also - * get like totally ready to like, receive the file, kay? - */ -static void -peer_oft_recv_frame_prompt(PeerConnection *conn, OftFrame *frame) -{ - /* Record the file information and send an ack */ - peer_oft_copy_xfer_data(conn, frame); - peer_oft_send_ack(conn); - - /* Remove our watchers and use the file transfer watchers in the core */ - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = 0; - conn->sending_data_timer = g_timeout_add(100, - start_transfer_when_done_sending_data, conn); -} - -/** - * We are sending a file to someone else. They have just acknowledged our - * prompt, so we want to start sending data like there's no tomorrow. - */ -static void -peer_oft_recv_frame_ack(PeerConnection *conn, OftFrame *frame) -{ - if (memcmp(conn->cookie, frame->cookie, 8) != 0) - { - purple_debug_info("oscar", "Received an incorrect cookie. " - "Closing connection.\n"); - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL); - return; - } - - /* Remove our watchers and use the file transfer watchers in the core */ - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = 0; - conn->sending_data_timer = g_timeout_add(100, - start_transfer_when_done_sending_data, conn); -} - -static gboolean -peer_oft_recv_frame_resume_checksum_calculated_cb(gpointer data) -{ - ChecksumData *checksum_data; - PeerConnection *conn; - - checksum_data = data; - conn = checksum_data->conn; - - /* Check the checksums here. If not match, don't allow resume */ - if (checksum_data->checksum != conn->xferdata.recvcsum || checksum_data->total != conn->xferdata.nrecvd) - { - /* Reset internal structure */ - conn->xferdata.recvcsum = 0xffff0000; - conn->xferdata.rfrcsum = 0xffff0000; - conn->xferdata.nrecvd = 0; - } - else - /* Accept the change */ - purple_xfer_set_bytes_sent(checksum_data->xfer, conn->xferdata.nrecvd); - - peer_oft_send_resume_accept(conn); - - return FALSE; -} - -/** - * We are sending a file to someone else. They have just acknowledged our - * prompt and are asking to resume, so we accept their resume and await - * a resume ack. - */ -static void -peer_oft_recv_frame_resume(PeerConnection *conn, OftFrame *frame) -{ - if (memcmp(conn->cookie, frame->cookie, 8) != 0) - { - purple_debug_info("oscar", "Received an incorrect cookie. " - "Closing connection.\n"); - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL); - return; - } - - /* Copy resume data into internal structure */ - conn->xferdata.recvcsum = frame->recvcsum; - conn->xferdata.rfrcsum = frame->rfrcsum; - conn->xferdata.nrecvd = frame->nrecvd; - - peer_oft_checksum_file(conn, conn->xfer, - peer_oft_recv_frame_resume_checksum_calculated_cb, - frame->nrecvd); -} - -/* - * We just sent a file to someone. They said they got it and everything, - * so we can close our direct connection and what not. - */ -static void -peer_oft_recv_frame_done(PeerConnection *conn, OftFrame *frame) -{ - /* - * The core ft code sets the xfer to completed automatically if we've - * sent all bytes to the other user. But this function can be called - * even if we haven't sent all bytes to the other user (in the case - * where the user already has this file on their computer and the - * checksum matches). - */ - if (!purple_xfer_is_completed(conn->xfer)) - purple_xfer_set_completed(conn->xfer, TRUE); - - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = 0; - purple_xfer_set_fd(conn->xfer, conn->fd); - conn->fd = -1; - conn->disconnect_reason = OSCAR_DISCONNECT_DONE; - peer_connection_schedule_destroy(conn, conn->disconnect_reason, NULL); -} - -/** - * Handle an incoming OftFrame. If there is a payload associated - * with this frame, then we remove the old watcher and add the - * OFT watcher to read in the payload. - */ -void -peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs) -{ - OftFrame frame; - - frame.type = byte_stream_get16(bs); - byte_stream_getrawbuf(bs, frame.cookie, 8); - frame.encrypt = byte_stream_get16(bs); - frame.compress = byte_stream_get16(bs); - frame.totfiles = byte_stream_get16(bs); - frame.filesleft = byte_stream_get16(bs); - frame.totparts = byte_stream_get16(bs); - frame.partsleft = byte_stream_get16(bs); - frame.totsize = byte_stream_get32(bs); - frame.size = byte_stream_get32(bs); - frame.modtime = byte_stream_get32(bs); - frame.checksum = byte_stream_get32(bs); - frame.rfrcsum = byte_stream_get32(bs); - frame.rfsize = byte_stream_get32(bs); - frame.cretime = byte_stream_get32(bs); - frame.rfcsum = byte_stream_get32(bs); - frame.nrecvd = byte_stream_get32(bs); - frame.recvcsum = byte_stream_get32(bs); - byte_stream_getrawbuf(bs, frame.idstring, 32); - frame.flags = byte_stream_get8(bs); - frame.lnameoffset = byte_stream_get8(bs); - frame.lsizeoffset = byte_stream_get8(bs); - byte_stream_getrawbuf(bs, frame.dummy, 69); - byte_stream_getrawbuf(bs, frame.macfileinfo, 16); - frame.nencode = byte_stream_get16(bs); - frame.nlanguage = byte_stream_get16(bs); - frame.name_length = bs->len - 186; - frame.name = byte_stream_getraw(bs, frame.name_length); - - purple_debug_info("oscar", "Incoming OFT frame from %s with " - "type=0x%04x\n", conn->bn, frame.type); - - /* TODOFT: peer_oft_dirconvert_fromstupid(frame->name); */ - - switch(frame.type) - { - case PEER_TYPE_PROMPT: - peer_oft_recv_frame_prompt(conn, &frame); - break; - case PEER_TYPE_ACK: - case PEER_TYPE_RESUMEACK: - peer_oft_recv_frame_ack(conn, &frame); - break; - case PEER_TYPE_RESUME: - peer_oft_recv_frame_resume(conn, &frame); - break; - case PEER_TYPE_DONE: - peer_oft_recv_frame_done(conn, &frame); - break; - default: - break; - } - - g_free(frame.name); -} - -/*******************************************************************/ -/* Begin PurpleXfer callbacks for use when receiving a file */ -/*******************************************************************/ - -void -peer_oft_recvcb_init(PurpleXfer *xfer) -{ - PeerConnection *conn; - - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer)); - conn->flags |= PEER_CONNECTION_FLAG_APPROVED; - peer_connection_trynext(conn); -} - -void -peer_oft_recvcb_end(PurpleXfer *xfer) -{ - PeerConnection *conn; - - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer)); - - /* Tell the other person that we've received everything */ - conn->fd = purple_xfer_get_fd(conn->xfer); - purple_xfer_set_fd(conn->xfer, -1); - peer_oft_send_done(conn); - - conn->disconnect_reason = OSCAR_DISCONNECT_DONE; - conn->sending_data_timer = g_timeout_add(100, - destroy_connection_when_done_sending_data, conn); -} - -void -peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size) -{ - PeerConnection *conn; - - /* Update our rolling checksum. Like Walmart, yo. */ - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer)); - conn->xferdata.recvcsum = peer_oft_checksum_chunk(buffer, - size, conn->xferdata.recvcsum, purple_xfer_get_bytes_sent(xfer) & 1); -} - -/*******************************************************************/ -/* End PurpleXfer callbacks for use when receiving a file */ -/*******************************************************************/ - -/*******************************************************************/ -/* Begin PurpleXfer callbacks for use when sending a file */ -/*******************************************************************/ - -static gboolean -peer_oft_checksum_calculated_cb(gpointer data) -{ - ChecksumData *checksum_data; - PeerConnection *conn; - - checksum_data = data; - conn = checksum_data->conn; - - conn->xferdata.checksum = checksum_data->checksum; - - /* Start the connection process */ - peer_connection_trynext(checksum_data->conn); - - return FALSE; -} - -void -peer_oft_sendcb_init(PurpleXfer *xfer) -{ - PeerConnection *conn; - goffset size; - - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer)); - conn->flags |= PEER_CONNECTION_FLAG_APPROVED; - - /* Make sure the file size can be represented in 32 bits */ - size = purple_xfer_get_size(xfer); - if (size > G_MAXUINT32) - { - gchar *tmp, *size1, *size2; - size1 = g_format_size(size); - size2 = g_format_size(G_MAXUINT32); - tmp = g_strdup_printf(_("File %s is %s, which is larger than " - "the maximum size of %s."), - purple_xfer_get_local_filename(xfer), size1, size2); - purple_xfer_error(purple_xfer_get_xfer_type(xfer), - purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer), tmp); - g_free(size1); - g_free(size2); - g_free(tmp); - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); - return; - } - - /* Keep track of file transfer info */ - conn->xferdata.totfiles = 1; - conn->xferdata.filesleft = 1; - conn->xferdata.totparts = 1; - conn->xferdata.partsleft = 1; - conn->xferdata.totsize = size; - conn->xferdata.size = size; - conn->xferdata.checksum = 0xffff0000; - conn->xferdata.rfrcsum = 0xffff0000; - conn->xferdata.rfcsum = 0xffff0000; - conn->xferdata.recvcsum = 0xffff0000; - strncpy((gchar *)conn->xferdata.idstring, "Cool FileXfer", 31); - conn->xferdata.modtime = 0; - conn->xferdata.cretime = 0; - purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer))); - conn->xferdata.name_length = MAX(64, strlen(purple_xfer_get_filename(xfer)) + 1); - conn->xferdata.name = (guchar *)g_strndup(purple_xfer_get_filename(xfer), conn->xferdata.name_length - 1); - - peer_oft_checksum_file(conn, xfer, - peer_oft_checksum_calculated_cb, G_MAXUINT32); -} - -/* - * AIM file transfers aren't really meant to be thought - * of as a transferring just a single file. The rendezvous - * establishes a connection between two computers, and then - * those computers can use the same connection for transferring - * multiple files. So we don't want the Purple core up and closing - * the socket all willy-nilly. We want to do that in the oscar - * protocol, whenever one side or the other says they're finished - * using the connection. There might be a better way to intercept - * the socket from the core... - */ -void -peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size) -{ - PeerConnection *conn; - - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer)); - - /* - * If we're done sending, intercept the socket from the core ft code - * and wait for the other guy to send the "done" OFT packet. - */ - if (purple_xfer_get_bytes_remaining(xfer) <= 0) - { - purple_input_remove(purple_xfer_get_watcher(xfer)); - conn->fd = purple_xfer_get_fd(xfer); - purple_xfer_set_fd(xfer, -1); - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, peer_connection_recv_cb, conn); - } -} - -/*******************************************************************/ -/* End PurpleXfer callbacks for use when sending a file */ -/*******************************************************************/ - -/*******************************************************************/ -/* Begin PurpleXfer callbacks for use when sending and receiving */ -/*******************************************************************/ - -void -peer_oft_cb_generic_cancel(PurpleXfer *xfer) -{ - PeerConnection *conn; - - conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer)); - - if (conn == NULL) - return; - - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); -} - -/*******************************************************************/ -/* End PurpleXfer callbacks for use when sending and receiving */ -/*******************************************************************/ - -#ifdef TODOFT -/* - * This little area in oscar.c is the nexus of file transfer code, - * so I wrote a little explanation of what happens. I am such a - * ninja. - * - * The series of events for a file send is: - * -Create xfer and call purple_xfer_request (this happens in oscar_ask_sendfile) - * -User chooses a file and oscar_xfer_init is called. It establishes a - * listening socket, then asks the remote user to connect to us (and - * gives them the file name, port, IP, etc.) - * -They connect to us and we send them an PEER_TYPE_PROMPT (this happens - * in peer_oft_recv_frame_established) - * -They send us an PEER_TYPE_ACK and then we start sending data - * -When we finish, they send us an PEER_TYPE_DONE and they close the - * connection. - * -We get drunk because file transfer kicks ass. - * - * The series of events for a file receive is: - * -Create xfer and call purple_xfer request (this happens in incomingim_chan2) - * -Purple user selects file to name and location to save file to and - * oscar_xfer_init is called - * -It connects to the remote user using the IP they gave us earlier - * -After connecting, they send us an PEER_TYPE_PROMPT. In reply, we send - * them an PEER_TYPE_ACK. - * -They begin to send us lots of raw data. - * -When they finish sending data we send an PEER_TYPE_DONE and then close - * the connection. - * - * Update August 2005: - * The series of events for transfers has been seriously complicated by the addition - * of transfer redirects and proxied connections. I could throw a whole lot of words - * at trying to explain things here, but it probably wouldn't do much good. To get - * a better idea of what happens, take a look at the diagrams and documentation - * from my Summer of Code project. -- Jonathan Clark - */ - -/** - * Convert the directory separator from / (0x2f) to ^A (0x01) - * - * @param name The filename to convert. - */ -static void -peer_oft_dirconvert_tostupid(char *name) -{ - while (name[0]) { - if (name[0] == 0x01) - name[0] = G_DIR_SEPARATOR; - name++; - } -} - -/** - * Convert the directory separator from ^A (0x01) to / (0x2f) - * - * @param name The filename to convert. - */ -static void -peer_oft_dirconvert_fromstupid(char *name) -{ - while (name[0]) { - if (name[0] == G_DIR_SEPARATOR) - name[0] = 0x01; - name++; - } -} -#endif
--- a/libpurple/protocols/oscar/oscar.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5839 +0,0 @@ -/* - * purple - * - * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> - * Some code copyright (C) 1999-2001, Eric Warmenhoven - * Some code copyright (C) 2001-2003, Sean Egan - * Some code copyright (C) 2001-2007, Mark Doliner <thekingant@users.sourceforge.net> - * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net> - * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com> - * Some code copyright (C) 2008, Aman Gupta - * - * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx> - * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#include "internal.h" - -#include "account.h" -#include "buddyicon.h" -#include "conversation.h" -#include "core.h" -#include "debug.h" -#include "encoding.h" -#include "image-store.h" -#include "network.h" -#include "notify.h" -#include "protocol.h" -#include "proxy.h" -#include "purpleaccountoption.h" -#include "request.h" -#include "util.h" -#include "version.h" -#include "visibility.h" - -#include "aim.h" -#include "icq.h" -#include "oscarcommon.h" -#include "oscar.h" -#include "peer.h" - -static PurpleProtocol *aim_protocol = NULL; -static PurpleProtocol *icq_protocol = NULL; - -static guint64 purple_caps = - OSCAR_CAPABILITY_CHAT - | OSCAR_CAPABILITY_BUDDYICON - | OSCAR_CAPABILITY_DIRECTIM - | OSCAR_CAPABILITY_SENDFILE - | OSCAR_CAPABILITY_UNICODE - | OSCAR_CAPABILITY_INTEROPERATE - | OSCAR_CAPABILITY_SHORTCAPS - | OSCAR_CAPABILITY_TYPING - | OSCAR_CAPABILITY_ICQSERVERRELAY - | OSCAR_CAPABILITY_NEWCAPS - | OSCAR_CAPABILITY_XTRAZ - | OSCAR_CAPABILITY_HTML_MSGS; - -static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02}; -static guint8 features_icq[] = {0x01}; - -struct create_room { - char *name; - int exchange; -}; - -struct oscar_ask_directim_data -{ - OscarData *od; - char *who; -}; - -/* All the libfaim->purple callback functions */ - -/* Only used when connecting with the old-style BUCP login */ -static int purple_parse_auth_resp (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...); - -static int purple_handle_redirect (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_info_change (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_account_confirm (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_oncoming (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_offgoing (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_incoming_im(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_misses (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_motd (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_chatnav_info (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_chat_conversation_join (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_chat_conversation_left (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_chat_conversation_info_update (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_chat_conversation_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_bosrights (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_connerr (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_mtn (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_popup (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_parseerr (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_parserights (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_parselist (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_parseack (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_parseaddmod (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_authgiven (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_authrequest (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_authreply (OscarData *, FlapConnection *, FlapFrame *, ...); -static int purple_ssi_gotadded (OscarData *, FlapConnection *, FlapFrame *, ...); - -static void purple_icons_fetch(PurpleConnection *gc); - -void oscar_set_info(PurpleConnection *gc, const char *info); -static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status); -static void oscar_set_extended_status(PurpleConnection *gc); -static gboolean purple_ssi_rerequestdata(gpointer data); - -void oscar_free_name_data(struct name_data *data) { - g_free(data->name); - g_free(data->nick); - g_free(data); -} - -#ifdef _WIN32 -const char *oscar_get_locale_charset(void) { - static const char *charset = NULL; - if (charset == NULL) - g_get_charset(&charset); - return charset; -} -#endif - -static char *oscar_icqstatus(int state) { - /* Make a cute little string that shows the status of the dude or dudet */ - if (state & AIM_ICQ_STATE_CHAT) - return g_strdup(_("Free For Chat")); - else if (state & AIM_ICQ_STATE_DND) - return g_strdup(_("Do Not Disturb")); - else if (state & AIM_ICQ_STATE_OUT) - return g_strdup(_("Not Available")); - else if (state & AIM_ICQ_STATE_BUSY) - return g_strdup(_("Occupied")); - else if (state & AIM_ICQ_STATE_AWAY) - return g_strdup(_("Away")); - else if (state & AIM_ICQ_STATE_WEBAWARE) - return g_strdup(_("Web Aware")); - else if (state & AIM_ICQ_STATE_INVISIBLE) - return g_strdup(_("Invisible")); - else if (state & AIM_ICQ_STATE_EVIL) - return g_strdup(_("Evil")); - else if (state & AIM_ICQ_STATE_DEPRESSION) - return g_strdup(_("Depression")); - else if (state & AIM_ICQ_STATE_ATHOME) - return g_strdup(_("At home")); - else if (state & AIM_ICQ_STATE_ATWORK) - return g_strdup(_("At work")); - else if (state & AIM_ICQ_STATE_LUNCH) - return g_strdup(_("At lunch")); - else - return g_strdup(_("Online")); -} - -static char *extract_name(const char *name) { - char *tmp, *x; - int i, j; - - if (!name) - return NULL; - - x = strchr(name, '-'); - if (!x) - return NULL; - - x = strchr(x + 1, '-'); - if (!x) - return NULL; - - tmp = g_strdup(++x); - - for (i = 0, j = 0; x[i]; i++) { - char hex[3]; - if (x[i] != '%') { - tmp[j++] = x[i]; - continue; - } - strncpy(hex, x + ++i, 2); - hex[2] = 0; - i++; - tmp[j++] = strtol(hex, NULL, 16); - } - - tmp[j] = 0; - return tmp; -} - -static struct chat_connection * -find_oscar_chat(PurpleConnection *gc, int id) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - GSList *cur; - struct chat_connection *cc; - - for (cur = od->oscar_chats; cur != NULL; cur = cur->next) - { - cc = (struct chat_connection *)cur->data; - if (cc->id == id) - return cc; - } - - return NULL; -} - -static struct chat_connection * -find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - GSList *cur; - struct chat_connection *cc; - - for (cur = od->oscar_chats; cur != NULL; cur = cur->next) - { - cc = (struct chat_connection *)cur->data; - if (cc->conn == conn) - return cc; - } - - return NULL; -} - -static struct chat_connection * -find_oscar_chat_by_conv(PurpleConnection *gc, PurpleChatConversation *conv) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - GSList *cur; - struct chat_connection *cc; - - for (cur = od->oscar_chats; cur != NULL; cur = cur->next) - { - cc = (struct chat_connection *)cur->data; - if (cc->conv == conv) - return cc; - } - - return NULL; -} - -void -oscar_chat_destroy(struct chat_connection *cc) -{ - g_free(cc->name); - g_free(cc->show); - g_free(cc); -} - -static void -oscar_chat_kill(PurpleConnection *gc, struct chat_connection *cc) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - - /* Notify the conversation window that we've left the chat */ - purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(cc->conv)); - - /* Destroy the chat_connection */ - od->oscar_chats = g_slist_remove(od->oscar_chats, cc); - oscar_chat_destroy(cc); -} - -/** - * This is called from the callback functions for establishing - * a TCP connection with an oscar host if an error occurred. - */ -static void -connection_common_error_cb(FlapConnection *conn, const gchar *error_message) -{ - OscarData *od; - PurpleConnection *gc; - - od = conn->od; - gc = od->gc; - - purple_debug_error("oscar", "unable to connect to FLAP " - "server of type 0x%04hx\n", conn->type); - - if (conn->type == SNAC_FAMILY_AUTH) - { - /* This only happens when connecting with the old-style BUCP login */ - gchar *msg; - msg = g_strdup_printf(_("Unable to connect to authentication server: %s"), - error_message); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - } - else if (conn->type == SNAC_FAMILY_LOCATE) - { - gchar *msg; - msg = g_strdup_printf(_("Unable to connect to BOS server: %s"), - error_message); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - } - else - { - /* Maybe we should call this for BOS connections, too? */ - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message); - } -} - -/** - * This is called from the callback functions for establishing - * a TCP connection with an oscar host. Depending on the type - * of host, we do a few different things here. - */ -static void -connection_common_established_cb(FlapConnection *conn) -{ - OscarData *od; - PurpleConnection *gc; - PurpleAccount *account; - - od = conn->od; - gc = od->gc; - account = purple_connection_get_account(gc); - - purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n", - conn->type); - - if (conn->cookie == NULL) - flap_connection_send_version(od, conn); - else - { - const gchar *login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN); - - if (!purple_strequal(login_type, OSCAR_MD5_LOGIN)) - { - ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM; - ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ; - flap_connection_send_version_with_cookie_and_clientinfo(od, - conn, conn->cookielen, conn->cookie, - od->icq ? &icqinfo : &aiminfo, - purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS)); - } else { - flap_connection_send_version_with_cookie(od, conn, - conn->cookielen, conn->cookie); - } - - - g_free(conn->cookie); - conn->cookie = NULL; - } - - if (conn->type == SNAC_FAMILY_AUTH) - { - /* This only happens when connecting with the old-style BUCP login */ - aim_request_login(od, conn, purple_account_get_username(account)); - purple_debug_info("oscar", "Username sent, waiting for response\n"); - purple_connection_update_progress(gc, _("Username sent"), 1, OSCAR_CONNECT_STEPS); - } - else if (conn->type == SNAC_FAMILY_LOCATE) - { - purple_connection_update_progress(gc, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS); - } - else if (conn->type == SNAC_FAMILY_CHAT) - { - od->oscar_chats = g_slist_prepend(od->oscar_chats, conn->new_conn_data); - conn->new_conn_data = NULL; - } -} - -static void -connection_established_cb(gpointer data, gint source, const gchar *error_message) -{ - FlapConnection *conn; - - conn = data; - - conn->connect_data = NULL; - conn->fd = source; - - if (source < 0) - { - connection_common_error_cb(conn, error_message); - return; - } - - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, flap_connection_recv_cb, conn); - connection_common_established_cb(conn); -} - -static void -ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond) -{ - FlapConnection *conn; - - conn = data; - - purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn); - connection_common_established_cb(conn); -} - -static void -ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, - gpointer data) -{ - FlapConnection *conn; - - conn = data; - - if (conn->watcher_outgoing) - { - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - } - - /* sslconn frees the connection on error */ - conn->gsc = NULL; - - connection_common_error_cb(conn, purple_ssl_strerror(error)); -} - -static void -flap_connection_established_bos(OscarData *od, FlapConnection *conn) -{ - PurpleConnection *gc = od->gc; - - aim_srv_reqpersonalinfo(od, conn); - - purple_debug_info("oscar", "ssi: requesting rights and list\n"); - aim_ssi_reqrights(od); - aim_ssi_reqdata(od); - if (od->getblisttimer > 0) - g_source_remove(od->getblisttimer); - od->getblisttimer = g_timeout_add_seconds(30, purple_ssi_rerequestdata, od); - - aim_locate_reqrights(od); - aim_buddylist_reqrights(od, conn); - aim_im_reqparams(od); - aim_bos_reqrights(od, conn); /* TODO: Don't call this with ssi */ - - purple_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS); -} - -static void -flap_connection_established_admin(OscarData *od, FlapConnection *conn) -{ - aim_srv_clientready(od, conn); - purple_debug_info("oscar", "connected to admin\n"); - - if (od->chpass) { - purple_debug_info("oscar", "changing password\n"); - aim_admin_changepasswd(od, conn, od->newp, od->oldp); - g_free(od->oldp); - od->oldp = NULL; - g_free(od->newp); - od->newp = NULL; - od->chpass = FALSE; - } - if (od->setnick) { - purple_debug_info("oscar", "formatting username\n"); - aim_admin_setnick(od, conn, od->newformatting); - g_free(od->newformatting); - od->newformatting = NULL; - od->setnick = FALSE; - } - if (od->conf) { - purple_debug_info("oscar", "confirming account\n"); - aim_admin_reqconfirm(od, conn); - od->conf = FALSE; - } - if (od->reqemail) { - purple_debug_info("oscar", "requesting email address\n"); - aim_admin_getinfo(od, conn, 0x0011); - od->reqemail = FALSE; - } - if (od->setemail) { - purple_debug_info("oscar", "setting email address\n"); - aim_admin_setemail(od, conn, od->email); - g_free(od->email); - od->email = NULL; - od->setemail = FALSE; - } -} - -static void -flap_connection_established_chat(OscarData *od, FlapConnection *conn) -{ - PurpleConnection *gc = od->gc; - struct chat_connection *chatcon; - static int id = 1; - - aim_srv_clientready(od, conn); - - chatcon = find_oscar_chat_by_conn(gc, conn); - if (chatcon) { - chatcon->id = id; - chatcon->conv = purple_serv_got_joined_chat(gc, id++, chatcon->show); - } -} - -static void -flap_connection_established_chatnav(OscarData *od, FlapConnection *conn) -{ - aim_srv_clientready(od, conn); - aim_chatnav_reqrights(od, conn); -} - -static void -flap_connection_established_alert(OscarData *od, FlapConnection *conn) -{ - aim_email_sendcookies(od); - aim_email_activate(od); - aim_srv_clientready(od, conn); -} - -static void -flap_connection_established_bart(OscarData *od, FlapConnection *conn) -{ - PurpleConnection *gc = od->gc; - - aim_srv_clientready(od, conn); - - od->iconconnecting = FALSE; - - purple_icons_fetch(gc); -} - -static int -flap_connection_established(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - conn->connected = TRUE; - purple_debug_info("oscar", "FLAP connection of type 0x%04hx is " - "now fully connected\n", conn->type); - if (conn->type == SNAC_FAMILY_LOCATE) - flap_connection_established_bos(od, conn); - else if (conn->type == SNAC_FAMILY_ADMIN) - flap_connection_established_admin(od, conn); - else if (conn->type == SNAC_FAMILY_CHAT) - flap_connection_established_chat(od, conn); - else if (conn->type == SNAC_FAMILY_CHATNAV) - flap_connection_established_chatnav(od, conn); - else if (conn->type == SNAC_FAMILY_ALERT) - flap_connection_established_alert(od, conn); - else if (conn->type == SNAC_FAMILY_BART) - flap_connection_established_bart(od, conn); - - return 1; -} - -static void -idle_reporting_pref_cb(const char *name, PurplePrefType type, - gconstpointer value, gpointer data) -{ - PurpleConnection *gc; - OscarData *od; - gboolean report_idle; - guint32 presence; - - gc = data; - od = purple_connection_get_protocol_data(gc); - report_idle = !purple_strequal((const char *)value, "none"); - presence = aim_ssi_getpresence(&od->ssi.local); - - if (report_idle) - aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_SHOWIDLE); - else - aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE); -} - -/** - * Should probably make a "Use recent buddies group" account preference - * so that this option is surfaced to the user. - */ -static void -recent_buddies_pref_cb(const char *name, PurplePrefType type, - gconstpointer value, gpointer data) -{ - PurpleConnection *gc; - OscarData *od; - guint32 presence; - - gc = data; - od = purple_connection_get_protocol_data(gc); - presence = aim_ssi_getpresence(&od->ssi.local); - - if (value) - aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES); - else - aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES); -} - -static const gchar *login_servers[] = { - AIM_DEFAULT_LOGIN_SERVER, - AIM_DEFAULT_SSL_LOGIN_SERVER, - ICQ_DEFAULT_LOGIN_SERVER, - ICQ_DEFAULT_SSL_LOGIN_SERVER, -}; - -const gchar * -oscar_get_login_server(gboolean is_icq, gboolean use_ssl) -{ - return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)]; -} - -static gint -compare_handlers(gconstpointer a, gconstpointer b) -{ - guint aa = GPOINTER_TO_UINT(a); - guint bb = GPOINTER_TO_UINT(b); - guint family1 = aa >> 16; - guint family2 = bb >> 16; - guint subtype1 = aa & 0xFFFF; - guint subtype2 = bb & 0xFFFF; - if (family1 != family2) { - return family1 - family2; - } - return subtype1 - subtype2; -} - -void -oscar_login(PurpleAccount *account) -{ - PurpleConnection *gc; - OscarData *od; - const gchar *encryption_type; - const gchar *login_type; - GList *handlers; - GList *sorted_handlers; - GList *cur; - GString *msg = g_string_new(""); - PurpleConnectionFlags flags; - - gc = purple_account_get_connection(account); - od = oscar_data_new(); - od->gc = gc; - purple_connection_set_protocol_data(gc, od); - - oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, purple_connerr, 0); - oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, flap_connection_established, 0); - - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0003, purple_info_change, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0005, purple_info_change, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0007, purple_account_confirm, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ALERT, 0x0001, purple_parse_genericerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ALERT, SNAC_SUBTYPE_ALERT_MAILSTATUS, purple_email_parseupdate, 0); - - /* These are only needed when connecting with the old-style BUCP login */ - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0); - oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0); - - oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, purple_parse_genericerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, purple_bosrights, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, 0x0001, purple_parse_genericerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_RIGHTSINFO, purple_parse_buddyrights, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_ONCOMING, purple_parse_oncoming, 0); - oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_OFFGOING, purple_parse_offgoing, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, 0x0001, purple_parse_genericerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, purple_chat_conversation_join, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, purple_chat_conversation_left, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, purple_chat_conversation_info_update, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, purple_chat_conversation_incoming_msg, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, 0x0001, purple_parse_genericerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, SNAC_SUBTYPE_CHATNAV_INFO, purple_chatnav_info, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ERROR, purple_ssi_parseerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO, purple_ssi_parserights, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_LIST, purple_ssi_parselist, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SRVACK, purple_ssi_parseack, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, purple_ssi_parseaddmod, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_MOD, purple_ssi_parseaddmod, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTH, purple_ssi_authgiven, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0); - oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0); - oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0); - oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0); - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, purple_parse_genericerr, 0); - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0); - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, purple_handle_redirect, 0); - oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, purple_parse_motd, 0); - oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, purple_popup, 0); - oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, purple_parse_searcherror, 0); - oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0); - - g_string_append(msg, "Registered handlers: "); - handlers = g_hash_table_get_keys(od->handlerlist); - sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers); - for (cur = sorted_handlers; cur; cur = cur->next) { - guint x = GPOINTER_TO_UINT(cur->data); - g_string_append_printf(msg, "%04x/%04x, ", x >> 16, x & 0xFFFF); - } - g_list_free(sorted_handlers); - g_list_free(handlers); - purple_debug_misc("oscar", "%s\n", msg->str); - g_string_free(msg, TRUE); - - purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc); - - if (!oscar_util_valid_name(purple_account_get_username(account))) { - gchar *buf; - buf = g_strdup_printf(_("Unable to sign on as %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account)); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf); - g_free(buf); - return; - } - - flags = PURPLE_CONNECTION_FLAG_HTML; - if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) { - od->icq = TRUE; - } else { - flags |= PURPLE_CONNECTION_FLAG_AUTO_RESP; - } - - /* Set this flag based on the protocol_id rather than the username, - because that is what's tied to the get_moods protocol callback. */ - if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) - flags |= PURPLE_CONNECTION_FLAG_SUPPORT_MOODS; - - purple_connection_set_flags(gc, flags); - - od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT); - - login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN); - encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); - od->use_ssl = purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION) == FALSE; - - /* Connect to core Purple signals */ - purple_prefs_connect_callback(purple_connection_get_protocol(gc), "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); - purple_prefs_connect_callback(purple_connection_get_protocol(gc), "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc); - - if (purple_strequal(login_type, OSCAR_CLIENT_LOGIN) || - purple_strequal(login_type, OSCAR_KERBEROS_LOGIN)) { - /* Create a SoupSession to be used for HTTP login requests. */ - GProxyResolver *resolver; - GError *error; - - resolver = purple_proxy_get_proxy_resolver(account, &error); - if (resolver == NULL) { - purple_debug_error("oscar", - "Unable to get account proxy resolver: %s", - error->message); - purple_connection_g_error(gc, error); - return; - } - - od->http_conns = soup_session_new_with_options( - SOUP_SESSION_PROXY_RESOLVER, resolver, NULL); - g_object_unref(resolver); - } - - /* - * On 2008-03-05 AOL released some documentation on the OSCAR protocol - * which includes a new login method called clientLogin. It is similar - * (though not the same?) as what the AIM 6.0 series uses to - * authenticate. - * - * AIM 5.9 and lower use an MD5-based login procedure called "BUCP". - * This authentication method is used for both ICQ and AIM when - * clientLogin is not enabled. - */ - if (purple_strequal(login_type, OSCAR_CLIENT_LOGIN)) { - /* Note: Actual server/port configuration is ignored here */ - send_client_login(od, purple_account_get_username(account)); - } else if (purple_strequal(login_type, OSCAR_KERBEROS_LOGIN)) { - const char *server; - - if (!od->use_ssl) { - purple_connection_error( - gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("You required Kerberos authentication but encryption is disabled in your account settings.")); - return; - } - server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER); - /* - * If the account's server is what the oscar protocol has offered as - * the default login server through the vast eons (all two of - * said default options, AFAIK) and the user wants KDC, we'll - * do what we know is best for them and change the setting out - * from under them to the KDC login server. - */ - if (purple_strequal(server, oscar_get_login_server(od->icq, FALSE)) || - purple_strequal(server, oscar_get_login_server(od->icq, TRUE)) || - purple_strequal(server, AIM_ALT_LOGIN_SERVER) || - purple_strequal(server, "")) { - purple_debug_info("oscar", "Account uses Kerberos auth, so changing server to default KDC server\n"); - purple_account_set_string(account, "server", AIM_DEFAULT_KDC_SERVER); - purple_account_set_int(account, "port", AIM_DEFAULT_KDC_PORT); - } - send_kerberos_login(od, purple_account_get_username(account)); - } else { - FlapConnection *newconn; - const char *server; - - newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); - - if (od->use_ssl) { - server = purple_account_get_string(account, "server", oscar_get_login_server(od->icq, TRUE)); - - /* - * If the account's server is what the oscar protocol has offered as - * the default login server through the vast eons (all two of - * said default options, AFAIK) and the user wants SSL, we'll - * do what we know is best for them and change the setting out - * from under them to the SSL login server. - */ - if (purple_strequal(server, oscar_get_login_server(od->icq, FALSE)) || - purple_strequal(server, AIM_ALT_LOGIN_SERVER) || - purple_strequal(server, AIM_DEFAULT_KDC_SERVER) || - purple_strequal(server, "")) { - purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n"); - purple_account_set_string(account, "server", oscar_get_login_server(od->icq, TRUE)); - purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), - server = oscar_get_login_server(od->icq, TRUE); - } - - newconn->gsc = purple_ssl_connect(account, server, - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), - ssl_connection_established_cb, ssl_connection_error_cb, newconn); - } else { - server = purple_account_get_string(account, "server", oscar_get_login_server(od->icq, FALSE)); - - /* - * See the comment above. We do the reverse here. If they don't want - * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER, - * set it back to the default. - */ - if (purple_strequal(server, oscar_get_login_server(od->icq, TRUE)) || - purple_strequal(server, AIM_DEFAULT_KDC_SERVER) || - purple_strequal(server, "")) { - purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n"); - purple_account_set_string(account, "server", oscar_get_login_server(od->icq, FALSE)); - purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), - server = oscar_get_login_server(od->icq, FALSE); - } - - newconn->connect_data = purple_proxy_connect(NULL, account, server, - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), - connection_established_cb, newconn); - } - - if (newconn->gsc == NULL && newconn->connect_data == NULL) { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - return; - } - } - - purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS); -} - -void -oscar_close(PurpleConnection *gc) -{ - OscarData *od; - - od = purple_connection_get_protocol_data(gc); - - g_slist_free_full(od->oscar_chats, (GDestroyNotify)oscar_chat_destroy); - while (od->create_rooms) - { - struct create_room *cr = od->create_rooms->data; - g_free(cr->name); - od->create_rooms = g_slist_remove(od->create_rooms, cr); - g_free(cr); - } - oscar_data_destroy(od); - purple_connection_set_protocol_data(gc, NULL); - - purple_prefs_disconnect_by_handle(gc); - - purple_debug_info("oscar", "Signed off.\n"); -} - -int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname) -{ - PurpleAccount *account; - FlapConnection *conn; - - account = purple_connection_get_account(gc); - - conn = flap_connection_new(od, SNAC_FAMILY_LOCATE); - conn->cookielen = cookielen; - conn->cookie = g_memdup(cookie, cookielen); - - /* - * Use TLS only if the server provided us with a tls_certname. The server might not specify a tls_certname even if we requested to use TLS, - * and that is something we should be prepared to. - */ - if (tls_certname) - { - conn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port, - ssl_connection_established_cb, ssl_connection_error_cb, - tls_certname, conn); - } - else - { - conn->connect_data = purple_proxy_connect(NULL, - account, host, port, - connection_established_cb, conn); - } - - if (conn->gsc == NULL && conn->connect_data == NULL) - { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect")); - return 0; - } - - od->default_port = port; - - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); - - return 1; -} - -/** - * Only used when connecting with the old-style BUCP login. - */ -static int -purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - char *host; int port; - gsize i; - FlapConnection *newconn; - va_list ap; - struct aim_authresp_info *info; - - port = purple_account_get_int(account, "port", od->default_port); - - va_start(ap, fr); - info = va_arg(ap, struct aim_authresp_info *); - va_end(ap); - - purple_debug_info("oscar", - "inside auth_resp (Username: %s)\n", info->bn); - - if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) { - char buf[256]; - switch (info->errorcode) { - case 0x01: - /* Unregistered username */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist")); - break; - case 0x05: - /* Incorrect password */ - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL, NULL, NULL); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password")); - break; - case 0x11: - /* Suspended account */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended")); - break; - case 0x02: - case 0x14: - /* service temporarily unavailable */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable.")); - break; - case 0x18: - /* username connecting too frequently */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); - break; - case 0x1c: - { - /* client too old */ - g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), - oscar_get_ui_info_string("website", PURPLE_WEBSITE)); - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf); - break; - } - case 0x1d: - /* IP address connecting too frequently */ - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer.")); - break; - default: - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason")); - break; - } - purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode); - purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : ""); - return 1; - } - - purple_debug_misc("oscar", - "Reg status: %hu\n" - "Email: %s\n" - "BOSIP: %s\n", - info->regstatus, info->email ? info->email : "null", - info->bosip); - purple_debug_info("oscar", "Closing auth connection...\n"); - flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL); - - for (i = 0; i < strlen(info->bosip); i++) { - if (info->bosip[i] == ':') { - port = atoi(&(info->bosip[i+1])); - break; - } - } - host = g_strndup(info->bosip, i); - newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE); - newconn->cookielen = info->cookielen; - newconn->cookie = g_memdup(info->cookie, info->cookielen); - - if (od->use_ssl) - { - /* - * This shouldn't be hardcoded to "bos.oscar.aol.com" except that - * the server isn't sending us a name to use for comparing the - * certificate common name. - */ - newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port, - ssl_connection_established_cb, ssl_connection_error_cb, - "bos.oscar.aol.com", newconn); - } - else - { - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - connection_established_cb, newconn); - } - - g_free(host); - if (newconn->gsc == NULL && newconn->connect_data == NULL) - { - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect")); - return 0; - } - - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); - - return 1; -} - -/** - * Only used when connecting with the old-style BUCP login. - */ -static void -purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg) -{ - PurpleConnection *gc = user_data; - OscarData *od = purple_connection_get_protocol_data(gc); - - aim_auth_securid_send(od, msg); -} - -/** - * Only used when connecting with the old-style BUCP login. - */ -static void -purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value) -{ - PurpleConnection *gc = user_data; - - /* Disconnect */ - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("The SecurID key entered is invalid")); -} - -/** - * Only used when connecting with the old-style BUCP login. - */ -static int -purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - gchar *primary; - - purple_debug_info("oscar", "Got SecurID request\n"); - - primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account)); - purple_request_input(gc, NULL, _("Enter SecurID"), primary, - _("Enter the 6 digit number from the digital display."), - FALSE, FALSE, NULL, - _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb), - _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb), - purple_request_cpar_from_connection(gc), - gc); - g_free(primary); - - return 1; -} - -static int -purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - char *host, *separator; - int port; - FlapConnection *newconn; - va_list ap; - struct aim_redirect_data *redir; - - va_start(ap, fr); - redir = va_arg(ap, struct aim_redirect_data *); - va_end(ap); - - port = od->default_port; - separator = strchr(redir->ip, ':'); - if (separator != NULL) - { - host = g_strndup(redir->ip, separator - redir->ip); - port = atoi(separator + 1); - } - else - host = g_strdup(redir->ip); - - if (!redir->use_ssl) { - const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); - if (purple_strequal(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION)) { - purple_debug_warning("oscar", "We won't use SSL for FLAP type 0x%04hx.\n", redir->group); - } else if (purple_strequal(encryption_type, OSCAR_REQUIRE_ENCRYPTION)) { - purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.\n", host, port, redir->group); - purple_connection_error( - gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("You required encryption in your account settings, but one of the servers doesn't support it.")); - return 0; - } - } - - /* - * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts - * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext. - */ - if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN || - redir->group == SNAC_FAMILY_BART)) - { - purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", redir->group); - redir->use_ssl = 0; - } - - purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n", host, port, redir->group); - - newconn = flap_connection_new(od, redir->group); - newconn->cookielen = redir->cookielen; - newconn->cookie = g_memdup(redir->cookie, redir->cookielen); - if (newconn->type == SNAC_FAMILY_CHAT) - { - struct chat_connection *cc; - cc = g_new0(struct chat_connection, 1); - cc->conn = newconn; - cc->gc = gc; - cc->name = g_strdup(redir->chat.room); - cc->exchange = redir->chat.exchange; - cc->instance = redir->chat.instance; - cc->show = extract_name(redir->chat.room); - newconn->new_conn_data = cc; - purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange); - } - - - if (redir->use_ssl) - { - newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port, - ssl_connection_established_cb, ssl_connection_error_cb, - redir->ssl_cert_cn, newconn); - } - else - { - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - connection_established_cb, newconn); - } - - if (newconn->gsc == NULL && newconn->connect_data == NULL) - { - flap_connection_schedule_destroy(newconn, - OSCAR_DISCONNECT_COULD_NOT_CONNECT, - _("Unable to initialize connection")); - purple_debug_error("oscar", "Unable to connect to FLAP server " - "of type 0x%04hx\n", redir->group); - } - g_free(host); - - return 1; -} - - -static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc; - PurpleAccount *account; - PurpleBuddy *buddy = NULL; - PurpleStatus *previous_status = NULL; - struct buddyinfo *bi; - time_t time_idle = 0, signon = 0; - int type = 0; - gboolean buddy_is_away = FALSE; - const char *status_id; - va_list ap; - aim_userinfo_t *info; - char *message; - char *itmsurl = NULL; - - gc = od->gc; - account = purple_connection_get_account(gc); - - va_start(ap, fr); - info = va_arg(ap, aim_userinfo_t *); - va_end(ap); - - g_return_val_if_fail(info != NULL, 1); - g_return_val_if_fail(info->bn != NULL, 1); - - buddy = purple_blist_find_buddy(account, info->bn); - if (buddy) { - previous_status = purple_presence_get_active_status(purple_buddy_get_presence(buddy)); - } - - /* - * If this is an AIM buddy and their name has formatting, set their - * server alias. - */ - if (!oscar_util_valid_name_icq(info->bn)) { - gboolean bn_has_formatting = FALSE; - char *c; - for (c = info->bn; *c != '\0'; c++) { - if (!islower(*c)) { - bn_has_formatting = TRUE; - break; - } - } - purple_serv_got_alias(gc, info->bn, - bn_has_formatting ? info->bn : NULL); - } - - if (info->present & AIM_USERINFO_PRESENT_FLAGS) { - if (info->flags & AIM_FLAG_AWAY) - buddy_is_away = TRUE; - } - if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) { - type = info->icqinfo.status; - if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) && - (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) { - buddy_is_away = TRUE; - } - } - - if (oscar_util_valid_name_icq(info->bn)) { - if (type & AIM_ICQ_STATE_CHAT) - status_id = OSCAR_STATUS_ID_FREE4CHAT; - else if (type & AIM_ICQ_STATE_DND) - status_id = OSCAR_STATUS_ID_DND; - else if (type & AIM_ICQ_STATE_OUT) - status_id = OSCAR_STATUS_ID_NA; - else if (type & AIM_ICQ_STATE_BUSY) - status_id = OSCAR_STATUS_ID_OCCUPIED; - else if (type & AIM_ICQ_STATE_AWAY) - status_id = OSCAR_STATUS_ID_AWAY; - else if (type & AIM_ICQ_STATE_INVISIBLE) - status_id = OSCAR_STATUS_ID_INVISIBLE; - else if (type & AIM_ICQ_STATE_EVIL) - status_id = OSCAR_STATUS_ID_EVIL; - else if (type & AIM_ICQ_STATE_DEPRESSION) - status_id = OSCAR_STATUS_ID_DEPRESSION; - else if (type & AIM_ICQ_STATE_ATHOME) - status_id = OSCAR_STATUS_ID_ATHOME; - else if (type & AIM_ICQ_STATE_ATWORK) - status_id = OSCAR_STATUS_ID_ATWORK; - else if (type & AIM_ICQ_STATE_LUNCH) - status_id = OSCAR_STATUS_ID_LUNCH; - else - status_id = OSCAR_STATUS_ID_AVAILABLE; - } else { - if (type & AIM_ICQ_STATE_INVISIBLE) - status_id = OSCAR_STATUS_ID_INVISIBLE; - else if (buddy_is_away) - status_id = OSCAR_STATUS_ID_AWAY; - else - status_id = OSCAR_STATUS_ID_AVAILABLE; - } - - if (info->flags & AIM_FLAG_WIRELESS) { - purple_protocol_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL); - } else { - purple_protocol_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); - } - - message = (info->status && info->status_len > 0) - ? oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len) - : NULL; - - if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE)) { - /* TODO: If itmsurl is NULL, does that mean the URL has been - cleared? Or does it mean the URL should remain unchanged? */ - if (info->itmsurl != NULL) { - itmsurl = (info->itmsurl_len > 0) ? oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len) : NULL; - } else if (previous_status != NULL && purple_status_is_available(previous_status)) { - itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl")); - } - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message ? message : "(null)", itmsurl ? itmsurl : "(null)"); - purple_protocol_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL); - } else { - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message ? message : "(null)"); - purple_protocol_got_user_status(account, info->bn, status_id, "message", message, NULL); - } - - g_free(message); - g_free(itmsurl); - - /* Login time stuff */ - if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) - signon = info->onlinesince; - else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) - signon = time(NULL) - info->sessionlen; - purple_protocol_got_user_login_time(account, info->bn, signon); - - /* Idle time stuff */ - /* info->idletime is the number of minutes that this user has been idle */ - if (info->present & AIM_USERINFO_PRESENT_IDLE) - time_idle = time(NULL) - info->idletime * 60; - - if (time_idle > 0) - purple_protocol_got_user_idle(account, info->bn, TRUE, time_idle); - else - purple_protocol_got_user_idle(account, info->bn, FALSE, 0); - - /* Server stored icon stuff */ - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->bn)); - if (!bi) { - bi = g_new0(struct buddyinfo, 1); - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->bn)), bi); - } - bi->typingnot = FALSE; - bi->ico_informed = FALSE; - bi->ipaddr = info->icqinfo.ipaddr; - - if (info->iconcsumlen) { - const char *saved_b16 = NULL; - char *b16 = NULL; - PurpleBuddy *b = NULL; - - b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen); - b = purple_blist_find_buddy(account, info->bn); - if (b != NULL) - saved_b16 = purple_buddy_icons_get_checksum_for_user(b); - - if (!b16 || !saved_b16 || !purple_strequal(b16, saved_b16)) { - /* Invalidate the old icon for this user */ - purple_buddy_icons_set_for_user(account, info->bn, NULL, 0, NULL); - - /* Fetch the new icon (if we're not already doing so) */ - if (g_slist_find_custom(od->requesticon, info->bn, - (GCompareFunc)oscar_util_name_compare) == NULL) - { - od->requesticon = g_slist_prepend(od->requesticon, - g_strdup(purple_normalize(account, info->bn))); - purple_icons_fetch(gc); - } - } - g_free(b16); - } - - return 1; -} - -static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - va_list ap; - aim_userinfo_t *info; - - va_start(ap, fr); - info = va_arg(ap, aim_userinfo_t *); - va_end(ap); - - purple_protocol_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL); - purple_protocol_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); - g_hash_table_remove(od->buddyinfo, purple_normalize(purple_connection_get_account(gc), info->bn)); - - return 1; -} - -static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) { - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleMessageFlags flags = 0; - struct buddyinfo *bi; - PurpleImage *img; - gchar *tmp; - const char *start, *end; - GData *attribs; - - purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn); - - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn)); - if (!bi) { - bi = g_new0(struct buddyinfo, 1); - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, userinfo->bn)), bi); - } - - if (args->icbmflags & AIM_IMFLAGS_AWAY) - flags |= PURPLE_MESSAGE_AUTO_RESP; - - if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT) - bi->typingnot = TRUE; - else - bi->typingnot = FALSE; - - if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) { - purple_debug_misc("oscar", "%s has an icon\n", userinfo->bn); - if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) { - bi->ico_need = TRUE; - bi->ico_len = args->iconlen; - bi->ico_csum = args->iconsum; - bi->ico_time = args->iconstamp; - } - } - - img = purple_buddy_icons_find_account_icon(account); - if ((img != NULL) && - (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) { - gconstpointer data = purple_image_get_data(img); - size_t len = purple_image_get_data_size(img); - purple_debug_info("oscar", - "Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n", - userinfo->bn, len); - aim_im_sendch2_icon(od, userinfo->bn, data, len, - purple_buddy_icons_get_account_icon_timestamp(account), - aimutil_iconsum(data, len)); - } - g_object_unref(img); - - tmp = g_strdup(args->msg); - - /* - * Convert iChat color tags to normal font tags. - */ - if (purple_markup_find_tag("body", tmp, &start, &end, &attribs)) - { - int len; - char *tmp2, *body; - const char *ichattextcolor, *ichatballooncolor; - const char *slash_body_start, *slash_body_end = NULL; /* </body> */ - GData *unused; - - /* - * Find the ending </body> so we can strip off the outer <html/> - * and <body/> - */ - if (purple_markup_find_tag("/body", end + 1, &slash_body_start, &slash_body_end, &unused)) - { - body = g_strndup(start, slash_body_end - start + 1); - g_datalist_clear(&unused); - } - else - { - purple_debug_warning("oscar", "Broken message contains <body> but not </body>!\n"); - /* Take everything after <body> */ - body = g_strdup(start); - } - - ichattextcolor = g_datalist_get_data(&attribs, "ichattextcolor"); - if (ichattextcolor != NULL) - { - tmp2 = g_strdup_printf("<font color=\"%s\">%s</font>", ichattextcolor, body); - g_free(body); - body = tmp2; - } - - ichatballooncolor = g_datalist_get_data(&attribs, "ichatballooncolor"); - if (ichatballooncolor != NULL) - { - tmp2 = g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor, body); - g_free(body); - body = tmp2; - } - - g_datalist_clear(&attribs); - - len = start - tmp; - tmp2 = g_strdup_printf("%.*s%s%s", len, tmp, body, slash_body_end ? slash_body_end + 1: "</body>"); - g_free(tmp); - g_free(body); - - tmp = tmp2; - } - - /* - * Are there <html/> surrounding tags? If so, strip them out, too. - */ - if (purple_markup_find_tag("html", tmp, &start, &end, &attribs)) - { - gchar *tmp2; - int len; - - g_datalist_clear(&attribs); - - len = start - tmp; - tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1); - g_free(tmp); - tmp = tmp2; - } - - if (purple_markup_find_tag("/html", tmp, &start, &end, &attribs)) - { - gchar *tmp2; - int len; - - g_datalist_clear(&attribs); - - len = start - tmp; - tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1); - g_free(tmp); - tmp = tmp2; - } - - purple_serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL)); - g_free(tmp); - - return 1; -} - -static int -incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, IcbmArgsCh2 *args) -{ - PurpleConnection *gc; - PurpleAccount *account; - PurpleMessageFlags flags = 0; - char *message = NULL; - - g_return_val_if_fail(od != NULL, 0); - g_return_val_if_fail(od->gc != NULL, 0); - - gc = od->gc; - account = purple_connection_get_account(gc); - od = purple_connection_get_protocol_data(gc); - - if (args == NULL) - return 0; - - purple_debug_misc("oscar", "Incoming rendezvous message of type %" - G_GUINT64_FORMAT ", user %s, status %hu\n", - args->type, userinfo->bn, args->status); - - if (args->msg != NULL) { - message = oscar_encoding_to_utf8(args->encoding, args->msg, args->msglen); - } - - if (args->type & OSCAR_CAPABILITY_CHAT) - { - char *utf8name, *tmp; - GHashTable *components; - - if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) { - g_free(message); - return 1; - } - utf8name = oscar_encoding_to_utf8(args->encoding, args->info.chat.roominfo.name, args->info.chat.roominfo.namelen); - - tmp = extract_name(utf8name); - if (tmp != NULL) - { - g_free(utf8name); - utf8name = tmp; - } - - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - g_free); - g_hash_table_replace(components, g_strdup("room"), utf8name); - g_hash_table_replace(components, g_strdup("exchange"), - g_strdup_printf("%d", args->info.chat.roominfo.exchange)); - purple_serv_got_chat_invite(gc, - utf8name, - userinfo->bn, - message, - components); - } - - else if ((args->type & OSCAR_CAPABILITY_SENDFILE) || (args->type & OSCAR_CAPABILITY_DIRECTIM)) - { - if (args->status == AIM_RENDEZVOUS_PROPOSE) - { - peer_connection_got_proposition(od, userinfo->bn, message, args); - } - else if (args->status == AIM_RENDEZVOUS_CANCEL) - { - /* The other user cancelled a peer request */ - PeerConnection *conn; - - conn = peer_connection_find_by_cookie(od, userinfo->bn, args->cookie); - /* - * If conn is NULL it means we haven't tried to create - * a connection with that user. They may be trying to - * do something malicious. - */ - if (conn != NULL) - { - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - } - } - else if (args->status == AIM_RENDEZVOUS_CONNECTED) - { - /* - * Remote user has accepted our peer request. If we - * wanted to we could look up the PeerConnection using - * args->cookie, but we don't need to do anything here. - */ - } - } - - else if (args->type & OSCAR_CAPABILITY_GETFILE) - { - } - - else if (args->type & OSCAR_CAPABILITY_TALK) - { - } - - else if (args->type & OSCAR_CAPABILITY_BUDDYICON) - { - purple_buddy_icons_set_for_user(account, userinfo->bn, - g_memdup(args->info.icon.icon, args->info.icon.length), - args->info.icon.length, - NULL); - } - - else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY) - { - purple_debug_info("oscar", "Got an ICQ Server Relay message of " - "type %d\n", args->info.rtfmsg.msgtype); - - if (args->info.rtfmsg.msgtype == 1) { - if (args->info.rtfmsg.msg != NULL) { - char *rtfmsg; - const char *encoding = args->encoding; - size_t len = strlen(args->info.rtfmsg.msg); - char *tmp, *tmp2; - - if (encoding == NULL && !g_utf8_validate(args->info.rtfmsg.msg, len, NULL)) { - /* Yet another wonderful Miranda-related hack. If their user disables the "Send Unicode messages" setting, - * Miranda sends us ch2 messages in whatever Windows codepage is set as default on their user's system (instead of UTF-8). - * Of course, they don't bother to specify that codepage. Let's just fallback to the encoding OUR users can - * specify in account options as a last resort. - */ - encoding = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - purple_debug_info("oscar", "Miranda, is that you? Using '%s' as encoding\n", encoding); - } - - rtfmsg = oscar_encoding_to_utf8(encoding, args->info.rtfmsg.msg, len); - - /* Channel 2 messages are supposed to be plain-text (never mind the name "rtfmsg", even - * the official client doesn't parse them as RTF). Therefore, we should escape them before - * showing to the user. */ - tmp = g_markup_escape_text(rtfmsg, -1); - g_free(rtfmsg); - tmp2 = purple_strreplace(tmp, "\r\n", "<br>"); - g_free(tmp); - - purple_serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL)); - aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie); - g_free(tmp2); - } - } else if (args->info.rtfmsg.msgtype == 26) { - purple_debug_info("oscar", "Sending X-Status Reply\n"); - icq_relay_xstatus(od, userinfo->bn, args->cookie); - } - } - else - { - purple_debug_error("oscar", "Unknown request class %" - G_GUINT64_FORMAT "\n", args->type); - } - - g_free(message); - - return 1; -} - -/* When someone sends you buddies */ -static void -purple_icq_buddyadd(struct name_data *data) -{ - PurpleConnection *gc = data->gc; - - purple_blist_request_add_buddy(purple_connection_get_account(gc), data->name, NULL, data->nick); - - oscar_free_name_data(data); -} - -static int -incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, - struct aim_incomingim_ch4_args *args) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - gchar **msg1, **msg2; - int i, numtoks; - - if (!args->type || !args->msg || !args->uin) - return 1; - - purple_debug_info("oscar", - "Received a channel 4 message of type 0x%02hx.", - (guint16)args->type); - - /* - * Split up the message at the delimeter character, then convert each - * string to UTF-8. Unless, of course, this is a type 1 message. If - * this is a type 1 message, then the delimiter 0xfe could be a valid - * character in whatever encoding the message was sent in. Type 1 - * messages are always made up of only one part, so we can easily account - * for this suck-ass part of the protocol by splitting the string into at - * most 1 baby string. - */ - msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0)); - for (numtoks=0; msg1[numtoks]; numtoks++); - msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *)); - for (i=0; msg1[i]; i++) { - gchar *uin = g_strdup_printf("%u", args->uin); - - purple_str_strip_char(msg1[i], '\r'); - /* TODO: Should use an encoding other than ASCII? */ - msg2[i] = oscar_decode_im(account, uin, AIM_CHARSET_ASCII, msg1[i], strlen(msg1[i])); - g_free(uin); - } - msg2[i] = NULL; - - switch (args->type) { - case 0x01: { /* MacICQ message or basic offline message */ - if (i >= 1) { - gchar *uin = g_strdup_printf("%u", args->uin); - gchar *tmp; - - /* If the message came from an ICQ user then escape any HTML */ - tmp = g_markup_escape_text(msg2[0], -1); - - purple_serv_got_im(gc, uin, tmp, 0, time(NULL)); - - g_free(uin); - g_free(tmp); - } - } break; - - case 0x04: { /* Someone sent you a URL */ - if (i >= 2) { - if (msg2[1] != NULL) { - gchar *uin = g_strdup_printf("%u", args->uin); - gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>", - msg2[1], - (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]); - purple_serv_got_im(gc, uin, message, 0, time(NULL)); - g_free(uin); - g_free(message); - } - } - } break; - - case 0x06: { /* Someone requested authorization */ - if (i >= 6) { - gchar *bn = g_strdup_printf("%u", args->uin); - gchar *reason = NULL; - - if (msg2[5] != NULL) - reason = oscar_decode_im(account, bn, AIM_CHARSET_LATIN_1, msg2[5], strlen(msg2[5])); - - purple_debug_info("oscar", - "Received an authorization request from UIN %u\n", - args->uin); - aim_icq_getalias(od, bn, TRUE, reason); - g_free(bn); - g_free(reason); - } - } break; - - case 0x07: { /* Someone has denied you authorization */ - if (i >= 1) { - gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given.")); - purple_notify_info(gc, NULL, _("ICQ authorization denied."), dialog_msg, purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - } - } break; - - case 0x08: { /* Someone has granted you authorization */ - gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin); - purple_notify_info(gc, NULL, "ICQ authorization accepted.", dialog_msg, purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - } break; - - case 0x09: { /* Message from the Godly ICQ server itself, I think */ - if (i >= 5) { - gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]); - purple_notify_info(gc, NULL, "ICQ Server Message", dialog_msg, purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - } - } break; - - case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */ - if (i >= 6) { - gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]); - purple_notify_info(gc, NULL, "ICQ Page", dialog_msg, purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - } - } break; - - case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */ - if (i >= 6) { - gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]); - purple_notify_info(gc, NULL, "ICQ Email", dialog_msg, purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - } - } break; - - case 0x12: { - /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */ - /* Someone added you to their buddy list? */ - } break; - - case 0x13: { /* Someone has sent you some ICQ buddies */ - guint i, num; - gchar **text; - text = g_strsplit(args->msg, "\376", 0); - if (text) { - /* Read the number of contacts that we were sent */ - errno = 0; - num = text[0] ? strtoul(text[0], NULL, 10) : 0; - - if (num > 0 && errno == 0) { - for (i=0; i<num; i++) { - struct name_data *data; - gchar *message; - - if (!text[i*2 + 1] || !text[i*2 + 2]) { - /* We're missing the contact name or nickname. Bail out. */ - gchar *tmp = g_strescape(args->msg, NULL); - purple_debug_error("oscar", "Unknown syntax parsing " - "ICQ buddies. args->msg=%s\n", tmp); - g_free(tmp); - break; - } - - message = g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]); - - data = g_new(struct name_data, 1); - data->gc = gc; - data->name = g_strdup(text[i*2+1]); - data->nick = g_strdup(text[i*2+2]); - - purple_request_action(gc, NULL, message, - _("Do you want to add this buddy " - "to your buddy list?"), - PURPLE_DEFAULT_ACTION_NONE, - purple_request_cpar_from_connection(gc), - data, 2, - _("_Add"), G_CALLBACK(purple_icq_buddyadd), - _("_Decline"), G_CALLBACK(oscar_free_name_data)); - g_free(message); - } - } else { - gchar *tmp = g_strescape(args->msg, NULL); - purple_debug_error("oscar", "Unknown syntax parsing " - "ICQ buddies. args->msg=%s\n", tmp); - g_free(tmp); - } - g_strfreev(text); - } - } break; - - case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */ - ByteStream qbs; - guint16 smstype; - guint32 taglen, smslen; - char *tagstr = NULL, *smsmsg = NULL; - PurpleXmlNode *xmlroot = NULL, *xmltmp = NULL; - gchar *uin = NULL, *message = NULL; - - /* From libicq2000-0.3.2/src/ICQ.cpp */ - byte_stream_init(&qbs, (guint8 *)args->msg, args->msglen); - byte_stream_advance(&qbs, 21); - /* expected: 01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/ - /* unexpected: 00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */ - smstype = byte_stream_getle16(&qbs); - if (smstype != 0) - break; - taglen = byte_stream_getle32(&qbs); - if (taglen > 2000) { - /* Avoid trying to allocate large amounts of memory, in - case we get something unexpected. */ - break; - } - tagstr = byte_stream_getstr(&qbs, taglen); - if (tagstr == NULL) - break; - byte_stream_advance(&qbs, 3); - byte_stream_advance(&qbs, 4); - smslen = byte_stream_getle32(&qbs); - if (smslen > 2000) { - /* Avoid trying to allocate large amounts of memory, in - case we get something unexpected. */ - g_free(tagstr); - break; - } - smsmsg = byte_stream_getstr(&qbs, smslen); - - /* Check if this is an SMS being sent from server */ - if (purple_strequal(tagstr, "ICQSMS") && smsmsg != NULL) { - xmlroot = purple_xmlnode_from_str(smsmsg, -1); - if (xmlroot != NULL) - { - xmltmp = purple_xmlnode_get_child(xmlroot, "sender"); - if (xmltmp != NULL) - uin = purple_xmlnode_get_data(xmltmp); - - xmltmp = purple_xmlnode_get_child(xmlroot, "text"); - if (xmltmp != NULL) - message = purple_xmlnode_get_data(xmltmp); - - if ((uin != NULL) && (message != NULL)) - purple_serv_got_im(gc, uin, message, 0, time(NULL)); - - g_free(uin); - g_free(message); - purple_xmlnode_free(xmlroot); - } - } - g_free(tagstr); - g_free(smsmsg); - } break; - - default: { - purple_debug_info("oscar", - "Received a channel 4 message of unknown type " - "(type 0x%02x).\n", args->type & 0xFF); - } break; - } - - g_strfreev(msg1); - g_strfreev(msg2); - - return 1; -} - -static int purple_parse_incoming_im(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - guint16 channel; - int ret = 0; - aim_userinfo_t *userinfo; - va_list ap; - - va_start(ap, fr); - channel = (guint16)va_arg(ap, unsigned int); - userinfo = va_arg(ap, aim_userinfo_t *); - - switch (channel) { - case 1: { /* standard message */ - struct aim_incomingim_ch1_args *args; - args = va_arg(ap, struct aim_incomingim_ch1_args *); - ret = incomingim_chan1(od, conn, userinfo, args); - } break; - - case 2: { /* rendezvous */ - IcbmArgsCh2 *args; - args = va_arg(ap, IcbmArgsCh2 *); - ret = incomingim_chan2(od, conn, userinfo, args); - } break; - - case 4: { /* ICQ */ - struct aim_incomingim_ch4_args *args; - args = va_arg(ap, struct aim_incomingim_ch4_args *); - ret = incomingim_chan4(od, conn, userinfo, args); - } break; - - default: { - purple_debug_warning("oscar", - "ICBM received on unsupported channel (channel " - "0x%04hx).", channel); - } break; - } - - va_end(ap); - - return ret; -} - -static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - char *buf; - va_list ap; - guint16 nummissed, reason; - aim_userinfo_t *userinfo; - - va_start(ap, fr); - va_arg(ap, unsigned int); /* guint16 chan */ - userinfo = va_arg(ap, aim_userinfo_t *); - nummissed = (guint16)va_arg(ap, unsigned int); - reason = (guint16)va_arg(ap, unsigned int); - va_end(ap); - - switch(reason) { - case 0: /* Invalid (0) */ - buf = g_strdup_printf( - dngettext(PACKAGE, - "You missed %hu message from %s because it was invalid.", - "You missed %hu messages from %s because they were invalid.", - nummissed), - nummissed, - userinfo->bn); - break; - case 1: /* Message too large */ - buf = g_strdup_printf( - dngettext(PACKAGE, - "You missed %hu message from %s because it was too large.", - "You missed %hu messages from %s because they were too large.", - nummissed), - nummissed, - userinfo->bn); - break; - case 2: /* Rate exceeded */ - buf = g_strdup_printf( - dngettext(PACKAGE, - "You missed %hu message from %s because the rate limit has been exceeded.", - "You missed %hu messages from %s because the rate limit has been exceeded.", - nummissed), - nummissed, - userinfo->bn); - break; - case 3: /* Evil Sender */ - buf = g_strdup_printf( - dngettext(PACKAGE, - "You missed %hu message from %s because his/her warning level is too high.", - "You missed %hu messages from %s because his/her warning level is too high.", - nummissed), - nummissed, - userinfo->bn); - break; - case 4: /* Evil Receiver */ - buf = g_strdup_printf( - dngettext(PACKAGE, - "You missed %hu message from %s because your warning level is too high.", - "You missed %hu messages from %s because your warning level is too high.", - nummissed), - nummissed, - userinfo->bn); - break; - default: - buf = g_strdup_printf( - dngettext(PACKAGE, - "You missed %hu message from %s for an unknown reason.", - "You missed %hu messages from %s for an unknown reason.", - nummissed), - nummissed, - userinfo->bn); - break; - } - - if (!purple_conversation_present_error(userinfo->bn, account, buf)) { - purple_notify_error(od->gc, NULL, buf, NULL, - purple_request_cpar_from_connection(od->gc)); - } - g_free(buf); - - return 1; -} - -static int -purple_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, const guchar *cookie) -{ - if (reason == 0x0003) - { - /* Rendezvous was refused. */ - PeerConnection *conn; - - conn = peer_connection_find_by_cookie(od, who, cookie); - - if (conn == NULL) - { - purple_debug_info("oscar", "Received a rendezvous cancel message " - "for a nonexistant connection from %s.\n", who); - } - else - { - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_REFUSED, NULL); - } - } - else - { - purple_debug_warning("oscar", "Received an unknown rendezvous " - "message from %s. Type 0x%04hx\n", who, reason); - } - - return 0; -} - -static int purple_parse_clientauto_ch4(OscarData *od, const char *who, guint16 reason, guint32 state, char *msg) { - PurpleConnection *gc = od->gc; - - switch(reason) { - /* Reply from an ICQ status message request */ - case 0x0003: - case 0x0006: { - char *statusmsg, **splitmsg; - PurpleNotifyUserInfo *user_info; - - statusmsg = oscar_icqstatus(state); - - /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */ - /* TODO: Don't we need to escape each piece? */ - splitmsg = g_strsplit(msg, "\r\n", 0); - - user_info = purple_notify_user_info_new(); - - purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who); - /* TODO: Check whether it's correct to call add_pair_html, - or if we should be using add_pair_plaintext */ - purple_notify_user_info_add_pair_html(user_info, _("Status"), statusmsg); - purple_notify_user_info_add_section_break(user_info); - purple_notify_user_info_add_pair_html(user_info, NULL, g_strjoinv("<BR>", splitmsg)); - - g_free(statusmsg); - g_strfreev(splitmsg); - - purple_notify_userinfo(gc, who, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - - } break; - - default: { - purple_debug_warning("oscar", - "Received an unknown client auto-response from %s. " - "Type 0x%04hx\n", who, reason); - } break; - } /* end of switch */ - - return 0; -} - -static int purple_parse_clientauto(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - guint16 chan, reason; - char *who; - int ret = 1; - - va_start(ap, fr); - chan = (guint16)va_arg(ap, unsigned int); - who = va_arg(ap, char *); - reason = (guint16)va_arg(ap, unsigned int); - - if (chan == 0x0002) { /* File transfer declined */ - guchar *cookie = va_arg(ap, guchar *); - ret = purple_parse_clientauto_ch2(od, who, reason, cookie); - } else if (chan == 0x0004) { /* ICQ message */ - guint32 state = 0; - char *msg = NULL; - if (reason == 0x0003) { - state = va_arg(ap, guint32); - msg = va_arg(ap, char *); - } - ret = purple_parse_clientauto_ch4(od, who, reason, state, msg); - } - - va_end(ap); - - return ret; -} - -static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - guint16 reason; - - va_start(ap, fr); - reason = (guint16) va_arg(ap, unsigned int); - va_end(ap); - - purple_debug_error("oscar", "snac threw error (reason 0x%04hx: %s)\n", - reason, oscar_get_msgerr_reason(reason)); - return 1; -} - -static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - guint16 channel, event; - char *bn; - - va_start(ap, fr); - channel = (guint16) va_arg(ap, unsigned int); - bn = va_arg(ap, char *); - event = (guint16) va_arg(ap, unsigned int); - va_end(ap); - - switch (event) { - case 0x0000: /* Text has been cleared */ - case 0x000f: /* Closed IM window */ - purple_serv_got_typing_stopped(gc, bn); - break; - - case 0x0001: /* Paused typing */ - purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPED); - break; - - case 0x0002: /* Typing */ - purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPING); - break; - - default: - purple_debug_info("oscar", "Received unknown typing " - "notification message from %s. Channel is 0x%04x " - "and event is 0x%04hx.\n", bn, channel, event); - break; - } - - return 1; -} - -static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - char *msg; - guint16 id; - va_list ap; - - va_start(ap, fr); - id = (guint16) va_arg(ap, unsigned int); - msg = va_arg(ap, char *); - va_end(ap); - - purple_debug_misc("oscar", - "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id); - if (id < 4) { - purple_notify_warning(od->gc, NULL, - _("Your AIM connection may be lost."), NULL, - purple_request_cpar_from_connection(od->gc)); - } - - return 1; -} - -static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - guint16 type; - - va_start(ap, fr); - type = (guint16) va_arg(ap, unsigned int); - - switch(type) { - case 0x0002: { - GString *msg = g_string_new(""); - guint8 maxrooms; - struct aim_chat_exchangeinfo *exchanges; - int exchangecount, i; - - maxrooms = (guint8) va_arg(ap, unsigned int); - exchangecount = va_arg(ap, int); - exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); - - g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %d, Exchange List (%d total): ", (int)maxrooms, exchangecount); - for (i = 0; i < exchangecount; i++) { - g_string_append_printf(msg, "%hu", exchanges[i].number); - if (exchanges[i].name) { - g_string_append_printf(msg, " %s", exchanges[i].name); - } - g_string_append(msg, ", "); - } - purple_debug_misc("oscar", "%s\n", msg->str); - g_string_free(msg, TRUE); - - while (od->create_rooms) { - struct create_room *cr = od->create_rooms->data; - purple_debug_info("oscar", - "creating room %s\n", cr->name); - aim_chatnav_createroom(od, conn, cr->name, cr->exchange); - g_free(cr->name); - od->create_rooms = g_slist_remove(od->create_rooms, cr); - g_free(cr); - } - } - break; - case 0x0008: { - char *fqcn, *name, *ck; - guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange; - guint8 createperms; - guint32 createtime; - - fqcn = va_arg(ap, char *); - instance = (guint16)va_arg(ap, unsigned int); - exchange = (guint16)va_arg(ap, unsigned int); - flags = (guint16)va_arg(ap, unsigned int); - createtime = va_arg(ap, guint32); - maxmsglen = (guint16)va_arg(ap, unsigned int); - maxoccupancy = (guint16)va_arg(ap, unsigned int); - createperms = (guint8)va_arg(ap, unsigned int); - unknown = (guint16)va_arg(ap, unsigned int); - name = va_arg(ap, char *); - ck = va_arg(ap, char *); - - purple_debug_misc("oscar", - "created room: %s %hu %hu %hu %u %hu %hu %u %hu %s %s\n", - fqcn ? fqcn : "(null)", exchange, instance, flags, createtime, - maxmsglen, maxoccupancy, (guint)createperms, unknown, - name ? name : "(null)", ck); - aim_chat_join(od, exchange, ck, instance); - } - break; - default: - purple_debug_warning("oscar", - "chatnav info: unknown type (%04hx)\n", type); - break; - } - - va_end(ap); - - return 1; -} - -static int purple_chat_conversation_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - int count, i; - aim_userinfo_t *info; - PurpleConnection *gc = od->gc; - - struct chat_connection *c = NULL; - - va_start(ap, fr); - count = va_arg(ap, int); - info = va_arg(ap, aim_userinfo_t *); - va_end(ap); - - c = find_oscar_chat_by_conn(gc, conn); - if (!c) - return 1; - - for (i = 0; i < count; i++) - purple_chat_conversation_add_user(c->conv, info[i].bn, NULL, PURPLE_CHAT_USER_NONE, TRUE); - - return 1; -} - -static int purple_chat_conversation_left(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - int count, i; - aim_userinfo_t *info; - PurpleConnection *gc = od->gc; - - struct chat_connection *c = NULL; - - va_start(ap, fr); - count = va_arg(ap, int); - info = va_arg(ap, aim_userinfo_t *); - va_end(ap); - - c = find_oscar_chat_by_conn(gc, conn); - if (!c) - return 1; - - for (i = 0; i < count; i++) - purple_chat_conversation_remove_user(c->conv, info[i].bn, NULL); - - return 1; -} - -static int purple_chat_conversation_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - guint16 maxmsglen, maxvisiblemsglen; - PurpleConnection *gc = od->gc; - struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn); - - if (!ccon) - return 1; - - va_start(ap, fr); - maxmsglen = (guint16)va_arg(ap, unsigned int); - maxvisiblemsglen = (guint16)va_arg(ap, unsigned int); - va_end(ap); - - purple_debug_misc("oscar", - "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n", - maxmsglen, maxvisiblemsglen); - - ccon->maxlen = maxmsglen; - ccon->maxvis = maxvisiblemsglen; - - return 1; -} - -static int purple_chat_conversation_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn); - gchar *utf8; - va_list ap; - aim_userinfo_t *info; - int len; - char *msg; - char *charset; - - if (!ccon) - return 1; - - va_start(ap, fr); - info = va_arg(ap, aim_userinfo_t *); - len = va_arg(ap, int); - msg = va_arg(ap, char *); - charset = va_arg(ap, char *); - va_end(ap); - - utf8 = oscar_encoding_to_utf8(charset, msg, len); - purple_serv_got_chat_in(gc, ccon->id, info->bn, - PURPLE_MESSAGE_RECV, utf8, time(NULL)); - g_free(utf8); - - return 1; -} - -static int purple_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - PurpleConnection *gc; - PurpleAccount *account; - struct aim_emailinfo *emailinfo; - int havenewmail; - char *alertitle, *alerturl; - - gc = od->gc; - account = purple_connection_get_account(gc); - - va_start(ap, fr); - emailinfo = va_arg(ap, struct aim_emailinfo *); - havenewmail = va_arg(ap, int); - alertitle = va_arg(ap, char *); - alerturl = va_arg(ap, char *); - va_end(ap); - - if (account != NULL && emailinfo != NULL && purple_account_get_check_mail(account) && - emailinfo->unread && havenewmail) { - gchar *to = g_strdup_printf("%s%s%s", - purple_account_get_username(account), - emailinfo->domain ? "@" : "", - emailinfo->domain ? emailinfo->domain : ""); - const char *tos[2] = { to }; - const char *urls[2] = { emailinfo->url }; - purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, - tos, urls, NULL, NULL); - g_free(to); - } - - if (alertitle) - purple_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : ""); - - return 1; -} - -static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - char *bn; - guint8 *iconcsum, *icon; - guint16 iconcsumlen, iconlen; - - va_start(ap, fr); - bn = va_arg(ap, char *); - va_arg(ap, int); /* iconsumtype */ - iconcsum = va_arg(ap, guint8 *); - iconcsumlen = va_arg(ap, int); - icon = va_arg(ap, guint8 *); - iconlen = va_arg(ap, int); - va_end(ap); - - /* - * Some AIM clients will send a blank GIF image with iconlen 90 when - * no icon is set. Ignore these. - */ - if ((iconlen > 0) && (iconlen != 90)) { - char *b16 = purple_base16_encode(iconcsum, iconcsumlen); - purple_buddy_icons_set_for_user(purple_connection_get_account(gc), - bn, g_memdup(icon, iconlen), iconlen, b16); - g_free(b16); - } - - return 1; -} - -static void -purple_icons_fetch(PurpleConnection *gc) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - aim_userinfo_t *userinfo; - FlapConnection *conn; - - conn = flap_connection_getbytype(od, SNAC_FAMILY_BART); - if (!conn) { - if (!od->iconconnecting) { - aim_srv_requestnew(od, SNAC_FAMILY_BART); - od->iconconnecting = TRUE; - } - return; - } - - if (od->set_icon) { - PurpleAccount *account = purple_connection_get_account(gc); - PurpleImage *img = purple_buddy_icons_find_account_icon(account); - if (img == NULL) { - aim_ssi_delicon(od); - } else { - purple_debug_info("oscar", - "Uploading icon to icon server"); - aim_bart_upload(od, purple_image_get_data(img), - purple_image_get_data_size(img)); - g_object_unref(img); - } - od->set_icon = FALSE; - } - - while (od->requesticon != NULL) - { - userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data); - if ((userinfo != NULL) && (userinfo->iconcsumlen > 0)) - aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen); - - g_free(od->requesticon->data); - od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon); - } - - purple_debug_misc("oscar", "no more icons to request\n"); -} - -static int purple_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - aim_userinfo_t *info; - - va_start(ap, fr); - info = va_arg(ap, aim_userinfo_t *); - va_end(ap); - - purple_connection_set_display_name(od->gc, info->bn); - - return 1; -} - -static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - guint16 code; - char *msg; - - va_start(ap, fr); - code = (guint16)va_arg(ap, int); - msg = va_arg(ap, char *); - va_end(ap); - - purple_debug_info("oscar", "Disconnected. Code is 0x%04x and msg is %s\n", - code, (msg != NULL ? msg : "")); - - g_return_val_if_fail(conn != NULL, 1); - - if (conn->type == SNAC_FAMILY_CHAT) { - struct chat_connection *cc; - PurpleChatConversation *chat = NULL; - - cc = find_oscar_chat_by_conn(gc, conn); - if (cc != NULL) - { - chat = purple_conversations_find_chat(gc, cc->id); - - if (chat != NULL) - { - /* - * TOOD: Have flap_connection_destroy_cb() send us the - * error message stored in 'tmp', which should be - * human-friendly, and print that to the chat room. - */ - gchar *buf; - buf = g_strdup_printf(_("You have been disconnected from chat " - "room %s."), cc->name); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(chat), buf, - PURPLE_MESSAGE_ERROR); - g_free(buf); - } - oscar_chat_kill(gc, cc); - } - } - - return 1; -} - -static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - va_list ap; - guint16 maxsiglen; - - va_start(ap, fr); - maxsiglen = (guint16) va_arg(ap, int); - va_end(ap); - - purple_debug_misc("oscar", - "locate rights: max sig len = %d\n", maxsiglen); - - od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen; - - aim_locate_setcaps(od, purple_caps); - oscar_set_info_and_status(account, TRUE, purple_account_get_user_info(account), TRUE, - purple_account_get_active_status(account)); - - return 1; -} - -static int purple_parse_buddyrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - guint16 maxbuddies, maxwatchers; - - va_start(ap, fr); - maxbuddies = (guint16) va_arg(ap, unsigned int); - maxwatchers = (guint16) va_arg(ap, unsigned int); - va_end(ap); - - purple_debug_misc("oscar", - "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers); - - od->rights.maxbuddies = (guint)maxbuddies; - od->rights.maxwatchers = (guint)maxwatchers; - - return 1; -} - -static void oscar_format_username(PurpleConnection *gc, const char *new_display_name) -{ - OscarData *od; - const char *old_display_name, *username; - char *tmp, *at_sign; - - old_display_name = purple_connection_get_display_name(gc); - if (old_display_name && strchr(old_display_name, '@')) { - purple_debug_info("oscar", "Cowardly refusing to attempt to format " - "screen name because the current formatting according to " - "the server (%s) appears to be an email address\n", - old_display_name); - return; - } - - username = purple_account_get_username(purple_connection_get_account(gc)); - if (oscar_util_name_compare(username, new_display_name)) { - purple_notify_error(gc, NULL, _("The new formatting is invalid."), - _("Username formatting can change only capitalization and whitespace."), - purple_request_cpar_from_connection(gc)); - return; - } - - tmp = g_strdup(new_display_name); - - /* - * If our local username is an email address then strip off the domain. - * This allows formatting to work if the user entered their username as - * 'something@aim.com' or possibly other AOL-owned domains. - */ - at_sign = strchr(tmp, '@'); - if (at_sign) - at_sign[0] = '\0'; - - od = purple_connection_get_protocol_data(gc); - if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) { - /* We don't have a connection to an "admin" server. Make one. */ - od->setnick = TRUE; - g_free(od->newformatting); - od->newformatting = tmp; - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN); - } else { - aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), tmp); - g_free(tmp); - } -} - -static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc; - PurpleAccount *account; - PurpleStatus *status; - gboolean is_available; - PurplePresence *presence; - const char *username, *message, *itmsurl; - char *tmp; - va_list ap; - guint16 maxpermits, maxdenies; - - gc = od->gc; - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - va_start(ap, fr); - maxpermits = (guint16) va_arg(ap, unsigned int); - maxdenies = (guint16) va_arg(ap, unsigned int); - va_end(ap); - - purple_debug_misc("oscar", - "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies); - - od->rights.maxpermits = (guint)maxpermits; - od->rights.maxdenies = (guint)maxdenies; - - purple_debug_info("oscar", "buddy list loaded\n"); - - if (purple_account_get_user_info(account) != NULL) - purple_serv_set_info(gc, purple_account_get_user_info(account)); - - username = purple_account_get_username(account); - if (!od->icq && !purple_strequal(username, purple_connection_get_display_name(gc))) { - /* - * Format the username for AIM accounts if it's different - * than what's currently set. - */ - oscar_format_username(gc, username); - } - - /* Set our available message based on the current status */ - status = purple_account_get_active_status(account); - is_available = purple_status_is_available(status); - if (is_available) - message = purple_status_get_attr_string(status, "message"); - else - message = NULL; - tmp = purple_markup_strip_html(message); - itmsurl = purple_status_get_attr_string(status, "itmsurl"); - aim_srv_setextrainfo(od, FALSE, 0, is_available, tmp, itmsurl); - aim_srv_set_dc_info(od); - g_free(tmp); - - presence = purple_status_get_presence(status); - aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence)); - - if (od->icq) { - oscar_set_extended_status(gc); - aim_icq_setsecurity(od, - purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION), - purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE)); - } - - aim_srv_requestnew(od, SNAC_FAMILY_ALERT); - aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV); - - od->bos.have_rights = TRUE; - - /* - * If we've already received our feedbag data then we're not waiting on - * anything else, so send the server clientready. - * - * Normally we get bos rights before we get our feedbag data, so this - * rarely (never?) happens. And I'm not sure it actually matters if we - * wait for bos rights before calling clientready. But it seems safer - * to do it this way. - */ - if (od->ssi.received_data) { - aim_srv_clientready(od, conn); - - /* Request offline messages for AIM and ICQ */ - aim_im_reqofflinemsgs(od); - - purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED); - } - - return 1; -} - -static int purple_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - gchar *text; - va_list ap; - char *msg, *url; - - va_start(ap, fr); - msg = va_arg(ap, char *); - url = va_arg(ap, char *); - va_arg(ap, int); /* guint16 wid */ - va_arg(ap, int); /* guint16 hei */ - va_arg(ap, int); /* guint16 delay */ - va_end(ap); - - text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url); - purple_notify_formatted(gc, NULL, _("Pop-Up Message"), NULL, text, NULL, NULL); - g_free(text); - - return 1; -} - -static void oscar_searchresults_add_buddy_cb(PurpleConnection *gc, GList *row, void *user_data) -{ - purple_blist_request_add_buddy(purple_connection_get_account(gc), - g_list_nth_data(row, 0), NULL, NULL); -} - -static int purple_parse_searchreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleNotifySearchResults *results; - PurpleNotifySearchColumn *column; - gchar *secondary; - int i, num; - va_list ap; - char *email, *usernames; - - va_start(ap, fr); - email = va_arg(ap, char *); - num = va_arg(ap, int); - usernames = va_arg(ap, char *); - va_end(ap); - - results = purple_notify_searchresults_new(); - - if (results == NULL) { - purple_debug_error("oscar", "purple_parse_searchreply: " - "Unable to display the search results.\n"); - purple_notify_error(gc, NULL, _("Unable to display the search " - "results."), NULL, - purple_request_cpar_from_connection(gc)); - return 1; - } - - secondary = g_strdup_printf( - dngettext(PACKAGE, "The following username is associated with %s", - "The following usernames are associated with %s", - num), - email); - - column = purple_notify_searchresults_column_new(_("Username")); - purple_notify_searchresults_column_add(results, column); - - for (i = 0; i < num; i++) { - GList *row; - row = g_list_append(NULL, g_strdup(&usernames[i * (MAXSNLEN + 1)])); - purple_notify_searchresults_row_add(results, row); - } - purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD, - oscar_searchresults_add_buddy_cb); - purple_notify_searchresults(gc, NULL, NULL, secondary, results, NULL, NULL); - - g_free(secondary); - - return 1; -} - -static int purple_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - va_list ap; - char *email; - char *buf; - - va_start(ap, fr); - email = va_arg(ap, char *); - va_end(ap); - - buf = g_strdup_printf(_("No results found for email address %s"), email); - purple_notify_error(od->gc, NULL, buf, NULL, - purple_request_cpar_from_connection(od->gc)); - g_free(buf); - - return 1; -} - -static int purple_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - guint16 status; - va_list ap; - char msg[256]; - - va_start(ap, fr); - status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */ - va_end(ap); - - purple_debug_info("oscar", - "account confirmation returned status 0x%04x (%s)\n", status, - status ? "unknown" : "email sent"); - if (!status) { - g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."), - purple_account_get_username(purple_connection_get_account(gc))); - purple_notify_info(gc, NULL, _("Account Confirmation Requested"), - msg, purple_request_cpar_from_connection(gc)); - } - - return 1; -} - -static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - guint16 perms, err; - char *url, *bn, *email; - int change; - - va_start(ap, fr); - change = va_arg(ap, int); - perms = (guint16) va_arg(ap, unsigned int); - err = (guint16) va_arg(ap, unsigned int); - url = va_arg(ap, char *); - bn = va_arg(ap, char *); - email = va_arg(ap, char *); - va_end(ap); - - purple_debug_misc("oscar", - "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, bn=%s, email=%s\n", - change ? "change" : "request", perms, err, - (url != NULL) ? url : "(null)", - (bn != NULL) ? bn : "(null)", - (email != NULL) ? email : "(null)"); - - if ((err > 0) && (url != NULL)) { - char *dialog_msg; - - if (err == 0x0001) - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name differs from the original."), err); - else if (err == 0x0006) - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because it is invalid."), err); - else if (err == 0x00b) - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name is too long."), err); - else if (err == 0x001d) - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this username."), err); - else if (err == 0x0021) - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many usernames associated with it."), err); - else if (err == 0x0023) - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err); - else - dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err); - purple_notify_error(gc, NULL, _("Error Changing Account Info"), - dialog_msg, purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - return 1; - } - - if (email != NULL) { - char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"), - purple_account_get_username(purple_connection_get_account(gc)), email); - purple_notify_info(gc, NULL, _("Account Info"), dialog_msg, - purple_request_cpar_from_connection(gc)); - g_free(dialog_msg); - } - - return 1; -} - -void -oscar_keepalive(PurpleConnection *gc) -{ - OscarData *od; - GSList *l; - - od = purple_connection_get_protocol_data(gc); - for (l = od->oscar_connections; l; l = l->next) { - flap_connection_send_keepalive(od, l->data); - } -} - -unsigned int -oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state) -{ - OscarData *od; - PeerConnection *conn; - - od = purple_connection_get_protocol_data(gc); - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM); - - if ((conn != NULL) && (conn->ready)) - { - peer_odc_send_typing(conn, state); - } - else { - /* Don't send if this turkey is in our deny list */ - PurpleAccount *account = purple_connection_get_account(gc); - GSList *list = purple_account_privacy_get_denied(account); - - if (!g_slist_find_custom(list, name, (GCompareFunc)oscar_util_name_compare)) { - struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name)); - if (bi && bi->typingnot) { - if (state == PURPLE_IM_TYPING) - aim_im_sendmtn(od, 0x0001, name, 0x0002); - else if (state == PURPLE_IM_TYPED) - aim_im_sendmtn(od, 0x0001, name, 0x0001); - else - aim_im_sendmtn(od, 0x0001, name, 0x0000); - } - } - } - return 0; -} - -/* TODO: Move this into odc.c! */ -static void -purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags imflags) -{ - GString *msg; - GString *data; - gchar *tmp; - gsize tmplen; - guint16 charset; - GData *attribs; - const char *start, *end, *last; - int oscar_id = 0; - - msg = g_string_new("<HTML><BODY>"); - data = g_string_new("<BINARY>"); - last = message; - - /* for each valid IMG tag... */ - while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs)) - { - PurpleImage *image = NULL; - const gchar *src; - - if (start - last) { - g_string_append_len(msg, last, start - last); - } - - src = g_datalist_get_data(&attribs, "src"); - if (src) - image = purple_image_store_get_from_uri(src); - - /* ... if it refers to a valid purple image ... */ - if (image) { - /* ... append the message from start to the tag ... */ - unsigned long size = purple_image_get_data_size(image); - const gchar *filename = purple_image_get_friendly_filename(image); - gconstpointer imgdata = purple_image_get_data(image); - - oscar_id++; - - /* ... insert a new img tag with the oscar id ... */ - if (filename) - g_string_append_printf(msg, - "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">", - filename, oscar_id, size); - else - g_string_append_printf(msg, - "<IMG ID=\"%d\" DATASIZE=\"%lu\">", - oscar_id, size); - - /* ... and append the data to the binary section ... */ - g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%lu\">", - oscar_id, size); - g_string_append_len(data, imgdata, size); - g_string_append(data, "</DATA>"); - } - /* If the tag is invalid, skip it, thus no else here */ - - g_datalist_clear(&attribs); - - /* continue from the end of the tag */ - last = end + 1; - } - - /* append any remaining message data */ - if (last && *last) - g_string_append(msg, last); - - g_string_append(msg, "</BODY></HTML>"); - - /* Convert the message to a good encoding */ - tmp = oscar_encode_im(msg->str, &tmplen, &charset, NULL); - g_string_free(msg, TRUE); - msg = g_string_new_len(tmp, tmplen); - g_free(tmp); - - /* Append any binary data that we may have */ - if (oscar_id) { - msg = g_string_append_len(msg, data->str, data->len); - msg = g_string_append(msg, "</BINARY>"); - } - g_string_free(data, TRUE); - - purple_debug_info("oscar", "sending direct IM %s using charset %i", msg->str, charset); - - peer_odc_send_im(conn, msg->str, msg->len, charset, - imflags & PURPLE_MESSAGE_AUTO_RESP); - g_string_free(msg, TRUE); -} - -int -oscar_send_im(PurpleConnection *gc, PurpleMessage *msg) -{ - OscarData *od; - PurpleAccount *account; - PeerConnection *conn; - int ret; - char *tmp1, *tmp2; - gboolean is_sms, is_html; - const gchar *name, *message; - PurpleMessageFlags imflags; - - name = purple_message_get_recipient(msg); - message = purple_message_get_contents(msg); - imflags = purple_message_get_flags(msg); - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - ret = 0; - - is_sms = oscar_util_valid_name_sms(name); - - if (od->icq && is_sms) { - /* - * We're sending to a phone number and this is ICQ, - * so send the message as an SMS using aim_icq_sendsms() - */ - int ret; - purple_debug_info("oscar", "Sending SMS to %s.\n", name); - ret = aim_icq_sendsms(od, name, message, purple_account_get_username(account)); - return (ret >= 0 ? 1 : ret); - } - - if (imflags & PURPLE_MESSAGE_AUTO_RESP) - tmp1 = oscar_util_format_string(message, name); - else - tmp1 = g_strdup(message); - - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM); - if ((conn != NULL) && (conn->ready)) - { - /* If we're directly connected, send a direct IM */ - purple_debug_info("oscar", "Sending direct IM with flags %i\n", imflags); - purple_odc_send_im(conn, tmp1, imflags); - } else { - struct buddyinfo *bi; - struct aim_sendimext_args args; - PurpleIMConversation *im; - PurpleImage *img; - PurpleBuddy *buddy; - - im = purple_conversations_find_im_with_account(name, account); - - if (strstr(tmp1, "<img ")) - purple_conversation_write_system_message(PURPLE_CONVERSATION(im), - _("Your IM Image was not sent. " - "You must be Direct Connected to send IM Images."), - PURPLE_MESSAGE_ERROR); - - buddy = purple_blist_find_buddy(account, name); - - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name)); - if (!bi) { - bi = g_new0(struct buddyinfo, 1); - g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi); - } - - args.flags = 0; - - if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy))) - args.flags |= AIM_IMFLAGS_OFFLINE; - - if (od->icq) { - args.features = features_icq; - args.featureslen = sizeof(features_icq); - } else { - args.features = features_aim; - args.featureslen = sizeof(features_aim); - - if (imflags & PURPLE_MESSAGE_AUTO_RESP) - args.flags |= AIM_IMFLAGS_AWAY; - } - - if (bi->ico_need) { - purple_debug_info("oscar", - "Sending buddy icon request with message\n"); - args.flags |= AIM_IMFLAGS_BUDDYREQ; - bi->ico_need = FALSE; - } - - img = purple_buddy_icons_find_account_icon(account); - if (img) { - gconstpointer data = purple_image_get_data(img); - args.iconlen = purple_image_get_data_size(img); - args.iconsum = aimutil_iconsum(data, args.iconlen); - args.iconstamp = purple_buddy_icons_get_account_icon_timestamp(account); - - if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) { - bi->ico_informed = FALSE; - bi->ico_sent = FALSE; - } - - /* - * TODO: - * For some reason sending our icon to people only works - * when we're the ones who initiated the conversation. If - * the other person sends the first IM then they never get - * the icon. We should fix that. - */ - if (!bi->ico_informed) { - purple_debug_info("oscar", - "Claiming to have a buddy icon\n"); - args.flags |= AIM_IMFLAGS_HASICON; - bi->ico_me_len = args.iconlen; - bi->ico_me_csum = args.iconsum; - bi->ico_me_time = args.iconstamp; - bi->ico_informed = TRUE; - } - - g_object_unref(img); - } - - args.destbn = name; - - if (oscar_util_valid_name_sms(name)) { - /* Messaging an SMS (mobile) user--strip HTML */ - tmp2 = purple_markup_strip_html(tmp1); - is_html = FALSE; - } else { - /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */ - tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1); - is_html = TRUE; - } - g_free(tmp1); - tmp1 = tmp2; - - args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL); - if (is_html && (args.msglen > MAXMSGLEN)) { - /* If the length was too long, try stripping the HTML and then running it back through - * purple_strdup_withhtml() and the encoding process. The result may be shorter. */ - g_free((char *)args.msg); - - tmp2 = purple_markup_strip_html(tmp1); - g_free(tmp1); - - /* re-escape the entities */ - tmp1 = g_markup_escape_text(tmp2, -1); - g_free(tmp2); - - tmp2 = purple_strdup_withhtml(tmp1); - g_free(tmp1); - tmp1 = tmp2; - - args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL); - purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n", - message, (char *)args.msg); - } - - purple_debug_info("oscar", "Sending IM, charset=0x%04hx, length=%" G_GSIZE_FORMAT "\n", args.charset, args.msglen); - ret = aim_im_sendch1_ext(od, &args); - g_free((char *)args.msg); - } - - g_free(tmp1); - - if (ret >= 0) - return 1; - - return ret; -} - -/* - * As of 26 June 2006, ICQ users can request AIM info from - * everyone, and can request ICQ info from ICQ users, and - * AIM users can only request AIM info. - */ -void oscar_get_info(PurpleConnection *gc, const char *name) { - OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->icq && oscar_util_valid_name_icq(name)) - aim_icq_getallinfo(od, name); - else - aim_locate_getinfoshort(od, name, 0x00000003); -} - -void oscar_set_idle(PurpleConnection *gc, int time) { - OscarData *od = purple_connection_get_protocol_data(gc); - aim_srv_setidle(od, time); -} - -void -oscar_set_info(PurpleConnection *gc, const char *rawinfo) -{ - PurpleAccount *account; - PurpleStatus *status; - - account = purple_connection_get_account(gc); - status = purple_account_get_active_status(account); - oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status); -} - -static guint32 -oscar_get_extended_status(PurpleConnection *gc) -{ - PurpleAccount *account; - PurpleStatus *status; - const gchar *status_id; - guint32 data = 0x00000000; - - account = purple_connection_get_account(gc); - status = purple_account_get_active_status(account); - status_id = purple_status_get_id(status); - - data |= AIM_ICQ_STATE_HIDEIP; - if (purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE)) - data |= AIM_ICQ_STATE_WEBAWARE; - - if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE)) - data |= AIM_ICQ_STATE_NORMAL; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_AWAY)) - data |= AIM_ICQ_STATE_AWAY; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_DND)) - data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_NA)) - data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_OCCUPIED)) - data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_FREE4CHAT)) - data |= AIM_ICQ_STATE_CHAT; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_INVISIBLE)) - data |= AIM_ICQ_STATE_INVISIBLE; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_EVIL)) - data |= AIM_ICQ_STATE_EVIL; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_DEPRESSION)) - data |= AIM_ICQ_STATE_DEPRESSION; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATWORK)) - data |= AIM_ICQ_STATE_ATWORK; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATHOME)) - data |= AIM_ICQ_STATE_ATHOME; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_LUNCH)) - data |= AIM_ICQ_STATE_LUNCH; - else if (purple_strequal(status_id, OSCAR_STATUS_ID_CUSTOM)) - data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY; - - return data; -} - -static void -oscar_set_extended_status(PurpleConnection *gc) -{ - aim_srv_setextrainfo(purple_connection_get_protocol_data(gc), TRUE, oscar_get_extended_status(gc), FALSE, NULL, NULL); -} - -static void -oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, - gboolean setstatus, PurpleStatus *status) -{ - PurpleConnection *gc = purple_account_get_connection(account); - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleStatusType *status_type; - PurpleStatusPrimitive primitive; - - char *info_encoding = NULL; - char *info = NULL; - gsize infolen = 0; - - char *away_encoding = NULL; - char *away = NULL; - gsize awaylen = 0; - - char *status_text = NULL; - const char *itmsurl = NULL; - - status_type = purple_status_get_status_type(status); - primitive = purple_status_type_get_primitive(status_type); - - if (!setinfo) - { - /* Do nothing! */ - } - else if (od->rights.maxsiglen == 0) - { - purple_notify_warning(gc, NULL, _("Unable to set AIM profile."), - _("You have probably requested to set your " - "profile before the login procedure completed. " - "Your profile remains unset; try setting it " - "again when you are fully connected."), - purple_request_cpar_from_connection(gc)); - } - else if (rawinfo != NULL) - { - char *htmlinfo = purple_strdup_withhtml(rawinfo); - info = oscar_encode_im(htmlinfo, &infolen, NULL, &info_encoding); - g_free(htmlinfo); - - if (infolen > od->rights.maxsiglen) - { - gchar *errstr; - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum profile length of %d byte " - "has been exceeded. It has been truncated for you.", - "The maximum profile length of %d bytes " - "has been exceeded. It has been truncated for you.", - od->rights.maxsiglen), od->rights.maxsiglen); - purple_notify_warning(gc, NULL, _("Profile too long."), - errstr, purple_request_cpar_from_connection(gc)); - g_free(errstr); - } - } - - if (setstatus) - { - const char *status_html; - - status_html = purple_status_get_attr_string(status, "message"); - - if (status_html == NULL || primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE) - { - /* This is needed for us to un-set any previous away message. */ - away = g_strdup(""); - } - else - { - gchar *linkified; - - /* We do this for icq too so that they work for old third party clients */ - linkified = purple_markup_linkify(status_html); - away = oscar_encode_im(linkified, &awaylen, NULL, &away_encoding); - g_free(linkified); - - if (awaylen > od->rights.maxawaymsglen) - { - gchar *errstr; - - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte " - "has been exceeded. It has been truncated for you.", - "The maximum away message length of %d bytes " - "has been exceeded. It has been truncated for you.", - od->rights.maxawaymsglen), od->rights.maxawaymsglen); - purple_notify_warning(gc, NULL, - _("Away message too long."), errstr, - purple_request_cpar_from_connection(gc)); - g_free(errstr); - } - } - } - - aim_locate_setprofile(od, - info_encoding, info, MIN(infolen, od->rights.maxsiglen), - away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen)); - g_free(info); - g_free(away); - - if (setstatus) - { - const char *status_html; - - status_html = purple_status_get_attr_string(status, "message"); - if (status_html != NULL) - { - status_text = purple_markup_strip_html(status_html); - /* If the status_text is longer than 251 characters then truncate it */ - if (strlen(status_text) > MAXAVAILMSGLEN) - { - char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]); - strcpy(tmp, "..."); - } - } - - itmsurl = purple_status_get_attr_string(status, "itmsurl"); - - aim_srv_setextrainfo(od, TRUE, oscar_get_extended_status(gc), TRUE, status_text, itmsurl); - g_free(status_text); - } -} - -static void -oscar_set_icq_permdeny(PurpleAccount *account) -{ - PurpleConnection *gc = purple_account_get_connection(account); - OscarData *od = purple_connection_get_protocol_data(gc); - gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE); - - /* - * For ICQ the permit/deny setting controls who can see you - * online. Mimicking the official client's behavior, we use PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS - * when our status is "invisible" and PURPLE_ACCOUNT_PRIVACY_DENY_USERS otherwise. - * In the former case, we are visible only to buddies on our "permanently visible" list. - * In the latter, we are invisible only to buddies on our "permanently invisible" list. - */ - aim_ssi_setpermdeny(od, invisible ? PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS : PURPLE_ACCOUNT_PRIVACY_DENY_USERS); -} - -void -oscar_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *pc; - OscarData *od; - - purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status)); - - /* Either setting a new status active or setting a status inactive. - * (Only possible for independent status (i.e. X-Status moods.) */ - if (!purple_status_is_active(status) && !purple_status_is_independent(status)) - return; - - if (!purple_account_is_connected(account)) - return; - - pc = purple_account_get_connection(account); - od = purple_connection_get_protocol_data(pc); - - /* There's no need to do the stuff below for mood updates. */ - if (purple_status_type_get_primitive(purple_status_get_status_type(status)) == PURPLE_STATUS_MOOD) { - aim_locate_setcaps(od, purple_caps); - return; - } - - if (od->icq) { - /* Set visibility */ - oscar_set_icq_permdeny(account); - } - - /* Set the AIM-style away message for both AIM and ICQ accounts */ - oscar_set_info_and_status(account, FALSE, NULL, TRUE, status); -} - -void -oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg) -{ - OscarData *od; - PurpleAccount *account; - const char *bname, *gname; - - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - bname = purple_buddy_get_name(buddy); - gname = purple_group_get_name(group); - - if (!oscar_util_valid_name(bname)) { - gchar *buf; - buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname); - if (!purple_conversation_present_error(bname, account, buf)) - purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc)); - g_free(buf); - - /* Remove from local list */ - purple_blist_remove_buddy(buddy); - - return; - } - - if (od->ssi.received_data) { - if (!aim_ssi_itemlist_finditem(&od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) { - purple_debug_info("oscar", - "ssi: adding buddy %s to group %s\n", bname, gname); - aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0); - - /* Mobile users should always be online */ - if (bname[0] == '+') { - purple_protocol_got_user_status(account, bname, - OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_protocol_got_user_status(account, bname, - OSCAR_STATUS_ID_MOBILE, NULL); - } - } else if (aim_ssi_waitingforauth(&od->ssi.local, - aim_ssi_itemlist_findparentname(&od->ssi.local, bname), - bname)) { - /* Not authorized -- Re-request authorization */ - oscar_auth_sendrequest(gc, bname, msg); - } - } - - /* XXX - Should this be done from AIM accounts, as well? */ - if (od->icq) - aim_icq_getalias(od, bname, FALSE, NULL); -} - -void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { - OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->ssi.received_data) { - const char *gname = purple_group_get_name(group); - const char *bname = purple_buddy_get_name(buddy); - purple_debug_info("oscar", - "ssi: deleting buddy %s from group %s\n", bname, gname); - aim_ssi_delbuddy(od, bname, gname); - } -} - -void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group) { - OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->ssi.received_data && !purple_strequal(old_group, new_group)) { - purple_debug_info("oscar", - "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group); - aim_ssi_movebuddy(od, old_group, new_group, name); - } -} - -void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias) { - OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->ssi.received_data) { - char *gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name); - if (gname) { - purple_debug_info("oscar", - "ssi: changing the alias for buddy %s to %s\n", name, alias ? alias : "(none)"); - aim_ssi_aliasbuddy(od, gname, name, alias); - } - } -} - -/* - * FYI, the OSCAR SSI code removes empty groups automatically. - */ -void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) { - OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->ssi.received_data) { - const char *gname = purple_group_get_name(group); - if (aim_ssi_itemlist_finditem(&od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) { - GList *cur, *groups = NULL; - PurpleAccount *account = purple_connection_get_account(gc); - - /* Make a list of what the groups each buddy is in */ - for (cur = moved_buddies; cur != NULL; cur = cur->next) { - PurpleBlistNode *node = cur->data; - /* node is PurpleBuddy, parent is a PurpleContact. - * We must go two levels up to get the Group */ - groups = g_list_append(groups, - purple_buddy_get_group((PurpleBuddy*)node)); - } - - purple_account_remove_buddies(account, moved_buddies, groups); - purple_account_add_buddies(account, moved_buddies, NULL); - g_list_free(groups); - purple_debug_info("oscar", - "ssi: moved all buddies from group %s to %s\n", old_name, gname); - } else { - aim_ssi_rename_group(od, old_name, gname); - purple_debug_info("oscar", - "ssi: renamed group %s to %s\n", old_name, gname); - } - } -} - -void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group) -{ - aim_ssi_delgroup(purple_connection_get_protocol_data(gc), purple_group_get_name(group)); -} - -static gboolean purple_ssi_rerequestdata(gpointer data) { - OscarData *od = data; - - aim_ssi_reqdata(od); - - return TRUE; -} - -static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - guint16 reason; - - va_start(ap, fr); - reason = (guint16)va_arg(ap, unsigned int); - va_end(ap); - - purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason); - - if (reason == 0x0005) { - if (od->getblisttimer > 0) - g_source_remove(od->getblisttimer); - else - /* We only show this error the first time it happens */ - purple_notify_error(gc, NULL, - _("Unable to Retrieve Buddy List"), - _("The AIM servers were temporarily unable to send " - "your buddy list. Your buddy list is not lost, and " - "will probably become available in a few minutes."), - purple_request_cpar_from_connection(gc)); - od->getblisttimer = g_timeout_add_seconds(30, purple_ssi_rerequestdata, od); - return 1; - } - - return 1; -} - -static int purple_ssi_parserights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - int i; - va_list ap; - int numtypes; - guint16 *maxitems; - GString *msg; - - va_start(ap, fr); - numtypes = va_arg(ap, int); - maxitems = va_arg(ap, guint16 *); - va_end(ap); - - msg = g_string_new("ssi rights:"); - for (i=0; i<numtypes; i++) - g_string_append_printf(msg, " max type 0x%04x=%hd,", i, maxitems[i]); - g_string_append(msg, "\n"); - purple_debug_misc("oscar", "%s", msg->str); - g_string_free(msg, TRUE); - - if (numtypes >= 0) - od->rights.maxbuddies = maxitems[0]; - if (numtypes >= 1) - od->rights.maxgroups = maxitems[1]; - if (numtypes >= 2) - od->rights.maxpermits = maxitems[2]; - if (numtypes >= 3) - od->rights.maxdenies = maxitems[3]; - - return 1; -} - -static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc; - PurpleAccount *account; - PurpleGroup *g; - PurpleBuddy *b; - GSList *cur, *next, *buddies; - struct aim_ssi_item *curitem; - guint32 tmp; - PurpleImage *img; - va_list ap; - guint16 deny_entry_type = aim_ssi_getdenyentrytype(od); - - gc = od->gc; - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - va_start(ap, fr); - va_arg(ap, int); /* guint16 fmtver */ - va_arg(ap, int); /* guint16 numitems */ - va_arg(ap, guint32); /* timestamp */ - va_end(ap); - - /* Don't attempt to re-request our buddy list later */ - if (od->getblisttimer != 0) { - g_source_remove(od->getblisttimer); - od->getblisttimer = 0; - } - - purple_debug_info("oscar", "ssi: syncing local list and server list\n"); - - /*** Begin code for pruning buddies from local list if they're not in server list ***/ - - /* Buddies */ - cur = NULL; - for (buddies = purple_blist_find_buddies(account, NULL); - buddies; - buddies = g_slist_delete_link(buddies, buddies)) - { - PurpleGroup *g; - const char *gname; - const char *bname; - - b = buddies->data; - g = purple_buddy_get_group(b); - gname = purple_group_get_name(g); - bname = purple_buddy_get_name(b); - - if (aim_ssi_itemlist_exists(&od->ssi.local, bname)) { - /* If the buddy is an ICQ user then load his nickname */ - const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick"); - char *alias; - const char *balias; - if (servernick) - purple_serv_got_alias(gc, bname, servernick); - - /* Store local alias on server */ - alias = aim_ssi_getalias(&od->ssi.local, gname, bname); - balias = purple_buddy_get_local_alias(b); - if (!alias && balias && *balias) - aim_ssi_aliasbuddy(od, gname, bname, balias); - g_free(alias); - } else { - purple_debug_info("oscar", - "ssi: removing buddy %s from local list\n", bname); - /* Queue the buddy for removal from the local list */ - cur = g_slist_prepend(cur, b); - } - } - g_slist_free_full(cur, (GDestroyNotify)purple_blist_remove_buddy); - - /* Permit list (ICQ doesn't have one) */ - if (!od->icq) { - next = purple_account_privacy_get_permitted(account); - while (next != NULL) { - cur = next; - next = next->next; - if (!aim_ssi_itemlist_finditem(&od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) { - purple_debug_info("oscar", - "ssi: removing permit %s from local list\n", (const char *)cur->data); - purple_account_privacy_permit_remove(account, cur->data, TRUE); - } - } - } - - /* Deny list */ - next = purple_account_privacy_get_denied(account); - while (next != NULL) { - cur = next; - next = next->next; - if (!aim_ssi_itemlist_finditem(&od->ssi.local, NULL, cur->data, deny_entry_type)) { - purple_debug_info("oscar", - "ssi: removing deny %s from local list\n", (const char *)cur->data); - purple_account_privacy_deny_remove(account, cur->data, TRUE); - } - } - - /* Presence settings (idle time visibility) */ - tmp = aim_ssi_getpresence(&od->ssi.local); - if (tmp != 0xFFFFFFFF) { - const char *idle_reporting_pref; - gboolean report_idle; - - idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting"); - report_idle = !purple_strequal(idle_reporting_pref, "none"); - - if (report_idle) - aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE); - else - aim_ssi_setpresence(od, tmp & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE); - } - - /*** End code for pruning buddies from local list ***/ - - /*** Begin code for adding from server list to local list ***/ - - for (curitem=od->ssi.local.data; curitem; curitem=curitem->next) { - if (curitem->name && !g_utf8_validate(curitem->name, -1, NULL)) { - /* Got node with invalid UTF-8 in the name. Skip it. */ - purple_debug_warning("oscar", "ssi: server list contains item of " - "type 0x%04hx with a non-utf8 name\n", curitem->type); - continue; - } - - switch (curitem->type) { - case AIM_SSI_TYPE_BUDDY: { /* Buddy */ - if (curitem->name) { - struct aim_ssi_item *groupitem; - char *gname, *gname_utf8, *alias, *alias_utf8; - - groupitem = aim_ssi_itemlist_find(&od->ssi.local, curitem->gid, 0x0000); - gname = groupitem ? groupitem->name : NULL; - gname_utf8 = oscar_utf8_try_convert(account, od, gname); - - g = purple_blist_find_group(gname_utf8); - if (g == NULL) { - g = purple_group_new(gname_utf8); - purple_blist_add_group(g, NULL); - } - - alias = aim_ssi_getalias_from_item(curitem); - alias_utf8 = oscar_utf8_try_convert(account, od, alias); - - b = purple_blist_find_buddy_in_group(account, curitem->name, g); - if (b) { - /* Get server stored alias */ - purple_buddy_set_local_alias(b, alias_utf8); - } else { - b = purple_buddy_new(account, curitem->name, alias_utf8); - - purple_debug_info("oscar", - "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname); - purple_blist_add_buddy(b, NULL, g, NULL); - } - - /* Mobile users should always be online */ - if (curitem->name[0] == '+') { - purple_protocol_got_user_status(account, - purple_buddy_get_name(b), - OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_protocol_got_user_status(account, - purple_buddy_get_name(b), - OSCAR_STATUS_ID_MOBILE, NULL); - } - - g_free(gname_utf8); - g_free(alias); - g_free(alias_utf8); - } - } break; - - case AIM_SSI_TYPE_GROUP: { /* Group */ - if (curitem->name != NULL && purple_blist_find_group(curitem->name) == NULL) { - g = purple_group_new(curitem->name); - purple_blist_add_group(g, NULL); - } - } break; - - case AIM_SSI_TYPE_PERMIT: { /* Permit buddy (unless we're on ICQ) */ - if (!od->icq && curitem->name) { - cur = purple_account_privacy_get_permitted(account); - if (!g_slist_find_custom(cur, curitem->name, (GCompareFunc)oscar_util_name_compare)) { - purple_debug_info("oscar", - "ssi: adding permit buddy %s to local list\n", curitem->name); - purple_account_privacy_permit_add(account, curitem->name, TRUE); - } - } - } break; - - case AIM_SSI_TYPE_ICQDENY: - case AIM_SSI_TYPE_DENY: { /* Deny buddy */ - if (curitem->type == deny_entry_type && curitem->name) { - cur = purple_account_privacy_get_denied(account); - if (!g_slist_find_custom(cur, curitem->name, (GCompareFunc)oscar_util_name_compare)) { - purple_debug_info("oscar", - "ssi: adding deny buddy %s to local list\n", curitem->name); - purple_account_privacy_deny_add(account, curitem->name, TRUE); - } - } - } break; - - case AIM_SSI_TYPE_PDINFO: { /* Permit/deny setting */ - /* - * We don't inherit the permit/deny setting from the server - * for ICQ because, for ICQ, this setting controls who can - * see your online status when you are invisible. Thus it is - * a part of your status and not really related to blocking. - */ - if (!od->icq && curitem->data) { - guint8 perm_deny = aim_ssi_getpermdeny(&od->ssi.local); - if (perm_deny != 0 && perm_deny != purple_account_get_privacy_type(account)) - { - purple_debug_info("oscar", - "ssi: changing permdeny from %d to %u\n", purple_account_get_privacy_type(account), (guint)perm_deny); - purple_account_set_privacy_type(account, perm_deny); - } - } - } break; - - case AIM_SSI_TYPE_PRESENCEPREFS: { /* Presence setting */ - /* We don't want to change Purple's setting because it applies to all accounts */ - } break; - } /* End of switch on curitem->type */ - } /* End of for loop */ - - /*** End code for adding from server list to local list ***/ - - if (od->icq) { - oscar_set_icq_permdeny(account); - } else { - oscar_set_aim_permdeny(gc); - } - - /* Activate SSI */ - /* Sending the enable causes other people to be able to see you, and you to see them */ - /* Make sure your privacy setting/invisibility is set how you want it before this! */ - purple_debug_info("oscar", - "ssi: activating server-stored buddy list\n"); - aim_ssi_enable(od); - - /* - * Make sure our server-stored icon is updated correctly in - * the event that the local user set a new icon while this - * account was offline. - */ - img = purple_buddy_icons_find_account_icon(account); - oscar_set_icon(gc, img); - g_object_unref(img); - - /* - * If we've already received our bos rights then we're not waiting on - * anything else, so send the server clientready. - */ - if (od->bos.have_rights) { - aim_srv_clientready(od, conn); - - /* Request offline messages for AIM and ICQ */ - aim_im_reqofflinemsgs(od); - - purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED); - } - - return 1; -} - -static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - struct aim_ssi_tmp *retval; - - va_start(ap, fr); - retval = va_arg(ap, struct aim_ssi_tmp *); - va_end(ap); - - while (retval) { - purple_debug_misc("oscar", - "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack, retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item"); - - if (retval->ack != 0xffff) - switch (retval->ack) { - case 0x0000: { /* added successfully */ - } break; - - case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */ - gchar *buf; - buf = g_strdup_printf(_("Unable to add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)"))); - if ((retval->name != NULL) && !purple_conversation_present_error(retval->name, purple_connection_get_account(gc), buf)) - purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc)); - g_free(buf); - } break; - - case 0x000e: { /* buddy requires authorization */ - if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name)) - oscar_auth_sendrequest(gc, retval->name, NULL); - } break; - - default: { /* La la la */ - gchar *buf; - purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack); - buf = g_strdup_printf(_("Unable to add the buddy %s for an unknown reason."), - (retval->name ? retval->name : _("(no name)"))); - if ((retval->name != NULL) && !purple_conversation_present_error(retval->name, purple_connection_get_account(gc), buf)) - purple_notify_error(gc, NULL, _("Unable to Add"), buf, purple_request_cpar_from_connection(gc)); - g_free(buf); - } break; - } - - retval = retval->next; - } - - return 1; -} - -static int -purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc; - PurpleAccount *account; - char *gname, *gname_utf8, *alias, *alias_utf8; - PurpleBuddy *b; - PurpleGroup *g; - struct aim_ssi_item *ssi_item; - va_list ap; - guint16 snac_subtype, type; - const char *name; - - gc = od->gc; - account = purple_connection_get_account(gc); - - va_start(ap, fr); - snac_subtype = (guint16)va_arg(ap, int); - type = (guint16)va_arg(ap, int); - name = va_arg(ap, char *); - va_end(ap); - - if ((type != 0x0000) || (name == NULL)) - return 1; - - gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name); - gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL; - - alias = aim_ssi_getalias(&od->ssi.local, gname, name); - alias_utf8 = oscar_utf8_try_convert(account, od, alias); - g_free(alias); - - b = purple_blist_find_buddy(account, name); - if (b) { - /* - * You're logged in somewhere else and you aliased one - * of your buddies, so update our local buddy list with - * the person's new alias. - */ - purple_buddy_set_local_alias(b, alias_utf8); - } else if (snac_subtype == 0x0008) { - /* - * You're logged in somewhere else and you added a buddy to - * your server list, so add them to your local buddy list. - */ - b = purple_buddy_new(account, name, alias_utf8); - - if (!(g = purple_blist_find_group(gname_utf8))) { - g = purple_group_new(gname_utf8); - purple_blist_add_group(g, NULL); - } - - purple_debug_info("oscar", "ssi: adding buddy %s to group %s to" - " local list", name, gname_utf8 ? gname_utf8 : "(default)"); - purple_blist_add_buddy(b, NULL, g, NULL); - - /* Mobile users should always be online */ - if (name[0] == '+') { - purple_protocol_got_user_status(account, - name, OSCAR_STATUS_ID_AVAILABLE, NULL); - purple_protocol_got_user_status(account, - name, OSCAR_STATUS_ID_MOBILE, NULL); - } - - } - - ssi_item = aim_ssi_itemlist_finditem(&od->ssi.local, - gname, name, AIM_SSI_TYPE_BUDDY); - if (ssi_item == NULL) - { - purple_debug_error("oscar", "purple_ssi_parseaddmod: " - "Could not find ssi item for oncoming buddy %s, " - "group %s\n", name, gname); - } - - g_free(gname_utf8); - g_free(alias_utf8); - - return 1; -} - -static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - char *bn; - gchar *dialog_msg, *nombre; - struct name_data *data; - PurpleBuddy *buddy; - - va_start(ap, fr); - bn = va_arg(ap, char *); - va_arg(ap, char *); /* msg */ - va_end(ap); - - purple_debug_info("oscar", - "ssi: %s has given you permission to add him to your buddy list\n", bn); - - buddy = purple_blist_find_buddy(purple_connection_get_account(gc), bn); - if (buddy && (purple_buddy_get_alias_only(buddy))) - nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy)); - else - nombre = g_strdup(bn); - - dialog_msg = g_strdup_printf(_("The user %s has given you permission to add him or her to your buddy list. Do you want to add this user?"), nombre); - g_free(nombre); - - data = g_new(struct name_data, 1); - data->gc = gc; - data->name = g_strdup(bn); - data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL); - - purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg, - PURPLE_DEFAULT_ACTION_NONE, - purple_request_cpar_from_connection(gc), - data, - G_CALLBACK(purple_icq_buddyadd), - G_CALLBACK(oscar_free_name_data)); - g_free(dialog_msg); - - return 1; -} - -static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - va_list ap; - const char *bn; - char *msg; - - va_start(ap, fr); - bn = va_arg(ap, const char *); - msg = va_arg(ap, char *); - va_end(ap); - - purple_debug_info("oscar", - "ssi: received authorization request from %s\n", bn); - - if (!msg) { - purple_debug_warning("oscar", "Received auth request from %s with " - "empty message\n", bn); - } else if (!g_utf8_validate(msg, -1, NULL)) { - purple_debug_warning("oscar", "Received auth request from %s with " - "invalid UTF-8 message\n", bn); - msg = NULL; - } - - aim_icq_getalias(od, bn, TRUE, msg); - return 1; -} - -static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - va_list ap; - char *bn, *msg; - gchar *dialog_msg, *nombre; - guint8 reply; - PurpleBuddy *buddy; - - va_start(ap, fr); - bn = va_arg(ap, char *); - reply = (guint8)va_arg(ap, int); - msg = va_arg(ap, char *); - va_end(ap); - - purple_debug_info("oscar", - "ssi: received authorization reply from %s. Reply is 0x%02hx\n", bn, (guint16)reply); - - buddy = purple_blist_find_buddy(purple_connection_get_account(gc), bn); - if (buddy && (purple_buddy_get_alias_only(buddy))) - nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy)); - else - nombre = g_strdup(bn); - - if (reply) { - /* Granted */ - dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre); - purple_notify_info(gc, NULL, _("Authorization Granted"), - dialog_msg, purple_request_cpar_from_connection(gc)); - } else { - /* Denied */ - dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given.")); - purple_notify_info(gc, NULL, _("Authorization Denied"), - dialog_msg, purple_request_cpar_from_connection(gc)); - } - g_free(dialog_msg); - g_free(nombre); - - return 1; -} - -static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - va_list ap; - char *bn; - PurpleBuddy *buddy; - - va_start(ap, fr); - bn = va_arg(ap, char *); - va_end(ap); - - buddy = purple_blist_find_buddy(account, bn); - purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn); - purple_account_notify_added(account, bn, NULL, - (buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL); - - return 1; -} - -GList *oscar_chat_info(PurpleConnection *gc) { - GList *m = NULL; - PurpleProtocolChatEntry *pce; - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("_Room:"); - pce->identifier = "room"; - pce->required = TRUE; - m = g_list_append(m, pce); - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("_Exchange:"); - pce->identifier = "exchange"; - pce->required = TRUE; - pce->is_int = TRUE; - pce->min = 4; - pce->max = 20; - m = g_list_append(m, pce); - - return m; -} - -GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name) -{ - GHashTable *defaults; - - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - if (chat_name != NULL) - g_hash_table_insert(defaults, "room", g_strdup(chat_name)); - g_hash_table_insert(defaults, "exchange", g_strdup("4")); - - return defaults; -} - -char * -oscar_get_chat_name(GHashTable *data) -{ - return g_strdup(g_hash_table_lookup(data, "room")); -} - -void -oscar_join_chat(PurpleConnection *gc, GHashTable *data) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - FlapConnection *conn; - char *name, *exchange; - int exchange_int; - - name = g_hash_table_lookup(data, "room"); - exchange = g_hash_table_lookup(data, "exchange"); - - g_return_if_fail(name != NULL && *name != '\0'); - g_return_if_fail(exchange != NULL); - - errno = 0; - exchange_int = strtol(exchange, NULL, 10); - g_return_if_fail(errno == 0); - - purple_debug_info("oscar", "Attempting to join chat room %s.\n", name); - - if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV))) - { - purple_debug_info("oscar", "chatnav exists, creating room\n"); - aim_chatnav_createroom(od, conn, name, exchange_int); - } else { - /* this gets tricky */ - struct create_room *cr = g_new0(struct create_room, 1); - purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n"); - cr->exchange = exchange_int; - cr->name = g_strdup(name); - od->create_rooms = g_slist_prepend(od->create_rooms, cr); - aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV); - } -} - -void -oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - struct chat_connection *ccon = find_oscar_chat(gc, id); - - if (ccon == NULL) - return; - - aim_im_sendch2_chatinvite(od, name, message ? message : "", - ccon->exchange, ccon->name, 0x0); -} - -void -oscar_chat_leave(PurpleConnection *gc, int id) -{ - PurpleChatConversation *conv; - struct chat_connection *cc; - - conv = purple_conversations_find_chat(gc, id); - - g_return_if_fail(conv != NULL); - - purple_debug_info("oscar", "Leaving chat room %s\n", - purple_conversation_get_name(PURPLE_CONVERSATION(conv))); - - cc = find_oscar_chat(gc, purple_chat_conversation_get_id(conv)); - flap_connection_schedule_destroy(cc->conn, OSCAR_DISCONNECT_DONE, NULL); - oscar_chat_kill(gc, cc); -} - -int oscar_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleChatConversation *conv = NULL; - struct chat_connection *c = NULL; - char *buf, *buf2, *buf3; - guint16 charset; - char *charsetstr; - gsize len; - const gchar *message = purple_message_get_contents(msg); - - if (!(conv = purple_conversations_find_chat(gc, id))) - return -EINVAL; - - if (!(c = find_oscar_chat_by_conv(gc, conv))) - return -EINVAL; - - buf = purple_strdup_withhtml(message); - - if (strstr(buf, "<img ")) { - purple_conversation_write_system_message(PURPLE_CONVERSATION(conv), - _("Your IM Image was not sent. " - "You cannot send IM Images in AIM chats."), - PURPLE_MESSAGE_ERROR); - } - - buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr); - /* - * Evan S. suggested that maxvis really does mean "number of - * visible characters" and not "number of bytes" - */ - if ((len > c->maxlen) || (len > c->maxvis)) { - /* If the length was too long, try stripping the HTML and then running it back through - * purple_strdup_withhtml() and the encoding process. The result may be shorter. */ - g_free(buf2); - - buf3 = purple_markup_strip_html(buf); - g_free(buf); - - buf = purple_strdup_withhtml(buf3); - g_free(buf3); - - buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr); - - if ((len > c->maxlen) || (len > c->maxvis)) { - purple_debug_warning("oscar", - "Could not send %s because (%" G_GSIZE_FORMAT " > maxlen %i) or (%" G_GSIZE_FORMAT " > maxvis %i)\n", - buf2, len, c->maxlen, len, c->maxvis); - g_free(buf); - g_free(buf2); - return -E2BIG; - } - - purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n", - message, buf2); - } - - aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en"); - g_free(buf2); - g_free(buf); - - return 0; -} - -PurpleMood* oscar_get_purple_moods(PurpleAccount *account) -{ - return icq_get_purple_moods(account); -} - -const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b) -{ - const char *name = b ? purple_buddy_get_name(b) : NULL; - if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name)) - return "icq"; - - return "icq"; -} - -const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b) -{ - const char *name = b ? purple_buddy_get_name(b) : NULL; - if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name)) - return "icq"; - - return "aim"; -} - -const char *oscar_list_emblem(PurpleBuddy *b) -{ - PurpleConnection *gc = NULL; - OscarData *od = NULL; - PurpleAccount *account = NULL; - PurplePresence *presence; - aim_userinfo_t *userinfo = NULL; - const char *name; - - account = purple_buddy_get_account(b); - name = purple_buddy_get_name(b); - if (account != NULL) - gc = purple_account_get_connection(account); - if (gc != NULL) - od = purple_connection_get_protocol_data(gc); - if (od != NULL) - userinfo = aim_locate_finduserinfo(od, name); - - presence = purple_buddy_get_presence(b); - - if (purple_presence_is_online(presence) == FALSE) { - char *gname; - if ((name) && (od) && (od->ssi.received_data) && - (gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name)) && - (aim_ssi_waitingforauth(&od->ssi.local, gname, name))) { - return "not-authorized"; - } - } - - if (userinfo != NULL ) { - if (userinfo->flags & AIM_FLAG_ADMINISTRATOR) - return "admin"; - if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY) - return "bot"; - if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM) - return "secure"; - if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY) - return "birthday"; - - /* Make the mood icon override anything below this. */ - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD)) - return NULL; - - if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP) - return "hiptop"; - } - return NULL; -} - -void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) -{ - PurpleConnection *gc; - PurpleAccount *account; - OscarData *od; - aim_userinfo_t *userinfo; - - if (!PURPLE_BUDDY_IS_ONLINE(b)) - return; - - account = purple_buddy_get_account(b); - gc = purple_account_get_connection(account); - od = purple_connection_get_protocol_data(gc); - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b)); - - oscar_user_info_append_status(gc, user_info, b, userinfo, /* use_html_status */ FALSE); - - if (full) - oscar_user_info_append_extra_info(gc, user_info, b, userinfo); -} - -char *oscar_status_text(PurpleBuddy *b) -{ - PurpleConnection *gc; - PurpleAccount *account; - OscarData *od; - PurplePresence *presence; - PurpleStatus *status; - const char *message; - gchar *ret = NULL; - - gc = purple_account_get_connection(purple_buddy_get_account(b)); - account = purple_connection_get_account(gc); - od = purple_connection_get_protocol_data(gc); - presence = purple_buddy_get_presence(b); - status = purple_presence_get_active_status(presence); - - if ((od != NULL) && !purple_presence_is_online(presence)) - { - const char *name = purple_buddy_get_name(b); - char *gname = aim_ssi_itemlist_findparentname(&od->ssi.local, name); - if (aim_ssi_waitingforauth(&od->ssi.local, gname, name)) - ret = g_strdup(_("Not Authorized")); - else - ret = g_strdup(_("Offline")); - } - else - { - message = purple_status_get_attr_string(status, "message"); - if (message != NULL) - { - gchar *tmp = oscar_util_format_string(message, purple_account_get_username(account)); - ret = purple_markup_escape_text(tmp, -1); - g_free(tmp); - } - else if (purple_status_is_available(status)) - { - /* Don't show "Available" as status message in case buddy doesn't have a status message */ - } - else - { - ret = g_strdup(purple_status_get_name(status)); - } - } - - return ret; -} - -void oscar_set_aim_permdeny(PurpleConnection *gc) { - PurpleAccount *account = purple_connection_get_account(gc); - OscarData *od = purple_connection_get_protocol_data(gc); - - /* - * Conveniently there is a one-to-one mapping between the - * values of libpurple's PurplePrivacyType and the values used - * by the oscar protocol. - */ - aim_ssi_setpermdeny(od, purple_account_get_privacy_type(account)); -} - -void oscar_add_permit(PurpleConnection *gc, const char *who) { - OscarData *od = purple_connection_get_protocol_data(gc); - purple_debug_info("oscar", "ssi: About to add a permit\n"); - aim_ssi_add_to_private_list(od, who, AIM_SSI_TYPE_PERMIT); -} - -void oscar_add_deny(PurpleConnection *gc, const char *who) { - OscarData *od = purple_connection_get_protocol_data(gc); - purple_debug_info("oscar", "ssi: About to add a deny\n"); - aim_ssi_add_to_private_list(od, who, aim_ssi_getdenyentrytype(od)); -} - -void oscar_rem_permit(PurpleConnection *gc, const char *who) { - OscarData *od = purple_connection_get_protocol_data(gc); - purple_debug_info("oscar", "ssi: About to delete a permit\n"); - aim_ssi_del_from_private_list(od, who, AIM_SSI_TYPE_PERMIT); -} - -void oscar_rem_deny(PurpleConnection *gc, const char *who) { - OscarData *od = purple_connection_get_protocol_data(gc); - purple_debug_info("oscar", "ssi: About to delete a deny\n"); - aim_ssi_del_from_private_list(od, who, aim_ssi_getdenyentrytype(od)); -} - -GList * -oscar_status_types(PurpleAccount *account) -{ - gboolean is_icq; - GList *status_types = NULL; - PurpleStatusType *type; - - g_return_val_if_fail(account != NULL, NULL); - - /* Used to flag some statuses as "user settable" or not */ - is_icq = oscar_util_valid_name_icq(purple_account_get_username(account)); - - /* Common status types */ - /* Really the available message should only be settable for AIM accounts */ - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_AVAILABLE, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), - "itmsurl", _("iTunes Music Store Link"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_FREE4CHAT, - _("Free For Chat"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_EVIL, - _("Evil"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_DEPRESSION, - _("Depression"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_ATHOME, - _("At home"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_ATWORK, - _("At work"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - - status_types = g_list_prepend(status_types, type); - - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - OSCAR_STATUS_ID_LUNCH, - _("Lunch"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, - OSCAR_STATUS_ID_AWAY, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE, - OSCAR_STATUS_ID_INVISIBLE, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE); - status_types = g_list_prepend(status_types, type); - - /* ICQ-specific status types */ - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, - OSCAR_STATUS_ID_OCCUPIED, - _("Occupied"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, - OSCAR_STATUS_ID_DND, - _("Do Not Disturb"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY, - OSCAR_STATUS_ID_NA, - _("Not Available"), TRUE, is_icq, FALSE, - "message", _("Message"), - purple_value_new(G_TYPE_STRING), NULL); - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - OSCAR_STATUS_ID_OFFLINE, - NULL, TRUE, TRUE, FALSE); - status_types = g_list_prepend(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD, - "mood", NULL, TRUE, is_icq, TRUE, - PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(G_TYPE_STRING), - PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(G_TYPE_STRING), - NULL); - status_types = g_list_prepend(status_types, type); - - return g_list_reverse(status_types); -} - -static void oscar_ssi_editcomment(struct name_data *data, const char *text) { - PurpleConnection *gc; - PurpleAccount *account; - OscarData *od; - PurpleBuddy *b; - PurpleGroup *g; - - gc = data->gc; - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - b = purple_blist_find_buddy(account, data->name); - if (b == NULL) { - oscar_free_name_data(data); - return; - } - - g = purple_buddy_get_group(b); - if (g == NULL) { - oscar_free_name_data(data); - return; - } - - aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text); - oscar_free_name_data(data); -} - -static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) { - - PurpleBuddy *buddy; - PurpleConnection *gc; - OscarData *od; - struct name_data *data; - PurpleGroup *g; - char *comment; - gchar *comment_utf8; - gchar *title; - PurpleAccount *account; - const char *name; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - name = purple_buddy_get_name(buddy); - account = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - od = purple_connection_get_protocol_data(gc); - - if (!(g = purple_buddy_get_group(buddy))) - return; - - data = g_new(struct name_data, 1); - - comment = aim_ssi_getcomment(&od->ssi.local, purple_group_get_name(g), name); - comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL; - - data->gc = gc; - data->name = g_strdup(name); - data->nick = g_strdup(purple_buddy_get_alias_only(buddy)); - - title = g_strdup_printf(_("Buddy Comment for %s"), data->name); - purple_request_input(gc, title, _("Buddy Comment:"), NULL, - comment_utf8, TRUE, FALSE, NULL, - _("_OK"), G_CALLBACK(oscar_ssi_editcomment), - _("_Cancel"), G_CALLBACK(oscar_free_name_data), - purple_request_cpar_from_connection(gc), - data); - g_free(title); - - g_free(comment); - g_free(comment_utf8); -} - -static void -oscar_ask_directim_yes_cb(struct oscar_ask_directim_data *data) -{ - peer_connection_propose(data->od, OSCAR_CAPABILITY_DIRECTIM, data->who); - g_free(data->who); - g_free(data); -} - -static void -oscar_ask_directim_no_cb(struct oscar_ask_directim_data *data) -{ - g_free(data->who); - g_free(data); -} - -/* This is called from right-click menu on a buddy node. */ -static void -oscar_ask_directim(gpointer object, gpointer ignored) -{ - PurpleBlistNode *node; - PurpleBuddy *buddy; - PurpleConnection *gc; - gchar *buf; - struct oscar_ask_directim_data *data; - PurpleAccount *account; - - node = object; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *)node; - account = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - - data = g_new0(struct oscar_ask_directim_data, 1); - data->who = g_strdup(purple_buddy_get_name(buddy)); - data->od = purple_connection_get_protocol_data(gc); - buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."), - data->who); - - purple_request_action(gc, NULL, buf, - _("Because this reveals your IP address, it " - "may be considered a security risk. Do you " - "wish to continue?"), - 0, /* Default action is "connect" */ - purple_request_cpar_from_account(account), - data, 2, - _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb), - _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb)); - g_free(buf); -} - -static void -oscar_close_directim(gpointer object, gpointer ignored) -{ - PurpleBlistNode *node; - PurpleBuddy *buddy; - PurpleAccount *account; - PurpleConnection *gc; - PurpleIMConversation *im; - OscarData *od; - PeerConnection *conn; - const char *name; - - node = object; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy*)node; - name = purple_buddy_get_name(buddy); - account = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - od = purple_connection_get_protocol_data(gc); - conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM); - - if (conn != NULL) - { - if (!conn->ready) - aim_im_sendch2_cancel(conn); - - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); - - /* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo - * window. Let the user know that we cancelled the Direct IM. */ - im = purple_im_conversation_new(account, name); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), _("You closed the connection."), 0); - } -} - -static void oscar_get_icqxstatusmsg(PurpleBlistNode *node, gpointer ignore) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - OscarData *od; - PurpleAccount *account; - const char *bname; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *)node; - bname = purple_buddy_get_name(buddy); - - account = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - od = purple_connection_get_protocol_data(gc); - - purple_debug_info("oscar", "Manual X-Status Get From %s to %s:\n", bname, purple_account_get_username(account)); - - icq_im_xstatus_request(od, bname); -} - -static void -oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *)node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - aim_locate_getinfoshort(purple_connection_get_protocol_data(gc), - purple_buddy_get_name(buddy), 0x00000003); -} - -static GList * -oscar_buddy_menu(PurpleBuddy *buddy) { - PurpleConnection *gc; - OscarData *od; - GList *menu; - PurpleActionMenu *act; - aim_userinfo_t *userinfo; - PurpleAccount *account; - const char *bname = purple_buddy_get_name(buddy); - - account = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(account); - od = purple_connection_get_protocol_data(gc); - userinfo = aim_locate_finduserinfo(od, bname); - menu = NULL; - - if (od->icq && oscar_util_valid_name_icq(bname)) - { - act = purple_action_menu_new(_("Get AIM Info"), - PURPLE_CALLBACK(oscar_get_aim_info_cb), - NULL, NULL); - menu = g_list_prepend(menu, act); - } - - if (purple_buddy_get_group(buddy) != NULL) - { - /* We only do this if the user is in our buddy list */ - act = purple_action_menu_new(_("Edit Buddy Comment"), - PURPLE_CALLBACK(oscar_buddycb_edit_comment), - NULL, NULL); - menu = g_list_prepend(menu, act); - } - - if (od->icq) - { - act = purple_action_menu_new(_("Get X-Status Msg"), - PURPLE_CALLBACK(oscar_get_icqxstatusmsg), - NULL, NULL); - menu = g_list_prepend(menu, act); - menu = g_list_prepend(menu, create_visibility_menu_item(od, bname)); - } - - if (userinfo && - oscar_util_name_compare(purple_account_get_username(account), bname) && - PURPLE_BUDDY_IS_ONLINE(buddy)) - { - PeerConnection *conn; - conn = peer_connection_find_by_type(od, bname, OSCAR_CAPABILITY_DIRECTIM); - - if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM) - { - if (conn) - { - act = purple_action_menu_new(_("End Direct IM Session"), - PURPLE_CALLBACK(oscar_close_directim), - NULL, NULL); - } - else - { - act = purple_action_menu_new(_("Direct IM"), - PURPLE_CALLBACK(oscar_ask_directim), - NULL, NULL); - } - menu = g_list_prepend(menu, act); - } - } - - if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL) - { - /* - * We only do this if the user is in our buddy list and we're - * waiting for authorization. - */ - char *gname; - gname = aim_ssi_itemlist_findparentname(&od->ssi.local, bname); - if (gname && aim_ssi_waitingforauth(&od->ssi.local, gname, bname)) - { - act = purple_action_menu_new(_("Re-request Authorization"), - PURPLE_CALLBACK(oscar_auth_sendrequest_menu), - NULL, NULL); - menu = g_list_prepend(menu, act); - } - } - - menu = g_list_reverse(menu); - - return menu; -} - - -GList *oscar_blist_node_menu(PurpleBlistNode *node) { - if(PURPLE_IS_BUDDY(node)) { - return oscar_buddy_menu((PurpleBuddy *) node); - } else { - return NULL; - } -} - -static void -oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - PurpleRequestField *f; - gboolean auth, web_aware; - - f = purple_request_fields_get_field(fields, "authorization"); - auth = purple_request_field_bool_get_value(f); - - f = purple_request_fields_get_field(fields, "web_aware"); - web_aware = purple_request_field_bool_get_value(f); - - purple_account_set_bool(account, "authorization", auth); - purple_account_set_bool(account, "web_aware", web_aware); - - oscar_set_extended_status(gc); - aim_icq_setsecurity(od, auth, web_aware); -} - -static void -oscar_show_icq_privacy_opts(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - gboolean auth, web_aware; - - auth = purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION); - web_aware = purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - - f = purple_request_field_bool_new("authorization", _("Require authorization"), auth); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_bool_new("web_aware", _("Web aware (enabling this will cause you to receive SPAM!)"), web_aware); - purple_request_field_group_add_field(g, f); - - purple_request_fields_add_group(fields, g); - - purple_request_fields(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"), - NULL, fields, - _("OK"), G_CALLBACK(oscar_icq_privacy_opts), - _("Cancel"), NULL, - purple_request_cpar_from_connection(gc), - gc); -} - -static void oscar_confirm_account(PurpleProtocolAction *action) -{ - PurpleConnection *gc; - OscarData *od; - FlapConnection *conn; - - gc = action->connection; - od = purple_connection_get_protocol_data(gc); - - conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN); - if (conn != NULL) { - aim_admin_reqconfirm(od, conn); - } else { - od->conf = TRUE; - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN); - } -} - -static void oscar_show_email(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - OscarData *od = purple_connection_get_protocol_data(gc); - FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN); - - if (conn) { - aim_admin_getinfo(od, conn, 0x11); - } else { - od->reqemail = TRUE; - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN); - } -} - -static void oscar_change_email(PurpleConnection *gc, const char *email) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN); - - if (conn) { - aim_admin_setemail(od, conn, email); - } else { - od->setemail = TRUE; - od->email = g_strdup(email); - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN); - } -} - -static void oscar_show_change_email(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - purple_request_input(gc, NULL, _("Change Address To:"), NULL, NULL, - FALSE, FALSE, NULL, - _("_OK"), G_CALLBACK(oscar_change_email), - _("_Cancel"), NULL, - purple_request_cpar_from_connection(gc), - gc); -} - -static void oscar_show_awaitingauth(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - GSList *buddies, *filtered_buddies, *cur; - gchar *text; - - buddies = purple_blist_find_buddies(account, NULL); - filtered_buddies = NULL; - for (cur = buddies; cur != NULL; cur = cur->next) { - PurpleBuddy *buddy; - const gchar *bname, *gname; - - buddy = cur->data; - bname = purple_buddy_get_name(buddy); - gname = purple_group_get_name(purple_buddy_get_group(buddy)); - if (aim_ssi_waitingforauth(&od->ssi.local, gname, bname)) { - filtered_buddies = g_slist_prepend(filtered_buddies, buddy); - } - } - - g_slist_free(buddies); - - filtered_buddies = g_slist_reverse(filtered_buddies); - text = oscar_format_buddies(filtered_buddies, _("you are not waiting for authorization")); - g_slist_free(filtered_buddies); - - purple_notify_formatted(gc, NULL, _("You are awaiting authorization from " - "the following buddies"), _("You can re-request " - "authorization from these buddies by " - "right-clicking on them and selecting " - "\"Re-request Authorization.\""), text, NULL, NULL); - g_free(text); -} - -static void search_by_email_cb(PurpleConnection *gc, const char *email) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - - aim_search_address(od, email); -} - -static void oscar_show_find_email(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - purple_request_input(gc, _("Find Buddy by Email"), - _("Search for a buddy by email address"), - _("Type the email address of the buddy you are " - "searching for."), - NULL, FALSE, FALSE, NULL, - _("_Search"), G_CALLBACK(search_by_email_cb), - _("_Cancel"), NULL, - purple_request_cpar_from_connection(gc), - gc); -} - -static void oscar_show_set_info(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - purple_account_request_change_user_info(purple_connection_get_account(gc)); -} - -static void oscar_show_set_info_icqurl(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - purple_notify_uri(gc, "http://www.icq.com/whitepages/user_details.php"); -} - -static void oscar_change_pass(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - purple_account_request_change_password(purple_connection_get_account(gc)); -} - -/** - * Only used when connecting with the old-style BUCP login. - */ -static void oscar_show_chpassurl(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - OscarData *od = purple_connection_get_protocol_data(gc); - gchar *substituted = purple_strreplace(od->authinfo->chpassurl, "%s", purple_account_get_username(purple_connection_get_account(gc))); - purple_notify_uri(gc, substituted); - g_free(substituted); -} - -static void oscar_show_imforwardingurl(PurpleProtocolAction *action) -{ - PurpleConnection *gc = action->connection; - purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1"); -} - -void oscar_set_icon(PurpleConnection *gc, PurpleImage *img) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - - if (img == NULL) { - aim_ssi_delicon(od); - } else { - GChecksum *hash; - guchar md5[16]; - gsize digest_len = 16; - gconstpointer data = purple_image_get_data(img); - size_t len = purple_image_get_data_size(img); - - hash = g_checksum_new(G_CHECKSUM_MD5); - g_checksum_update(hash, data, len); - g_checksum_get_digest(hash, md5, &digest_len); - g_checksum_free(hash); - - aim_ssi_seticon(od, md5, 16); - } -} - -/** - * Called by the Purple core to determine whether or not we're - * allowed to send a file to this user. - */ -gboolean -oscar_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) -{ - OscarData *od; - PurpleAccount *account; - - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - if (od != NULL) - { - aim_userinfo_t *userinfo; - userinfo = aim_locate_finduserinfo(od, who); - - /* - * Don't allowing sending a file to a user that does not support - * file transfer, and don't allow sending to ourselves. - */ - if (((userinfo == NULL) || - (userinfo->capabilities & OSCAR_CAPABILITY_SENDFILE)) && - oscar_util_name_compare(who, purple_account_get_username(account))) - { - return TRUE; - } - } - - return FALSE; -} - -PurpleXfer * -oscar_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) -{ - OscarXfer *xfer; - OscarData *od; - PurpleAccount *account; - PeerConnection *conn; - - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who); - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME; - conn->flags |= PEER_CONNECTION_FLAG_APPROVED; - - xfer = g_object_new( - OSCAR_TYPE_XFER, - "account", account, - "type", PURPLE_XFER_TYPE_SEND, - "remote-user", who, - "conn", conn, - NULL - ); - - aim_icbm_makecookie(conn->cookie); - conn->xfer = PURPLE_XFER(xfer); - - return PURPLE_XFER(xfer); -} - -/* - * Called by the Purple core when the user indicates that a - * file is to be sent to a special someone. - */ -void -oscar_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file) -{ - PurpleXfer *xfer; - - xfer = oscar_new_xfer(prplxfer, gc, who); - - if (file != NULL) - purple_xfer_request_accepted(xfer, file); - else - purple_xfer_request(xfer); -} - -GList * -oscar_get_actions(PurpleConnection *gc) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - GList *menu = NULL; - PurpleProtocolAction *act; - - act = purple_protocol_action_new(_("Set User Info..."), - oscar_show_set_info); - menu = g_list_prepend(menu, act); - - if (od->icq) - { - act = purple_protocol_action_new(_("Set User Info (web)..."), - oscar_show_set_info_icqurl); - menu = g_list_prepend(menu, act); - } - - act = purple_protocol_action_new(_("Change Password..."), - oscar_change_pass); - menu = g_list_prepend(menu, act); - - if (od->authinfo != NULL && od->authinfo->chpassurl != NULL) - { - /* This only happens when connecting with the old-style BUCP login */ - act = purple_protocol_action_new(_("Change Password (web)"), - oscar_show_chpassurl); - menu = g_list_prepend(menu, act); - } - - if (!od->icq) - { - act = purple_protocol_action_new(_("Configure IM Forwarding (web)"), - oscar_show_imforwardingurl); - menu = g_list_prepend(menu, act); - } - - menu = g_list_prepend(menu, NULL); - - if (od->icq) - { - /* ICQ actions */ - act = purple_protocol_action_new(_("Set Privacy Options..."), - oscar_show_icq_privacy_opts); - menu = g_list_prepend(menu, act); - - act = purple_protocol_action_new(_("Show Visible List"), oscar_show_visible_list); - menu = g_list_prepend(menu, act); - - act = purple_protocol_action_new(_("Show Invisible List"), oscar_show_invisible_list); - menu = g_list_prepend(menu, act); - } - else - { - /* AIM actions */ - act = purple_protocol_action_new(_("Confirm Account"), - oscar_confirm_account); - menu = g_list_prepend(menu, act); - - act = purple_protocol_action_new(_("Display Currently Registered Email Address"), - oscar_show_email); - menu = g_list_prepend(menu, act); - - act = purple_protocol_action_new(_("Change Currently Registered Email Address..."), - oscar_show_change_email); - menu = g_list_prepend(menu, act); - } - - menu = g_list_prepend(menu, NULL); - - act = purple_protocol_action_new(_("Show Buddies Awaiting Authorization"), - oscar_show_awaitingauth); - menu = g_list_prepend(menu, act); - - menu = g_list_prepend(menu, NULL); - - act = purple_protocol_action_new(_("Search for Buddy by Email Address..."), - oscar_show_find_email); - menu = g_list_prepend(menu, act); - - menu = g_list_reverse(menu); - - return menu; -} - -void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new) -{ - OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->icq) { - aim_icq_changepasswd(od, new); - } else { - FlapConnection *conn; - conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN); - if (conn) { - aim_admin_changepasswd(od, conn, new, old); - } else { - od->chpass = TRUE; - od->oldp = g_strdup(old); - od->newp = g_strdup(new); - aim_srv_requestnew(od, SNAC_FAMILY_ADMIN); - } - } -} - -void -oscar_convo_closed(PurpleConnection *gc, const char *who) -{ - OscarData *od; - PeerConnection *conn; - - od = purple_connection_get_protocol_data(gc); - conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM); - - if (conn != NULL) - { - if (!conn->ready) - aim_im_sendch2_cancel(conn); - - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); - } -} - -const char * -oscar_normalize(const PurpleAccount *account, const char *str) -{ - static char buf[BUF_LEN]; - char *tmp1, *tmp2; - int i, j; - - g_return_val_if_fail(str != NULL, NULL); - - /* copy str to buf and skip all blanks */ - i = 0; - for (j = 0; str[j]; j++) { - if (str[j] != ' ') { - buf[i++] = str[j]; - if (i >= BUF_LEN - 1) - break; - } - } - buf[i] = '\0'; - - tmp1 = g_utf8_strdown(buf, -1); - tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT); - if (strlen(tmp2) > sizeof(buf) - 1) { - purple_debug_error("oscar", "normalized string exceeds buffer length!\n"); - } - g_strlcpy(buf, tmp2, sizeof(buf)); - g_free(tmp2); - g_free(tmp1); - - return buf; -} - -gboolean -oscar_offline_message(const PurpleBuddy *buddy) -{ - return TRUE; -} - -gssize -oscar_get_max_message_size(PurpleConversation *conv) -{ - /* XXX: got from pidgin-otr - verify and document it */ - return 2343; -} - -/* TODO: Find somewhere to put this instead of including it in a bunch of places. - * Maybe just change purple_accounts_find() to return anything for the protocol if there is no acct_id. - */ -static PurpleAccount *find_acct(const char *protocol, const char *acct_id) -{ - PurpleAccount *acct = NULL; - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_accounts_find(acct_id, protocol); - if (acct && !purple_account_is_connected(acct)) - acct = NULL; - } else { /* Otherwise find an active account for the protocol */ - GList *l = purple_accounts_get_all(); - while (l) { - if (purple_strequal(protocol, purple_account_get_protocol_id(l->data)) - && purple_account_is_connected(l->data)) { - acct = l->data; - break; - } - l = l->next; - } - } - - return acct; -} - -gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params) -{ - char *acct_id; - char prpl[11]; - PurpleAccount *acct; - - if (g_ascii_strcasecmp(proto, "aim") && g_ascii_strcasecmp(proto, "icq")) - return FALSE; - - if (params == NULL) { - /* All Oscar URI actions require some parameters eventually */ - purple_debug_warning("oscar", - "No required params for handling URI"); - return FALSE; - } - - acct_id = g_hash_table_lookup(params, "account"); - g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto); - - acct = find_acct(proto, acct_id); - - if (!acct) - return FALSE; - - /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */ - if (!g_ascii_strcasecmp(cmd, "GoIM")) { - char *bname = g_hash_table_lookup(params, "screenname"); - if (bname) { - char *message = g_hash_table_lookup(params, "message"); - - PurpleIMConversation *im = purple_conversations_find_im_with_account( - bname, acct); - if (im == NULL) - im = purple_im_conversation_new(acct, bname); - purple_conversation_present(PURPLE_CONVERSATION(im)); - - if (message) { - /* Spaces are encoded as '+' */ - g_strdelimit(message, "+", ' '); - purple_conversation_send_confirm(PURPLE_CONVERSATION(im), message); - } - } - /*else - **If pidgindialogs_im() was in the core, we could use it here. - * It is all purple_request_* based, but I'm not sure it really belongs in the core - pidgindialogs_im();*/ - - return TRUE; - } - /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */ - else if (!g_ascii_strcasecmp(cmd, "GoChat")) { - char *rname = g_hash_table_lookup(params, "roomname"); - if (rname) { - /* This is somewhat hacky, but the params aren't useful after this command */ - g_hash_table_insert(params, g_strdup("exchange"), g_strdup("4")); - g_hash_table_insert(params, g_strdup("room"), g_strdup(rname)); - purple_serv_join_chat(purple_account_get_connection(acct), params); - } - /*else - ** Same as above (except that this would have to be re-written using purple_request_*) - pidgin_blist_joinchat_show(); */ - - return TRUE; - } - /* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/ - else if (!g_ascii_strcasecmp(cmd, "AddBuddy")) { - char *bname = g_hash_table_lookup(params, "screenname"); - char *gname = g_hash_table_lookup(params, "groupname"); - purple_blist_request_add_buddy(acct, bname, gname, NULL); - return TRUE; - } - - return FALSE; -} - -void oscar_init_account_options(PurpleProtocol *protocol, gboolean is_icq) -{ - PurpleAccountOption *option; - static const gchar *encryption_keys[] = { - N_("Use encryption if available"), - N_("Require encryption"), - N_("Don't use encryption"), - NULL - }; - static const gchar *encryption_values[] = { - OSCAR_OPPORTUNISTIC_ENCRYPTION, - OSCAR_REQUIRE_ENCRYPTION, - OSCAR_NO_ENCRYPTION, - NULL - }; - static const gchar *aim_login_keys[] = { - N_("Use clientLogin authentication"), - N_("Use Kerberos-based authentication"), - N_("Use MD5 based authentication"), - NULL - }; - static const gchar *aim_login_values[] = { - OSCAR_CLIENT_LOGIN, - OSCAR_KERBEROS_LOGIN, - OSCAR_MD5_LOGIN, - NULL - }; - static const gchar *icq_login_keys[] = { - N_("Use clientLogin authentication"), - N_("Use MD5 based authentication"), - NULL - }; - static const gchar *icq_login_values[] = { - OSCAR_CLIENT_LOGIN, - OSCAR_MD5_LOGIN, - NULL - }; - const gchar **login_keys; - const gchar **login_values; - GList *encryption_options = NULL; - GList *login_options = NULL; - int i; - - option = purple_account_option_string_new(_("Server"), "server", oscar_get_login_server(is_icq, TRUE)); - protocol->account_options = g_list_append(protocol->account_options, option); - - option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); - protocol->account_options = g_list_append(protocol->account_options, option); - - for (i = 0; encryption_keys[i]; i++) { - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(_(encryption_keys[i])); - kvp->value = g_strdup(encryption_values[i]); - encryption_options = g_list_append(encryption_options, kvp); - } - option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options); - protocol->account_options = g_list_append(protocol->account_options, option); - - if (is_icq) { - login_keys = icq_login_keys; - login_values = icq_login_values; - } else { - login_keys = aim_login_keys; - login_values = aim_login_values; - } - for (i = 0; login_keys[i]; i++) { - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(_(login_keys[i])); - kvp->value = g_strdup(login_values[i]); - login_options = g_list_append(login_options, kvp); - } - option = purple_account_option_list_new(_("Authentication method"), "login_type", login_options); - protocol->account_options = g_list_append(protocol->account_options, option); - - option = purple_account_option_bool_new( - _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy", - OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); - protocol->account_options = g_list_append(protocol->account_options, option); - - if (is_icq) { - option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); - protocol->account_options = g_list_append(protocol->account_options, option); - } else { - option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins", - OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS); - protocol->account_options = g_list_append(protocol->account_options, option); - } -} - -static void -oscar_protocol_init(OscarProtocol *self) -{ - PurpleProtocol *protocol = PURPLE_PROTOCOL(self); - - protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE | - OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE; - protocol->icon_spec = purple_buddy_icon_spec_new("gif,jpeg,bmp,ico", - 0, 0, 64, 64, 7168, - PURPLE_ICON_SCALE_SEND | - PURPLE_ICON_SCALE_DISPLAY); -} - -static void -oscar_protocol_class_init(OscarProtocolClass *klass) -{ - PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass); - - protocol_class->login = oscar_login; - protocol_class->close = oscar_close; - protocol_class->status_types = oscar_status_types; -} - -static void -oscar_protocol_class_finalize(G_GNUC_UNUSED OscarProtocolClass *klass) -{ -} - -static void -oscar_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface) -{ - client_iface->get_actions = oscar_get_actions; - client_iface->list_emblem = oscar_list_emblem; - client_iface->status_text = oscar_status_text; - client_iface->tooltip_text = oscar_tooltip_text; - client_iface->blist_node_menu = oscar_blist_node_menu; - client_iface->convo_closed = oscar_convo_closed; - client_iface->normalize = oscar_normalize; - client_iface->offline_message = oscar_offline_message; -} - -static void -oscar_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface) -{ - server_iface->set_info = oscar_set_info; - server_iface->get_info = oscar_get_info; - server_iface->set_status = oscar_set_status; - server_iface->set_idle = oscar_set_idle; - server_iface->change_passwd = oscar_change_passwd; - server_iface->add_buddy = oscar_add_buddy; - server_iface->remove_buddy = oscar_remove_buddy; - server_iface->keepalive = oscar_keepalive; - server_iface->alias_buddy = oscar_alias_buddy; - server_iface->group_buddy = oscar_move_buddy; - server_iface->rename_group = oscar_rename_group; - server_iface->set_buddy_icon = oscar_set_icon; - server_iface->remove_group = oscar_remove_group; -} - -static void -oscar_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface) -{ - im_iface->send = oscar_send_im; - im_iface->send_typing = oscar_send_typing; -} - -static void -oscar_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface) -{ - chat_iface->info = oscar_chat_info; - chat_iface->info_defaults = oscar_chat_info_defaults; - chat_iface->join = oscar_join_chat; - chat_iface->get_name = oscar_get_chat_name; - chat_iface->invite = oscar_chat_invite; - chat_iface->leave = oscar_chat_leave; - chat_iface->send = oscar_send_chat; -} - -static void -oscar_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface) -{ - privacy_iface->add_deny = oscar_add_deny; - privacy_iface->rem_deny = oscar_rem_deny; -} - -static void -oscar_protocol_xfer_iface_init(PurpleProtocolXferInterface *xfer_iface) -{ - xfer_iface->can_receive = oscar_can_receive_file; - xfer_iface->send_file = oscar_send_file; - xfer_iface->new_xfer = oscar_new_xfer; -} - -G_DEFINE_DYNAMIC_TYPE_EXTENDED( - OscarProtocol, oscar_protocol, PURPLE_TYPE_PROTOCOL, - G_TYPE_FLAG_ABSTRACT, - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT, - oscar_protocol_client_iface_init) - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER, - oscar_protocol_server_iface_init) - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM, - oscar_protocol_im_iface_init) - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT, - oscar_protocol_chat_iface_init) - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY, - oscar_protocol_privacy_iface_init) - - G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER, - oscar_protocol_xfer_iface_init)); - -static PurplePluginInfo * -plugin_query(GError **error) -{ - return purple_plugin_info_new( - "id", "prpl-oscar", - "name", "Oscar Protocols", - "version", DISPLAY_VERSION, - "category", N_("Protocol"), - "summary", N_("Oscar (AIM/ICQ) Protocols Plugin"), - "description", N_("Oscar (AIM/ICQ) Protocols Plugin"), - "website", PURPLE_WEBSITE, - "abi-version", PURPLE_ABI_VERSION, - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, - NULL - ); -} - -static gboolean -plugin_load(PurplePlugin *plugin, GError **error) -{ - oscar_protocol_register_type(G_TYPE_MODULE(plugin)); - - aim_protocol_register(plugin); - icq_protocol_register(plugin); - - oscar_xfer_register(G_TYPE_MODULE(plugin)); - - aim_protocol = purple_protocols_add(AIM_TYPE_PROTOCOL, error); - if (!aim_protocol) - return FALSE; - - icq_protocol = purple_protocols_add(ICQ_TYPE_PROTOCOL, error); - if (!icq_protocol) - return FALSE; - - purple_signal_connect(purple_get_core(), "uri-handler", aim_protocol, - PURPLE_CALLBACK(oscar_uri_handler), NULL); - purple_signal_connect(purple_get_core(), "uri-handler", icq_protocol, - PURPLE_CALLBACK(oscar_uri_handler), NULL); - - /* Preferences */ - purple_prefs_add_none("/plugins/prpl/oscar"); - purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE); - - purple_prefs_remove("/plugins/prpl/oscar/show_idle"); - purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy"); - - return TRUE; -} - -static gboolean -plugin_unload(PurplePlugin *plugin, GError **error) -{ - if (!purple_protocols_remove(icq_protocol, error)) - return FALSE; - - if (!purple_protocols_remove(aim_protocol, error)) - return FALSE; - - return TRUE; -} - -PURPLE_PLUGIN_INIT(oscar, plugin_query, plugin_load, plugin_unload);
--- a/libpurple/protocols/oscar/oscar.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1379 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Main libfaim header. Must be included in client for prototypes/macros. - * - * "come on, i turned a chick lesbian; i think this is the hackish equivalent" - * -- Josh Myer - * - */ - -#ifndef PURPLE_OSCAR_OSCAR_H -#define PURPLE_OSCAR_OSCAR_H - -#include "internal.h" -#include "circularbuffer.h" -#include "debug.h" -#include "eventloop.h" -#include "proxy.h" -#include "sslconn.h" - -#include <stdio.h> -#include <string.h> -#include <fcntl.h> -#include <sys/types.h> -#include <stdlib.h> -#include <stdarg.h> -#include <errno.h> -#include <time.h> - -#include <gmodule.h> -#include <libsoup/soup.h> - -#ifndef _WIN32 -#include <sys/time.h> -#include <unistd.h> -#include <netdb.h> -#include <netinet/in.h> -#include <sys/socket.h> -#endif - -#define OSCAR_TYPE_PROTOCOL (oscar_protocol_get_type()) -#define OSCAR_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OSCAR_TYPE_PROTOCOL, OscarProtocol)) -#define OSCAR_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OSCAR_TYPE_PROTOCOL, OscarProtocolClass)) -#define OSCAR_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), OSCAR_TYPE_PROTOCOL)) -#define OSCAR_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), OSCAR_TYPE_PROTOCOL)) -#define OSCAR_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), OSCAR_TYPE_PROTOCOL, OscarProtocolClass)) - -typedef struct _ByteStream ByteStream; -typedef struct _ClientInfo ClientInfo; -typedef struct _FlapConnection FlapConnection; -typedef struct _FlapFrame FlapFrame; -typedef struct _IcbmArgsCh2 IcbmArgsCh2; -typedef struct _IcbmCookie IcbmCookie; -typedef struct _OscarData OscarData; -typedef struct _QueuedSnac QueuedSnac; - -typedef guint32 aim_snacid_t; - -#include "snactypes.h" - -G_BEGIN_DECLS - -#define FAIM_SNAC_HASH_SIZE 16 - -/* - * Current Maximum Length for usernames (not including NULL) - * - * Currently only names up to 16 characters can be registered - * however it is apparently legal for them to be larger. - */ -#define MAXSNLEN 97 - -/* - * Current Maximum Length for Instant Messages - * - * This was found basically by experiment, but not wholly - * accurate experiment. It should not be regarded - * as completely correct. But its a decent approximation. - * - * Note that although we can send this much, its impossible - * for WinAIM clients (up through the latest (4.0.1957)) to - * send any more than 1kb. Amaze all your windows friends - * with utterly oversized instant messages! - */ -#define MAXMSGLEN 2544 - -/* - * Maximum size of a Buddy Icon. - */ -#define MAXICONLEN 7168 -#define AIM_ICONIDENT "AVT1picture.id" - -/* - * Found by trial and error. - */ -#define MAXAVAILMSGLEN 251 - -/** - * Maximum length for the password of an ICQ account - */ -#define MAXICQPASSLEN 16 - -#define AIM_MD5_STRING "AOL Instant Messenger (SM)" - -#define OSCAR_CONNECT_STEPS 6 - -/* - * Client info. Filled in by the client and passed in to - * aim_send_login(). The information ends up getting passed to OSCAR - * through the initial login command. - * - */ -struct _ClientInfo -{ - const char *clientstring; - guint16 clientid; - guint16 major; - guint16 minor; - guint16 point; - guint16 build; - guint32 distrib; - const char *country; /* two-letter abbrev */ - const char *lang; /* two-letter abbrev */ -}; - -/* - * We need to use the major-minor-micro versions from the official - * AIM and ICQ programs here or AOL won't let us use certain features. - * - * 0x00000611 is the distid given to us by AOL for use as the default - * libpurple distid. - */ -#define CLIENTINFO_PURPLE_AIM { \ - NULL, \ - 0x0109, \ - 0x0005, 0x0001, \ - 0x0000, 0x0bdc, \ - 0x00000611, \ - "us", "en", \ -} - -#define CLIENTINFO_PURPLE_ICQ { \ - NULL, \ - 0x010a, \ - 0x0014, 0x0034, \ - 0x0000, 0x0c18, \ - 0x00000611, \ - "us", "en", \ -} - -typedef enum -{ - OSCAR_DISCONNECT_DONE, /* not considered an error */ - OSCAR_DISCONNECT_LOCAL_CLOSED, /* peer connections only, not considered an error */ - OSCAR_DISCONNECT_REMOTE_CLOSED, - OSCAR_DISCONNECT_REMOTE_REFUSED, /* peer connections only */ - OSCAR_DISCONNECT_LOST_CONNECTION, - OSCAR_DISCONNECT_INVALID_DATA, - OSCAR_DISCONNECT_COULD_NOT_CONNECT, - OSCAR_DISCONNECT_RETRYING /* peer connections only */ -} OscarDisconnectReason; - -#define OSCAR_CAPABILITY_BUDDYICON 0x0000000000000001LL -#define OSCAR_CAPABILITY_TALK 0x0000000000000002LL -#define OSCAR_CAPABILITY_DIRECTIM 0x0000000000000004LL -#define OSCAR_CAPABILITY_CHAT 0x0000000000000008LL -#define OSCAR_CAPABILITY_GETFILE 0x0000000000000010LL -#define OSCAR_CAPABILITY_SENDFILE 0x0000000000000020LL -#define OSCAR_CAPABILITY_GAMES 0x0000000000000040LL -#define OSCAR_CAPABILITY_ADDINS 0x0000000000000080LL -#define OSCAR_CAPABILITY_SENDBUDDYLIST 0x0000000000000100LL -#define OSCAR_CAPABILITY_GAMES2 0x0000000000000200LL -#define OSCAR_CAPABILITY_ICQ_DIRECT 0x0000000000000400LL -#define OSCAR_CAPABILITY_APINFO 0x0000000000000800LL -#define OSCAR_CAPABILITY_ICQRTF 0x0000000000001000LL -#define OSCAR_CAPABILITY_EMPTY 0x0000000000002000LL -#define OSCAR_CAPABILITY_ICQSERVERRELAY 0x0000000000004000LL -#define OSCAR_CAPABILITY_UNICODEOLD 0x0000000000008000LL -#define OSCAR_CAPABILITY_TRILLIANCRYPT 0x0000000000010000LL -#define OSCAR_CAPABILITY_UNICODE 0x0000000000020000LL -#define OSCAR_CAPABILITY_INTEROPERATE 0x0000000000040000LL -#define OSCAR_CAPABILITY_SHORTCAPS 0x0000000000080000LL -#define OSCAR_CAPABILITY_HIPTOP 0x0000000000100000LL -#define OSCAR_CAPABILITY_SECUREIM 0x0000000000200000LL -#define OSCAR_CAPABILITY_SMS 0x0000000000400000LL -#define OSCAR_CAPABILITY_VIDEO 0x0000000000800000LL -#define OSCAR_CAPABILITY_ICHATAV 0x0000000001000000LL -#define OSCAR_CAPABILITY_LIVEVIDEO 0x0000000002000000LL -#define OSCAR_CAPABILITY_CAMERA 0x0000000004000000LL -#define OSCAR_CAPABILITY_ICHAT_SCREENSHARE 0x0000000008000000LL -#define OSCAR_CAPABILITY_TYPING 0x0000000010000000LL -#define OSCAR_CAPABILITY_NEWCAPS 0x0000000020000000LL -#define OSCAR_CAPABILITY_XTRAZ 0x0000000040000000LL -#define OSCAR_CAPABILITY_GENERICUNKNOWN 0x0000000080000000LL -#define OSCAR_CAPABILITY_HTML_MSGS 0x0000000100000000LL -#define OSCAR_CAPABILITY_LAST 0x0000000200000000LL - -#define OSCAR_STATUS_ID_INVISIBLE "invisible" -#define OSCAR_STATUS_ID_OFFLINE "offline" -#define OSCAR_STATUS_ID_AVAILABLE "available" -#define OSCAR_STATUS_ID_AWAY "away" -#define OSCAR_STATUS_ID_DND "dnd" -#define OSCAR_STATUS_ID_NA "na" -#define OSCAR_STATUS_ID_OCCUPIED "occupied" -#define OSCAR_STATUS_ID_FREE4CHAT "free4chat" -#define OSCAR_STATUS_ID_CUSTOM "custom" -#define OSCAR_STATUS_ID_MOBILE "mobile" -#define OSCAR_STATUS_ID_EVIL "evil" -#define OSCAR_STATUS_ID_DEPRESSION "depression" -#define OSCAR_STATUS_ID_ATHOME "athome" -#define OSCAR_STATUS_ID_ATWORK "atwork" -#define OSCAR_STATUS_ID_LUNCH "lunch" - -typedef struct -{ - PurpleProtocol parent; -} OscarProtocol; - -typedef struct -{ - PurpleProtocolClass parent_class; -} OscarProtocolClass; - -/** - * Returns the GType for the OscarProtocol object. - */ -G_MODULE_EXPORT GType oscar_protocol_get_type(void); - -/* - * Byte Stream type. Sort of. - * - * Use of this type serves a couple purposes: - * - Buffer/buflen pairs are passed all around everywhere. This turns - * that into one value, as well as abstracting it slightly. - * - Through the abstraction, it is possible to enable bounds checking - * for robustness at the cost of performance. But a clean failure on - * weird packets is much better than a segfault. - * - I like having variables named "bs". - * - * Don't touch the insides of this struct. Or I'll have to kill you. - * - */ -struct _ByteStream -{ - guint8 *data; - size_t len; - size_t offset; -}; - -struct _QueuedSnac -{ - guint16 family; - guint16 subtype; - FlapFrame *frame; -}; - -struct _FlapFrame -{ - guint8 channel; - guint16 seqnum; - ByteStream data; /* payload stream */ -}; - -struct _FlapConnection -{ - OscarData *od; /**< Pointer to parent session. */ - gboolean connected; - time_t lastactivity; /**< Time of last transmit. */ - guint destroy_timeout; - OscarDisconnectReason disconnect_reason; - gchar *error_message; - guint16 disconnect_code; - - /* A few variables that are only used when connecting */ - PurpleProxyConnectData *connect_data; - guint16 cookielen; - guint8 *cookie; - gpointer new_conn_data; - - int fd; - PurpleSslConnection *gsc; - guint8 header[6]; - gssize header_received; - FlapFrame buffer_incoming; - PurpleCircularBuffer *buffer_outgoing; - guint watcher_incoming; - guint watcher_outgoing; - - guint16 type; - guint16 subtype; - guint16 seqnum_out; /**< The sequence number of most recently sent packet. */ - guint16 seqnum_in; /**< The sequence number of most recently received packet. */ - GSList *groups; - GSList *rateclasses; /* Contains nodes of struct rateclass. */ - struct rateclass *default_rateclass; - GHashTable *rateclass_members; /* Key is family and subtype, value is pointer to the rateclass struct to use. */ - - GQueue *queued_snacs; /**< Contains QueuedSnacs. */ - GQueue *queued_lowpriority_snacs; /**< Contains QueuedSnacs to send only once queued_snacs is empty */ - guint queued_timeout; - - void *internal; /* internal conn-specific libfaim data */ -}; - -struct _IcbmCookie -{ - guchar cookie[8]; - int type; - void *data; - time_t addtime; - struct _IcbmCookie *next; -}; - -#include "peer.h" - -struct aim_ssi_itemlist { - struct aim_ssi_item *data; - GHashTable *idx_gid_bid; - GHashTable *idx_all_named_items; -}; - -/** - * The main client-data interface. - */ -struct _OscarData -{ - /* Only used when connecting with clientLogin or Kerberos. */ - SoupSession *http_conns; - - gboolean iconconnecting; - gboolean set_icon; - - GSList *create_rooms; - - gboolean conf; - gboolean reqemail; - gboolean setemail; - char *email; - gboolean setnick; - char *newformatting; - gboolean chpass; - char *oldp; - char *newp; - - GSList *oscar_chats; - GHashTable *buddyinfo; - GSList *requesticon; - - gboolean use_ssl; - gboolean icq; - guint getblisttimer; - - struct { - guint maxwatchers; /* max users who can watch you */ - guint maxbuddies; /* max users you can watch */ - guint maxgroups; /* max groups in server list */ - guint maxpermits; /* max users on permit list */ - guint maxdenies; /* max users on deny list */ - guint maxsiglen; /* max size (bytes) of profile */ - guint maxawaymsglen; /* max size (bytes) of posted away message */ - } rights; - - PurpleConnection *gc; - - void *modlistv; - - /* - * Outstanding snac handling - * - * TODO: Should these be per-connection? -mid - */ - void *snac_hash[FAIM_SNAC_HASH_SIZE]; - aim_snacid_t snacid_next; - - /* - * TODO: Data specific to a certain family should go into a - * hashtable and the core parts of libfaim shouldn't - * need to know about them. - */ - - IcbmCookie *msgcookies; - GSList *icq_info; - - /** Only used when connecting with the old-style BUCP login. */ - struct aim_authresp_info *authinfo; - struct aim_emailinfo *emailinfo; - - struct { - struct aim_userinfo_s *userinfo; - } locate; - - struct { - gboolean have_rights; - } bos; - - /* Server-stored information (ssi) */ - struct { - gboolean received_data; - guint16 numitems; - struct aim_ssi_itemlist official; - struct aim_ssi_itemlist local; - struct aim_ssi_tmp *pending; - time_t timestamp; - gboolean waiting_for_ack; - gboolean in_transaction; - } ssi; - - /** Contains pointers to handler functions for each family/subtype. */ - GHashTable *handlerlist; - - /** A linked list containing FlapConnections. */ - GSList *oscar_connections; - guint16 default_port; - - /** A linked list containing PeerConnections. */ - GSList *peer_connections; -}; - -/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ -#define AIM_ICQ_STATE_NORMAL 0x00000000 -#define AIM_ICQ_STATE_AWAY 0x00000001 -#define AIM_ICQ_STATE_DND 0x00000002 -#define AIM_ICQ_STATE_OUT 0x00000004 -#define AIM_ICQ_STATE_BUSY 0x00000010 -#define AIM_ICQ_STATE_CHAT 0x00000020 -#define AIM_ICQ_STATE_INVISIBLE 0x00000100 -#define AIM_ICQ_STATE_EVIL 0x00003000 -#define AIM_ICQ_STATE_DEPRESSION 0x00004000 -#define AIM_ICQ_STATE_ATHOME 0x00005000 -#define AIM_ICQ_STATE_ATWORK 0x00006000 -#define AIM_ICQ_STATE_LUNCH 0x00002001 -#define AIM_ICQ_STATE_EVIL 0x00003000 -#define AIM_ICQ_STATE_WEBAWARE 0x00010000 -#define AIM_ICQ_STATE_HIDEIP 0x00020000 -#define AIM_ICQ_STATE_BIRTHDAY 0x00080000 -#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000 -#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000 - -/** - * Only used when connecting with the old-style BUCP login. - */ -struct aim_clientrelease -{ - char *name; - guint32 build; - char *url; - char *info; -}; - -/** - * Only used when connecting with the old-style BUCP login. - */ -struct aim_authresp_info -{ - char *bn; - guint16 errorcode; - char *errorurl; - guint16 regstatus; - char *email; - char *bosip; - guint16 cookielen; - guint8 *cookie; - char *chpassurl; - struct aim_clientrelease latestrelease; - struct aim_clientrelease latestbeta; -}; - -/* Callback data for redirect. */ -struct aim_redirect_data -{ - guint16 group; - const char *ip; - guint16 cookielen; - const guint8 *cookie; - const char *ssl_cert_cn; - guint8 use_ssl; - struct { /* group == SNAC_FAMILY_CHAT */ - guint16 exchange; - const char *room; - guint16 instance; - } chat; -}; - -int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname); - -/* family_auth.c */ - -/** - * Only used when connecting with the old-style BUCP login. - */ -int aim_request_login(OscarData *od, FlapConnection *conn, const char *bn); - -/** - * Only used when connecting with the old-style BUCP login. - */ -int aim_send_login(OscarData *od, FlapConnection *conn, const char *bn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins); - -/** - * Only used when connecting with the old-style BUCP login. - */ -/* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid); - -/** - * Only used when connecting with clientLogin. - */ -void send_client_login(OscarData *od, const char *username); - -/** - * Only used when connecting with kerberos login. - */ -void send_kerberos_login(OscarData *od, const char *username); - - -/* flap_connection.c */ -FlapConnection *flap_connection_new(OscarData *, int type); -void flap_connection_close(OscarData *od, FlapConnection *conn); -void flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message); -void flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message); -FlapConnection *flap_connection_findbygroup(OscarData *od, guint16 group); -FlapConnection *flap_connection_getbytype(OscarData *, int type); -FlapConnection *flap_connection_getbytype_all(OscarData *, int type); -void flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond); -void flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); - -void flap_connection_send(FlapConnection *conn, FlapFrame *frame); -void flap_connection_send_version(OscarData *od, FlapConnection *conn); -void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy); -void flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_login); -void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data); -void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority); -void flap_connection_send_keepalive(OscarData *od, FlapConnection *conn); -FlapFrame *flap_frame_new(OscarData *od, guint16 channel, int datalen); - -/* oscar_data.c */ -typedef int (*aim_rxcallback_t)(OscarData *od, FlapConnection *conn, FlapFrame *frame, ...); - -OscarData *oscar_data_new(void); -void oscar_data_destroy(OscarData *); -void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags); -aim_rxcallback_t aim_callhandler(OscarData *od, guint16 family, guint16 subtype); - -/* 0x0001 - family_oservice.c */ -/* 0x0002 */ void aim_srv_clientready(OscarData *od, FlapConnection *conn); -/* 0x0004 */ void aim_srv_requestnew(OscarData *od, guint16 serviceid); -/* 0x0006 */ void aim_srv_reqrates(OscarData *od, FlapConnection *conn); -/* 0x0008 */ void aim_srv_rates_addparam(OscarData *od, FlapConnection *conn); -/* 0x000e */ void aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn); -/* 0x0011 */ void aim_srv_setidle(OscarData *od, guint32 idletime); -/* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn); -/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl); -void aim_srv_set_dc_info(OscarData *od); - - -void aim_bos_reqrights(OscarData *od, FlapConnection *conn); - -#define AIM_RATE_CODE_LIMIT 0x0003 - -/* family_icbm.c */ -#define AIM_OFT_SUBTYPE_SEND_DIR 0x0002 - -#define AIM_TRANSFER_DENY_DECLINE 0x0001 - -#define AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED 0x00000001 -#define AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED 0x00000002 -#define AIM_IMPARAM_FLAG_EVENTS_ALLOWED 0x00000008 -#define AIM_IMPARAM_FLAG_SMS_SUPPORTED 0x00000010 -#define AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED 0x00000100 - -/** - * This flag tells the server that we always send HTML in messages - * sent from an ICQ account to an ICQ account. (If this flag is - * not sent then plaintext is sent ICQ<-->ICQ (HTML is sent in all - * other cases)). - * - * If we send an HTML message to an old client that doesn't support - * HTML messages, then the oscar servers will merrily strip the HTML - * for us. - * - * All incoming IMs are treated as HTML. - */ -#define AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ 0x00000400 - -struct aim_icbmparameters -{ - guint16 maxchan; - guint32 flags; /* AIM_IMPARAM_FLAG_ */ - guint16 maxmsglen; /* message size that you will accept */ - guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */ - guint16 maxrecverwarn; - guint32 minmsginterval; /* in milliseconds? */ -}; - -/* - * TODO: Should probably combine this with struct chat_connection. - */ -struct aim_chat_roominfo -{ - guint16 exchange; - char *name; - guint8 namelen; - guint16 instance; -}; - -struct chat_connection -{ - char *name; - char *show; /* AOL did something funny to us */ - guint16 exchange; - guint16 instance; - FlapConnection *conn; - int id; - PurpleConnection *gc; - PurpleChatConversation *conv; - guint16 maxlen; - guint16 maxvis; -}; - -/* - * All this chat struct stuff should be in family_chat.c - */ -void oscar_chat_destroy(struct chat_connection *cc); - -#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */ -#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */ -#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */ -#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */ -#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */ -#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */ -#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */ -#define AIM_IMFLAGS_TYPINGNOT 0x1000 /* typing notification */ - -#define AIM_CHARSET_ASCII 0x0000 /* ISO 646 */ -#define AIM_CHARSET_UNICODE 0x0002 /* ISO 10646 (UTF-16/UCS-2BE) */ -#define AIM_CHARSET_LATIN_1 0x0003 /* ISO 8859-1 */ - -/* - * Arguments to aim_send_im_ext(). - * - * This is really complicated. But immensely versatile. - * - */ -struct aim_sendimext_args -{ - /* These are _required_ */ - const char *destbn; - guint32 flags; /* often 0 */ - - const char *msg; - gsize msglen; - - /* Only used if AIM_IMFLAGS_HASICON is set */ - guint32 iconlen; - time_t iconstamp; - guint32 iconsum; - - guint16 featureslen; - guint8 *features; - - guint16 charset; -}; - -/* - * This information is provided in the Incoming ICBM callback for - * Channel 1 ICBM's. - */ -struct aim_incomingim_ch1_args -{ - guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */ - time_t timestamp; /* Only set for offline messages */ - - gchar *msg; - - /* Only provided if AIM_IMFLAGS_HASICON is set */ - time_t iconstamp; - guint32 iconlen; - guint16 iconsum; -}; - -/* Valid values for channel 2 args->status */ -#define AIM_RENDEZVOUS_PROPOSE 0x0000 -#define AIM_RENDEZVOUS_CANCEL 0x0001 -#define AIM_RENDEZVOUS_CONNECTED 0x0002 - -struct _IcbmArgsCh2 -{ - guint16 status; - guchar cookie[8]; - guint64 type; /* One of the OSCAR_CAPABILITY_ constants */ - const char *proxyip; - const char *clientip; - const char *verifiedip; - guint16 port; - gboolean use_proxy; - guint16 errorcode; - const char *msg; /* invite message or file description */ - guint16 msglen; - const char *encoding; - const char *language; - guint16 requestnumber; - union { - struct { - guint32 checksum; - guint32 length; - time_t timestamp; - guint8 *icon; - } icon; - struct { - struct aim_chat_roominfo roominfo; - } chat; - struct { - guint8 msgtype; - const char *msg; - } rtfmsg; - struct { - guint16 subtype; - guint16 totfiles; - guint32 totsize; - char *filename; - } sendfile; - } info; - void *destructor; /* used internally only */ -}; - -struct aim_incomingim_ch4_args -{ - guint32 uin; /* Of the sender of the ICBM */ - guint8 type; - guint8 flags; - gchar *msg; /* Reason for auth request, deny, or accept */ - int msglen; -}; - -/* SNAC sending functions */ -/* 0x0002 */ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params); -/* 0x0004 */ int aim_im_reqparams(OscarData *od); -/* 0x0006 */ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args); -/* 0x0006 */ int aim_im_sendch1(OscarData *, const char *destbn, guint16 flags, const char *msg); -/* 0x0006 */ int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance); -/* 0x0006 */ int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum); - -/* 0x0006 */ void aim_im_sendch2_cancel(PeerConnection *peer_conn); -/* 0x0006 */ void aim_im_sendch2_connected(PeerConnection *peer_conn); -/* 0x0006 */ void aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber); -/* 0x0006 */ void aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber); -/* 0x0006 */ void aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles); -/* 0x0006 */ void aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles); - -/* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code); -/* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od); -/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2); -/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie); -void aim_icbm_makecookie(guchar* cookie); -void aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie); - -/* 0x0002 - family_locate.c */ -/* - * AIM User Info, Standard Form. - */ -#define AIM_FLAG_ADMINISTRATOR 0x0002 -#define AIM_FLAG_AOL 0x0004 -#define AIM_FLAG_AWAY 0x0020 -#define AIM_FLAG_WIRELESS 0x0080 -#define AIM_FLAG_ICQ 0x0040 -#define AIM_FLAG_ACTIVEBUDDY 0x0400 - -#define AIM_USERINFO_PRESENT_FLAGS 0x00000001 -#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002 -#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004 -#define AIM_USERINFO_PRESENT_IDLE 0x00000008 -#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010 -#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020 -#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040 -#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080 -#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100 -#define AIM_USERINFO_PRESENT_CREATETIME 0x00000200 - -struct userinfo_node -{ - char *bn; - struct userinfo_node *next; -}; - -typedef struct aim_userinfo_s -{ - char *bn; - guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */ - guint16 idletime; /* in seconds */ - guint16 flags; - guint32 createtime; /* time_t */ - guint32 membersince; /* time_t */ - guint32 onlinesince; /* time_t */ - guint32 sessionlen; /* in seconds */ - guint64 capabilities; - struct { - guint32 status; - guint32 ipaddr; - guint8 crap[0x25]; /* until we figure it out... */ - } icqinfo; - guint32 present; - - guint8 iconcsumtype; - guint16 iconcsumlen; - guint8 *iconcsum; - - char *info; - char *info_encoding; - guint16 info_len; - - char *status; - char *status_encoding; - guint16 status_len; - - char *itmsurl; - char *itmsurl_encoding; - guint16 itmsurl_len; - - char *away; - char *away_encoding; - guint16 away_len; - - struct aim_userinfo_s *next; -} aim_userinfo_t; - -struct aim_invite_priv -{ - char *bn; - char *roomname; - guint16 exchange; - guint16 instance; -}; - -#define AIM_COOKIETYPE_CHAT 0x01 -#define AIM_COOKIETYPE_INVITE 0x02 - -aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn); -void aim_locate_dorequest(OscarData *od); - -/* 0x0002 */ int aim_locate_reqrights(OscarData *od); -/* 0x0004 */ int aim_locate_setcaps(OscarData *od, guint64 caps); -/* 0x0004 */ int aim_locate_setprofile(OscarData *od, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len); -/* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags); - -guint64 aim_locate_getcaps(OscarData *od, ByteStream *bs, int len); -guint64 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len); -void aim_info_free(aim_userinfo_t *); -int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *); -int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info); -PurpleMood* icq_get_purple_moods(PurpleAccount *account); -const char* icq_get_custom_icon_description(const char *mood); -guint8* icq_get_custom_icon_data(const char *mood); -int icq_im_xstatus_request(OscarData *od, const char *sn); - -/* 0x0003 - family_buddy.c */ -/* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *); - - -/* 0x000a - family_userlookup.c */ -int aim_search_address(OscarData *, const char *); - -struct aim_chat_exchangeinfo -{ - guint16 number; - guint16 flags; - char *name; - char *charset1; - char *lang1; - char *charset2; - char *lang2; -}; - -#define AIM_CHATFLAGS_NOREFLECT 0x0001 -#define AIM_CHATFLAGS_AWAY 0x0002 -int aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language); -int aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance); - -void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn); - -int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange); - - -/* 0x0010 - family_bart.c */ -int aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen); -int aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen); - - - -/* 0x0013 - family_feedbag.c */ -#define AIM_SSI_TYPE_BUDDY 0x0000 -#define AIM_SSI_TYPE_GROUP 0x0001 -#define AIM_SSI_TYPE_PERMIT 0x0002 -#define AIM_SSI_TYPE_DENY 0x0003 -#define AIM_SSI_TYPE_PDINFO 0x0004 -#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005 -#define AIM_SSI_TYPE_ICQDENY 0x000e -#define AIM_SSI_TYPE_ICONINFO 0x0014 - -/* These flags are set in the 0x00c9 TLV of SSI type 0x0005 */ -#define AIM_SSI_PRESENCE_FLAG_SHOWIDLE 0x00000400 -#define AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES 0x00020000 - -struct aim_ssi_item -{ - char *name; - guint16 gid; - guint16 bid; - guint16 type; - GSList *data; - struct aim_ssi_item *next; -}; - -struct aim_ssi_tmp -{ - guint16 action; - guint16 ack; - char *name; - struct aim_ssi_item *item; - struct aim_ssi_tmp *next; -}; - -/* These build the actual SNACs and queue them to be sent */ -/* 0x0002 */ int aim_ssi_reqrights(OscarData *od); -/* 0x0004 */ int aim_ssi_reqdata(OscarData *od); -/* 0x0007 */ int aim_ssi_enable(OscarData *od); -/* 0x0011 */ int aim_ssi_modbegin(OscarData *od); -/* 0x0012 */ int aim_ssi_modend(OscarData *od); -/* 0x0018 */ int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg); -/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg); - -/* Client functions for retrieving SSI data */ -struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_itemlist *list, guint16 gid, guint16 bid); -struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_itemlist *list, const char *gn, const char *bn, guint16 type); -struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_itemlist *list, const char *bn); -char *aim_ssi_itemlist_findparentname(struct aim_ssi_itemlist *list, const char *bn); -int aim_ssi_getpermdeny(struct aim_ssi_itemlist *list); -guint32 aim_ssi_getpresence(struct aim_ssi_itemlist *list); -char *aim_ssi_getalias(struct aim_ssi_itemlist *list, const char *gn, const char *bn); -char *aim_ssi_getalias_from_item(struct aim_ssi_item *item); -char *aim_ssi_getcomment(struct aim_ssi_itemlist *list, const char *gn, const char *bn); -gboolean aim_ssi_waitingforauth(struct aim_ssi_itemlist *list, const char *gn, const char *bn); - -/* Client functions for changing SSI data */ -int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *tlvlist, const char *alias, const char *comment, const char *smsnum, gboolean needauth); -int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group); -int aim_ssi_delgroup(OscarData *od, const char *group); -int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn); -int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias); -int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *alias); -int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn); -int aim_ssi_deletelist(OscarData *od); -int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny); -int aim_ssi_setpresence(OscarData *od, guint32 presence); -int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen); -int aim_ssi_delicon(OscarData *od); -int aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type); -int aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type); - -guint16 aim_ssi_getdenyentrytype(OscarData* od); - -struct aim_icq_info -{ - guint16 reqid; - - /* simple */ - guint32 uin; - - /* general and "home" information (0x00c8) */ - char *nick; - char *first; - char *last; - char *email; - char *homecity; - char *homestate; - char *homephone; - char *homefax; - char *homeaddr; - char *mobile; - char *homezip; - guint16 homecountry; -/* guint8 timezone; - guint8 hideemail; */ - - /* personal (0x00dc) */ - guint8 age; - guint8 unknown; - guint8 gender; - char *personalwebpage; - guint16 birthyear; - guint8 birthmonth; - guint8 birthday; - guint8 language1; - guint8 language2; - guint8 language3; - - /* work (0x00d2) */ - char *workcity; - char *workstate; - char *workphone; - char *workfax; - char *workaddr; - char *workzip; - guint16 workcountry; - char *workcompany; - char *workdivision; - char *workposition; - char *workwebpage; - - /* additional personal information (0x00e6) */ - char *info; - - /* email (0x00eb) */ - guint16 numaddresses; - char **email2; - - /* status note info */ - guint8 icbm_cookie[8]; - char *status_note_title; - - gboolean for_auth_request; - char *auth_request_reason; -}; - -int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware); -int aim_icq_changepasswd(OscarData *od, const char *passwd); -int aim_icq_getalias(OscarData *od, const char *uin, gboolean for_auth_request, char *auth_request_reason); -int aim_icq_getallinfo(OscarData *od, const char *uin); -int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias); - - -/* 0x0017 - family_auth.c */ -void aim_sendcookie(OscarData *, FlapConnection *, const guint16 length, const guint8 *); -void aim_admin_changepasswd(OscarData *, FlapConnection *, const char *newpw, const char *curpw); -void aim_admin_reqconfirm(OscarData *od, FlapConnection *conn); -void aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info); -void aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail); -void aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick); - - - -/* 0x0018 - family_alert.c */ -struct aim_emailinfo -{ - guint8 *cookie16; - guint8 *cookie8; - char *url; - guint16 nummsgs; - guint8 unread; - char *domain; - guint16 flag; - struct aim_emailinfo *next; -}; - -int aim_email_sendcookies(OscarData *od); -int aim_email_activate(OscarData *od); - - - -/* tlv.c - TLV handling */ - -/* TLV structure */ -typedef struct aim_tlv_s -{ - guint16 type; - guint16 length; - guint8 *value; -} aim_tlv_t; - -/* TLV handling functions */ -char *aim_tlv_getvalue_as_string(aim_tlv_t *tlv); - -aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth); -int aim_tlv_getlength(GSList *list, const guint16 type, const int nth); -char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth); -guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth); -guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth); -guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth); - -/* TLV list handling functions */ -GSList *aim_tlvlist_read(ByteStream *bs); -GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num); -GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len); -GSList *aim_tlvlist_copy(GSList *orig); - -int aim_tlvlist_count(GSList *list); -size_t aim_tlvlist_size(GSList *list); -int aim_tlvlist_cmp(GSList *one, GSList *two); -int aim_tlvlist_write(ByteStream *bs, GSList **list); -void aim_tlvlist_free(GSList *list); - -int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value); -int aim_tlvlist_add_noval(GSList **list, const guint16 type); -int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value); -int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value); -int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value); -int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value); -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood); -int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo); -int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance); -int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl); - -int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 lenth, const guint8 *value); -int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str); -int aim_tlvlist_replace_noval(GSList **list, const guint16 type); -int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value); -int aim_tlvlist_replace_16(GSList **list, const guint16 type, const guint16 value); -int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value); - -void aim_tlvlist_remove(GSList **list, const guint16 type); - - - -/* util.c */ -/* These are really ugly. You'd think this was LISP. I wish it was. */ -#define aimutil_put8(buf, data) ((*(buf) = (guint8)(data)&0xff),1) -#define aimutil_get8(buf) ((*(buf))&0xff) -#define aimutil_put16(buf, data) ( \ - (*(buf) = (guint8)((data)>>8)&0xff), \ - (*((buf)+1) = (guint8)(data)&0xff), \ - 2) -#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) -#define aimutil_put32(buf, data) ( \ - (*((buf)) = (guint8)((data)>>24)&0xff), \ - (*((buf)+1) = (guint8)((data)>>16)&0xff), \ - (*((buf)+2) = (guint8)((data)>>8)&0xff), \ - (*((buf)+3) = (guint8)(data)&0xff), \ - 4) -#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ - (((*((buf)+1))<<16)&0x00ff0000) + \ - (((*((buf)+2))<< 8)&0x0000ff00) + \ - (((*((buf)+3) )&0x000000ff))) - -/* Little-endian versions (damn ICQ) */ -#define aimutil_putle8(buf, data) ( \ - (*(buf) = (guint8)(data) & 0xff), \ - 1) -#define aimutil_getle8(buf) ( \ - (*(buf)) & 0xff \ - ) -#define aimutil_putle16(buf, data) ( \ - (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \ - (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \ - 2) -#define aimutil_getle16(buf) ( \ - (((*((buf)+0)) << 0) & 0x00ff) + \ - (((*((buf)+1)) << 8) & 0xff00) \ - ) -#define aimutil_putle32(buf, data) ( \ - (*((buf)+0) = (guint8)((data) >> 0) & 0xff), \ - (*((buf)+1) = (guint8)((data) >> 8) & 0xff), \ - (*((buf)+2) = (guint8)((data) >> 16) & 0xff), \ - (*((buf)+3) = (guint8)((data) >> 24) & 0xff), \ - 4) -#define aimutil_getle32(buf) ( \ - (((*((buf)+0)) << 0) & 0x000000ff) + \ - (((*((buf)+1)) << 8) & 0x0000ff00) + \ - (((*((buf)+2)) << 16) & 0x00ff0000) + \ - (((*((buf)+3)) << 24) & 0xff000000)) - -const char *oscar_get_msgerr_reason(size_t reason); -int oscar_get_ui_info_int(const char *str, int default_value); -const char *oscar_get_ui_info_string(const char *str, const char *default_value); -gchar *oscar_get_clientstring(void); - -guint16 aimutil_iconsum(const guint8 *buf, int buflen); - -gboolean oscar_util_valid_name(const char *bn); -gboolean oscar_util_valid_name_icq(const char *bn); -gboolean oscar_util_valid_name_sms(const char *bn); -int oscar_util_name_compare(const char *bn1, const char *bn2); -gchar *oscar_util_format_string(const char *str, const char *name); -gchar *oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message); - -typedef struct { - guint16 family; - guint16 subtype; - guint16 flags; - guint32 id; -} aim_modsnac_t; - -#define AIM_MODULENAME_MAXLEN 16 -#define AIM_MODFLAG_MULTIFAMILY 0x0001 -typedef struct aim_module_s -{ - guint16 family; - guint16 version; - guint16 toolid; - guint16 toolversion; - guint16 flags; - char name[AIM_MODULENAME_MAXLEN+1]; - int (*snachandler)(OscarData *od, FlapConnection *conn, struct aim_module_s *mod, FlapFrame *rx, aim_modsnac_t *snac, ByteStream *bs); - void (*shutdown)(OscarData *od, struct aim_module_s *mod); - void *priv; - struct aim_module_s *next; -} aim_module_t; - -int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *)); -void aim__shutdownmodules(OscarData *od); -aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group); -aim_module_t *aim__findmodule(OscarData *od, const char *name); - -int admin_modfirst(OscarData *od, aim_module_t *mod); -int buddylist_modfirst(OscarData *od, aim_module_t *mod); -int bos_modfirst(OscarData *od, aim_module_t *mod); -int search_modfirst(OscarData *od, aim_module_t *mod); -int stats_modfirst(OscarData *od, aim_module_t *mod); -int auth_modfirst(OscarData *od, aim_module_t *mod); -int msg_modfirst(OscarData *od, aim_module_t *mod); -int misc_modfirst(OscarData *od, aim_module_t *mod); -int chatnav_modfirst(OscarData *od, aim_module_t *mod); -int chat_modfirst(OscarData *od, aim_module_t *mod); -int locate_modfirst(OscarData *od, aim_module_t *mod); -int service_modfirst(OscarData *od, aim_module_t *mod); -int popups_modfirst(OscarData *od, aim_module_t *mod); -int bart_modfirst(OscarData *od, aim_module_t *mod); -int ssi_modfirst(OscarData *od, aim_module_t *mod); -int icq_modfirst(OscarData *od, aim_module_t *mod); -int email_modfirst(OscarData *od, aim_module_t *mod); - -void aim_genericreq_n(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype); -void aim_genericreq_n_snacid(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype); -void aim_genericreq_l(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint32 *); - -/* bstream.c */ -int byte_stream_new(ByteStream *bs, size_t len); -int byte_stream_init(ByteStream *bs, guint8 *data, size_t len); -void byte_stream_destroy(ByteStream *bs); -size_t byte_stream_bytes_left(ByteStream *bs); -int byte_stream_curpos(ByteStream *bs); -int byte_stream_setpos(ByteStream *bs, size_t off); -void byte_stream_rewind(ByteStream *bs); -int byte_stream_advance(ByteStream *bs, int n); -guint8 byte_stream_get8(ByteStream *bs); -guint16 byte_stream_get16(ByteStream *bs); -guint32 byte_stream_get32(ByteStream *bs); -guint8 byte_stream_getle8(ByteStream *bs); -guint16 byte_stream_getle16(ByteStream *bs); -guint32 byte_stream_getle32(ByteStream *bs); -int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, size_t len); -guint8 *byte_stream_getraw(ByteStream *bs, size_t len); -char *byte_stream_getstr(ByteStream *bs, size_t len); -int byte_stream_put8(ByteStream *bs, guint8 v); -int byte_stream_put16(ByteStream *bs, guint16 v); -int byte_stream_put32(ByteStream *bs, guint32 v); -int byte_stream_putle8(ByteStream *bs, guint8 v); -int byte_stream_putle16(ByteStream *bs, guint16 v); -int byte_stream_putle32(ByteStream *bs, guint32 v); -int byte_stream_putraw(ByteStream *bs, const guint8 *v, size_t len); -int byte_stream_putstr(ByteStream *bs, const char *str); -int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, size_t len); -int byte_stream_putuid(ByteStream *bs, OscarData *od); -int byte_stream_putcaps(ByteStream *bs, guint64 caps); - -/** - * Inserts a BART asset block into the given byte stream. The flags - * and length are set appropriately based on the value of data. - */ -void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data); - -/** - * A helper function that calls byte_stream_put_bart_asset with the - * appropriate data ByteStream given the datastr. - */ -void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr); - -/* - * Generic SNAC structure. Rarely if ever used. - */ -typedef struct aim_snac_s { - aim_snacid_t id; - guint16 family; - guint16 type; - guint16 flags; - void *data; - time_t issuetime; - struct aim_snac_s *next; -} aim_snac_t; - -/* snac.c */ -void aim_initsnachash(OscarData *od); -aim_snacid_t aim_newsnac(OscarData *, aim_snac_t *newsnac); -aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen); -aim_snac_t *aim_remsnac(OscarData *, aim_snacid_t id); -void aim_cleansnacs(OscarData *, int maxage); -int aim_putsnac(ByteStream *, guint16 family, guint16 type, aim_snacid_t id); - -struct chatsnacinfo { - guint16 exchange; - char name[128]; - guint16 instance; -}; - -struct rateclass { - guint16 classid; - guint32 windowsize; - guint32 clear; - guint32 alert; - guint32 limit; - guint32 disconnect; - guint32 current; - guint32 max; - guint8 dropping_snacs; - - struct timeval last; /**< The time when we last sent a SNAC of this rate class. */ -}; - -int aim_cachecookie(OscarData *od, IcbmCookie *cookie); -IcbmCookie *aim_uncachecookie(OscarData *od, guint8 *cookie, int type); -IcbmCookie *aim_mkcookie(guint8 *, int, void *); -IcbmCookie *aim_checkcookie(OscarData *, const unsigned char *, const int); -int aim_freecookie(OscarData *od, IcbmCookie *cookie); -int aim_cookie_free(OscarData *od, IcbmCookie *cookie); - -int aim_chat_readroominfo(ByteStream *bs, struct aim_chat_roominfo *outinfo); - -void flap_connection_destroy_chat(OscarData *od, FlapConnection *conn); - -/* userinfo.c - displaying user information */ - -void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status); -void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo); -void oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy); -void oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info); -void oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo); - -/* authorization.c - OSCAR authorization requests */ -void oscar_auth_sendrequest(PurpleConnection *gc, const char *name, const char *msg); -void oscar_auth_sendrequest_menu(PurpleBlistNode *node, gpointer ignored); -void oscar_auth_recvrequest(PurpleConnection *gc, gchar *name, gchar *nick, gchar *reason); - -void oscar_set_aim_permdeny(PurpleConnection *gc); - -struct buddyinfo -{ - gboolean typingnot; - guint32 ipaddr; - - unsigned long ico_me_len; - unsigned long ico_me_csum; - time_t ico_me_time; - gboolean ico_informed; - - unsigned long ico_len; - unsigned long ico_csum; - time_t ico_time; - gboolean ico_need; - gboolean ico_sent; -}; - -struct name_data -{ - PurpleConnection *gc; - gchar *name; - gchar *nick; -}; - -void oscar_free_name_data(struct name_data *data); - -void oscar_init_account_options(PurpleProtocol *protocol, gboolean is_icq); - -G_END_DECLS - -#endif /* PURPLE_OSCAR_OSCAR_H */
--- a/libpurple/protocols/oscar/oscar_data.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#include "oscar.h" - -typedef struct _SnacHandler SnacHandler; - -struct _SnacHandler -{ - guint16 family; - guint16 subtype; - aim_rxcallback_t handler; - guint16 flags; -}; - -/** - * Allocates a new OscarData and initializes it with default values. - */ -OscarData * -oscar_data_new(void) -{ - OscarData *od; - aim_module_t *cur; - GString *msg; - - od = g_new0(OscarData, 1); - - aim_initsnachash(od); - od->snacid_next = 0x00000001; - od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - od->handlerlist = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); - - od->ssi.local.idx_gid_bid = g_hash_table_new(g_direct_hash, g_direct_equal); - od->ssi.local.idx_all_named_items = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - od->ssi.official.idx_gid_bid = g_hash_table_new(g_direct_hash, g_direct_equal); - od->ssi.official.idx_all_named_items = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - - /* - * Register all the modules for this session... - */ - aim__registermodule(od, misc_modfirst); /* load the catch-all first */ - aim__registermodule(od, service_modfirst); - aim__registermodule(od, locate_modfirst); - aim__registermodule(od, buddylist_modfirst); - aim__registermodule(od, msg_modfirst); - aim__registermodule(od, admin_modfirst); - aim__registermodule(od, popups_modfirst); - aim__registermodule(od, bos_modfirst); - aim__registermodule(od, search_modfirst); - aim__registermodule(od, stats_modfirst); - aim__registermodule(od, chatnav_modfirst); - aim__registermodule(od, chat_modfirst); - aim__registermodule(od, bart_modfirst); - /* missing 0x11 - 0x12 */ - aim__registermodule(od, ssi_modfirst); - /* missing 0x14 */ - aim__registermodule(od, icq_modfirst); - /* missing 0x16 */ - /* auth_modfirst is only needed if we're connecting with the old-style BUCP login */ - aim__registermodule(od, auth_modfirst); - aim__registermodule(od, email_modfirst); - - msg = g_string_new("Registered modules: "); - for (cur = od->modlistv; cur; cur = cur->next) { - g_string_append_printf( - msg, - "%s (family=0x%04x, version=0x%04x, toolid=0x%04x, toolversion=0x%04x), ", - cur->name, - cur->family, - cur->version, - cur->toolid, - cur->toolversion); - } - purple_debug_misc("oscar", "%s\n", msg->str); - g_string_free(msg, TRUE); - - return od; -} - -/** - * Logoff and deallocate a session. - * - * @param od Session to kill - */ -void -oscar_data_destroy(OscarData *od) -{ - aim_cleansnacs(od, -1); - - /* Only used when connecting with clientLogin or Kerberos. */ - if (od->http_conns) { - soup_session_abort(od->http_conns); - g_object_unref(od->http_conns); - } - - g_slist_free_full(od->requesticon, g_free); - g_free(od->email); - g_free(od->newp); - g_free(od->oldp); - if (od->getblisttimer > 0) - g_source_remove(od->getblisttimer); - while (od->oscar_connections != NULL) - flap_connection_destroy(od->oscar_connections->data, - OSCAR_DISCONNECT_DONE, NULL); - - while (od->peer_connections != NULL) - peer_connection_destroy(od->peer_connections->data, - OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); - - aim__shutdownmodules(od); - - g_hash_table_destroy(od->buddyinfo); - g_hash_table_destroy(od->handlerlist); - - g_hash_table_destroy(od->ssi.local.idx_gid_bid); - g_hash_table_destroy(od->ssi.local.idx_all_named_items); - - g_hash_table_destroy(od->ssi.official.idx_gid_bid); - g_hash_table_destroy(od->ssi.official.idx_all_named_items); - - g_free(od); -} - -void -oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags) -{ - SnacHandler *snac_handler; - - snac_handler = g_new0(SnacHandler, 1); - - snac_handler->family = family; - snac_handler->subtype = subtype; - snac_handler->flags = flags; - snac_handler->handler = newhandler; - - g_hash_table_insert(od->handlerlist, - GUINT_TO_POINTER((family << 16) + subtype), - snac_handler); -} - -aim_rxcallback_t -aim_callhandler(OscarData *od, guint16 family, guint16 subtype) -{ - SnacHandler *snac_handler; - - snac_handler = g_hash_table_lookup(od->handlerlist, GUINT_TO_POINTER((family << 16) + subtype)); - - return snac_handler ? snac_handler->handler : NULL; -}
--- a/libpurple/protocols/oscar/oscarcommon.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -/* oscarcommon.h contains prototypes for the protocol functions used by libaim.c - * and libicq.c - */ - -#ifndef PURPLE_OSCAR_OSCARCOMMON_H -#define PURPLE_OSCAR_OSCARCOMMON_H - -#include "internal.h" - -#include "protocol.h" -#include "version.h" -#include "notify.h" -#include "purpleaccountoption.h" -#include "status.h" - -#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com" -#define AIM_ALT_LOGIN_SERVER "login.messaging.aol.com" -#define AIM_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com" -#define ICQ_DEFAULT_LOGIN_SERVER "login.icq.com" -#define ICQ_DEFAULT_SSL_LOGIN_SERVER "slogin.icq.com" - -#define AIM_DEFAULT_KDC_SERVER "kdc.uas.aol.com" -#define AIM_DEFAULT_KDC_PORT 443 - -/* - * Using clientLogin requires a developer ID. This key is for libpurple. - * It is the default key for all libpurple-based clients. AOL encourages - * UIs (especially ones with lots of users) to override this with their - * own key. - */ -#define ICQ_DEFAULT_DIST_ID 1553 -#define ICQ_DEFAULT_CLIENT_KEY "ma15d7JTxbmVG-RP" -#define AIM_DEFAULT_DIST_ID 1717 -#define AIM_DEFAULT_CLIENT_KEY "ma19CwYN9i9Mw5nY" - -#define OSCAR_DEFAULT_LOGIN_PORT 5190 - -#define OSCAR_OPPORTUNISTIC_ENCRYPTION "opportunistic_encryption" -#define OSCAR_REQUIRE_ENCRYPTION "require_encryption" -#define OSCAR_NO_ENCRYPTION "no_encryption" - -#define OSCAR_MD5_LOGIN "md5_login" -#define OSCAR_CLIENT_LOGIN "client_login" -#define OSCAR_KERBEROS_LOGIN "kerberos_login" - -#ifndef _WIN32 -#define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1" -#else -#define OSCAR_DEFAULT_CUSTOM_ENCODING oscar_get_locale_charset() -#endif -#define OSCAR_DEFAULT_AUTHORIZATION TRUE -#define OSCAR_DEFAULT_HIDE_IP TRUE -#define OSCAR_DEFAULT_WEB_AWARE FALSE -#define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE -#define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE -#define OSCAR_DEFAULT_LOGIN OSCAR_CLIENT_LOGIN -#define OSCAR_DEFAULT_ENCRYPTION OSCAR_OPPORTUNISTIC_ENCRYPTION - -#ifdef _WIN32 -const char *oscar_get_locale_charset(void); -#endif -PurpleMood* oscar_get_purple_moods(PurpleAccount *account); -const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b); -const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b); -const char* oscar_list_emblem(PurpleBuddy *b); -char *oscar_status_text(PurpleBuddy *b); -void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); -GList *oscar_status_types(PurpleAccount *account); -GList *oscar_blist_node_menu(PurpleBlistNode *node); -GList *oscar_chat_info(PurpleConnection *gc); -GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name); -void oscar_login(PurpleAccount *account); -void oscar_close(PurpleConnection *gc); -int oscar_send_im(PurpleConnection *gc, PurpleMessage *msg); -void oscar_set_info(PurpleConnection *gc, const char *rawinfo); -unsigned int oscar_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state); -void oscar_get_info(PurpleConnection *gc, const char *name); -void oscar_set_status(PurpleAccount *account, PurpleStatus *status); -void oscar_set_idle(PurpleConnection *gc, int time); -void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new); -void oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg); -void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void oscar_add_permit(PurpleConnection *gc, const char *who); -void oscar_add_deny(PurpleConnection *gc, const char *who); -void oscar_rem_permit(PurpleConnection *gc, const char *who); -void oscar_rem_deny(PurpleConnection *gc, const char *who); -void oscar_join_chat(PurpleConnection *gc, GHashTable *data); -char *oscar_get_chat_name(GHashTable *data); -void oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name); -void oscar_chat_leave(PurpleConnection *gc, int id); -int oscar_send_chat(PurpleConnection *gc, int id, PurpleMessage *msg); -void oscar_keepalive(PurpleConnection *gc); -void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias); -void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group); -void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies); -void oscar_convo_closed(PurpleConnection *gc, const char *who); -const char *oscar_normalize(const PurpleAccount *account, const char *str); -void oscar_set_icon(PurpleConnection *gc, PurpleImage *img); -void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group); -gboolean oscar_can_receive_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who); -void oscar_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file); -PurpleXfer *oscar_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who); -gboolean oscar_offline_message(const PurpleBuddy *buddy); -gssize oscar_get_max_message_size(PurpleConversation *conv); -GList *oscar_get_actions(PurpleConnection *gc); -const gchar *oscar_get_login_server(gboolean is_icq, gboolean use_ssl); -gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params); - -#endif /* PURPLE_OSCAR_OSCARCOMMON_H */
--- a/libpurple/protocols/oscar/peer.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1212 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Functions dealing with peer connections. This includes the code - * used to establish a peer connection for both Oscar File transfer - * (OFT) and Oscar Direct Connect (ODC). (ODC is also referred to - * as DirectIM and IM Image.) - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -/* From the oscar PRPL */ -#include "oscar.h" -#include "peer.h" - -/* From Purple */ -#include "conversation.h" -#include "xfer.h" -#include "network.h" -#include "notify.h" -#include "request.h" -#include "util.h" - -#ifndef _WIN32 -#include <stdio.h> -#include <netdb.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> /* for inet_ntoa */ -#include <limits.h> /* for UINT_MAX */ -#endif - -/* - * I really want to switch all our networking code to using IPv6 only, - * but that really isn't a good idea at all. Evan S. of Adium says - * OS X sets all connections as "AF_INET6/PF_INET6," even if there is - * nothing inherently IPv6 about them. And I feel like Linux kernel - * 2.6.5 is doing the same thing. So we REALLY should accept - * connections if they're showing up as IPv6. Old OSes (Solaris?) - * that might not have full IPv6 support yet will fail if we try - * to use PF_INET6 but it isn't defined. --Mark Doliner - */ -#ifndef PF_INET6 -#define PF_INET6 PF_INET -#endif - -PeerConnection * -peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type) -{ - GSList *cur; - PeerConnection *conn; - - for (cur = od->peer_connections; cur != NULL; cur = cur->next) - { - conn = cur->data; - if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn)) - return conn; - } - - return NULL; -} - -/** - * @param cookie This must be exactly 8 characters. - */ -PeerConnection * -peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie) -{ - GSList *cur; - PeerConnection *conn; - - for (cur = od->peer_connections; cur != NULL; cur = cur->next) - { - conn = cur->data; - if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn)) - return conn; - } - - return NULL; -} - -PeerConnection * -peer_connection_new(OscarData *od, guint64 type, const char *bn) -{ - PeerConnection *conn; - PurpleAccount *account; - - account = purple_connection_get_account(od->gc); - - conn = g_new0(PeerConnection, 1); - conn->od = od; - conn->type = type; - conn->bn = g_strdup(bn); - conn->buffer_outgoing = purple_circular_buffer_new(0); - conn->listenerfd = -1; - conn->fd = -1; - conn->lastactivity = time(NULL); - conn->use_proxy |= purple_account_get_bool(account, "always_use_rv_proxy", FALSE); - - if (type == OSCAR_CAPABILITY_DIRECTIM) - memcpy(conn->magic, "ODC2", 4); - else if (type == OSCAR_CAPABILITY_SENDFILE) - memcpy(conn->magic, "OFT2", 4); - - od->peer_connections = g_slist_prepend(od->peer_connections, conn); - - return conn; -} - -static void -peer_connection_close(PeerConnection *conn) -{ - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - peer_odc_close(conn); - else if (conn->type == OSCAR_CAPABILITY_SENDFILE) - peer_oft_close(conn); - - if (conn->verified_connect_data != NULL) - { - purple_proxy_connect_cancel(conn->verified_connect_data); - conn->verified_connect_data = NULL; - } - - if (conn->client_connect_data != NULL) - { - purple_proxy_connect_cancel(conn->client_connect_data); - conn->client_connect_data = NULL; - } - - if (conn->listen_data != NULL) - { - purple_network_listen_cancel(conn->listen_data); - conn->listen_data = NULL; - } - - if (conn->connect_timeout_timer != 0) - { - g_source_remove(conn->connect_timeout_timer); - conn->connect_timeout_timer = 0; - } - - if (conn->watcher_incoming != 0) - { - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = 0; - } - if (conn->watcher_outgoing != 0) - { - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - } - if (conn->listenerfd >= 0) - { - close(conn->listenerfd); - conn->listenerfd = -1; - } - if (conn->fd >= 0) - { - close(conn->fd); - conn->fd = -1; - } - - g_free(conn->buffer_incoming.data); - conn->buffer_incoming.data = NULL; - conn->buffer_incoming.len = 0; - conn->buffer_incoming.offset = 0; - - g_object_unref(G_OBJECT(conn->buffer_outgoing)); - conn->buffer_outgoing = purple_circular_buffer_new(0); - - conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING; -} - -static gboolean -peer_connection_destroy_cb(gpointer data) -{ - PeerConnection *conn; - - conn = data; - - purple_request_close_with_handle(conn); - - peer_connection_close(conn); - - if (conn->checksum_data != NULL) - peer_oft_checksum_destroy(conn->checksum_data); - - if (conn->xfer != NULL) - { - PurpleXferStatus status; - status = purple_xfer_get_status(conn->xfer); - if ((status != PURPLE_XFER_STATUS_DONE) && - (status != PURPLE_XFER_STATUS_CANCEL_LOCAL) && - (status != PURPLE_XFER_STATUS_CANCEL_REMOTE)) - { - if ((conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) || - (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED)) - purple_xfer_cancel_remote(conn->xfer); - else - purple_xfer_cancel_local(conn->xfer); - } - g_object_unref(conn->xfer); - conn->xfer = NULL; - } - - g_free(conn->bn); - g_free(conn->error_message); - g_free(conn->proxyip); - g_free(conn->clientip); - g_free(conn->verifiedip); - g_free(conn->xferdata.name); - g_object_unref(G_OBJECT(conn->buffer_outgoing)); - - conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn); - - g_free(conn); - - return FALSE; -} - -void -peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message) -{ - if (conn->destroy_timeout != 0) - g_source_remove(conn->destroy_timeout); - conn->disconnect_reason = reason; - g_free(conn->error_message); - conn->error_message = g_strdup(error_message); - peer_connection_destroy_cb(conn); -} - -void -peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message) -{ - if (conn->destroy_timeout != 0) - /* Already taken care of */ - return; - - purple_debug_info("oscar", "Scheduling destruction of peer connection\n"); - conn->disconnect_reason = reason; - g_free(conn->error_message); - conn->error_message = g_strdup(error_message); - conn->destroy_timeout = g_timeout_add(0, peer_connection_destroy_cb, conn); -} - -/*******************************************************************/ -/* Begin code for receiving data on a peer connection */ -/*******************************************************************/ - -/** - * This should be used to read ODC and OFT framing info. It should - * NOT be used to read the payload sent across the connection (IMs, - * file data, etc), and it should NOT be used to read proxy negotiation - * headers. - * - * Unlike flap_connection_recv_cb(), this only reads one frame at a - * time. This is done so that the watcher can be changed during the - * handling of the frame. If the watcher is changed then this - * function will not read in any more data. This happens when - * reading the payload of a direct IM frame, or when we're - * receiving a file from the remote user. Once the data has been - * read, the watcher will be switched back to this function to - * continue reading the next frame. - */ -void -peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - PeerConnection *conn; - gssize read; - - conn = data; - - /* Start reading a new ODC/OFT frame */ - if (conn->buffer_incoming.data == NULL) - { - /* Read the first 6 bytes (magic string and frame length) */ - read = recv(conn->fd, conn->header + conn->header_received, - 6 - conn->header_received, 0); - - /* Check if the remote user closed the connection */ - if (read == 0) - { - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - return; - } - - /* If there was an error then close the connection */ - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - return; - - peer_connection_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); - return; - } - - conn->lastactivity = time(NULL); - - /* If we don't even have the first 6 bytes then do nothing */ - conn->header_received += read; - if (conn->header_received < 6) - return; - - /* All ODC/OFT frames must start with a magic string */ - if (memcmp(conn->magic, conn->header, 4)) - { - purple_debug_warning("oscar", "Expecting magic string to " - "be %c%c%c%c but received magic string %c%c%c%c. " - "Closing connection.\n", - conn->magic[0], conn->magic[1], conn->magic[2], - conn->magic[3], conn->header[0], conn->header[1], - conn->header[2], conn->header[3]); - peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL); - return; - } - - /* Initialize a new temporary ByteStream for incoming data */ - conn->buffer_incoming.len = aimutil_get16(&conn->header[4]) - 6; - conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len); - conn->buffer_incoming.offset = 0; - } - - /* Read data into the temporary buffer until it is complete */ - read = recv(conn->fd, - &conn->buffer_incoming.data[conn->buffer_incoming.offset], - conn->buffer_incoming.len - conn->buffer_incoming.offset, - 0); - - /* Check if the remote user closed the connection */ - if (read == 0) - { - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - return; - } - - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - return; - - peer_connection_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); - return; - } - - conn->lastactivity = time(NULL); - conn->buffer_incoming.offset += read; - if (conn->buffer_incoming.offset < conn->buffer_incoming.len) - /* Waiting for more data to arrive */ - return; - - /* We have a complete ODC/OFT frame! Handle it and continue reading */ - byte_stream_rewind(&conn->buffer_incoming); - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - { - peer_odc_recv_frame(conn, &conn->buffer_incoming); - } - else if (conn->type == OSCAR_CAPABILITY_SENDFILE) - { - peer_oft_recv_frame(conn, &conn->buffer_incoming); - } - - g_free(conn->buffer_incoming.data); - conn->buffer_incoming.data = NULL; - - conn->header_received = 0; -} - -/*******************************************************************/ -/* End code for receiving data on a peer connection */ -/*******************************************************************/ - -/*******************************************************************/ -/* Begin code for sending data on a peer connection */ -/*******************************************************************/ - -static void -send_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - PeerConnection *conn; - gsize writelen; - gssize wrotelen; - const gchar *output = NULL; - - conn = data; - writelen = purple_circular_buffer_get_max_read(conn->buffer_outgoing); - - if (writelen == 0) - { - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - /* - * The buffer is currently empty, so reset the current input - * and output positions to the start of the buffer. We do - * this so that the next chunk of data that we put into the - * buffer can be read back out of the buffer in one fell swoop. - * Otherwise it gets fragmented and we have to read from the - * second half of the buffer than go back and read the rest of - * the chunk from the first half. - * - * We're using TCP, which is a stream based protocol, so this - * isn't supposed to matter. However, experience has shown - * that at least the proxy file transfer code in AIM 6.1.41.2 - * requires that the entire OFT frame arrive all at once. If - * the frame is fragmented then AIM freaks out and aborts the - * file transfer. Somebody should teach those guys how to - * write good TCP code. - */ - purple_circular_buffer_reset(conn->buffer_outgoing); - return; - } - - output = purple_circular_buffer_get_output(conn->buffer_outgoing); - - wrotelen = send(conn->fd, output, writelen, 0); - if (wrotelen <= 0) - { - if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) - /* No worries */ - return; - - if (conn->ready) - { - purple_input_remove(conn->watcher_outgoing); - conn->watcher_outgoing = 0; - close(conn->fd); - conn->fd = -1; - peer_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_LOST_CONNECTION, NULL); - } - else - { - /* - * This could happen when unable to send a negotiation - * frame to a peer proxy server. - */ - peer_connection_trynext(conn); - } - return; - } - - purple_circular_buffer_mark_read(conn->buffer_outgoing, wrotelen); - conn->lastactivity = time(NULL); -} - -/** - * This should be called by OFT/ODC code to send a standard OFT or ODC - * frame across the peer connection along with some payload data. Or - * maybe a file. Anything, really. - */ -void -peer_connection_send(PeerConnection *conn, ByteStream *bs) -{ - /* Add everything to our outgoing buffer */ - purple_circular_buffer_append(conn->buffer_outgoing, bs->data, bs->len); - - /* If we haven't already started writing stuff, then start the cycle */ - if ((conn->watcher_outgoing == 0) && (conn->fd >= 0)) - { - conn->watcher_outgoing = purple_input_add(conn->fd, - PURPLE_INPUT_WRITE, send_cb, conn); - send_cb(conn, conn->fd, 0); - } -} - -/*******************************************************************/ -/* End code for sending data on a peer connection */ -/*******************************************************************/ - -/*******************************************************************/ -/* Begin code for establishing a peer connection */ -/*******************************************************************/ - -void -peer_connection_finalize_connection(PeerConnection *conn) -{ - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, peer_connection_recv_cb, conn); - - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - { - /* - * If we are connecting to them then send our cookie so they - * can verify who we are. Note: This doesn't seem to be - * necessary, but it also doesn't seem to hurt. - */ - if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)) - peer_odc_send_cookie(conn); - } - else if (conn->type == OSCAR_CAPABILITY_SENDFILE) - { - if (purple_xfer_get_xfer_type(conn->xfer) == PURPLE_XFER_TYPE_SEND) - { - peer_oft_send_prompt(conn); - } - } - - /* - * Tell the remote user that we're connected (which may also imply - * that we've accepted their request). - */ - if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING)) - aim_im_sendch2_connected(conn); -} - -/** - * We tried to make an outgoing connection to a remote user. It - * either connected or failed to connect. - */ -static void -peer_connection_common_established_cb(gpointer data, gint source, const gchar *error_message, gboolean verified) -{ - PeerConnection *conn; - - conn = data; - - if (verified) - conn->verified_connect_data = NULL; - else - conn->client_connect_data = NULL; - - if (source < 0) - { - if ((conn->verified_connect_data == NULL) && - (conn->client_connect_data == NULL)) - { - /* Our parallel connection attemps have both failed. */ - peer_connection_trynext(conn); - } - return; - } - - g_source_remove(conn->connect_timeout_timer); - conn->connect_timeout_timer = 0; - - if (conn->client_connect_data != NULL) - { - purple_proxy_connect_cancel(conn->client_connect_data); - conn->client_connect_data = NULL; - } - - if (conn->verified_connect_data != NULL) - { - purple_proxy_connect_cancel(conn->verified_connect_data); - conn->verified_connect_data = NULL; - } - - conn->fd = source; - - peer_connection_finalize_connection(conn); -} - -static void -peer_connection_verified_established_cb(gpointer data, gint source, const gchar *error_message) -{ - peer_connection_common_established_cb(data, source, error_message, TRUE); -} - -static void -peer_connection_client_established_cb(gpointer data, gint source, const gchar *error_message) -{ - peer_connection_common_established_cb(data, source, error_message, FALSE); -} - -/** - * This is the watcher callback for any listening socket that is - * waiting for a peer to connect. When a peer connects we set the - * input watcher to start reading data from the peer. - * - * To make sure that the connection is with the intended person and - * not with a malicious middle man, we don't send anything until we've - * received a peer frame from the remote user and have verified that - * the cookie in the peer frame matches the cookie that was exchanged - * in the channel 2 ICBM. - */ -void -peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - PeerConnection *conn; - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - - conn = data; - - purple_debug_info("oscar", "Accepting connection on listener socket.\n"); - - conn->fd = accept(conn->listenerfd, &addr, &addrlen); - if (conn->fd < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No connection yet--no worries */ - /* TODO: Hmm, but they SHOULD be connected if we're here, right? */ - return; - - peer_connection_trynext(conn); - return; - } - - if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) - { - /* Invalid connection type?! Continue waiting. */ - close(conn->fd); - return; - } - - _purple_network_set_common_socket_flags(conn->fd); - - purple_input_remove(conn->watcher_incoming); - - peer_connection_finalize_connection(conn); -} - -/** - * Converts a dot-decimal IP address to an array of unsigned - * chars. For example, converts 192.168.0.1 to a 4 byte - * array containing 192, 168, 0 and 1. - * - * @param ip An IP address in dot-decimal notiation. - * @return An array of 4 bytes containing an IP addresses - * equivalent to the given parameter, or NULL if - * the given IP address is invalid. This value - * is statically allocated and should not be - * freed. - */ -static const unsigned char * -peer_ip_atoi(const char *ip) -{ - static unsigned char ret[4]; - gchar *delimiter = "."; - gchar **split; - int i; - - g_return_val_if_fail(ip != NULL, NULL); - - split = g_strsplit(ip, delimiter, 4); - for (i = 0; split[i] != NULL; i++) - ret[i] = atoi(split[i]); - g_strfreev(split); - - /* i should always be 4 */ - if (i != 4) - return NULL; - - return ret; -} - -/** - * We've just opened a listener socket, so we send the remote - * user an ICBM and ask them to connect to us. - */ -static void -peer_connection_establish_listener_cb(int listenerfd, gpointer data) -{ - PeerConnection *conn; - OscarData *od; - PurpleConnection *gc; - PurpleAccount *account; - PurpleIMConversation *im; - char *tmp; - FlapConnection *bos_conn; - const char *listener_ip; - const guchar *ip_atoi; - unsigned short listener_port; - - conn = data; - conn->listen_data = NULL; - - if (listenerfd < 0) - { - /* Could not open listener socket */ - peer_connection_trynext(conn); - return; - } - - od = conn->od; - gc = od->gc; - account = purple_connection_get_account(gc); - conn->listenerfd = listenerfd; - - /* Watch for new connections on our listener socket */ - conn->watcher_incoming = purple_input_add(conn->listenerfd, - PURPLE_INPUT_READ, peer_connection_listen_cb, conn); - - /* Send the "please connect to me!" ICBM */ - bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM); - if (bos_conn == NULL) - { - /* Not good */ - peer_connection_trynext(conn); - return; - } - - if (bos_conn->gsc) - listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd); - else - listener_ip = purple_network_get_my_ip(bos_conn->fd); - - ip_atoi = peer_ip_atoi(listener_ip); - if (ip_atoi == NULL) { - /* Could not convert IP to 4 byte array--weird, but this does - happen for some users (#4829, Adium #15839). Maybe they're - connecting with IPv6...? Maybe through a proxy? */ - purple_debug_error("oscar", "Can't ask peer to connect to us " - "because peer_ip_atoi(%s) returned NULL. " - "fd=%d. is_ssl=%d\n", - listener_ip ? listener_ip : "(null)", - bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd, - bos_conn->gsc ? 1 : 0); - peer_connection_trynext(conn); - return; - } - - listener_port = purple_network_get_port_from_fd(conn->listenerfd); - - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - { - aim_im_sendch2_odc_requestdirect(od, - conn->cookie, conn->bn, ip_atoi, - listener_port, ++conn->lastrequestnumber); - - /* Print a message to a local conversation window */ - im = purple_im_conversation_new(account, conn->bn); - tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for " - "Direct IM."), conn->bn, listener_ip, listener_port); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), tmp, 0); - g_free(tmp); - } - else if (conn->type == OSCAR_CAPABILITY_SENDFILE) - { - aim_im_sendch2_sendfile_requestdirect(od, - conn->cookie, conn->bn, - ip_atoi, - listener_port, ++conn->lastrequestnumber, - (const gchar *)conn->xferdata.name, - conn->xferdata.size, conn->xferdata.totfiles); - } -} - -/** - * This is a callback function used when we're connecting to a peer - * using either the client IP or the verified IP and the connection - * took longer than 5 seconds to complete. We do this because - * waiting for the OS to time out the connection attempt is not - * practical--the default timeout on many OSes can be 3 minutes or - * more, and users are impatient. - * - * Worst case scenario: the user is connected to the Internet using - * a modem with severe lag. The peer connections fail and Purple falls - * back to using a proxied connection. The lower bandwidth - * limitations imposed by the proxied connection won't matter because - * the user is using a modem. - * - * I suppose this line of thinking is discriminatory against people - * with very high lag but decent throughput who are transferring - * large files. But we don't care about those people. - * - * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is - * too long for a user to wait to send a file. I'm also parallelizing - * requests when possible. The longest we should have to wait now is 10 - * seconds. We shouldn't make it shorter than this. - */ -static gboolean -peer_connection_tooktoolong(gpointer data) -{ - PeerConnection *conn; - - conn = data; - - purple_debug_info("oscar", "Peer connection timed out after 5 seconds. " - "Trying next method...\n"); - - peer_connection_trynext(conn); - - /* Cancel this timer. It'll be added again, if needed. */ - return FALSE; -} - -/** - * Try to establish the given PeerConnection using a defined - * sequence of steps. - */ -void -peer_connection_trynext(PeerConnection *conn) -{ - PurpleAccount *account; - - account = purple_connection_get_account(conn->od->gc); - - /* - * Close any remnants of a previous failed connection attempt. - */ - peer_connection_close(conn); - - /* - * 1. Attempt to connect to the remote user using their verifiedip and clientip. - * We try these at the same time and use whichever succeeds first, so we don't - * have to wait for a timeout. - */ - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_DIRECT) && - (conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy)) - { - conn->flags |= PEER_CONNECTION_FLAG_TRIED_DIRECT; - - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - { - gchar *tmp; - PurpleIMConversation *im; - tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."), - conn->verifiedip, conn->port); - im = purple_im_conversation_new(account, conn->bn); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), tmp, 0); - g_free(tmp); - } - - conn->verified_connect_data = purple_proxy_connect(NULL, account, - conn->verifiedip, conn->port, - peer_connection_verified_established_cb, conn); - - if ((conn->verifiedip == NULL) || - !purple_strequal(conn->verifiedip, conn->clientip)) - { - conn->client_connect_data = purple_proxy_connect(NULL, account, - conn->clientip, conn->port, - peer_connection_client_established_cb, conn); - } - - if ((conn->verified_connect_data != NULL) || - (conn->client_connect_data != NULL)) - { - /* Connecting... */ - conn->connect_timeout_timer = g_timeout_add_seconds(5, - peer_connection_tooktoolong, conn); - return; - } - } - - /* - * 2. Attempt to have the remote user connect to us (using both - * our verifiedip and our clientip). - */ - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) && - (!conn->use_proxy)) - { - conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING; - - /* - * Remote user is connecting to us, so we'll need to verify - * that the user who connected is our friend. - */ - conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING; - - conn->listen_data = purple_network_listen_range(5190, 5290, AF_UNSPEC, SOCK_STREAM, TRUE, - peer_connection_establish_listener_cb, conn); - if (conn->listen_data != NULL) - { - /* Opening listener socket... */ - return; - } - } - - /* - * 3. Attempt to have both users connect to an intermediate proxy - * server. - */ - if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY)) - { - conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY; - - /* - * If we initiate the proxy connection, then the remote user - * could be anyone, so we need to verify that the user who - * connected is our friend. - */ - if (!conn->use_proxy) - conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING; - - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - { - gchar *tmp; - PurpleIMConversation *im; - tmp = g_strdup(_("Attempting to connect via proxy server.")); - im = purple_im_conversation_new(account, conn->bn); - purple_conversation_write_system_message( - PURPLE_CONVERSATION(im), tmp, 0); - g_free(tmp); - } - - conn->verified_connect_data = purple_proxy_connect(NULL, account, - (conn->proxyip != NULL) - ? conn->proxyip - : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER), - PEER_PROXY_PORT, - peer_proxy_connection_established_cb, conn); - if (conn->verified_connect_data != NULL) - { - /* Connecting... */ - return; - } - } - - /* Give up! */ - peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL); -} - -/** - * Initiate a peer connection with someone. - */ -void -peer_connection_propose(OscarData *od, guint64 type, const char *bn) -{ - PeerConnection *conn; - - if (type == OSCAR_CAPABILITY_DIRECTIM) - { - conn = peer_connection_find_by_type(od, bn, type); - if (conn != NULL) - { - if (conn->ready) - { - PurpleAccount *account; - PurpleIMConversation *im; - - purple_debug_info("oscar", "Already have a direct IM " - "session with %s.\n", bn); - account = purple_connection_get_account(od->gc); - im = purple_conversations_find_im_with_account(bn, account); - if (im != NULL) - purple_conversation_present(PURPLE_CONVERSATION(im)); - return; - } - - /* Cancel the old connection and try again */ - peer_connection_destroy(conn, OSCAR_DISCONNECT_RETRYING, NULL); - } - } - - conn = peer_connection_new(od, type, bn); - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME; - conn->flags |= PEER_CONNECTION_FLAG_APPROVED; - aim_icbm_makecookie(conn->cookie); - - peer_connection_trynext(conn); -} - -/** - * Someone else wants to establish a peer connection with us, - * and we said yes. - */ -static void -peer_connection_got_proposition_yes_cb(gpointer data, gint id) -{ - PeerConnection *conn; - - conn = data; - - conn->flags |= PEER_CONNECTION_FLAG_APPROVED; - peer_connection_trynext(conn); -} - -/** - * Someone else wants to establish a peer connection with us, - * and we said no. - * - * "Well, one time my friend asked me if I wanted to play the - * piccolo. But I said no." - */ -static void -peer_connection_got_proposition_no_cb(gpointer data, gint id) -{ - PeerConnection *conn; - - conn = data; - - aim_im_denytransfer(conn->od, conn->bn, conn->cookie, - AIM_TRANSFER_DENY_DECLINE); - peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); -} - -/** - * Someone else wants to establish a peer connection with us. - */ -void -peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args) -{ - PurpleConnection *gc; - PurpleAccount *account; - PeerConnection *conn; - gchar *buf; - - gc = od->gc; - account = purple_connection_get_account(gc); - - /* - * If we have a connection with this same cookie then they are - * probably just telling us they weren't able to connect to us - * and we should try connecting to them, instead. Or they want - * to go through a proxy. - */ - conn = peer_connection_find_by_cookie(od, bn, args->cookie); - if ((conn != NULL) && (conn->type == args->type)) - { - purple_debug_info("oscar", "Remote user wants to try a " - "different connection method\n"); - g_free(conn->proxyip); - g_free(conn->clientip); - g_free(conn->verifiedip); - if (args->use_proxy) - conn->proxyip = g_strdup(args->proxyip); - else - conn->proxyip = NULL; - conn->verifiedip = g_strdup(args->verifiedip); - conn->clientip = g_strdup(args->clientip); - conn->port = args->port; - conn->use_proxy |= args->use_proxy; - conn->lastrequestnumber++; - peer_connection_trynext(conn); - return; - } - - /* If this is a direct IM, then close any existing session */ - if (args->type == OSCAR_CAPABILITY_DIRECTIM) - { - conn = peer_connection_find_by_type(od, bn, args->type); - if (conn != NULL) - { - /* Close the old direct IM and start a new one */ - purple_debug_info("oscar", "Received new direct IM request " - "from %s. Destroying old connection.\n", bn); - peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL); - } - } - - /* Check for proper arguments */ - if (args->type == OSCAR_CAPABILITY_SENDFILE) - { - if ((args->info.sendfile.filename == NULL) || - (args->info.sendfile.totsize == 0) || - (args->info.sendfile.totfiles == 0)) - { - purple_debug_warning("oscar", - "%s tried to send you a file with incomplete " - "information.\n", bn); - return; - } - } - - conn = peer_connection_new(od, args->type, bn); - memcpy(conn->cookie, args->cookie, 8); - if (args->use_proxy) - conn->proxyip = g_strdup(args->proxyip); - conn->clientip = g_strdup(args->clientip); - conn->verifiedip = g_strdup(args->verifiedip); - conn->port = args->port; - conn->use_proxy |= args->use_proxy; - conn->lastrequestnumber++; - - if (args->type == OSCAR_CAPABILITY_DIRECTIM) - { - buf = g_strdup_printf(_("%s has just asked to directly connect to %s"), - bn, purple_account_get_username(account)); - - purple_request_action(conn, NULL, buf, - _("This requires a direct connection between " - "the two computers and is necessary for IM " - "Images. Because your IP address will be " - "revealed, this may be considered a privacy " - "risk."), - PURPLE_DEFAULT_ACTION_NONE, - purple_request_cpar_from_account(account), - conn, 2, - _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb), - _("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb)); - } - else if (args->type == OSCAR_CAPABILITY_SENDFILE) - { - gchar *filename; - - conn->xfer = PURPLE_XFER(g_object_new( - OSCAR_TYPE_XFER, - "account", account, - "type", PURPLE_XFER_TYPE_RECEIVE, - "remote-user", bn, - "conn", conn, - NULL - )); - purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize); - - /* Set the file name */ - if (g_utf8_validate(args->info.sendfile.filename, -1, NULL)) - filename = g_strdup(args->info.sendfile.filename); - else - filename = purple_utf8_salvage(args->info.sendfile.filename); - - if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR) - { - /* - * If they are sending us a directory then the last character - * of the file name will be an asterisk. We don't want to - * save stuff to a directory named "*" so we remove the - * asterisk from the file name. - */ - char *tmp = strrchr(filename, '\\'); - if ((tmp != NULL) && (tmp[1] == '*')) - tmp[0] = '\0'; - } - purple_xfer_set_filename(conn->xfer, filename); - g_free(filename); - - /* - * Set the message, unless this is the dummy message from an - * ICQ client or an empty message from an AIM client. - * TODO: Maybe we should strip HTML and then see if strlen>0? - */ - if ((message != NULL) && - (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) && - (g_ascii_strcasecmp(message, "<HTML>") != 0)) - { - purple_xfer_set_message(conn->xfer, message); - } - - /* Now perform the request */ - purple_xfer_request(conn->xfer); - } -} - -/*******************************************************************/ -/* End code for establishing a peer connection */ -/*******************************************************************/ - -G_DEFINE_DYNAMIC_TYPE(OscarXfer, oscar_xfer, PURPLE_TYPE_XFER); - -static void -oscar_xfer_init_xfer(PurpleXfer *xfer) { - PurpleXferType type = purple_xfer_get_xfer_type(xfer); - - if(type == PURPLE_XFER_TYPE_SEND) { - peer_oft_sendcb_init(xfer); - } else if(type == PURPLE_XFER_TYPE_RECEIVE) { - peer_oft_recvcb_init(xfer); - } -} - -static void -oscar_xfer_ack(PurpleXfer *xfer, const guchar *buffer, size_t size) { - PurpleXferType type = purple_xfer_get_xfer_type(xfer); - - if(type == PURPLE_XFER_TYPE_SEND) { - peer_oft_sendcb_ack(xfer, buffer, size); - } else if(type == PURPLE_XFER_TYPE_RECEIVE) { - peer_oft_recvcb_ack_recv(xfer, buffer, size); - } - -} - -static void -oscar_xfer_init(OscarXfer *xfer) { - -} - -static void -oscar_xfer_class_finalize(OscarXferClass *klass) { - -} - -static void -oscar_xfer_class_init(OscarXferClass *klass) { - PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass); - - xfer_class->init = oscar_xfer_init_xfer; - xfer_class->end = peer_oft_recvcb_end; - xfer_class->cancel_send = peer_oft_cb_generic_cancel; - xfer_class->cancel_recv = peer_oft_cb_generic_cancel; - xfer_class->request_denied = peer_oft_cb_generic_cancel; - xfer_class->ack = oscar_xfer_ack; -} - -void -oscar_xfer_register(GTypeModule *module) { - oscar_xfer_register_type(module); -} - -PeerConnection * -oscar_xfer_get_peer_connection(OscarXfer *xfer) { - g_return_val_if_fail(OSCAR_IS_XFER(xfer), NULL); - - return xfer->conn; -}
--- a/libpurple/protocols/oscar/peer.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * OFT and ODC Services - */ - -#ifndef PURPLE_OSCAR_PEER_H -#define PURPLE_OSCAR_PEER_H - -#include "xfer.h" -#include "network.h" -#include "proxy.h" - -typedef struct _ChecksumData ChecksumData; -typedef struct _OdcFrame OdcFrame; -typedef struct _OftFrame OftFrame; -typedef struct _ProxyFrame ProxyFrame; -typedef struct _PeerConnection PeerConnection; - -#define PEER_CONNECTION_FLAG_INITIATED_BY_ME 0x0001 -#define PEER_CONNECTION_FLAG_APPROVED 0x0002 -#define PEER_CONNECTION_FLAG_TRIED_DIRECT 0x0004 -#define PEER_CONNECTION_FLAG_TRIED_INCOMING 0x0008 -#define PEER_CONNECTION_FLAG_TRIED_PROXY 0x0010 -#define PEER_CONNECTION_FLAG_IS_INCOMING 0x0020 - -#define PEER_TYPE_PROMPT 0x0101 /* "I am going to send you this file, is that ok?" */ -#define PEER_TYPE_RESUMEACCEPT 0x0106 /* We are accepting the resume */ -#define PEER_TYPE_ACK 0x0202 /* "Yes, it is ok for you to send me that file" */ -#define PEER_TYPE_DONE 0x0204 /* "I received that file with no problems" or "I already have that file, great!" */ -#define PEER_TYPE_RESUME 0x0205 /* Resume transferring, sent by whoever receives */ -#define PEER_TYPE_RESUMEACK 0x0207 /* Our resume accept was ACKed */ - -#define PEER_TYPE_GETFILE_REQUESTLISTING 0x1108 /* "I have a listing.txt file, do you want it?" */ -#define PEER_TYPE_GETFILE_RECEIVELISTING 0x1209 /* "Yes, please send me your listing.txt file" */ -#define PEER_TYPE_GETFILE_RECEIVEDLISTING 0x120a /* received corrupt listing.txt file? I'm just guessing about this one... */ -#define PEER_TYPE_GETFILE_ACKLISTING 0x120b /* "I received the listing.txt file successfully" */ -#define PEER_TYPE_GETFILE_REQUESTFILE 0x120c /* "Please send me this file" */ - -/* - * For peer proxying - */ -#define AIM_PEER_PROXY_SERVER "ars.oscar.aol.com" -#define ICQ_PEER_PROXY_SERVER "ars.icq.com" -#define PEER_PROXY_PORT 5190 /* The port we should always connect to */ -#define PEER_PROXY_PACKET_VERSION 0x044a - -/* Thanks to Keith Lea and the Joust project for documenting these */ -#define PEER_PROXY_TYPE_ERROR 0x0001 -#define PEER_PROXY_TYPE_CREATE 0x0002 -#define PEER_PROXY_TYPE_CREATED 0x0003 -#define PEER_PROXY_TYPE_JOIN 0x0004 -#define PEER_PROXY_TYPE_READY 0x0005 - -struct _OdcFrame -{ - /* guchar magic[4]; */ /* 0 */ - /* guint16 length; */ /* 4 */ - guint16 type; /* 6 */ - guint16 subtype; /* 8 */ - /* Unknown */ /* 10 */ - guchar cookie[8]; /* 12 */ - /* Unknown */ - /* guint32 payloadlength; */ /* 28 */ - guint16 encoding; /* 32 */ - /* Unknown */ - guint16 flags; /* 38 */ - /* Unknown */ - guchar bn[32]; /* 44 */ - /* Unknown */ - ByteStream payload; /* 76 */ -}; - -struct _OftFrame -{ - /* guchar magic[4]; */ /* 0 */ - /* guint16 length; */ /* 4 */ - guint16 type; /* 6 */ - guchar cookie[8]; /* 8 */ - guint16 encrypt; /* 16 */ - guint16 compress; /* 18 */ - guint16 totfiles; /* 20 */ - guint16 filesleft; /* 22 */ - guint16 totparts; /* 24 */ - guint16 partsleft; /* 26 */ - guint32 totsize; /* 28 */ - guint32 size; /* 32 */ - guint32 modtime; /* 36 */ - guint32 checksum; /* 40 */ - guint32 rfrcsum; /* 44 */ - guint32 rfsize; /* 48 */ - guint32 cretime; /* 52 */ - guint32 rfcsum; /* 56 */ - guint32 nrecvd; /* 60 */ - guint32 recvcsum; /* 64 */ - guchar idstring[32]; /* 68 */ - guint8 flags; /* 100 */ - guint8 lnameoffset; /* 101 */ - guint8 lsizeoffset; /* 102 */ - guchar dummy[69]; /* 103 */ - guchar macfileinfo[16]; /* 172 */ - guint16 nencode; /* 188 */ - guint16 nlanguage; /* 190 */ - guchar *name; /* 192 */ - size_t name_length; - /* Payload? */ /* 256 */ -}; - -struct _ProxyFrame -{ - /* guint16 length; */ /* 0 */ - guint16 version; /* 2 */ - guint16 type; /* 4 */ - guint32 unknown; /* 6 */ - guint16 flags; /* 10 */ - ByteStream payload; /* 12 */ -}; - -struct _PeerConnection -{ - OscarData *od; - guint64 type; - char *bn; - guchar magic[4]; - guchar cookie[8]; - guint16 lastrequestnumber; - - gboolean ready; - int flags; /**< Bitmask of PEER_CONNECTION_FLAG_ */ - time_t lastactivity; /**< Time of last transmit. */ - guint destroy_timeout; - OscarDisconnectReason disconnect_reason; - char *error_message; - - /** - * A pointer to either an OdcFrame or an OftFrame. - */ - gpointer frame; - - /** - * This is only used when the peer connection is being established. - */ - PurpleProxyConnectData *client_connect_data; - PurpleProxyConnectData *verified_connect_data; - - /** - * This is only used when the peer connection is being established. - */ - PurpleNetworkListenData *listen_data; - - - /** - * This is only used when the peer connection is being established. - */ - guint connect_timeout_timer; - - /** - * This is only used while the remote user is attempting to - * connect to us. - */ - int listenerfd; - - int fd; - guint8 header[6]; - gssize header_received; - guint8 proxy_header[12]; - gssize proxy_header_received; - ByteStream buffer_incoming; - PurpleCircularBuffer *buffer_outgoing; - guint watcher_incoming; - guint watcher_outgoing; - - /** - * IP address of the proxy server, if applicable. - */ - gchar *proxyip; - - /** - * IP address of the remote user from THEIR point of view. - */ - gchar *clientip; - - /** - * IP address of the remote user from the oscar server's - * point of view. - */ - gchar *verifiedip; - - guint16 port; - gboolean use_proxy; - - /** - * Checksumming - */ - ChecksumData *checksum_data; - - /* TODOFT */ - PurpleXfer *xfer; - OftFrame xferdata; - guint sending_data_timer; -}; - -/* - * For all peer connections - */ - -/** - * Create a new PeerConnection structure and initialize it with some - * sane defaults. - * - * @param type The type of the peer connection. One of - * OSCAR_CAPABILITY_DIRECTIM or OSCAR_CAPABILITY_SENDFILE. - */ -PeerConnection *peer_connection_new(OscarData *od, guint64 type, const char *bn); - -void peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message); -void peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message); -PeerConnection *peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type); -PeerConnection *peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie); - -void peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond); -void peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond); -void peer_connection_send(PeerConnection *conn, ByteStream *bs); - -void peer_connection_trynext(PeerConnection *conn); -void peer_connection_finalize_connection(PeerConnection *conn); -void peer_connection_propose(OscarData *od, guint64 type, const char *bn); -void peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args); - -/* - * For ODC - */ -void peer_odc_close(PeerConnection *conn); -void peer_odc_recv_frame(PeerConnection *conn, ByteStream *bs); -void peer_odc_send_cookie(PeerConnection *conn); -void peer_odc_send_typing(PeerConnection *conn, PurpleIMTypingState typing); -void peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply); - -/* - * For OFT - */ -void peer_oft_close(PeerConnection *conn); -void peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs); -void peer_oft_send_prompt(PeerConnection *conn); -void peer_oft_checksum_destroy(ChecksumData *checksum_data); - -/* Xfer callbacks for receiving a file */ -void peer_oft_recvcb_init(PurpleXfer *xfer); -void peer_oft_recvcb_end(PurpleXfer *xfer); -void peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size); - -/* Xfer callbacks for sending a file */ -void peer_oft_sendcb_init(PurpleXfer *xfer); -void peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size); - -/* Xfer callbacks for both sending and receiving */ -void peer_oft_cb_generic_cancel(PurpleXfer *xfer); - -/* - * For peer proxying - */ -void peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message); - -/* File transfers */ -#define OSCAR_TYPE_XFER (oscar_xfer_get_type()) -G_DECLARE_FINAL_TYPE(OscarXfer, oscar_xfer, OSCAR, XFER, PurpleXfer); - -struct _OscarXfer { - PurpleXfer parent; - - PeerConnection *conn; -}; - -void oscar_xfer_register(GTypeModule *module); -PeerConnection *oscar_xfer_get_peer_connection(OscarXfer *xfer); - -#endif /* PURPLE_OSCAR_PEER_H */
--- a/libpurple/protocols/oscar/peer_proxy.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,354 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "oscar.h" -#include "peer.h" - -static void -peer_proxy_send(PeerConnection *conn, ProxyFrame *frame) -{ - size_t length; - ByteStream bs; - - purple_debug_info("oscar", "Outgoing peer proxy frame with " - "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and " - "payload length=%" G_GSIZE_FORMAT "\n", - frame->type, frame->unknown, - frame->flags, frame->payload.len); - - length = 12 + frame->payload.len; - byte_stream_new(&bs, length); - byte_stream_put16(&bs, length - 2); - byte_stream_put16(&bs, PEER_PROXY_PACKET_VERSION); - byte_stream_put16(&bs, frame->type); - byte_stream_put32(&bs, frame->unknown); - byte_stream_put16(&bs, frame->flags); - byte_stream_putraw(&bs, frame->payload.data, frame->payload.len); - - peer_connection_send(conn, &bs); - - byte_stream_destroy(&bs); -} - -/** - * Create a rendezvous "init send" packet and send it on its merry way. - * This is the first packet sent to the proxy server by the first client - * to indicate that this will be a proxied connection - * - * @param conn The peer connection. - */ -static void -peer_proxy_send_create_new_conn(PeerConnection *conn) -{ - ProxyFrame frame; - PurpleAccount *account; - const gchar *bn; - guint8 bn_length; - - memset(&frame, 0, sizeof(ProxyFrame)); - frame.type = PEER_PROXY_TYPE_CREATE; - frame.flags = 0x0000; - - account = purple_connection_get_account(conn->od->gc); - bn = purple_account_get_username(account); - bn_length = strlen(bn); - byte_stream_new(&frame.payload, 1 + bn_length + 8 + 20); - byte_stream_put8(&frame.payload, bn_length); - byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length); - byte_stream_putraw(&frame.payload, conn->cookie, 8); - - byte_stream_put16(&frame.payload, 0x0001); /* Type */ - byte_stream_put16(&frame.payload, 16); /* Length */ - byte_stream_putcaps(&frame.payload, conn->type); /* Value */ - - peer_proxy_send(conn, &frame); - - byte_stream_destroy(&frame.payload); -} - -/** - * Create a rendezvous "init recv" packet and send it on its merry way. - * This is the first packet sent to the proxy server by the second client - * involved in this rendezvous proxy session. - * - * @param conn The peer connection. - * @param pin The 2 byte PIN sent to us by the other user. This acts - * as our passcode when establishing the proxy session. - */ -static void -peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin) -{ - ProxyFrame frame; - PurpleAccount *account; - const gchar *bn; - guint8 bn_length; - - memset(&frame, 0, sizeof(ProxyFrame)); - frame.type = PEER_PROXY_TYPE_JOIN; - frame.flags = 0x0000; - - account = purple_connection_get_account(conn->od->gc); - bn = purple_account_get_username(account); - bn_length = strlen(bn); - byte_stream_new(&frame.payload, 1 + bn_length + 2 + 8 + 20); - byte_stream_put8(&frame.payload, bn_length); - byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length); - byte_stream_put16(&frame.payload, pin); - byte_stream_putraw(&frame.payload, conn->cookie, 8); - - byte_stream_put16(&frame.payload, 0x0001); /* Type */ - byte_stream_put16(&frame.payload, 16); /* Length */ - byte_stream_putcaps(&frame.payload, conn->type); /* Value */ - - peer_proxy_send(conn, &frame); - - byte_stream_destroy(&frame.payload); -} - -/** - * Handle an incoming peer proxy negotiation frame. - */ -static void -peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame) -{ - purple_debug_info("oscar", "Incoming peer proxy frame with " - "type=0x%04hx, unknown=0x%08x, flags=0x%04hx, and " - "payload length=%" G_GSIZE_FORMAT "\n", frame->type, - frame->unknown, frame->flags, frame->payload.len); - - if (frame->type == PEER_PROXY_TYPE_CREATED) - { - /* - * Read in 2 byte port then 4 byte IP and tell the - * remote user to connect to it by sending an ICBM. - */ - guint16 pin; - int i; - guint8 ip[4]; - - pin = byte_stream_get16(&frame->payload); - for (i = 0; i < 4; i++) - ip[i] = byte_stream_get8(&frame->payload); - if (conn->type == OSCAR_CAPABILITY_DIRECTIM) - aim_im_sendch2_odc_requestproxy(conn->od, - conn->cookie, - conn->bn, ip, pin, ++conn->lastrequestnumber); - else if (conn->type == OSCAR_CAPABILITY_SENDFILE) - { - aim_im_sendch2_sendfile_requestproxy(conn->od, - conn->cookie, conn->bn, - ip, pin, ++conn->lastrequestnumber, - (const gchar *)conn->xferdata.name, - conn->xferdata.size, conn->xferdata.totfiles); - } - } - else if (frame->type == PEER_PROXY_TYPE_READY) - { - purple_input_remove(conn->watcher_incoming); - conn->watcher_incoming = 0; - - peer_connection_finalize_connection(conn); - } - else if (frame->type == PEER_PROXY_TYPE_ERROR) - { - if (byte_stream_bytes_left(&frame->payload) >= 2) - { - guint16 error; - const char *msg; - error = byte_stream_get16(&frame->payload); - if (error == 0x000d) - msg = "bad request"; - else if (error == 0x0010) - msg = "initial request timed out"; - else if (error == 0x001a) - msg ="accept period timed out"; - else - msg = "unknown reason"; - purple_debug_info("oscar", "Proxy negotiation failed with " - "error 0x%04hx: %s\n", error, msg); - } - else - { - purple_debug_warning("oscar", "Proxy negotiation failed with " - "an unknown error\n"); - } - peer_connection_trynext(conn); - } - else - { - purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n", - frame->type); - } -} - -static void -peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - PeerConnection *conn; - gssize read; - ProxyFrame *frame; - - conn = data; - frame = conn->frame; - - /* Start reading a new proxy frame */ - if (frame == NULL) - { - /* Read the first 12 bytes (frame length and header) */ - read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received, - 12 - conn->proxy_header_received, 0); - - /* Check if the proxy server closed the connection */ - if (read == 0) - { - purple_debug_info("oscar", "Peer proxy server closed connection\n"); - peer_connection_trynext(conn); - return; - } - - /* If there was an error then close the connection */ - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - return; - - purple_debug_info("oscar", "Lost connection with peer proxy server\n"); - peer_connection_trynext(conn); - return; - } - - conn->lastactivity = time(NULL); - - /* If we don't even have the first 12 bytes then do nothing */ - conn->proxy_header_received += read; - if (conn->proxy_header_received < 12) - return; - - /* We only support a specific version of the proxy protocol */ - if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION) - { - purple_debug_warning("oscar", "Expected peer proxy protocol " - "version %u but received version %u. Closing " - "connection.\n", PEER_PROXY_PACKET_VERSION, - aimutil_get16(&conn->proxy_header[2])); - peer_connection_trynext(conn); - return; - } - - /* Initialize a new temporary ProxyFrame for incoming data */ - frame = g_new0(ProxyFrame, 1); - frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10; - frame->version = aimutil_get16(&conn->proxy_header[2]); - frame->type = aimutil_get16(&conn->proxy_header[4]); - frame->unknown = aimutil_get16(&conn->proxy_header[6]); - frame->flags = aimutil_get16(&conn->proxy_header[10]); - if (frame->payload.len > 0) - frame->payload.data = g_new(guint8, frame->payload.len); - conn->frame = frame; - } - - /* If this frame has a payload then attempt to read it */ - if (frame->payload.offset < frame->payload.len) { - /* Read data into the temporary buffer until it is complete */ - read = recv(conn->fd, - &frame->payload.data[frame->payload.offset], - frame->payload.len - frame->payload.offset, - 0); - - /* Check if the proxy server closed the connection */ - if (read == 0) - { - purple_debug_info("oscar", "Peer proxy server closed connection\n"); - g_free(frame->payload.data); - g_free(frame); - conn->frame = NULL; - peer_connection_trynext(conn); - return; - } - - /* If there was an error then close the connection */ - if (read < 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - return; - - purple_debug_info("oscar", "Lost connection with peer proxy server\n"); - g_free(frame->payload.data); - g_free(frame); - conn->frame = NULL; - peer_connection_trynext(conn); - return; - } - - frame->payload.offset += read; - } - - conn->lastactivity = time(NULL); - if (frame->payload.offset < frame->payload.len) - /* Waiting for more data to arrive */ - return; - - /* We have a complete proxy frame! Handle it and continue reading */ - conn->frame = NULL; - byte_stream_rewind(&frame->payload); - peer_proxy_recv_frame(conn, frame); - - g_free(frame->payload.data); - g_free(frame); - - conn->proxy_header_received = 0; -} - -/** - * We tried to make an outgoing connection to a proxy server. It - * either connected or failed to connect. - */ -void -peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message) -{ - PeerConnection *conn; - - conn = data; - - conn->verified_connect_data = NULL; - - if (source < 0) - { - peer_connection_trynext(conn); - return; - } - - conn->fd = source; - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, peer_proxy_connection_recv_cb, conn); - - if (conn->proxyip != NULL) - /* Connect to the session created by the remote user */ - peer_proxy_send_join_existing_conn(conn, conn->port); - else - /* Create a new session */ - peer_proxy_send_create_new_conn(conn); -}
--- a/libpurple/protocols/oscar/rxhandlers.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#include "oscar.h" -#include "peer.h" - -aim_module_t *aim__findmodulebygroup(OscarData *od, guint16 group) -{ - aim_module_t *cur; - - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) { - if (cur->family == group) - return cur; - } - - return NULL; -} - -aim_module_t *aim__findmodule(OscarData *od, const char *name) -{ - aim_module_t *cur; - - for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) { - if (purple_strequal(name, cur->name)) - return cur; - } - - return NULL; -} - -int aim__registermodule(OscarData *od, int (*modfirst)(OscarData *, aim_module_t *)) -{ - aim_module_t *mod; - - if (!od || !modfirst) - return -1; - - mod = g_new0(aim_module_t, 1); - - if (modfirst(od, mod) == -1) { - g_free(mod); - return -1; - } - - if (aim__findmodule(od, mod->name)) { - if (mod->shutdown) - mod->shutdown(od, mod); - g_free(mod); - return -1; - } - - mod->next = (aim_module_t *)od->modlistv; - od->modlistv = mod; - - return 0; -} - -void aim__shutdownmodules(OscarData *od) -{ - aim_module_t *cur; - - for (cur = (aim_module_t *)od->modlistv; cur; ) { - aim_module_t *tmp; - - tmp = cur->next; - - if (cur->shutdown) - cur->shutdown(od, cur); - - g_free(cur); - - cur = tmp; - } - - od->modlistv = NULL; - - return; -}
--- a/libpurple/protocols/oscar/snac.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * - * Various SNAC-related dodads... - * - * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added - * whenever a new SNAC is sent and it should remain in the list until the - * response for it has been received. - * - * cleansnacs() should be called periodically by the client in order - * to facilitate the aging out of unreplied-to SNACs. This can and does - * happen, so it should be handled. - * - */ - -#include "oscar.h" - -/* - * Called from oscar_session_new() to initialize the hash. - */ -void aim_initsnachash(OscarData *od) -{ - int i; - - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) - od->snac_hash[i] = NULL; - - return; -} - -aim_snacid_t aim_cachesnac(OscarData *od, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen) -{ - aim_snac_t snac; - - snac.id = od->snacid_next++; - snac.family = family; - snac.type = type; - snac.flags = flags; - - if (datalen) - snac.data = g_memdup(data, datalen); - else - snac.data = NULL; - - return aim_newsnac(od, &snac); -} - -/* - * Clones the passed snac structure and caches it in the - * list/hash. - */ -aim_snacid_t aim_newsnac(OscarData *od, aim_snac_t *newsnac) -{ - aim_snac_t *snac; - int index; - - if (!newsnac) - return 0; - - snac = g_memdup(newsnac, sizeof(aim_snac_t)); - snac->issuetime = time(NULL); - - index = snac->id % FAIM_SNAC_HASH_SIZE; - - snac->next = (aim_snac_t *)od->snac_hash[index]; - od->snac_hash[index] = (void *)snac; - - return snac->id; -} - -/* - * Finds a snac structure with the passed SNAC ID, - * removes it from the list/hash, and returns a pointer to it. - * - * The returned structure must be freed by the caller. - * - */ -aim_snac_t *aim_remsnac(OscarData *od, aim_snacid_t id) -{ - aim_snac_t *cur, **prev; - int index; - - index = id % FAIM_SNAC_HASH_SIZE; - - for (prev = (aim_snac_t **)&od->snac_hash[index]; (cur = *prev); ) { - if (cur->id == id) { - *prev = cur->next; - if (cur->flags & AIM_SNACFLAGS_DESTRUCTOR) { - g_free(cur->data); - cur->data = NULL; - } - return cur; - } else - prev = &cur->next; - } - - return cur; -} - -/* - * This is for cleaning up old SNACs that either don't get replies or - * a reply was never received for. Garbage collection. Plain and simple. - * - * maxage is the _minimum_ age in seconds to keep SNACs. - * - */ -void aim_cleansnacs(OscarData *od, int maxage) -{ - int i; - - for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { - aim_snac_t *cur, **prev; - time_t curtime; - - if (!od->snac_hash[i]) - continue; - - curtime = time(NULL); /* done here in case we waited for the lock */ - - for (prev = (aim_snac_t **)&od->snac_hash[i]; (cur = *prev); ) { - if ((curtime - cur->issuetime) > maxage) { - - *prev = cur->next; - - g_free(cur->data); - g_free(cur); - } else - prev = &cur->next; - } - } - - return; -} - -int aim_putsnac(ByteStream *bs, guint16 family, guint16 subtype, aim_snacid_t snacid) -{ - - byte_stream_put16(bs, family); - byte_stream_put16(bs, subtype); - byte_stream_put16(bs, 0x0000); - byte_stream_put32(bs, snacid); - - return 10; -}
--- a/libpurple/protocols/oscar/snactypes.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,288 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * AIM Callback Types - * - */ - -#ifndef PURPLE_OSCAR_SNACTYPES_H -#define PURPLE_OSCAR_SNACTYPES_H - -/* - * SNAC Families. - */ -#define SNAC_FAMILY_OSERVICE 0x0001 -#define SNAC_FAMILY_LOCATE 0x0002 -#define SNAC_FAMILY_BUDDY 0x0003 -#define SNAC_FAMILY_ICBM 0x0004 -#define SNAC_FAMILY_ADVERT 0x0005 -#define SNAC_FAMILY_INVITE 0x0006 -#define SNAC_FAMILY_ADMIN 0x0007 -#define SNAC_FAMILY_POPUP 0x0008 -#define SNAC_FAMILY_BOS 0x0009 -#define SNAC_FAMILY_USERLOOKUP 0x000a -#define SNAC_FAMILY_STATS 0x000b -#define SNAC_FAMILY_TRANSLATE 0x000c -#define SNAC_FAMILY_CHATNAV 0x000d -#define SNAC_FAMILY_CHAT 0x000e -#define SNAC_FAMILY_ODIR 0x000f -#define SNAC_FAMILY_BART 0x0010 -#define SNAC_FAMILY_FEEDBAG 0x0013 -#define SNAC_FAMILY_ICQ 0x0015 -#define SNAC_FAMILY_AUTH 0x0017 -#define SNAC_FAMILY_ALERT 0x0018 - -#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */ - -/* - * SNAC Family: Ack. - * - * Not really a family, but treating it as one really - * helps it fit into the libfaim callback structure better. - * - */ -#define AIM_CB_ACK_ACK 0x0001 - -/* - * SNAC Family: General. - */ -#define SNAC_SUBTYPE_OSERVICE_ERROR 0x0001 -#define SNAC_SUBTYPE_OSERVICE_CLIENTREADY 0x0002 -#define SNAC_SUBTYPE_OSERVICE_SERVERREADY 0x0003 -#define SNAC_SUBTYPE_OSERVICE_SERVICEREQ 0x0004 -#define SNAC_SUBTYPE_OSERVICE_REDIRECT 0x0005 -#define SNAC_SUBTYPE_OSERVICE_RATEINFOREQ 0x0006 -#define SNAC_SUBTYPE_OSERVICE_RATEINFO 0x0007 -#define SNAC_SUBTYPE_OSERVICE_RATEINFOACK 0x0008 -#define SNAC_SUBTYPE_OSERVICE_RATECHANGE 0x000a -#define SNAC_SUBTYPE_OSERVICE_SERVERPAUSE 0x000b -#define SNAC_SUBTYPE_OSERVICE_SERVERRESUME 0x000d -#define SNAC_SUBTYPE_OSERVICE_REQSELFINFO 0x000e -#define SNAC_SUBTYPE_OSERVICE_SELFINFO 0x000f -#define SNAC_SUBTYPE_OSERVICE_EVIL 0x0010 -#define SNAC_SUBTYPE_OSERVICE_SETIDLE 0x0011 -#define SNAC_SUBTYPE_OSERVICE_MIGRATIONREQ 0x0012 -#define SNAC_SUBTYPE_OSERVICE_MOTD 0x0013 -#define SNAC_SUBTYPE_OSERVICE_SETPRIVFLAGS 0x0014 -#define SNAC_SUBTYPE_OSERVICE_WELLKNOWNURL 0x0015 -#define SNAC_SUBTYPE_OSERVICE_NOP 0x0016 -#define SNAC_SUBTYPE_OSERVICE_DEFAULT 0xffff - -/* - * SNAC Family: Location Services. - */ -#define SNAC_SUBTYPE_LOCATE_ERROR 0x0001 -#define SNAC_SUBTYPE_LOCATE_REQRIGHTS 0x0002 -#define SNAC_SUBTYPE_LOCATE_RIGHTSINFO 0x0003 -#define SNAC_SUBTYPE_LOCATE_SETUSERINFO 0x0004 -#define SNAC_SUBTYPE_LOCATE_REQUSERINFO 0x0005 -#define SNAC_SUBTYPE_LOCATE_USERINFO 0x0006 -#define SNAC_SUBTYPE_LOCATE_WATCHERSUBREQ 0x0007 -#define SNAC_SUBTYPE_LOCATE_WATCHERNOT 0x0008 -#define SNAC_SUBTYPE_LOCATE_DEFAULT 0xffff - -/* - * SNAC Family: Buddy List Management Services. - */ -#define SNAC_SUBTYPE_BUDDY_ERROR 0x0001 -#define SNAC_SUBTYPE_BUDDY_REQRIGHTS 0x0002 -#define SNAC_SUBTYPE_BUDDY_RIGHTSINFO 0x0003 -#define SNAC_SUBTYPE_BUDDY_ADDBUDDY 0x0004 -#define SNAC_SUBTYPE_BUDDY_REMBUDDY 0x0005 -#define SNAC_SUBTYPE_BUDDY_REJECT 0x000a -#define SNAC_SUBTYPE_BUDDY_ONCOMING 0x000b -#define SNAC_SUBTYPE_BUDDY_OFFGOING 0x000c -#define SNAC_SUBTYPE_BUDDY_DEFAULT 0xffff - -/* - * SNAC Family: Messaging Services. - */ -#define SNAC_SUBTYPE_ICBM_ERROR 0x0001 -#define SNAC_SUBTYPE_ICBM_PARAMINFO 0x0005 -#define SNAC_SUBTYPE_ICBM_INCOMING 0x0007 -#define SNAC_SUBTYPE_ICBM_EVIL 0x0009 -#define SNAC_SUBTYPE_ICBM_MISSEDCALL 0x000a -#define SNAC_SUBTYPE_ICBM_CLIENTAUTORESP 0x000b -#define SNAC_SUBTYPE_ICBM_ACK 0x000c -#define SNAC_SUBTYPE_ICBM_MTN 0x0014 -#define SNAC_SUBTYPE_ICBM_DEFAULT 0xffff - -/* - * SNAC Family: Advertisement Services - */ -#define SNAC_SUBTYPE_ADVERT_ERROR 0x0001 -#define SNAC_SUBTYPE_ADVERT_DEFAULT 0xffff - -/* - * SNAC Family: Invitation Services. - */ -#define SNAC_SUBTYPE_INVITE_ERROR 0x0001 -#define SNAC_SUBTYPE_INVITE_DEFAULT 0xffff - -/* - * SNAC Family: Administrative Services. - */ -#define SNAC_SUBTYPE_ADMIN_ERROR 0x0001 -#define SNAC_SUBTYPE_ADMIN_INFOCHANGE_REPLY 0x0005 -#define SNAC_SUBTYPE_ADMIN_DEFAULT 0xffff - -/* - * SNAC Family: Popup Messages - */ -#define SNAC_SUBTYPE_POPUP_ERROR 0x0001 -#define SNAC_SUBTYPE_POPUP_DEFAULT 0xffff - -/* - * SNAC Family: Misc BOS Services. - */ -#define SNAC_SUBTYPE_BOS_ERROR 0x0001 -#define SNAC_SUBTYPE_BOS_RIGHTSQUERY 0x0002 -#define SNAC_SUBTYPE_BOS_RIGHTS 0x0003 -#define SNAC_SUBTYPE_BOS_DEFAULT 0xffff - -/* - * SNAC Family: User Lookup Services - */ -#define SNAC_SUBTYPE_USERLOOKUP_ERROR 0x0001 -#define SNAC_SUBTYPE_USERLOOKUP_DEFAULT 0xffff - -/* - * SNAC Family: User Status Services - */ -#define SNAC_SUBTYPE_STATS_ERROR 0x0001 -#define SNAC_SUBTYPE_STATS_SETREPORTINTERVAL 0x0002 -#define SNAC_SUBTYPE_STATS_REPORTACK 0x0004 -#define SNAC_SUBTYPE_STATS_DEFAULT 0xffff - -/* - * SNAC Family: Translation Services - */ -#define SNAC_SUBTYPE_TRANSLATE_ERROR 0x0001 -#define SNAC_SUBTYPE_TRANSLATE_DEFAULT 0xffff - -/* - * SNAC Family: Chat Navigation Services - */ -#define SNAC_SUBTYPE_CHATNAV_ERROR 0x0001 -#define SNAC_SUBTYPE_CHATNAV_CREATE 0x0008 -#define SNAC_SUBTYPE_CHATNAV_INFO 0x0009 -#define SNAC_SUBTYPE_CHATNAV_DEFAULT 0xffff - -/* - * SNAC Family: Chat Services - */ -#define SNAC_SUBTYPE_CHAT_ERROR 0x0001 -#define SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE 0x0002 -#define SNAC_SUBTYPE_CHAT_USERJOIN 0x0003 -#define SNAC_SUBTYPE_CHAT_USERLEAVE 0x0004 -#define SNAC_SUBTYPE_CHAT_OUTGOINGMSG 0x0005 -#define SNAC_SUBTYPE_CHAT_INCOMINGMSG 0x0006 -#define SNAC_SUBTYPE_CHAT_DEFAULT 0xffff - -/* - * SNAC Family: "New" Search - */ -#define SNAC_SUBTYPE_ODIR_ERROR 0x0001 -#define SNAC_SUBTYPE_ODIR_SEARCH 0x0002 -#define SNAC_SUBTYPE_ODIR_RESULTS 0x0003 - -/* - * SNAC Family: Buddy icons - */ -#define SNAC_SUBTYPE_BART_ERROR 0x0001 -#define SNAC_SUBTYPE_BART_REQUEST 0x0004 -#define SNAC_SUBTYPE_BART_RESPONSE 0x0005 - -/* - * SNAC Family: Server-Stored Buddy Lists - */ -#define SNAC_SUBTYPE_FEEDBAG_ERROR 0x0001 -#define SNAC_SUBTYPE_FEEDBAG_REQRIGHTS 0x0002 -#define SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO 0x0003 -#define SNAC_SUBTYPE_FEEDBAG_REQDATA 0x0004 -#define SNAC_SUBTYPE_FEEDBAG_REQIFCHANGED 0x0005 -#define SNAC_SUBTYPE_FEEDBAG_LIST 0x0006 -#define SNAC_SUBTYPE_FEEDBAG_ACTIVATE 0x0007 -#define SNAC_SUBTYPE_FEEDBAG_ADD 0x0008 -#define SNAC_SUBTYPE_FEEDBAG_MOD 0x0009 -#define SNAC_SUBTYPE_FEEDBAG_DEL 0x000A -#define SNAC_SUBTYPE_FEEDBAG_SRVACK 0x000E -#define SNAC_SUBTYPE_FEEDBAG_NOLIST 0x000F -#define SNAC_SUBTYPE_FEEDBAG_EDITSTART 0x0011 -#define SNAC_SUBTYPE_FEEDBAG_EDITSTOP 0x0012 -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTH 0x0014 -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTH 0x0015 -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ 0x0018 -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ 0x0019 -#define SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP 0x001a -#define SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP 0x001b -#define SNAC_SUBTYPE_FEEDBAG_ADDED 0x001c - -/* - * SNAC Family: ICQ - * - * Most of these are actually special. - */ -#define SNAC_SUBTYPE_ICQ_ERROR 0x0001 -#define SNAC_SUBTYPE_ICQ_OFFLINEMSG 0x00f0 -#define SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE 0x00f1 -#define SNAC_SUBTYPE_ICQ_INFO 0x00f2 -#define SNAC_SUBTYPE_ICQ_ALIAS 0x00f3 -#define SNAC_SUBTYPE_ICQ_DEFAULT 0xffff - -/* - * SNAC Family: Authorizer - * - * Used only in protocol versions three and above. - */ -#define SNAC_SUBTYPE_AUTH_ERROR 0x0001 -#define SNAC_SUBTYPE_AUTH_LOGINREQEST 0x0002 -#define SNAC_SUBTYPE_AUTH_LOGINRESPONSE 0x0003 -#define SNAC_SUBTYPE_AUTH_AUTHREQ 0x0006 -#define SNAC_SUBTYPE_AUTH_AUTHRESPONSE 0x0007 -#define SNAC_SUBTYPE_AUTH_SECURID_REQUEST 0x000a -#define SNAC_SUBTYPE_AUTH_SECURID_RESPONSE 0x000b - -/* - * SNAC Family: Email - * - * Used for getting information on the email address - * associated with your username. - */ -#define SNAC_SUBTYPE_ALERT_ERROR 0x0001 -#define SNAC_SUBTYPE_ALERT_SENDCOOKIES 0x0006 -#define SNAC_SUBTYPE_ALERT_MAILSTATUS 0x0007 -#define SNAC_SUBTYPE_ALERT_INIT 0x0016 - -/* - * SNAC Family: Internal Messages - * - * This isn't truly a SNAC family either, but using - * these, we can integrated non-SNAC services into - * the SNAC-centered libfaim callback structure. - */ -#define AIM_CB_SPECIAL_CONNERR 0x0003 -#define AIM_CB_SPECIAL_CONNINITDONE 0x0006 - -/* SNAC flags */ -#define AIM_SNACFLAGS_DESTRUCTOR 0x0001 - -#endif /* PURPLE_OSCAR_SNACTYPES_H */
--- a/libpurple/protocols/oscar/tests/meson.build Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -foreach prog : ['util'] - e = executable( - 'test_oscar_' + prog, 'test_oscar_@0@.c'.format(prog), - link_with : [oscar_prpl], - dependencies : [libpurple_dep, libsoup, glib]) - - test('oscar_' + prog, e) -endforeach
--- a/libpurple/protocols/oscar/tests/test_oscar_util.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -#include <glib.h> - -#include "../oscar.h" - -static void -test_oscar_util_name_compare(void) { - gsize i; - const gchar *good[] = { - "test", - "TEST", - "Test", - "teSt", - " TesT", - "test ", - " T E s T " - }; - const gchar *bad[] = { - "toast", - "test@example.com", - "test@aim.com" - }; - - for(i = 0; i < G_N_ELEMENTS(good); i++) { - g_assert_cmpint(0, ==, oscar_util_name_compare("test", good[i])); - g_assert_cmpint(0, ==, oscar_util_name_compare(good[i], "test")); - } - - for(i = 0; i < G_N_ELEMENTS(bad); i++) { - g_assert_cmpint(0, !=, oscar_util_name_compare("test", bad[i])); - g_assert_cmpint(0, !=, oscar_util_name_compare(bad[i], "test")); - } -} - -gint -main(gint argc, gchar **argv) { - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/oscar/util/name compare", - test_oscar_util_name_compare); - - return g_test_run(); -}
--- a/libpurple/protocols/oscar/tlv.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,815 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#include "oscar.h" - -static aim_tlv_t * -createtlv(guint16 type, guint16 length, guint8 *value) -{ - aim_tlv_t *ret; - - ret = g_new(aim_tlv_t, 1); - ret->type = type; - ret->length = length; - ret->value = value; - - return ret; -} - -static void -freetlv(aim_tlv_t *oldtlv) -{ - g_free(oldtlv->value); - g_free(oldtlv); -} - -static GSList * -aim_tlv_read(GSList *list, ByteStream *bs) -{ - guint16 type, length; - aim_tlv_t *tlv; - - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - - if (length > byte_stream_bytes_left(bs)) { - aim_tlvlist_free(list); - return NULL; - } - - tlv = createtlv(type, length, NULL); - if (tlv->length > 0) { - tlv->value = byte_stream_getraw(bs, length); - if (!tlv->value) { - freetlv(tlv); - aim_tlvlist_free(list); - return NULL; - } - } - - return g_slist_prepend(list, tlv); -} - -/** - * Read a TLV chain from a buffer. - * - * Reads and parses a series of TLV patterns from a data buffer; the - * returned structure is manipulatable with the rest of the TLV - * routines. When done with a TLV chain, aim_tlvlist_free() should - * be called to free the dynamic substructures. - * - * TODO: There should be a flag setable here to have the tlvlist contain - * bstream references, so that at least the ->value portion of each - * element doesn't need to be malloc/memcpy'd. This could prove to be - * just as efficient as the in-place TLV parsing used in a couple places - * in libfaim. - * - * @param bs Input bstream - * @return Return the TLV chain read - */ -GSList *aim_tlvlist_read(ByteStream *bs) -{ - GSList *list = NULL; - - while (byte_stream_bytes_left(bs) > 0) { - list = aim_tlv_read(list, bs); - if (list == NULL) - return NULL; - } - - return g_slist_reverse(list); -} - -/** - * Read a TLV chain from a buffer. - * - * Reads and parses a series of TLV patterns from a data buffer; the - * returned structure is manipulatable with the rest of the TLV - * routines. When done with a TLV chain, aim_tlvlist_free() should - * be called to free the dynamic substructures. - * - * TODO: There should be a flag setable here to have the tlvlist contain - * bstream references, so that at least the ->value portion of each - * element doesn't need to be malloc/memcpy'd. This could prove to be - * just as efficient as the in-place TLV parsing used in a couple places - * in libfaim. - * - * @param bs Input bstream - * @param num The max number of TLVs that will be read, or -1 if unlimited. - * There are a number of places where you want to read in a tlvchain, - * but the chain is not at the end of the SNAC, and the chain is - * preceded by the number of TLVs. So you can limit that with this. - * @return Return the TLV chain read - */ -GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num) -{ - GSList *list = NULL; - - while ((byte_stream_bytes_left(bs) > 0) && (num != 0)) { - list = aim_tlv_read(list, bs); - if (list == NULL) - return NULL; - num--; - } - - return g_slist_reverse(list); -} - -/** - * Read a TLV chain from a buffer. - * - * Reads and parses a series of TLV patterns from a data buffer; the - * returned structure is manipulatable with the rest of the TLV - * routines. When done with a TLV chain, aim_tlvlist_free() should - * be called to free the dynamic substructures. - * - * TODO: There should be a flag setable here to have the tlvlist contain - * bstream references, so that at least the ->value portion of each - * element doesn't need to be malloc/memcpy'd. This could prove to be - * just as efficient as the in-place TLV parsing used in a couple places - * in libfaim. - * - * @param bs Input bstream - * @param len The max length in bytes that will be read. - * There are a number of places where you want to read in a tlvchain, - * but the chain is not at the end of the SNAC, and the chain is - * preceded by the length of the TLVs. So you can limit that with this. - * @return Return the TLV chain read - */ -GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len) -{ - GSList *list = NULL; - - while ((byte_stream_bytes_left(bs) > 0) && (len > 0)) { - list = aim_tlv_read(list, bs); - if (list == NULL) - return NULL; - - len -= 2 + 2 + ((aim_tlv_t *)list->data)->length; - } - - return g_slist_reverse(list); -} - -/** - * Duplicate a TLV chain. - * This is pretty self explanatory. - * - * @param orig The TLV chain you want to make a copy of. - * @return A newly allocated TLV chain. - */ -GSList *aim_tlvlist_copy(GSList *orig) -{ - GSList *new = NULL; - aim_tlv_t *tlv; - - while (orig != NULL) { - tlv = orig->data; - aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value); - orig = orig->next; - } - - return new; -} - -/* - * Compare two TLV lists for equality. This probably is not the most - * efficient way to do this. - * - * @param one One of the TLV chains to compare. - * @param two The other TLV chain to compare. - * @return Return 0 if the lists are the same, return 1 if they are different. - */ -int aim_tlvlist_cmp(GSList *one, GSList *two) -{ - ByteStream bs1, bs2; - - if (aim_tlvlist_size(one) != aim_tlvlist_size(two)) - return 1; - - byte_stream_new(&bs1, aim_tlvlist_size(one)); - byte_stream_new(&bs2, aim_tlvlist_size(two)); - - aim_tlvlist_write(&bs1, &one); - aim_tlvlist_write(&bs2, &two); - - if (memcmp(bs1.data, bs2.data, bs1.len)) { - byte_stream_destroy(&bs1); - byte_stream_destroy(&bs2); - return 1; - } - - byte_stream_destroy(&bs1); - byte_stream_destroy(&bs2); - - return 0; -} - -/** - * Free a TLV chain structure - * - * Walks the list of TLVs in the passed TLV chain and - * frees each one. Note that any references to this data - * should be removed before calling this. - * - * @param list Chain to be freed - */ -void aim_tlvlist_free(GSList *list) -{ - g_slist_free_full(list, (GDestroyNotify)freetlv); -} - -/** - * Count the number of TLVs in a chain. - * - * @param list Chain to be counted. - * @return The number of TLVs stored in the passed chain. - */ -int aim_tlvlist_count(GSList *list) -{ - return (int)g_slist_length(list); -} - -/** - * Count the number of bytes in a TLV chain. - * - * @param list Chain to be sized - * @return The number of bytes that would be needed to - * write the passed TLV chain to a data buffer. - */ -size_t aim_tlvlist_size(GSList *list) -{ - GSList *cur; - size_t size; - - if (list == NULL) - return 0; - - for (cur = list, size = 0; cur; cur = cur->next) - size += (4 + ((aim_tlv_t *)cur->data)->length); - - return size; -} - -/** - * Adds the passed string as a TLV element of the passed type - * to the TLV chain. - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - * @param length Length of string to add (not including %NULL). - * @param value String to add. - * @return The size of the value added. - */ -int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value) -{ - aim_tlv_t *tlv; - - if (list == NULL) - return 0; - - tlv = createtlv(type, length, NULL); - if (tlv->length > 0) - tlv->value = g_memdup(value, length); - - *list = g_slist_append(*list, tlv); - - return tlv->length; -} - -/** - * Add a one byte integer to a TLV chain. - * - * @param list Destination chain. - * @param type TLV type to add. - * @param value Value to add. - * @return The size of the value added. - */ -int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value) -{ - guint8 v8[1]; - - (void)aimutil_put8(v8, value); - - return aim_tlvlist_add_raw(list, type, 1, v8); -} - -/** - * Add a two byte integer to a TLV chain. - * - * @param list Destination chain. - * @param type TLV type to add. - * @param value Value to add. - * @return The size of the value added. - */ -int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value) -{ - guint8 v16[2]; - - (void)aimutil_put16(v16, value); - - return aim_tlvlist_add_raw(list, type, 2, v16); -} - -/** - * Add a four byte integer to a TLV chain. - * - * @param list Destination chain. - * @param type TLV type to add. - * @param value Value to add. - * @return The size of the value added. - */ -int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value) -{ - guint8 v32[4]; - - (void)aimutil_put32(v32, value); - - return aim_tlvlist_add_raw(list, type, 4, v32); -} - -/** - * Add a string to a TLV chain. - * - * @param list Destination chain. - * @param type TLV type to add. - * @param value Value to add. - * @return The size of the value added. - */ -int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value) -{ - return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value); -} - -static int -count_caps(guint64 caps) -{ - int set_bits = 0; - while (caps) { - set_bits += caps & 1; - caps >>= 1; - } - return set_bits; -} - -/** - * Adds a block of capability blocks to a TLV chain. The bitfield - * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: - * - * %OSCAR_CAPABILITY_BUDDYICON Supports Buddy Icons - * %OSCAR_CAPABILITY_TALK Supports Voice Chat - * %OSCAR_CAPABILITY_IMIMAGE Supports DirectIM/IMImage - * %OSCAR_CAPABILITY_CHAT Supports Chat - * %OSCAR_CAPABILITY_GETFILE Supports Get File functions - * %OSCAR_CAPABILITY_SENDFILE Supports Send File functions - * - * @param list Destination chain - * @param type TLV type to add - * @param caps Bitfield of capability flags to send - * @return The size of the value added. - */ -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood) -{ - int len; - ByteStream bs; - guint32 bs_size; - guint8 *data; - - if (caps == 0) - return 0; /* nothing there anyway */ - - data = icq_get_custom_icon_data(mood); - bs_size = 16*(count_caps(caps) + (data != NULL ? 1 : 0)); - - byte_stream_new(&bs, bs_size); - byte_stream_putcaps(&bs, caps); - - /* adding of custom icon GUID */ - if (data != NULL) - byte_stream_putraw(&bs, data, 16); - - len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data); - - byte_stream_destroy(&bs); - - return len; -} - -/** - * Adds the given chatroom info to a TLV chain. - * - * @param list Destination chain. - * @param type TLV type to add. - * @param roomname The name of the chat. - * @param instance The instance. - * @return The size of the value added. - */ -int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) -{ - int len; - ByteStream bs; - - byte_stream_new(&bs, 2 + 1 + strlen(roomname) + 2); - - byte_stream_put16(&bs, exchange); - byte_stream_put8(&bs, strlen(roomname)); - byte_stream_putstr(&bs, roomname); - byte_stream_put16(&bs, instance); - - len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data); - - byte_stream_destroy(&bs); - - return len; -} - -/** - * Adds a TLV with a zero length to a TLV chain. - * - * @param list Destination chain. - * @param type TLV type to add. - * @return The size of the value added. - */ -int aim_tlvlist_add_noval(GSList **list, const guint16 type) -{ - return aim_tlvlist_add_raw(list, type, 0, NULL); -} - -/* - * Note that the inner TLV chain will not be modifiable as a tlvchain once - * it is written using this. Or rather, it can be, but updates won't be - * made to this. - * - * TODO: Should probably support sublists for real. - * - * This is so neat. - * - * @param list Destination chain. - * @param type TLV type to add. - * @param t1 The TLV chain you want to write. - * @return The number of bytes written to the destination TLV chain. - * 0 is returned if there was an error or if the destination - * TLV chain has length 0. - */ -int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tlvlist) -{ - int buflen; - ByteStream bs; - - buflen = aim_tlvlist_size(*tlvlist); - - if (buflen <= 0) - return 0; - - byte_stream_new(&bs, buflen); - - aim_tlvlist_write(&bs, tlvlist); - - aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data); - - byte_stream_destroy(&bs); - - return buflen; -} - -/** - * Substitute a TLV of a given type with a new TLV of the same type. If - * you attempt to replace a TLV that does not exist, this function will - * just add a new TLV as if you called aim_tlvlist_add_raw(). - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - * @param length Length of string to add (not including %NULL). - * @param value String to add. - * @return The length of the TLV. - */ -int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value) -{ - GSList *cur; - aim_tlv_t *tlv; - - if (list == NULL) - return 0; - - for (cur = *list; cur != NULL; cur = cur->next) - { - tlv = cur->data; - if (tlv->type == type) - break; - } - - if (cur == NULL) - /* TLV does not exist, so add a new one */ - return aim_tlvlist_add_raw(list, type, length, value); - - g_free(tlv->value); - tlv->length = length; - if (tlv->length > 0) { - tlv->value = g_memdup(value, length); - } else - tlv->value = NULL; - - return tlv->length; -} - -/** - * Substitute a TLV of a given type with a new TLV of the same type. If - * you attempt to replace a TLV that does not exist, this function will - * just add a new TLV as if you called aim_tlvlist_add_str(). - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - * @param str String to add. - * @return The length of the TLV. - */ -int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str) -{ - return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str); -} - -/** - * Substitute a TLV of a given type with a new TLV of the same type. If - * you attempt to replace a TLV that does not exist, this function will - * just add a new TLV as if you called aim_tlvlist_add_raw(). - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - * @return The length of the TLV. - */ -int aim_tlvlist_replace_noval(GSList **list, const guint16 type) -{ - return aim_tlvlist_replace_raw(list, type, 0, NULL); -} - -/** - * Substitute a TLV of a given type with a new TLV of the same type. If - * you attempt to replace a TLV that does not exist, this function will - * just add a new TLV as if you called aim_tlvlist_add_raw(). - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - * @param value 8 bit value to add. - * @return The length of the TLV. - */ -int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value) -{ - guint8 v8[1]; - - (void)aimutil_put8(v8, value); - - return aim_tlvlist_replace_raw(list, type, 1, v8); -} - -/** - * Substitute a TLV of a given type with a new TLV of the same type. If - * you attempt to replace a TLV that does not exist, this function will - * just add a new TLV as if you called aim_tlvlist_add_raw(). - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - * @param value 32 bit value to add. - * @return The length of the TLV. - */ -int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value) -{ - guint8 v32[4]; - - (void)aimutil_put32(v32, value); - - return aim_tlvlist_replace_raw(list, type, 4, v32); -} - -/** - * Remove all TLVs of a given type. If you attempt to remove a TLV - * that does not exist, nothing happens. - * - * @param list Desination chain (%NULL pointer if empty). - * @param type TLV type. - */ -void aim_tlvlist_remove(GSList **list, const guint16 type) -{ - GSList *cur, *next; - aim_tlv_t *tlv; - - if (list == NULL || *list == NULL) - return; - - cur = *list; - while (cur != NULL) - { - tlv = cur->data; - next = cur->next; - - if (tlv->type == type) - { - /* Delete this TLV */ - *list = g_slist_delete_link(*list, cur); - g_free(tlv->value); - g_free(tlv); - } - - cur = next; - } -} - -/** - * Write a TLV chain into a data buffer. - * - * Copies a TLV chain into a raw data buffer, writing only the number - * of bytes specified. This operation does not free the chain; - * aim_tlvlist_free() must still be called to free up the memory used - * by the chain structures. - * - * TODO: Clean this up, make better use of bstreams - * - * @param bs Input bstream - * @param list Source TLV chain - * @return Return 0 if the destination bstream is too small. - */ -int aim_tlvlist_write(ByteStream *bs, GSList **list) -{ - size_t goodbuflen; - GSList *cur; - aim_tlv_t *tlv; - - /* do an initial run to test total length */ - goodbuflen = aim_tlvlist_size(*list); - - if (goodbuflen > byte_stream_bytes_left(bs)) - return 0; /* not enough buffer */ - - /* do the real write-out */ - for (cur = *list; cur; cur = cur->next) { - tlv = cur->data; - byte_stream_put16(bs, tlv->type); - byte_stream_put16(bs, tlv->length); - if (tlv->length > 0) - byte_stream_putraw(bs, tlv->value, tlv->length); - } - - return 1; /* TODO: This is a nonsensical return */ -} - - -/** - * Grab the Nth TLV of type type in the TLV list list. - * - * Returns a pointer to an aim_tlv_t of the specified type; - * %NULL on error. The @nth parameter is specified starting at %1. - * In most cases, there will be no more than one TLV of any type - * in a chain. - * - * @param list Source chain. - * @param type Requested TLV type. - * @param nth Index of TLV of type to get. - * @return The TLV you were looking for, or NULL if one could not be found. - */ -aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth) -{ - GSList *cur; - aim_tlv_t *tlv; - int i; - - for (cur = list, i = 0; cur != NULL; cur = cur->next) { - tlv = cur->data; - if (tlv->type == type) - i++; - if (i >= nth) - return tlv; - } - - return NULL; -} - -/** - * Get the length of the data of the nth TLV in the given TLV chain. - * - * @param list Source chain. - * @param type Requested TLV type. - * @param nth Index of TLV of type to get. - * @return The length of the data in this TLV, or -1 if the TLV could not be - * found. Unless -1 is returned, this value will be 2 bytes. - */ -int aim_tlv_getlength(GSList *list, const guint16 type, const int nth) -{ - aim_tlv_t *tlv; - - tlv = aim_tlv_gettlv(list, type, nth); - if (tlv == NULL) - return -1; - - return tlv->length; -} - -char * -aim_tlv_getvalue_as_string(aim_tlv_t *tlv) -{ - char *ret; - - ret = g_malloc(tlv->length + 1); - memcpy(ret, tlv->value, tlv->length); - ret[tlv->length] = '\0'; - - return ret; -} - -/** - * Retrieve the data from the nth TLV in the given TLV chain as a string. - * - * @param list Source TLV chain. - * @param type TLV type to search for. - * @param nth Index of TLV to return. - * @return The value of the TLV you were looking for, or NULL if one could - * not be found. This is a dynamic buffer and must be freed by the - * caller. - */ -char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth) -{ - aim_tlv_t *tlv; - - tlv = aim_tlv_gettlv(list, type, nth); - if (tlv == NULL) - return NULL; - - return aim_tlv_getvalue_as_string(tlv); -} - -/** - * Retrieve the data from the nth TLV in the given TLV chain as an 8bit - * integer. - * - * @param list Source TLV chain. - * @param type TLV type to search for. - * @param nth Index of TLV to return. - * @return The value the TLV you were looking for, or 0 if one could - * not be found. - */ -guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth) -{ - aim_tlv_t *tlv; - - tlv = aim_tlv_gettlv(list, type, nth); - if (tlv == NULL) - return 0; /* erm */ - - return aimutil_get8(tlv->value); -} - -/** - * Retrieve the data from the nth TLV in the given TLV chain as a 16bit - * integer. - * - * @param list Source TLV chain. - * @param type TLV type to search for. - * @param nth Index of TLV to return. - * @return The value the TLV you were looking for, or 0 if one could - * not be found. - */ -guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth) -{ - aim_tlv_t *tlv; - - tlv = aim_tlv_gettlv(list, type, nth); - if (tlv == NULL) - return 0; /* erm */ - - return aimutil_get16(tlv->value); -} - -/** - * Retrieve the data from the nth TLV in the given TLV chain as a 32bit - * integer. - * - * @param list Source TLV chain. - * @param type TLV type to search for. - * @param nth Index of TLV to return. - * @return The value the TLV you were looking for, or 0 if one could - * not be found. - */ -guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth) -{ - aim_tlv_t *tlv; - - tlv = aim_tlv_gettlv(list, type, nth); - if (tlv == NULL) - return 0; /* erm */ - - return aimutil_get32(tlv->value); -}
--- a/libpurple/protocols/oscar/userinfo.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,553 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * Displaying various information about buddies. - */ - -#include "encoding.h" -#include "oscar.h" - -static gchar * -oscar_caps_to_string(guint64 caps) -{ - GString *str; - const gchar *tmp; - guint64 bit = 1; - - if (!caps) { - return NULL; - } - - str = g_string_new(""); - while (bit <= OSCAR_CAPABILITY_LAST) { - if (bit & caps) { - switch (bit) { - case OSCAR_CAPABILITY_BUDDYICON: - tmp = _("Buddy Icon"); - break; - case OSCAR_CAPABILITY_TALK: - tmp = _("Voice"); - break; - case OSCAR_CAPABILITY_DIRECTIM: - tmp = _("AIM Direct IM"); - break; - case OSCAR_CAPABILITY_CHAT: - tmp = _("Chat"); - break; - case OSCAR_CAPABILITY_GETFILE: - tmp = _("Get File"); - break; - case OSCAR_CAPABILITY_SENDFILE: - tmp = _("Send File"); - break; - case OSCAR_CAPABILITY_GAMES: - case OSCAR_CAPABILITY_GAMES2: - tmp = _("Games"); - break; - case OSCAR_CAPABILITY_XTRAZ: - case OSCAR_CAPABILITY_NEWCAPS: - tmp = _("ICQ Xtraz"); - break; - case OSCAR_CAPABILITY_ADDINS: - tmp = _("Add-Ins"); - break; - case OSCAR_CAPABILITY_SENDBUDDYLIST: - tmp = _("Send Buddy List"); - break; - case OSCAR_CAPABILITY_ICQ_DIRECT: - tmp = _("ICQ Direct Connect"); - break; - case OSCAR_CAPABILITY_APINFO: - tmp = _("AP User"); - break; - case OSCAR_CAPABILITY_ICQRTF: - tmp = _("ICQ RTF"); - break; - case OSCAR_CAPABILITY_EMPTY: - tmp = _("Nihilist"); - break; - case OSCAR_CAPABILITY_ICQSERVERRELAY: - tmp = _("ICQ Server Relay"); - break; - case OSCAR_CAPABILITY_UNICODEOLD: - tmp = _("Old ICQ UTF8"); - break; - case OSCAR_CAPABILITY_TRILLIANCRYPT: - tmp = _("Trillian Encryption"); - break; - case OSCAR_CAPABILITY_UNICODE: - tmp = _("ICQ UTF8"); - break; - case OSCAR_CAPABILITY_HIPTOP: - tmp = _("Hiptop"); - break; - case OSCAR_CAPABILITY_SECUREIM: - tmp = _("Security Enabled"); - break; - case OSCAR_CAPABILITY_VIDEO: - tmp = _("Video Chat"); - break; - /* Not actually sure about this one... WinAIM doesn't show anything */ - case OSCAR_CAPABILITY_ICHATAV: - tmp = _("iChat AV"); - break; - case OSCAR_CAPABILITY_LIVEVIDEO: - tmp = _("Live Video"); - break; - case OSCAR_CAPABILITY_CAMERA: - tmp = _("Camera"); - break; - case OSCAR_CAPABILITY_ICHAT_SCREENSHARE: - tmp = _("Screen Sharing"); - break; - default: - tmp = NULL; - break; - } - if (tmp) - g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp); - } - bit <<= 1; - } - - return g_string_free(str, FALSE); -} - -static void -oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info, - const char *name, const char *value) -{ - gchar *utf8; - - if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) { - /* TODO: Check whether it's correct to call add_pair_html, - or if we should be using add_pair_plaintext. Will - need to check callers of this function. */ - purple_notify_user_info_add_pair_html(user_info, name, utf8); - g_free(utf8); - } -} - -static void -oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info, - const char *name, const char *value, const char *url_prefix) -{ - gchar *utf8; - - if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) { - gchar *tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>", url_prefix, utf8, utf8); - /* TODO: Check whether it's correct to call add_pair_html, - or if we should be using add_pair_plaintext. Will - need to check callers of this function. */ - purple_notify_user_info_add_pair_html(user_info, name, tmp); - g_free(utf8); - g_free(tmp); - } -} - -/** - * @brief Append the status information to a user_info struct - * - * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML. - * - * @param gc The PurpleConnection - * @param user_info A PurpleNotifyUserInfo object to which status information will be added - * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status(). - * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status(). - * @param use_html_status If TRUE, prefer HTML-formatted away message over plaintext available message. - */ -void -oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status) -{ - PurpleAccount *account = purple_connection_get_account(gc); - OscarData *od; - PurplePresence *presence = NULL; - PurpleStatus *status = NULL; - gchar *message = NULL, *itmsurl = NULL, *tmp; - gboolean escaping_needed = TRUE; - - od = purple_connection_get_protocol_data(gc); - - if (b == NULL && userinfo == NULL) - return; - - if (b == NULL) - b = purple_blist_find_buddy(purple_connection_get_account(gc), userinfo->bn); - else - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b)); - - if (b) { - presence = purple_buddy_get_presence(b); - status = purple_presence_get_active_status(presence); - } - - /* If we have both b and userinfo we favor userinfo, because if we're - viewing someone's profile then we want the HTML away message, and - the "message" attribute of the status contains only the plaintext - message. */ - if (userinfo) { - if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) { - /* Away message */ - message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len); - escaping_needed = FALSE; - } else { - /* - * Available message or non-HTML away message (because that's - * all we have right now. - */ - if ((userinfo->status != NULL) && userinfo->status[0] != '\0') { - message = oscar_encoding_to_utf8(userinfo->status_encoding, userinfo->status, userinfo->status_len); - } -#if defined (_WIN32) || defined (__APPLE__) - if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) { - itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding, userinfo->itmsurl, userinfo->itmsurl_len); - } -#endif - } - } else { - message = g_strdup(purple_status_get_attr_string(status, "message")); - itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl")); - } - - if (message) { - tmp = oscar_util_format_string(message, purple_account_get_username(account)); - g_free(message); - message = tmp; - if (escaping_needed) { - tmp = purple_markup_escape_text(message, -1); - g_free(message); - message = tmp; - } - } - - if (use_html_status && itmsurl) { - tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message); - g_free(message); - message = tmp; - } - - if (b) { - if (purple_presence_is_online(presence)) { - gboolean is_away = ((status && !purple_status_is_available(status)) || (userinfo && (userinfo->flags & AIM_FLAG_AWAY))); - if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) { - /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message. - * If the status name and the message are the same, only show one. */ - const char *status_name = purple_status_get_name(status); - if (status_name && message && purple_strequal(status_name, message)) - status_name = NULL; - - tmp = g_strdup_printf("%s%s%s", - status_name ? status_name : "", - ((status_name && message) && *message) ? ": " : "", - (message && *message) ? message : ""); - g_free(message); - message = tmp; - } - - } else if (aim_ssi_waitingforauth(&od->ssi.local, - aim_ssi_itemlist_findparentname(&od->ssi.local, purple_buddy_get_name(b)), - purple_buddy_get_name(b))) - { - /* Note if an offline buddy is not authorized */ - tmp = g_strdup_printf("%s%s%s", - _("Not Authorized"), - (message && *message) ? ": " : "", - (message && *message) ? message : ""); - g_free(message); - message = tmp; - } else { - g_free(message); - message = g_strdup(_("Offline")); - } - } - - if (presence) { - const char *mood; - const char *comment; - char *description; - status = purple_presence_get_status(presence, "mood"); - mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME)); - if (mood) { - comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT); - if (comment) { - char *escaped_comment = purple_markup_escape_text(comment, -1); - description = g_strdup_printf("%s (%s)", _(mood), escaped_comment); - g_free(escaped_comment); - } else { - description = g_strdup(_(mood)); - } - purple_notify_user_info_add_pair_html(user_info, _("Mood"), description); - g_free(description); - } - } - - purple_notify_user_info_add_pair_html(user_info, _("Status"), message); - g_free(message); - g_free(itmsurl); -} - -void -oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo) -{ - OscarData *od; - PurpleAccount *account; - PurpleGroup *g = NULL; - struct buddyinfo *bi = NULL; - char *tmp; - const char *bname = NULL, *gname = NULL; - - od = purple_connection_get_protocol_data(gc); - account = purple_connection_get_account(gc); - - if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL))) - return; - - if (userinfo == NULL) - userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b)); - - if (b == NULL) - b = purple_blist_find_buddy(account, userinfo->bn); - - if (b != NULL) { - bname = purple_buddy_get_name(b); - g = purple_buddy_get_group(b); - gname = purple_group_get_name(g); - } - - if (userinfo != NULL) - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn)); - - if ((bi != NULL) && (bi->ipaddr != 0)) { - char tmp2[40]; - sprintf(tmp2, "%u.%u.%u.%u", - 0xFF & ((bi->ipaddr & 0xff000000) >> 24), - 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16), - 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8), - 0xFF & (bi->ipaddr & 0x000000ff)); - purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tmp2); - } - - if ((userinfo != NULL) && (userinfo->warnlevel != 0)) { - char tmp2[12]; - sprintf(tmp2, "%d", (int)(userinfo->warnlevel/10.0 + .5)); - purple_notify_user_info_add_pair_plaintext(user_info, _("Warning Level"), tmp2); - } - - if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) { - tmp = aim_ssi_getcomment(&od->ssi.local, gname, bname); - if (tmp != NULL) { - char *tmp2 = g_markup_escape_text(tmp, strlen(tmp)); - g_free(tmp); - - oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2); - g_free(tmp2); - } - } -} - -void -oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy) -{ - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); - gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason)); - purple_notify_user_info_add_pair_plaintext(user_info, NULL, buf); - purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - if (!purple_conversation_present_error(buddy, - purple_connection_get_account(od->gc), buf)) - { - purple_notify_error(od->gc, NULL, buf, NULL, - purple_request_cpar_from_connection(od->gc)); - } - g_free(buf); -} - -void -oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleBuddy *buddy; - struct buddyinfo *bi; - gchar who[16]; - PurpleNotifyUserInfo *user_info; - - if (!info->uin) - return; - - user_info = purple_notify_user_info_new(); - - g_snprintf(who, sizeof(who), "%u", info->uin); - buddy = purple_blist_find_buddy(account, who); - if (buddy != NULL) - bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy))); - else - bi = NULL; - - purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who); - oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick); - if ((bi != NULL) && (bi->ipaddr != 0)) { - char tstr[40]; - sprintf(tstr, "%u.%u.%u.%u", - 0xFF & ((bi->ipaddr & 0xff000000) >> 24), - 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16), - 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8), - 0xFF & (bi->ipaddr & 0x000000ff)); - purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tstr); - } - oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first); - oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last); - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email, "mailto:"); - if (info->numaddresses && info->email2) { - int i; - for (i = 0; i < info->numaddresses; i++) { - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email2[i], "mailto:"); - } - } - oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile); - - if (info->gender != 0) - purple_notify_user_info_add_pair_plaintext(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male"))); - - if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) { - /* Initialize the struct properly or strftime() will crash - * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */ - time_t t = time(NULL); - struct tm *tm = localtime(&t); - - tm->tm_mday = (int)info->birthday; - tm->tm_mon = (int)info->birthmonth - 1; - tm->tm_year = (int)info->birthyear - 1900; - - /* Ignore dst setting of today to avoid timezone shift between - * dates in summer and winter time. */ - tm->tm_isdst = -1; - - /* To be 100% sure that the fields are re-normalized. - * If you're sure strftime() ALWAYS does this EVERYWHERE, - * feel free to remove it. --rlaager */ - mktime(tm); - - oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm)); - } - if ((info->age > 0) && (info->age < 255)) { - char age[5]; - snprintf(age, sizeof(age), "%hhd", info->age); - purple_notify_user_info_add_pair_plaintext(user_info, _("Age"), age); - } - /* TODO: Is it correct to pass info->email here...? */ - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Personal Web Page"), info->email, ""); - if (buddy != NULL) - oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* use_html_status */ TRUE); - - oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info); - purple_notify_user_info_add_section_break(user_info); - - if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) { - purple_notify_user_info_add_section_header(user_info, _("Home Address")); - - oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr); - oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity); - oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate); - oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip); - } - if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) { - purple_notify_user_info_add_section_header(user_info, _("Work Address")); - - oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr); - oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity); - oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate); - oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip); - } - if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) { - purple_notify_user_info_add_section_header(user_info, _("Work Information")); - - oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany); - oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision); - oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition); - /* TODO: Is it correct to pass info->email here...? */ - oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Web Page"), info->email, ""); - } - - purple_notify_userinfo(gc, who, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); -} - -void -oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); - gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL; - - oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* use_html_status */ TRUE); - - if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) { - tmp = purple_str_seconds_to_string(userinfo->idletime*60); - purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp); - g_free(tmp); - } - - oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo); - - if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) { - /* An SMS contact is always online; its Online Since value is not useful */ - time_t t = userinfo->onlinesince; - purple_notify_user_info_add_pair_plaintext(user_info, _("Online Since"), purple_date_format_full(localtime(&t))); - } - - if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) { - time_t t = userinfo->membersince; - purple_notify_user_info_add_pair_plaintext(user_info, _("Member Since"), purple_date_format_full(localtime(&t))); - } - - if (userinfo->capabilities != 0) { - tmp = oscar_caps_to_string(userinfo->capabilities); - if (tmp && *tmp) - purple_notify_user_info_add_pair_plaintext(user_info, _("Capabilities"), tmp); - g_free(tmp); - } - - /* Info */ - if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) { - info_utf8 = oscar_encoding_to_utf8(userinfo->info_encoding, userinfo->info, userinfo->info_len); - tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account)); - purple_notify_user_info_add_section_break(user_info); - if (tmp && *tmp) { - /* TODO: Check whether it's correct to call add_pair_html, - or if we should be using add_pair_plaintext */ - purple_notify_user_info_add_pair_html(user_info, _("Profile"), tmp); - } - g_free(tmp); - g_free(info_utf8); - } - - purple_notify_user_info_add_section_break(user_info); - base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com"; - tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>", - base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile")); - purple_notify_user_info_add_pair_html(user_info, NULL, tmp); - g_free(tmp); - - purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); -}
--- a/libpurple/protocols/oscar/util.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,322 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -/* - * A little bit of this - * A little bit of that - * It started with a kiss - * Now we're up to bat - */ - -#include "oscar.h" - -#include "core.h" - -#include <ctype.h> - -static const char * const msgerrreason[] = { - N_("Invalid error"), - N_("Invalid SNAC"), - N_("Server rate limit exceeded"), - N_("Client rate limit exceeded"), - N_("Not logged in"), - N_("Service unavailable"), - N_("Service not defined"), - N_("Obsolete SNAC"), - N_("Not supported by host"), - N_("Not supported by client"), - N_("Refused by client"), - N_("Reply too big"), - N_("Responses lost"), - N_("Request denied"), - N_("Busted SNAC payload"), - N_("Insufficient rights"), - N_("In local permit/deny"), - N_("Warning level too high (sender)"), - N_("Warning level too high (receiver)"), - N_("User temporarily unavailable"), - N_("No match"), - N_("List overflow"), - N_("Request ambiguous"), - N_("Queue full"), - N_("Not while on AOL") -}; -static const gsize msgerrreasonlen = G_N_ELEMENTS(msgerrreason); - -const char *oscar_get_msgerr_reason(size_t reason) -{ - return (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason"); -} - -int oscar_get_ui_info_int(const char *str, int default_value) -{ - GHashTable *ui_info; - - ui_info = purple_core_get_ui_info(); - if (ui_info != NULL) { - gpointer value; - if (g_hash_table_lookup_extended(ui_info, str, NULL, &value)) - return GPOINTER_TO_INT(value); - } - - return default_value; -} - -const char *oscar_get_ui_info_string(const char *str, const char *default_value) -{ - GHashTable *ui_info; - const char *value = NULL; - - ui_info = purple_core_get_ui_info(); - if (ui_info != NULL) - value = g_hash_table_lookup(ui_info, str); - if (value == NULL) - value = default_value; - - return value; -} - -gchar *oscar_get_clientstring(void) -{ - const char *name, *version; - - name = oscar_get_ui_info_string("name", "Purple"); - version = oscar_get_ui_info_string("version", VERSION); - - return g_strdup_printf("%s/%s", name, version);; -} - -/** - * Calculate the checksum of a given icon. - */ -guint16 -aimutil_iconsum(const guint8 *buf, int buflen) -{ - guint32 sum; - int i; - - for (i=0, sum=0; i+1<buflen; i+=2) - sum += (buf[i+1] << 8) + buf[i]; - if (i < buflen) - sum += buf[i]; - sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff); - - return sum; -} - -/** - * Check if the given name is a valid AIM username. - * Example: BobDole - * Example: Henry_Ford@mac.com - * Example: 1KrazyKat@example.com - * - * @return TRUE if the name is valid, FALSE if not. - */ -static gboolean -oscar_util_valid_name_aim(const char *name) -{ - int i; - - if (purple_email_is_valid(name)) - return TRUE; - - /* Normal AIM usernames can't start with a number, period or underscore */ - if (isalnum(name[0]) == 0) - return FALSE; - - for (i = 0; name[i] != '\0'; i++) { - if (!isalnum(name[i]) && name[i] != ' ' && name[i] != '.' && name[i] != '_') - return FALSE; - } - - return TRUE; -} - -/** - * Check if the given name is a valid ICQ username. - * Example: 1234567 - * - * @return TRUE if the name is valid, FALSE if not. - */ -gboolean -oscar_util_valid_name_icq(const char *name) -{ - int i; - - for (i = 0; name[i] != '\0'; i++) { - if (!isdigit(name[i])) - return FALSE; - } - - return TRUE; -} - -/** - * Check if the given name is a valid SMS username. - * Example: +19195551234 - * - * @return TRUE if the name is valid, FALSE if not. - */ -gboolean -oscar_util_valid_name_sms(const char *name) -{ - int i; - - if (name[0] != '+') - return FALSE; - - for (i = 1; name[i] != '\0'; i++) { - if (!isdigit(name[i])) - return FALSE; - } - - return TRUE; -} - -/** - * Check if the given name is a valid oscar username. - * - * @return TRUE if the name is valid, FALSE if not. - */ -gboolean -oscar_util_valid_name(const char *name) -{ - if ((name == NULL) || (*name == '\0')) - return FALSE; - - return oscar_util_valid_name_icq(name) - || oscar_util_valid_name_sms(name) - || oscar_util_valid_name_aim(name); -} - -/** - * This takes two names and compares them using the rules - * on usernames for AIM/AOL. Mainly, this means case and space - * insensitivity (all case differences and spacing differences are - * ignored, with the exception that usernames can not start with - * a space). - * - * @return 0 if equal, non-0 if different - */ -/* TODO: Do something different for email addresses. */ -int -oscar_util_name_compare(const char *name1, const char *name2) -{ - - if ((name1 == NULL) || (name2 == NULL)) - return -1; - - do { - while (*name2 == ' ') - name2++; - while (*name1 == ' ') - name1++; - if (toupper(*name1) != toupper(*name2)) - return 1; - } while ((*name1 != '\0') && name1++ && name2++); - - return 0; -} - -/** - * Looks for %n, %d, or %t in a string, and replaces them with the - * specified name, date, and time, respectively. - * - * @param str The string that may contain the special variables. - * @param name The sender name. - * - * @return A newly allocated string where the special variables are - * expanded. This should be g_free'd by the caller. - */ -gchar * -oscar_util_format_string(const char *str, const char *name) -{ - char *c; - GString *cpy; - time_t t; - struct tm *tme; - - g_return_val_if_fail(str != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - /* Create an empty GString that is hopefully big enough for most messages */ - cpy = g_string_sized_new(1024); - - t = time(NULL); - tme = localtime(&t); - - c = (char *)str; - while (*c) { - switch (*c) { - case '%': - if (*(c + 1)) { - switch (*(c + 1)) { - case 'n': - /* append name */ - g_string_append(cpy, name); - c++; - break; - case 'd': - /* append date */ - g_string_append(cpy, purple_date_format_short(tme)); - c++; - break; - case 't': - /* append time */ - g_string_append(cpy, purple_time_format(tme)); - c++; - break; - default: - g_string_append_c(cpy, *c); - } - } else { - g_string_append_c(cpy, *c); - } - break; - default: - g_string_append_c(cpy, *c); - } - c++; - } - - return g_string_free(cpy, FALSE); -} - -gchar * -oscar_format_buddies(GSList *buddies, const gchar *no_buddies_message) -{ - GSList *cur; - GString *result; - if (!buddies) { - return g_strdup_printf("<i>%s</i>", no_buddies_message); - } - result = g_string_new(""); - for (cur = buddies; cur != NULL; cur = cur->next) { - PurpleBuddy *buddy = cur->data; - const gchar *bname = purple_buddy_get_name(buddy); - const gchar *alias = purple_buddy_get_alias_only(buddy); - g_string_append(result, bname); - if (alias) { - g_string_append_printf(result, " (%s)", alias); - } - g_string_append(result, "<br>"); - } - return g_string_free(result, FALSE); -}
--- a/libpurple/protocols/oscar/visibility.c Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#include "visibility.h" - -/* Translators: This string is a menu option that, if selected, will cause - you to appear online to the chosen user even when your status is set to - Invisible. */ -#define APPEAR_ONLINE N_("Appear Online") - -/* Translators: This string is a menu option that, if selected, will cause - you to appear offline to the chosen user when your status is set to - Invisible (this is the default). */ -#define DONT_APPEAR_ONLINE N_("Don't Appear Online") - -/* Translators: This string is a menu option that, if selected, will cause - you to always appear offline to the chosen user (even when your status - isn't Invisible). */ -#define APPEAR_OFFLINE N_("Appear Offline") - -/* Translators: This string is a menu option that, if selected, will cause - you to appear offline to the chosen user if you are invisible, and - appear online to the chosen user if you are not invisible (this is the - default). */ -#define DONT_APPEAR_OFFLINE N_("Don't Appear Offline") - -static guint16 -get_buddy_list_type(OscarData *od) -{ - PurpleAccount *account = purple_connection_get_account(od->gc); - return purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE) ? AIM_SSI_TYPE_PERMIT : AIM_SSI_TYPE_DENY; -} - -static gboolean -is_buddy_on_list(OscarData *od, const char *bname) -{ - return aim_ssi_itemlist_finditem(&od->ssi.local, NULL, bname, get_buddy_list_type(od)) != NULL; -} - -static void -visibility_cb(PurpleBlistNode *node, gpointer whatever) -{ - PurpleBuddy *buddy = PURPLE_BUDDY(node); - const char* bname = purple_buddy_get_name(buddy); - OscarData *od = purple_connection_get_protocol_data(purple_account_get_connection(purple_buddy_get_account(buddy))); - guint16 list_type = get_buddy_list_type(od); - - if (!is_buddy_on_list(od, bname)) { - aim_ssi_add_to_private_list(od, bname, list_type); - } else { - aim_ssi_del_from_private_list(od, bname, list_type); - } -} - -PurpleActionMenu * -create_visibility_menu_item(OscarData *od, const char *bname) -{ - PurpleAccount *account = purple_connection_get_account(od->gc); - gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE); - gboolean on_list = is_buddy_on_list(od, bname); - const gchar *label; - - if (invisible) { - label = on_list ? _(DONT_APPEAR_ONLINE) : _(APPEAR_ONLINE); - } else { - label = on_list ? _(DONT_APPEAR_OFFLINE) : _(APPEAR_OFFLINE); - } - return purple_action_menu_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL); -} - -static void -show_private_list(PurpleProtocolAction *action, guint16 list_type, const gchar *title, const gchar *list_description, const gchar *menu_action_name) -{ - PurpleConnection *gc = action->connection; - OscarData *od = purple_connection_get_protocol_data(gc); - PurpleAccount *account = purple_connection_get_account(gc); - GSList *buddies, *filtered_buddies, *cur; - gchar *text, *secondary; - - buddies = purple_blist_find_buddies(account, NULL); - filtered_buddies = NULL; - for (cur = buddies; cur != NULL; cur = cur->next) { - PurpleBuddy *buddy; - const gchar *bname; - - buddy = cur->data; - bname = purple_buddy_get_name(buddy); - if (aim_ssi_itemlist_finditem(&od->ssi.local, NULL, bname, list_type)) { - filtered_buddies = g_slist_prepend(filtered_buddies, buddy); - } - } - - g_slist_free(buddies); - - filtered_buddies = g_slist_reverse(filtered_buddies); - text = oscar_format_buddies(filtered_buddies, _("you have no buddies on this list")); - g_slist_free(filtered_buddies); - - secondary = g_strdup_printf(_("You can add a buddy to this list " - "by right-clicking on them and " - "selecting \"%s\""), menu_action_name); - purple_notify_formatted(gc, title, list_description, secondary, text, NULL, NULL); - g_free(secondary); - g_free(text); -} - -void -oscar_show_visible_list(PurpleProtocolAction *action) -{ - show_private_list(action, AIM_SSI_TYPE_PERMIT, _("Visible List"), - _("These buddies can see your status even when you're " - "invisible."), - _(APPEAR_ONLINE)); -} - -void -oscar_show_invisible_list(PurpleProtocolAction *action) -{ - show_private_list(action, AIM_SSI_TYPE_DENY, _("Invisible List"), - _("These buddies will always see you as offline."), - _(APPEAR_OFFLINE)); -}
--- a/libpurple/protocols/oscar/visibility.h Sun Oct 20 00:24:28 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Purple's oscar protocol plugin - * This file is the legal property of its developers. - * Please see the AUTHORS file distributed alongside this file. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA -*/ - -#ifndef PURPLE_OSCAR_VISIBILITY_H -#define PURPLE_OSCAR_VISIBILITY_H - -#include "oscar.h" -#include "plugins.h" -#include "action.h" - -PurpleActionMenu * create_visibility_menu_item(OscarData *od, const char *bname); -void oscar_show_visible_list(PurpleProtocolAction *action); -void oscar_show_invisible_list(PurpleProtocolAction *action); - -#endif /* PURPLE_OSCAR_VISIBILITY_H */
--- a/meson.build Sun Oct 20 00:24:28 2019 +0300 +++ b/meson.build Fri Oct 25 07:52:44 2019 +0000 @@ -481,7 +481,7 @@ DEFAULT_PRPLS = ['bonjour', 'facebook', 'gg', 'irc', 'jabber', 'novell', - 'null', 'oscar', 'sametime', 'silc', 'simple', 'zephyr'] + 'null', 'sametime', 'silc', 'simple', 'zephyr'] ALL_PRPLS = DEFAULT_PRPLS + ['null'] dynamic_list = get_option('dynamic-prpls').split(',') @@ -514,7 +514,6 @@ DYNAMIC_JABBER = DYNAMIC_PRPLS.contains('jabber') DYNAMIC_NOVELL = DYNAMIC_PRPLS.contains('novell') DYNAMIC_NULL = DYNAMIC_PRPLS.contains('null') -DYNAMIC_OSCAR = DYNAMIC_PRPLS.contains('oscar') or DYNAMIC_PRPLS.contains('aim') or DYNAMIC_PRPLS.contains('icq') DYNAMIC_SAMETIME = DYNAMIC_PRPLS.contains('sametime') DYNAMIC_SILC = DYNAMIC_PRPLS.contains('silc') DYNAMIC_SIMPLE = DYNAMIC_PRPLS.contains('simple')
--- a/po/POTFILES.in Sun Oct 20 00:24:28 2019 +0300 +++ b/po/POTFILES.in Fri Oct 25 07:52:44 2019 +0000 @@ -26,7 +26,6 @@ finch/plugins/grouping.c finch/plugins/lastlog.c libpurple/account.c -libpurple/accountopt.c libpurple/accounts.c libpurple/action.c libpurple/attention.c @@ -49,7 +48,6 @@ libpurple/eventloop.c libpurple/example/nullclient.c libpurple/group.c -libpurple/http.c libpurple/idle.c libpurple/image.c libpurple/image-store.c @@ -79,7 +77,7 @@ libpurple/plugins/idle.c libpurple/plugins/joinpart.c libpurple/plugins/keyrings/internalkeyring.c -libpurple/plugins/keyrings/kwallet.cpp +libpurple/plugins/keyrings/kwallet/purplekwallet.cpp libpurple/plugins/keyrings/secretservice.c libpurple/plugins/keyrings/wincred.c libpurple/plugins/log_reader.c @@ -208,45 +206,6 @@ libpurple/protocols/novell/nmuserrecord.c libpurple/protocols/novell/novell.c libpurple/protocols/null/nullprpl.c -libpurple/protocols/oscar/aim.c -libpurple/protocols/oscar/authorization.c -libpurple/protocols/oscar/bstream.c -libpurple/protocols/oscar/clientlogin.c -libpurple/protocols/oscar/encoding.c -libpurple/protocols/oscar/family_admin.c -libpurple/protocols/oscar/family_alert.c -libpurple/protocols/oscar/family_auth.c -libpurple/protocols/oscar/family_bart.c -libpurple/protocols/oscar/family_bos.c -libpurple/protocols/oscar/family_buddy.c -libpurple/protocols/oscar/family_chat.c -libpurple/protocols/oscar/family_chatnav.c -libpurple/protocols/oscar/family_feedbag.c -libpurple/protocols/oscar/family_icbm.c -libpurple/protocols/oscar/family_icq.c -libpurple/protocols/oscar/family_locate.c -libpurple/protocols/oscar/family_oservice.c -libpurple/protocols/oscar/family_popup.c -libpurple/protocols/oscar/family_stats.c -libpurple/protocols/oscar/family_userlookup.c -libpurple/protocols/oscar/flap_connection.c -libpurple/protocols/oscar/icq.c -libpurple/protocols/oscar/kerberos.c -libpurple/protocols/oscar/misc.c -libpurple/protocols/oscar/msgcookie.c -libpurple/protocols/oscar/odc.c -libpurple/protocols/oscar/oft.c -libpurple/protocols/oscar/oscar.c -libpurple/protocols/oscar/oscar_data.c -libpurple/protocols/oscar/peer.c -libpurple/protocols/oscar/peer_proxy.c -libpurple/protocols/oscar/rxhandlers.c -libpurple/protocols/oscar/snac.c -libpurple/protocols/oscar/tests/test_oscar_util.c -libpurple/protocols/oscar/tlv.c -libpurple/protocols/oscar/userinfo.c -libpurple/protocols/oscar/util.c -libpurple/protocols/oscar/visibility.c libpurple/protocols/sametime/im_mime.c libpurple/protocols/sametime/sametime.c libpurple/protocols/sametime/tests/test_sametime_im_mime.c @@ -437,7 +396,7 @@ pidgin/plugins/unity.c pidgin/plugins/win32/winprefs/gtkappbar.c pidgin/plugins/win32/winprefs/winprefs.c -pidgin/plugins/xmppconsole.c +pidgin/plugins/xmppconsole/xmppconsole.c pidgin/resources/About/about.ui pidgin/resources/Conversations/invite_dialog.ui pidgin/resources/Debug/debug.ui