From b6b44c0d8a71e0dcd525788cc50f2ef05fce98b2 Mon Sep 17 00:00:00 2001 From: barrett Date: Fri, 24 Sep 2010 13:48:01 +0000 Subject: [PATCH] =?UTF-8?q?=C3=82=C3=82moving=20libmsn=20code=20from=20bra?= =?UTF-8?q?nch=20to=20the=20Caya=20subproject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/Jamfile | 1 + libs/libmsn/COPYING | 353 ++++ libs/libmsn/Jamfile | 37 + libs/libmsn/README | 60 + libs/libmsn/THANKS | 21 + libs/libmsn/TODO | 42 + libs/libmsn/authdata.cpp | 28 + libs/libmsn/authdata.h | 44 + libs/libmsn/buddy.cpp | 76 + libs/libmsn/buddy.h | 138 ++ libs/libmsn/config.h | 5 + libs/libmsn/connection.cpp | 241 +++ libs/libmsn/connection.h | 154 ++ libs/libmsn/errorcodes.h | 101 + libs/libmsn/externals.h | 333 +++ libs/libmsn/libmsn_export.h | 24 + libs/libmsn/libsiren/common.cpp | 505 +++++ libs/libmsn/libsiren/common.h | 146 ++ libs/libmsn/libsiren/dct4.cpp | 184 ++ libs/libmsn/libsiren/dct4.h | 30 + libs/libmsn/libsiren/decoder.cpp | 234 +++ libs/libmsn/libsiren/decoder.h | 52 + libs/libmsn/libsiren/encoder.cpp | 234 +++ libs/libmsn/libsiren/encoder.h | 47 + libs/libmsn/libsiren/huffman.cpp | 382 ++++ libs/libmsn/libsiren/huffman.h | 35 + libs/libmsn/libsiren/huffman_consts.h | 528 +++++ libs/libmsn/libsiren/rmlt.cpp | 133 ++ libs/libmsn/libsiren/rmlt.h | 30 + libs/libmsn/libsiren/siren7.h | 30 + libs/libmsn/md5.cpp | 392 ++++ libs/libmsn/md5.h | 94 + libs/libmsn/message.cpp | 338 +++ libs/libmsn/message.h | 211 ++ libs/libmsn/msn.h | 38 + libs/libmsn/msnobject.cpp | 185 ++ libs/libmsn/msnobject.h | 64 + libs/libmsn/msntest.cpp | 1375 +++++++++++++ libs/libmsn/notificationserver.cpp | 1738 ++++++++++++++++ libs/libmsn/notificationserver.h | 549 +++++ libs/libmsn/p2p.cpp | 1413 +++++++++++++ libs/libmsn/p2p.h | 322 +++ libs/libmsn/passport.cpp | 68 + libs/libmsn/passport.h | 74 + libs/libmsn/soap.cpp | 2711 +++++++++++++++++++++++++ libs/libmsn/soap.h | 221 ++ libs/libmsn/sstream_fix.h | 74 + libs/libmsn/switchboardserver.cpp | 749 +++++++ libs/libmsn/switchboardserver.h | 253 +++ libs/libmsn/util.cpp | 624 ++++++ libs/libmsn/util.h | 279 +++ libs/libmsn/xmlParser.cpp | 2675 ++++++++++++++++++++++++ libs/libmsn/xmlParser.h | 553 +++++ 53 files changed, 19228 insertions(+) create mode 100755 libs/libmsn/COPYING create mode 100644 libs/libmsn/Jamfile create mode 100755 libs/libmsn/README create mode 100755 libs/libmsn/THANKS create mode 100755 libs/libmsn/TODO create mode 100644 libs/libmsn/authdata.cpp create mode 100644 libs/libmsn/authdata.h create mode 100644 libs/libmsn/buddy.cpp create mode 100644 libs/libmsn/buddy.h create mode 100644 libs/libmsn/config.h create mode 100644 libs/libmsn/connection.cpp create mode 100644 libs/libmsn/connection.h create mode 100644 libs/libmsn/errorcodes.h create mode 100644 libs/libmsn/externals.h create mode 100644 libs/libmsn/libmsn_export.h create mode 100644 libs/libmsn/libsiren/common.cpp create mode 100644 libs/libmsn/libsiren/common.h create mode 100644 libs/libmsn/libsiren/dct4.cpp create mode 100644 libs/libmsn/libsiren/dct4.h create mode 100644 libs/libmsn/libsiren/decoder.cpp create mode 100644 libs/libmsn/libsiren/decoder.h create mode 100644 libs/libmsn/libsiren/encoder.cpp create mode 100644 libs/libmsn/libsiren/encoder.h create mode 100644 libs/libmsn/libsiren/huffman.cpp create mode 100644 libs/libmsn/libsiren/huffman.h create mode 100644 libs/libmsn/libsiren/huffman_consts.h create mode 100644 libs/libmsn/libsiren/rmlt.cpp create mode 100644 libs/libmsn/libsiren/rmlt.h create mode 100644 libs/libmsn/libsiren/siren7.h create mode 100644 libs/libmsn/md5.cpp create mode 100644 libs/libmsn/md5.h create mode 100644 libs/libmsn/message.cpp create mode 100644 libs/libmsn/message.h create mode 100644 libs/libmsn/msn.h create mode 100644 libs/libmsn/msnobject.cpp create mode 100644 libs/libmsn/msnobject.h create mode 100644 libs/libmsn/msntest.cpp create mode 100644 libs/libmsn/notificationserver.cpp create mode 100644 libs/libmsn/notificationserver.h create mode 100644 libs/libmsn/p2p.cpp create mode 100644 libs/libmsn/p2p.h create mode 100644 libs/libmsn/passport.cpp create mode 100644 libs/libmsn/passport.h create mode 100644 libs/libmsn/soap.cpp create mode 100644 libs/libmsn/soap.h create mode 100644 libs/libmsn/sstream_fix.h create mode 100644 libs/libmsn/switchboardserver.cpp create mode 100644 libs/libmsn/switchboardserver.h create mode 100644 libs/libmsn/util.cpp create mode 100644 libs/libmsn/util.h create mode 100644 libs/libmsn/xmlParser.cpp create mode 100755 libs/libmsn/xmlParser.h diff --git a/libs/Jamfile b/libs/Jamfile index e4b419c..7bd47dc 100644 --- a/libs/Jamfile +++ b/libs/Jamfile @@ -2,4 +2,5 @@ SubDir TOP libs ; # Include all the components. SubInclude TOP libs libgloox ; +SubInclude TOP libs libmsn ; SubInclude TOP libs libsupport ; diff --git a/libs/libmsn/COPYING b/libs/libmsn/COPYING new file mode 100755 index 0000000..c77e520 --- /dev/null +++ b/libs/libmsn/COPYING @@ -0,0 +1,353 @@ +In addition to the license terms of the GNU General Public License, +as copied below, the developers of libmsn give you permission +to link the code of this release of libmsn with the OpenSSL project's +"OpenSSL" library (or with modified versions of it that use the same +license as the "OpenSSL" library), and distribute the linked executables. +You must obey the GNU General Public License in all respects for all of the +code used other than "OpenSSL". If you modify this file, you may +extend this exception to your version of the file, but you are not +obligated to do so. If you do not wish to do so, delete this exception +statement from your version. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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. + + + Copyright (C) + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/libs/libmsn/Jamfile b/libs/libmsn/Jamfile new file mode 100644 index 0000000..ec7bd45 --- /dev/null +++ b/libs/libmsn/Jamfile @@ -0,0 +1,37 @@ +SubDir TOP libs libmsn ; + +SubDirSysHdrs [ FDirName $(TOP) ] ; +SubDirSysHdrs [ FDirName $(TOP) libs ] ; + +SubDirSysHdrs [ FDirName $(OPENSSL_INCLUDE_DIR) ] ; + +SEARCH_SOURCE += [ FDirName $(TOP) libs ] ; +SEARCH_SOURCE += [ FDirName $(TOP) libs libmsn libsiren ] ; + +local sources = + # libmsn + authdata.cpp + buddy.cpp + connection.cpp + md5.cpp + message.cpp + msnobject.cpp +# msntest.cpp + notificationserver.cpp + p2p.cpp + passport.cpp + soap.cpp + switchboardserver.cpp + util.cpp + xmlParser.cpp + + # libsiren + common.cpp + dct4.cpp + decoder.cpp + encoder.cpp + huffman.cpp + rmlt.cpp +; + +StaticLibrary libmsn.a : $(sources) : be root $(TARGET_LIBSTDC++) ssl crypto ; diff --git a/libs/libmsn/README b/libs/libmsn/README new file mode 100755 index 0000000..f28539c --- /dev/null +++ b/libs/libmsn/README @@ -0,0 +1,60 @@ +libmsn 4.0 +=========== + +:Author: Mark Rowe +:Contact: bdash@users.sourceforge.net + +:Author: Tiago Salem Herrmann +:Contact: tiagosalem@users.sourceforge.net + +libmsn is a C++ library for Microsoft's MSN Messenger service. It provides a high-level +interface that allows an application to access instant messaging features with ease. + +libmsn is licensed under the terms of the GPL. Please see the file `COPYING' included +with the distribution for more information. + +Whats New? +---------- +In 4.0: libmsn 4.0 was refactored to support MSNP15 protocol. + Many features were added to this version, like offline messaging, file transfer with + preview, nugdes, custom emoticons, ink and winks sending and receiving, avatar support, + address book management with soap actions, and so on. + +In 3.2: With the release of libmsn 3.2 comes support for multiple simultaneous connections. The + callback mechanism has been refactored so that it is easy to distinguish between each + connection's callbacks. A large number of bug fixes have also been rolled in to this + release. +In 3.1.1: libmsn 3.1.1 is primarily a bug fix release. Changes include fixing up the #include's + so that it compiles cleanly on a wider range of platforms, working file transfer + support and improved error handling. +In 3.1: libmsn 3.1 brings support for server-side groups for managing buddies. It also + features rewritten network code that should help reduce CPU usage, and the amount + of time spent waiting for network data. +In 3.0b2: 3.0b2 tidies up the external API to be more consistent with the rest of libmsn. + The build system has also been adjusted so that the same program, msntest, is + built. + +Installation +------------ +You need to use cmake to build libmsn. (http://www.cmake.org/) +The basic installation procedure is similar to:: + + $ cd libmsn; mkdir build; cd build + $ cmake .. + # make install + +See `INSTALL' for more information. + +Documentation +------------- +Documentation for libmsn is available in the `doc/html' subdirectory. A brief overview of +the library can be found in the `doc/OVERVIEW' file. + +A sample program that uses libmsn can be found in `msntest/msntest.cpp'. + +Support +------- +For questions about usage of libmsn, please use the libmsn-discuss mailing list. A web +interface for subscribing to this mailing list is available at +http://lists.sourceforge.net/lists/listinfo/libmsn-discuss/. If you prefer real-time +discussion, please look to the #libmsn channel on irc.freenode.net. diff --git a/libs/libmsn/THANKS b/libs/libmsn/THANKS new file mode 100755 index 0000000..2c9c634 --- /dev/null +++ b/libs/libmsn/THANKS @@ -0,0 +1,21 @@ +libmsn THANKS File +================== +libmsn was originally written by Meredydd Luff, was rewritten by +Mark Rowe and now is been ported to MSNP15 by Tiago Salem Herrmann +Several other people have contributed code, submitted bugs or suggested features. +This file contains a list of those who are known to have contributed +to the libmsn project. + +Mark Rowe +Meredydd Luff +Tiago Salem Herrmann +Edmund Horner +Adam Palmer + +Thanks to msnpiki (msnpiki.msnfanatic.com), +ZoRoNaX (http://zoronax.spaces.live.com/) +people from pymsn (http://telepathy.freedesktop.org/wiki/Pymsn). +Gustavo Pichorim Boiko +Sascha Wittkowski +Christian Ehrlicher +Youness Alaoui (KaKaRoTo) (libsiren) diff --git a/libs/libmsn/TODO b/libs/libmsn/TODO new file mode 100755 index 0000000..07eb247 --- /dev/null +++ b/libs/libmsn/TODO @@ -0,0 +1,42 @@ + +TODO +- improve msnobject support + +Switchboard: +- p2p + - voice chat + - webcam + - ink support (in progress, missing p2p sending support) + +done: +Notification: + - SSO login ( MSNP15 ) + - retrieve lists (Allow, Block, Pending, Reverse) + - retrieve Address Book (Forward List) + - retrieve Groups and assign contacts + - Set user friendlyname + - get contacts personal info (media and friendlyname) + - get contacts status changes (Online, busy, etc) + - set personal info + - retrieve, send and delete OIMs (offline messages) + - Block/Unblock user + - move user to group (del user from group and add to another) + - add group + - remove group + - Add user + - del user + - get contact capabilities + +Switchboard: + - multipacket receiving support + - send and receive emoticons + - send and receive msgs + - send and receive nudges + - send and receive actions + - send msgs while in hidden status + - send and receive avatars + - send and receive files through Switchboard + - send and receive voice clips + - send and receive winks + - send and receive ink (multipacket support) + diff --git a/libs/libmsn/authdata.cpp b/libs/libmsn/authdata.cpp new file mode 100644 index 0000000..0521f42 --- /dev/null +++ b/libs/libmsn/authdata.cpp @@ -0,0 +1,28 @@ +/* + * authdata.cpp + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "authdata.h" + diff --git a/libs/libmsn/authdata.h b/libs/libmsn/authdata.h new file mode 100644 index 0000000..7a5f602 --- /dev/null +++ b/libs/libmsn/authdata.h @@ -0,0 +1,44 @@ +#ifndef __msn_authdata_h__ +#define __msn_authdata_h__ + +/* + * authdata.h + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "passport.h" + +#include "libmsn_export.h" + +namespace MSN +{ + class LIBMSN_EXPORT AuthData + { +public: + Passport username; + + AuthData(Passport username_) : username(username_) {}; + virtual ~AuthData() {}; + }; +} +#endif diff --git a/libs/libmsn/buddy.cpp b/libs/libmsn/buddy.cpp new file mode 100644 index 0000000..050a7e2 --- /dev/null +++ b/libs/libmsn/buddy.cpp @@ -0,0 +1,76 @@ +/* + * buddy.cpp + * libmsn + * + * Created by Mark Rowe on Mon Apr 19 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "buddy.h" +#include + +namespace MSN +{ + std::string buddyStatusToString(BuddyStatus state) + { + switch (state) + { + case STATUS_AVAILABLE: + return "NLN"; + case STATUS_BUSY: + return "BSY"; + case STATUS_IDLE: + return "IDL"; + case STATUS_BERIGHTBACK: + return "BRB"; + case STATUS_AWAY: + return "AWY"; + case STATUS_ONTHEPHONE: + return "PHN"; + case STATUS_OUTTOLUNCH: + return "LUN"; + case STATUS_INVISIBLE: + return "HDN"; + default: + assert(false); + } + } + + BuddyStatus buddyStatusFromString(std::string state) + { + if (state == "NLN") + return STATUS_AVAILABLE; + else if (state == "BSY") + return STATUS_BUSY; + else if (state == "IDL") + return STATUS_IDLE; + else if (state == "BRB") + return STATUS_BERIGHTBACK; + else if (state == "AWY") + return STATUS_AWAY; + else if (state == "PHN") + return STATUS_ONTHEPHONE; + else if (state == "LUN") + return STATUS_OUTTOLUNCH; + else if (state == "HDN") + return STATUS_INVISIBLE; + else + throw std::runtime_error("Unknown status!"); + } +} diff --git a/libs/libmsn/buddy.h b/libs/libmsn/buddy.h new file mode 100644 index 0000000..6fc43ee --- /dev/null +++ b/libs/libmsn/buddy.h @@ -0,0 +1,138 @@ +#ifndef __msn_buddy_h__ +#define __msn_buddy_h__ + +/* + * buddy.h + * libmsn + * + * Created by Mark Rowe on Mon Apr 19 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "passport.h" + +#include "libmsn_export.h" + +namespace MSN +{ + /** The online state of a buddy. + */ + + enum BuddyStatus + { + STATUS_AVAILABLE, /**< Contact is available */ + STATUS_BUSY, /**< Contact is busy */ + STATUS_IDLE, /**< Contact is idle */ + STATUS_BERIGHTBACK, /**< Contact will be right back */ + STATUS_AWAY, /**< Contact is away */ + STATUS_ONTHEPHONE, /**< Contact is on the phone */ + STATUS_OUTTOLUNCH, /**< Contact is out to lunch */ + STATUS_INVISIBLE /**< Contact is invisible */ + }; + + std::string LIBMSN_EXPORT buddyStatusToString(BuddyStatus s); + BuddyStatus LIBMSN_EXPORT buddyStatusFromString(std::string s); + + class Group; + + /** The Buddy class contains information about a member of a buddy list. + * + * Each Buddy is made up of their passport address (@a userName), + * user-visible display name (@a friendlyName), a list of properties + * (@a properties) and zero or more @a groups on the buddy list that they belong to. + * + */ + class LIBMSN_EXPORT Buddy + { +public: + /** The PhoneNumbers class contains information about one or more phone numbers + * that are retrieved during the buddy list synchronisation process. + */ + class PhoneNumber + { +public: + /** The name of this phone number. + * + * @todo Should this be an enumeration containing the possible + * types of phone number? + */ + std::string title; + + std::string number; + + bool enabled; + + PhoneNumber(std::string title_, std::string number_, bool enabled_=true) + : title(title_), number(number_), enabled(enabled_) {}; + + }; + + /** all the properties received at login time */ + std::map properties; + + /** Their passport address */ + Passport userName; + + /** Their friendly name */ + std::string friendlyName; + + /** A list of phone numbers related to this buddy */ + std::list phoneNumbers; + + /** A list of Group's that this buddy is a member of */ + std::list groups; + + /** Lists which this contact belong. Pending, Forward, Block... **/ + unsigned int lists; + + Buddy(Passport userName_, std::string friendlyName_ = "") : + userName(userName_), friendlyName(friendlyName_), lists(0) {}; + bool operator==(const Buddy &other) { return userName == other.userName; } + }; + + /** The Group class represents a group of contacts on the buddy list. + * + * Each group is represented by a @a groupID, a list of buddies @buddies + * and has a user-visible @a name. + */ + class LIBMSN_EXPORT Group + { +public: + + /** Id of this group **/ + std::string groupID; + + /** Name of this group **/ + std::string name; + + /** List of contacts in this group **/ + std::list buddies; + + Group(std::string groupID_, std::string name_) + : groupID(groupID_), name(name_) {}; + + Group() : name("INVALID") {}; + }; +} + +#endif diff --git a/libs/libmsn/config.h b/libs/libmsn/config.h new file mode 100644 index 0000000..db9f251 --- /dev/null +++ b/libs/libmsn/config.h @@ -0,0 +1,5 @@ +#ifndef LIBMSN_CONFIG_H +#define LIBMSN_CONFIG_H + + +#endif diff --git a/libs/libmsn/connection.cpp b/libs/libmsn/connection.cpp new file mode 100644 index 0000000..c793b61 --- /dev/null +++ b/libs/libmsn/connection.cpp @@ -0,0 +1,241 @@ +/* + * connection.cpp + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "connection.h" +#include "errorcodes.h" +#include "util.h" +#include "passport.h" +#include "externals.h" +#include "notificationserver.h" +#ifndef WIN32 +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +namespace MSN +{ + static std::vector errors; + + Connection::Connection() + : sock(NULL), connected(false), trID(1) + { + srand((unsigned int) time(NULL)); + + if (errors.size() != 0) + { + assert(errors.size() == 1000); + } + else + { + errors.resize(1000); + for (int a = 0; a < 1000; a++) + { + errors[a] = "Unknown error code"; + } + + errors[200] = "Syntax error"; + errors[201] = "Invalid parameter"; + errors[205] = "Invalid user"; + errors[206] = "Domain name missing from username"; + errors[207] = "Already logged in"; + errors[208] = "Invalid username"; + errors[209] = "Invalid friendly name"; + errors[210] = "List full"; + errors[215] = "This user is already on this list or in this session"; + errors[216] = "Not on list"; + errors[218] = "Already in this mode"; + errors[219] = "This user is already in the opposite list"; + errors[241] = "Unable to add user"; + errors[280] = "Switchboard server failed"; + errors[281] = "Transfer notification failed"; + errors[300] = "Required fields missing"; + errors[302] = "Not logged in"; + errors[500] = "Internal server error"; + errors[501] = "Database server error"; + errors[510] = "File operation failed at server"; + errors[520] = "Memory allocation failed on server"; + errors[600] = "The server is too busy"; + errors[601] = "The server is unavailable"; + errors[602] = "A Peer Notification Server is down"; + errors[603] = "Database connection failed"; + errors[604] = "Server going down for maintenance"; + errors[707] = "Server failed to create connection"; + errors[711] = "Blocking write failed on server"; + errors[712] = "Session overload on server"; + errors[713] = "You have been too active recently. Slow down!"; + errors[714] = "Too many sessions open"; + errors[715] = "Email Address Not verified"; + errors[717] = "Bad friend file on server"; + errors[911] = "Authentication failed. Check that you typed your username and password correctly."; + errors[913] = "This action is not allowed while you are offline"; + errors[920] = "This server is not accepting new users"; + errors[921] = "Error synchronizing lists"; + errors[922] = "Error synchronizing address book"; + } + + } + + Connection::~Connection() { } + + void Connection::disconnect() + { + this->connected = false; + this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock); + + this->myNotificationServer()->externalCallbacks.closeSocket(this->sock); + + this->sock = NULL; + this->writeBuffer.erase(); + this->readBuffer.erase(); + this->trID = 1; + } + + std::vector Connection::getLine() + { + assert(this->isWholeLineAvailable()); + std::string s = this->readBuffer.substr(0, this->readBuffer.find("\r\n")); + this->myNotificationServer()->externalCallbacks.log(0, (s + "\n").c_str()); + return splitString(s, " "); + } + + bool Connection::isWholeLineAvailable() + { + return this->readBuffer.find("\r\n") != std::string::npos; + } + + void Connection::errorOnSocket(int errno_) + { + this->myNotificationServer()->externalCallbacks.showError(this, strerror(errno_)); + this->disconnect(); + } + + void Connection::socketConnectionCompleted() + { + this->connected = true; + + if(this->writeBuffer.size()) + { + // We know that we are connected, so this will try writing to the network. + size_t writtenLength = this->write(this->writeBuffer, 1); + if(writtenLength > 0 && this->writeBuffer.size() > 0) + this->writeBuffer = this->writeBuffer.substr(writtenLength); + } + } + + size_t Connection::write(std::string s, bool log) throw (std::runtime_error) + { + if(s.size() < 0) + return 0; + + if (! this->connected) + { + this->writeBuffer.append(s); + } + else + { + if (log) + this->myNotificationServer()->externalCallbacks.log(1, s.c_str()); + + char *a = (char*)s.c_str(); + size_t written = this->myNotificationServer()-> + externalCallbacks.writeDataToSocket(sock, a, (int) (s.size())); + return written; + } + return s.size(); + } + + size_t Connection::write(std::ostringstream & ss, bool log) throw (std::runtime_error) + { + std::string s = ss.str(); +#ifdef DEBUG + std::cout << s << std::endl; +#endif + size_t result = write(s, log); + return result; + } + + void Connection::dataArrivedOnSocket() + { + char tempReadBuffer[8192]; + int amountRead = 8192; + std::string tempRead; + while (amountRead == 8192) + { + amountRead = this->myNotificationServer()->externalCallbacks.getDataFromSocket(sock, tempReadBuffer, 8192); + if(amountRead < 0) + break; + tempRead+= std::string(tempReadBuffer,amountRead); + } + if (tempRead.length() < 0) + { + // We shouldn't be here because dataArrivedOnSocket + // is only called when select/poll etc has told us that + // the socket is readable. + // assert(errno != EAGAIN); + this->myNotificationServer()->externalCallbacks.showError(this, "No data to read"); + this->disconnect(); + } + else if (amountRead == 0) + { + this->myNotificationServer()->externalCallbacks.showError(this, "Connection closed by remote endpoint."); + this->disconnect(); + } + else + { + this->readBuffer += tempRead; +#ifdef DEBUG + std::cout << tempRead << std::endl; +#endif + try + { + handleIncomingData(); + } + catch (std::exception & e) + { + this->myNotificationServer()->externalCallbacks.showError(this, e.what()); + } + } + } + + void Connection::showError(int errorCode) + { + std::ostringstream buf_; + buf_ << "Error code: " << errorCode << " (" << errors[errorCode] << ")"; + this->myNotificationServer()->externalCallbacks.showError(this, buf_.str()); + } +} diff --git a/libs/libmsn/connection.h b/libs/libmsn/connection.h new file mode 100644 index 0000000..c92539b --- /dev/null +++ b/libs/libmsn/connection.h @@ -0,0 +1,154 @@ +#ifndef __msn_connection_h__ +#define __msn_connection_h__ + +/* + * connection.h + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning( disable : 4290 ) +#endif + +#include "libmsn_export.h" + +namespace MSN +{ + class callback; + class Message; + class Passport; + class NotificationServerConnection; + + /** An abstract base class that represents a connection to another computer. + * + * Connection provides an interface and some functionality that is common + * to notification, switchboard and file transfer connections. + * + */ + class LIBMSN_EXPORT Connection + { +public: + /** The socket which connects this Connection to another computer + * + * @deprecated In the future, this member will be made private. Any + * functions that access this member should be converted to + * member functions of a subclass. + */ + void *sock; + + /** Indicates whether a connection has been established. + */ + bool connected; + + +protected: + + std::string readBuffer; +public: + /** The transaction ID of the next command to be sent. + */ + int trID; + + Connection(); + virtual ~Connection(); + + /** Dispatch a command to its appropriate handler routines based on @a args. + * + * @param args A vector of strings containing arguments, returned from readLine. + */ + virtual void dispatchCommand(std::vector & args) = 0; + + /** Read a line from the network and split it into its components. + * + * MSN commands and their arguments are separated by white space. + */ + std::vector getLine(); + + bool isWholeLineAvailable(); + bool bytesAvailable(); + + /** Write a string to the connection. + * + */ + virtual size_t write(std::string s, bool log=true) throw (std::runtime_error); + + /** Write the contents of a stringstream to the connection. + * + * @param s The stringstream to write. + * @param log Should we log this output to the console. + * + * write will buffer the output if a connection has not yet been established. + * In this case, the data will be written as soon as a connection is + * established. + */ + virtual size_t write(std::ostringstream & s, bool log=true) throw (std::runtime_error); + + /** Connect ourself to @a hostname on @a port. + */ + virtual void connect(const std::string & hostname, unsigned int port) = 0; + virtual void disconnect() = 0; + + /** @name External Socket Hooks + * + * These members should be called whenever an appropriate socket event + * occurs. + */ + /** @{ */ + + /** New data is available on the connection. + */ + virtual void dataArrivedOnSocket(); + + /** The connection has been established. + */ + virtual void socketConnectionCompleted(); + + virtual void socketIsWritable() {}; + + /** An error has occurred on the socket. + */ + virtual void errorOnSocket(int errno_); + /** @} */ + + /** Notify the calling library that an error with code @a errorCode has + * occured. + */ + void showError(int errorCode); + + /** Is this Connection connected to a remote endpoint? + */ + bool isConnected() { return this->connected; }; + virtual NotificationServerConnection *myNotificationServer() = 0; + +protected: + virtual void handleIncomingData() = 0; +private: + std::string writeBuffer; + }; +} +#endif diff --git a/libs/libmsn/errorcodes.h b/libs/libmsn/errorcodes.h new file mode 100644 index 0000000..aedd2ab --- /dev/null +++ b/libs/libmsn/errorcodes.h @@ -0,0 +1,101 @@ +#ifndef __msn_errorcodes_h__ +#define __msn_errorcodes_h__ + +/* + * errorcodes.h + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "switchboardserver.h" + +#include "libmsn_export.h" + +/** \mainpage libmsn Reference + * + * libmsn is a C++ library for Microsoft's MSN Messenger service. It + * provides a high-level interface that allows an application to access instant + * messaging features with ease. For more information, please visit the + * libmsn homepage. + * + */ + + +/** Contains all of the functionality provided by libmsn. + */ +namespace MSN +{ + /** Error codes that the MSN servers may return in response to commands. + */ + typedef enum + { + ERR_SYNTAX_ERROR = 200, + ERR_INVALID_PARAMETER, + + ERR_INVALID_USER = 205, + ERR_FQDN_MISSING, + ERR_ALREADY_LOGIN, + ERR_INVALID_USERNAME, + ERR_INVALID_FRIENDLY_NAME, + ERR_LIST_FULL, + + ERR_ALREADY_THERE = 215, + ERR_NOT_ON_LIST, + + ERR_ALREADY_IN_THE_MODE = 218, + ERR_ALREADY_IN_OPPOSITE_LIST, + + ERR_SWITCHBOARD_FAILED = 280, + ERR_NOTIFY_XFR_FAILED, + + ERR_REQUIRED_FIELDS_MISSING = 300, + ERR_NOT_LOGGED_IN = 302, + + ERR_INTERNAL_SERVER = 500, + ERR_DB_SERVER = 501, + + ERR_FILE_OPERATION = 510, + ERR_MEMORY_ALLOC = 520, + + ERR_SERVER_BUSY = 600, + ERR_SERVER_UNAVAILABLE, + ERR_PEER_NS_DOWN, + ERR_DB_CONNECT, + ERR_SERVER_GOING_DOWN, + + ERR_CREATE_CONNECTION = 707, + + ERR_BLOCKING_WRITE = 711, + ERR_SESSION_OVERLOAD, + ERR_USER_TOO_ACTIVE, + ERR_TOO_MANY_SESSIONS, + ERR_NOT_EXPECTED, + ERR_BAD_FRIEND_FILE = 717, + + ERR_AUTHENTICATION_FAILED = 911, + ERR_NOT_ALLOWED_WHEN_OFFLINE = 913, + ERR_NOT_ACCEPTING_NEW_USERS = 920 + } ErrorCodes; +} + +#endif diff --git a/libs/libmsn/externals.h b/libs/libmsn/externals.h new file mode 100644 index 0000000..1fe7582 --- /dev/null +++ b/libs/libmsn/externals.h @@ -0,0 +1,333 @@ +#ifndef __msn_externals_h__ +#define __msn_externals_h__ + +/* + * externals.h + * libmsn + * + * Created by Meredydd Luff. + * Refactored by Tiago Salem Herrmann. + * Copyright (c) 2004 Meredydd Luff. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "buddy.h" +#include "util.h" + +#include "libmsn_export.h" + +namespace MSN +{ + class ListSyncInfo; + /** The application should implement these callback functions to + * be able to receive notifications from the library. + */ + class LIBMSN_EXPORT Callbacks + { + public: + + /** Asks the application that libmsn only must to be notified when the + * socket become readable and/or writable. + */ + virtual void registerSocket(void *sock, int read, int write, bool isSSL) = 0; + + /** Asks the application to never notify libmsn again about events in the socket. + */ + virtual void unregisterSocket(void *sock) = 0; + + /** Asks the application to close the socket + */ + virtual void closeSocket(void *sock) = 0; + + /** Allow your application to be notified about errors on library layer + */ + virtual void showError(MSN::Connection * conn, std::string msg) = 0; + + /** Notifies the application about a buddy status change. + * msnobject is the object describing avatar file. + * To get it you should call requestDisplayPicture() on a switchboard + * connection with this user. + */ + virtual void buddyChangedStatus(MSN::NotificationServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::BuddyStatus state, unsigned int clientID, std::string msnobject) = 0; + + /** Notifies the application about a user who went offline + */ + virtual void buddyOffline(MSN::NotificationServerConnection * conn, MSN::Passport buddy) = 0; + + /** Allow your application to log some network traffic + */ + virtual void log(int writing, const char* buf) = 0; + + /** Notifies the application that your friendly name now is 'friendlyname'. + * Probably this is the reply to a previous setFriendlyName() call. + */ + virtual void gotFriendlyName(MSN::NotificationServerConnection * conn, std::string friendlyname) = 0; + + /** Notifies the application that you have received your lists + * (Address Book, allow list, block list, reverse list and pending list) + * You must call completeConnection on notification server connection to + * complete the initial process. An example can be found in msntest.cpp + */ + virtual void gotBuddyListInfo(MSN::NotificationServerConnection * conn, MSN::ListSyncInfo * data) = 0; + + /** Notifies the application that a contact has updated his/her personal info. + * Example: Current Song, personal messages.. + * Check all the possibilities in MSN::personalInfo struct + */ + virtual void buddyChangedPersonalInfo(MSN::NotificationServerConnection * conn, MSN::Passport fromPassport, MSN::personalInfo pInfo) = 0; + + /** Notifies the application that one change was made to your + * lists, and the current timestamp is lastChange. + * This number should be used on initial process to specify + * what version of your lists you already have. If the server + * version is not the same, it will provide only the diff between your + * version and the current one. + * THE PARTIAL LIST FETCH IS NOT WORKING YET. + */ + virtual void gotLatestListSerial(MSN::NotificationServerConnection * conn, std::string lastChange) = 0; + + /** This is a response to a previous sent GTC command. + */ + virtual void gotGTC(MSN::NotificationServerConnection * conn, char c) = 0; + + + /** This is a response to a previous sent BLP command. + */ + virtual void gotBLP(MSN::NotificationServerConnection * conn, char c) = 0; + + /** Notifies your application that some list was modified by adding + * some new contact. If ContactList is MSN::LST_RL, it means someone + * added you to its Address Book. So at this point the application should + * prompt to the user about adding or blocking this new contact. + */ + virtual void addedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport buddy, std::string friendlyname) = 0; + + /** Notifies your application that some list was modified by removing + * some old contact. If ContactList is MSN::LST_RL, it means this contact + * has removed you from its Address Book. + */ + virtual void removedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport buddy) = 0; + + /** Notifies your application that a new group was created, or not, depending on + * the 'added' boolean variable. The application should track the groupId to permorm + * actions with it. + */ + virtual void addedGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupName, std::string groupId) = 0; + + /** Notifies your application that an old group was removed, or not, depending on + * the 'removed' boolean variable. + */ + virtual void removedGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupId) = 0; + + /** Notifies your application that an old group was renamed. + */ + virtual void renamedGroup(MSN::NotificationServerConnection * conn, bool renamed, std::string newGroupName, std::string groupId) = 0; + + /** Notifies your application that a contact was added to a group, or not, depending on the + * 'added' boolean variable. + */ + virtual void addedContactToGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupId, std::string contactId) = 0; + + /** Notifies your application that a contact was removed from a group, or not, depending on the + * 'removed' boolean variable. + */ + virtual void removedContactFromGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupId, std::string contactId) = 0; + + /** Notifies your application that a contact was added to the Address Book, or not, depending on the + * 'added' boolean variable. + */ + virtual void addedContactToAddressBook(MSN::NotificationServerConnection * conn, bool added, std::string passport, std::string displayName, std::string guid) = 0; + + /** Notifies your application that a contact was removed from the Address Book, or not, depending on the + * 'removed' boolean variable. + */ + virtual void removedContactFromAddressBook(MSN::NotificationServerConnection * conn, bool removed, std::string contactId, std::string passport) = 0; + + /** Notifies your application that a contact was enabled on Address Book, or not, depending on the + * 'enabled' boolean variable. + */ + virtual void enabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool enabled, std::string contactId, std::string passport) = 0; + + /** Notifies your application that a contact was disabled on Address Book, or not, depending on the + * 'disabled' boolean variable. + */ + virtual void disabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool disabled, std::string contactId) = 0; + + /** Notifies your application that you got a new switchboard connection + */ + virtual void gotSwitchboard(MSN::SwitchboardServerConnection * conn, const void * tag) = 0; + + /** Notifies your application that a contact joined to a switchboard connection + */ + virtual void buddyJoinedConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, int is_initial) = 0; + + /** Notifies your application that a contact left a switchboard connection + */ + virtual void buddyLeftConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy) = 0; + + /** Notifies your application that received an instant message + */ + virtual void gotInstantMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::Message * msg) = 0; + + /** Notifies your application that the "trID" message was received by the other contact + */ + virtual void gotMessageSentACK(MSN::SwitchboardServerConnection * conn, int trID) = 0; + + /** Notifies your application that received an emoticon notification. + * To get it you should call requestEmoticon() and pass msnobject to it + */ + virtual void gotEmoticonNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string alias, std::string msnobject) = 0; + + /** Notifies your application that a message could not be delivered + */ + virtual void failedSendingMessage(MSN::Connection * conn) = 0; + + /** Notifies your application that you got a nudge + */ + virtual void gotNudge(MSN::SwitchboardServerConnection * conn, MSN::Passport username) = 0; + + /** Notifies your application that you got a new voice clip. + * To get it you should call requestVoiceClip() and pass msnobject to it + */ + virtual void gotVoiceClipNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string msnobject) = 0; + + /** Notifies your application that you got a new wink. + * To get it you should call requestWink() and pass msnobject to it + */ + virtual void gotWinkNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string msnobject) = 0; + + /** Notifies your application that you got a new Ink. + */ + virtual void gotInk(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string image) = 0; + + /** Notifies your application that you got an action. + */ + virtual void gotActionMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string message) = 0; + + /** Notifies your application that a buddy is typing. + */ + virtual void buddyTyping(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname) = 0; + + /** Notifies your application that you got an initial email notification. + */ + virtual void gotInitialEmailNotification(MSN::NotificationServerConnection * conn, int msgs_inbox, int unread_inbox, int msgs_folders, int unread_folders) = 0; + + /** Notifies your application that you got an email notification. + */ + virtual void gotNewEmailNotification(MSN::NotificationServerConnection * conn, std::string from, std::string subject) = 0; + + /** Notifies your application about a progress of a file transfer. + */ + virtual void fileTransferProgress(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, long long unsigned transferred, long long unsigned total) = 0; + + /** Notifies your application that some file transfer has failed + */ + virtual void fileTransferFailed(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, MSN::fileTransferError error) = 0; + + /** Notifies your application that some file transfer has succeeded. + */ + virtual void fileTransferSucceeded(MSN::SwitchboardServerConnection * conn, unsigned int sessionID) = 0; + + /** Notifies your application that the other contact replied a file transfer + * invitation. if "response" is true, then the other contact has accepted, + * if false, rejected. + */ + virtual void fileTransferInviteResponse(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, bool response) = 0; + + /** Notifies your application that the other contact sent you a voice clip + */ + virtual void gotVoiceClipFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file) = 0; + + /** Notifies your application that the other contact sent you an emoticon + */ + virtual void gotEmoticonFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string alias, std::string file) = 0; + + /** Notifies your application that the other contact sent a wink + */ + virtual void gotWinkFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file) = 0; + + /** Notifies your application that there is a new connection + */ + virtual void gotNewConnection(MSN::Connection * conn) = 0; + + /** Notifies your application that you got a new OIM list. + */ + virtual void gotOIMList(MSN::NotificationServerConnection * conn, std::vector OIMs) = 0; + + /** Notifies your application that you got (or not) one previously requested OIM. + */ + virtual void gotOIM(MSN::NotificationServerConnection * conn, bool success, std::string id, std::string message) = 0; + + /** Notifies your application if a previously sent OIM was delivered. + */ + virtual void gotOIMSendConfirmation(MSN::NotificationServerConnection * conn, bool success, int id) = 0; + + /** Notifies your application if a previously request to delete one OIM was successful. + */ + virtual void gotOIMDeleteConfirmation(MSN::NotificationServerConnection * conn, bool success, std::string id) = 0; + + /** Notifies your application that you got a new display picture from "passaport" + */ + virtual void gotContactDisplayPicture(MSN::SwitchboardServerConnection * conn, MSN::Passport passport, std::string filename ) = 0; + + /** Notifies your application that the connection "conn" is ready + */ + virtual void connectionReady(MSN::Connection * conn) = 0; + + /** Notifies your application that the connection "conn" is closed + */ + virtual void closingConnection(MSN::Connection * conn) = 0; + + /** Notifies your application that a contact has changed his/her status + * ex: Online, Away, Out to Lunch.. + */ + virtual void changedStatus(MSN::NotificationServerConnection * conn, MSN::BuddyStatus state) = 0; + + /** Asks your application to create a socket with "server" at port "port". + * This function must return a socket reference that will be used for + * getDataFromSocket() and writeDataToSocket() + */ + virtual void * connectToServer(std::string server, int port, bool *connected, bool isSSL = false) = 0; + + /** Notifies your application that someone is trying to send you a file. + */ + virtual void askFileTransfer(MSN::SwitchboardServerConnection *conn, MSN::fileTransferInvite ft) = 0; + + virtual int listenOnPort(int port) = 0; + + virtual std::string getOurIP() = 0; + + virtual std::string getSecureHTTPProxy() = 0; + + virtual int getSocketFileDescriptor (void *sock) = 0; + + /** Asks your application to get @c size bytes of data available in @p sock + * and store them in @p data. + * It must return the real size written to @p data + */ + virtual size_t getDataFromSocket (void *sock, char *data, size_t size) = 0; + + /** Asks your application to write @c size bytes from @p data to @p sock. + * It must return the real size written to *sock + */ + virtual size_t writeDataToSocket (void *sock, char *data, size_t size) = 0; + + virtual void gotInboxUrl (MSN::NotificationServerConnection * /*conn*/, MSN::hotmailInfo /*info*/) {}; + }; +} +#endif diff --git a/libs/libmsn/libmsn_export.h b/libs/libmsn/libmsn_export.h new file mode 100644 index 0000000..176e58d --- /dev/null +++ b/libs/libmsn/libmsn_export.h @@ -0,0 +1,24 @@ +#ifndef _LIBMSN_EXPORT_H_ +#define _LIBMSN_EXPORT_H_ + +/* export classes under windows + * visibility can be added later under all other systems +*/ +#ifdef _MSC_VER +// we need to include windows.h here to not get compiler errors inside excpt.h (system header) +#include +#pragma warning( disable : 4251 ) +#pragma warning( disable : 4996 ) +#endif + +#ifdef _WIN32 +# ifdef msn_EXPORTS +# define LIBMSN_EXPORT __declspec(dllexport) +# else +# define LIBMSN_EXPORT __declspec(dllimport) +#endif +#else +# define LIBMSN_EXPORT +#endif + +#endif // _LIBMSN_EXPORT_H_ diff --git a/libs/libmsn/libsiren/common.cpp b/libs/libmsn/libsiren/common.cpp new file mode 100644 index 0000000..a85e672 --- /dev/null +++ b/libs/libmsn/libsiren/common.cpp @@ -0,0 +1,505 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "siren7.h" + +int region_size; +float region_size_inverse; + +float standard_deviation[64]; +float deviation_inverse[64]; +float region_power_table_boundary[63]; + +int expected_bits_table[8] = {52, 47, 43, 37, 29, 22, 16, 0}; +int vector_dimension[8] = {2, 2, 2, 4, 4, 5, 5, 1}; +int number_of_vectors[8] = {10, 10, 10, 5, 5, 4, 4, 20}; +float dead_zone[8] = {0.3f, 0.33f, 0.36f, 0.39f, 0.42f, 0.45f, 0.5f, 0.5f}; + +int max_bin[8] = { + 13, + 9, + 6, + 4, + 3, + 2, + 1, + 1}; + +float step_size[8] = { + 0.3536f, + 0.5f, + 0.70709997f, + 1.0f, + 1.4141999f, + 2.0f, + 2.8283999f, + 2.8283999f}; + +float step_size_inverse[8]; + +static int siren_initialized = 0; + +/* + STEPSIZE = 2.0 * log(sqrt(2)); +*/ +#define STEPSIZE 0.3010299957 + +void siren_init() { + int i; + float region_power; + + if (siren_initialized == 1) + return; + + region_size = 20; + region_size_inverse = 1.0f/region_size; + + for (i = 0; i < 64; i++) { + region_power = (float) pow(10, (i-24) * STEPSIZE); + standard_deviation[i] = (float) sqrt(region_power); + deviation_inverse[i] = (float) 1.0 / standard_deviation[i]; + } + + for (i = 0; i < 63; i++) + region_power_table_boundary[i] = (float) pow(10, (i-24 + 0.5) * STEPSIZE); + + for (i = 0; i < 8; i++) + step_size_inverse[i] = (float) 1.0 / step_size[i]; + + siren_dct4_init(); + siren_rmlt_init(); + + siren_initialized = 1; +} + + +int categorize_regions(int number_of_regions, int number_of_available_bits, int *absolute_region_power_index, int *power_categories, int *category_balance) { + int region, delta, i, temp; + int expected_number_of_code_bits; + int min, max; + int offset, + num_rate_control_possibilities, + raw_value, + raw_max_idx = 0, + raw_min_idx = 0; + int max_rate_categories[28]; + int min_rate_categories[28]; + int temp_category_balances[64]; + int *min_rate_ptr = NULL; + int *max_rate_ptr = NULL; + + if (number_of_regions == 14) { + num_rate_control_possibilities = 16; + if ( number_of_available_bits > 320) + number_of_available_bits = ((number_of_available_bits - 320) * 5/8) + 320; + } else { + num_rate_control_possibilities = 32; + if (number_of_regions == 28 && number_of_available_bits > 640) + number_of_available_bits = ((number_of_available_bits - 640) * 5/8) + 640; + } + + offset = -32; + for (delta = 32; number_of_regions > 0 && delta > 0; delta /= 2) { + expected_number_of_code_bits = 0; + for (region = 0; region < number_of_regions; region++) { + i = (delta + offset - absolute_region_power_index[region]) >> 1; + if (i > 7) + i = 7; + else if (i < 0) + i = 0; + + power_categories[region] = i; + expected_number_of_code_bits += expected_bits_table[i]; + + } + if (expected_number_of_code_bits >= number_of_available_bits-32) + offset += delta; + } + + expected_number_of_code_bits = 0; + for (region = 0; region < number_of_regions; region++) { + i = (offset - absolute_region_power_index[region]) >> 1; + if (i > 7) + i = 7; + else if (i < 0) + i = 0; + max_rate_categories[region] = min_rate_categories[region] = power_categories[region] = i; + expected_number_of_code_bits += expected_bits_table[i]; + } + + + min = max = expected_number_of_code_bits; + min_rate_ptr = max_rate_ptr = temp_category_balances + num_rate_control_possibilities; + for (i = 0; i < num_rate_control_possibilities -1; i++) { + if (min + max > number_of_available_bits * 2) { + raw_value = -99; + for (region = number_of_regions-1; region >= 0; region--) { + if (min_rate_categories[region] < 7) { + temp = offset - absolute_region_power_index[region] - 2*min_rate_categories[region]; + if (temp > raw_value) { + raw_value = temp; + raw_min_idx = region; + } + } + } + *min_rate_ptr++ = raw_min_idx; + min += expected_bits_table[min_rate_categories[raw_min_idx] + 1] - expected_bits_table[min_rate_categories[raw_min_idx]]; + min_rate_categories[raw_min_idx]++; + } else { + raw_value = 99; + for (region = 0; region < number_of_regions; region++) { + if (max_rate_categories[region] > 0 ) { + temp = offset - absolute_region_power_index[region] - 2*max_rate_categories[region]; + if (temp < raw_value) { + raw_value = temp; + raw_max_idx = region; + } + } + } + + *--max_rate_ptr = raw_max_idx; + max += expected_bits_table[max_rate_categories[raw_max_idx] - 1] - expected_bits_table[max_rate_categories[raw_max_idx]]; + max_rate_categories[raw_max_idx]--; + } + } + + for (region = 0; region < number_of_regions; region++) + power_categories[region] = max_rate_categories[region]; + + for (i = 0; i < num_rate_control_possibilities-1; i++) + category_balance[i] = *max_rate_ptr++; + + + return 0; +} + + + +/* + Looks like the flag means what kind of encoding is used + for now, it looks like : + 0 : the sample rate is not encoded in the frame + 1 - 2 : the sample rate is fixed in the frame + 3 : sample rate is variable and there is one for each frame +*/ + +int GetSirenCodecInfo(int flag, int sample_rate, int *number_of_coefs, int *sample_rate_bits, int *rate_control_bits, int *rate_control_possibilities, int *checksum_bits, int *esf_adjustment, int *scale_factor, int *number_of_regions, int *sample_rate_code, int *bits_per_frame ) { + switch (flag) { + case 0: + *number_of_coefs = 320; + *sample_rate_bits = 0; + *rate_control_bits = 4; + *rate_control_possibilities = 16; + *checksum_bits = 0; + *esf_adjustment = 7; + *number_of_regions = 14; + *sample_rate_code = 0; + *scale_factor = 22; + break; + case 1: + *number_of_coefs = 320; + *sample_rate_bits = 2; + *rate_control_bits = 4; + *rate_control_possibilities = 16; + *checksum_bits = 4; + *esf_adjustment = -2; + *number_of_regions = 14; + *scale_factor = 1; + if (sample_rate == 16000) + *sample_rate_code = 1; + else if (sample_rate == 24000) + *sample_rate_code = 2; + else if (sample_rate == 32000) + *sample_rate_code = 3; + else + return 3; + break; + case 2: + *number_of_coefs = 640; + *sample_rate_bits = 2; + *rate_control_bits = 5; + *rate_control_possibilities = 32; + *checksum_bits = 4; + *esf_adjustment = 7; + *number_of_regions = 28; + *scale_factor = 33; + + if (sample_rate == 24000) + *sample_rate_code = 1; + else if (sample_rate == 24000) + *sample_rate_code = 2; + else if (sample_rate == 48000) + *sample_rate_code = 3; + else + return 3; + + break; + case 3: + *number_of_coefs = 640; + *sample_rate_bits = 6; + *rate_control_bits = 5; + *rate_control_possibilities = 32; + *checksum_bits = 4; + *esf_adjustment = 7; + *scale_factor = 33; + + switch (sample_rate) { + case 8800: + *number_of_regions = 12; + *sample_rate_code = 59; + break; + case 9600: + *number_of_regions = 12; + *sample_rate_code = 1; + break; + case 10400: + *number_of_regions = 12; + *sample_rate_code = 13; + break; + case 10800: + *number_of_regions = 12; + *sample_rate_code = 14; + break; + case 11200: + *number_of_regions = 12; + *sample_rate_code = 15; + break; + case 11600: + *number_of_regions = 12; + *sample_rate_code = 16; + break; + case 12000: + *number_of_regions = 12; + *sample_rate_code = 2; + break; + case 12400: + *number_of_regions = 12; + *sample_rate_code = 17; + break; + case 12800: + *number_of_regions = 12; + *sample_rate_code = 18; + break; + case 13200: + *number_of_regions = 12; + *sample_rate_code = 19; + break; + case 13600: + *number_of_regions = 12; + *sample_rate_code = 20; + break; + case 14000: + *number_of_regions = 12; + *sample_rate_code = 21; + break; + case 14400: + *number_of_regions = 16; + *sample_rate_code = 3; + break; + case 14800: + *number_of_regions = 16; + *sample_rate_code = 22; + break; + case 15200: + *number_of_regions = 16; + *sample_rate_code = 23; + break; + case 15600: + *number_of_regions = 16; + *sample_rate_code = 24; + break; + case 16000: + *number_of_regions = 16; + *sample_rate_code = 25; + break; + case 16400: + *number_of_regions = 16; + *sample_rate_code = 26; + break; + case 16800: + *number_of_regions = 18; + *sample_rate_code = 4; + break; + case 17200: + *number_of_regions = 18; + *sample_rate_code = 27; + break; + case 17600: + *number_of_regions = 18; + *sample_rate_code = 28; + break; + case 18000: + *number_of_regions = 18; + *sample_rate_code = 29; + break; + case 18400: + *number_of_regions = 18; + *sample_rate_code = 30; + break; + case 18800: + *number_of_regions = 18; + *sample_rate_code = 31; + break; + case 19200: + *number_of_regions = 20; + *sample_rate_code = 5; + break; + case 19600: + *number_of_regions = 20; + *sample_rate_code = 32; + break; + case 20000: + *number_of_regions = 20; + *sample_rate_code = 33; + break; + case 20400: + *number_of_regions = 20; + *sample_rate_code = 34; + break; + case 20800: + *number_of_regions = 20; + *sample_rate_code = 35; + break; + case 21200: + *number_of_regions = 20; + *sample_rate_code = 36; + break; + case 21600: + *number_of_regions = 22; + *sample_rate_code = 6; + break; + case 22000: + *number_of_regions = 22; + *sample_rate_code = 37; + break; + case 22400: + *number_of_regions = 22; + *sample_rate_code = 38; + break; + case 22800: + *number_of_regions = 22; + *sample_rate_code = 39; + break; + case 23200: + *number_of_regions = 22; + *sample_rate_code = 40; + break; + case 23600: + *number_of_regions = 22; + *sample_rate_code = 41; + break; + case 24000: + *number_of_regions = 24; + *sample_rate_code = 7; + break; + case 24400: + *number_of_regions = 24; + *sample_rate_code = 42; + break; + case 24800: + *number_of_regions = 24; + *sample_rate_code = 43; + break; + case 25200: + *number_of_regions = 24; + *sample_rate_code = 44; + break; + case 25600: + *number_of_regions = 24; + *sample_rate_code = 45; + break; + case 26000: + *number_of_regions = 24; + *sample_rate_code = 46; + break; + case 26400: + *number_of_regions = 26; + *sample_rate_code = 8; + break; + case 26800: + *number_of_regions = 26; + *sample_rate_code = 47; + break; + case 27200: + *number_of_regions = 26; + *sample_rate_code = 48; + break; + case 27600: + *number_of_regions = 26; + *sample_rate_code = 49; + break; + case 28000: + *number_of_regions = 26; + *sample_rate_code = 50; + break; + case 28400: + *number_of_regions = 26; + *sample_rate_code = 51; + break; + case 28800: + *number_of_regions = 28; + *sample_rate_code = 9; + break; + case 29200: + *number_of_regions = 28; + *sample_rate_code = 52; + break; + case 29600: + *number_of_regions = 28; + *sample_rate_code = 53; + break; + case 30000: + *number_of_regions = 28; + *sample_rate_code = 54; + break; + case 30400: + *number_of_regions = 28; + *sample_rate_code = 55; + break; + case 30800: + *number_of_regions = 28; + *sample_rate_code = 56; + break; + case 31200: + *number_of_regions = 28; + *sample_rate_code = 10; + break; + case 31600: + *number_of_regions = 28; + *sample_rate_code = 57; + break; + case 32000: + *number_of_regions = 28; + *sample_rate_code = 58; + break; + default: + return 3; + break; + } + break; + default: + return 6; + } + + *bits_per_frame = sample_rate / 50; + return 0; +} + diff --git a/libs/libmsn/libsiren/common.h b/libs/libmsn/libsiren/common.h new file mode 100644 index 0000000..3641654 --- /dev/null +++ b/libs/libmsn/libsiren/common.h @@ -0,0 +1,146 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _SIREN_COMMON_H +#define _SIREN_COMMON_H + +typedef struct { + unsigned int RiffId; + unsigned int RiffSize; +} RiffHeader; + +typedef struct { + unsigned short Format; + unsigned short Channels; + unsigned int SampleRate; + unsigned int ByteRate; + unsigned short BlockAlign; + unsigned short BitsPerSample; +} FmtChunk; + + +typedef struct { + FmtChunk fmt; + unsigned short ExtraSize; + unsigned short DctLength; +} SirenFmtChunk; + +typedef struct { + RiffHeader riff; + unsigned int WaveId; + + unsigned int FmtId; + unsigned int FmtSize; + + SirenFmtChunk fmt; + + unsigned int FactId; + unsigned int FactSize; + unsigned int Samples; + + unsigned int DataId; + unsigned int DataSize; +} SirenWavHeader; + +typedef struct { + RiffHeader riff; + unsigned int WaveId; + + unsigned int FmtId; + unsigned int FmtSize; + + FmtChunk fmt; + + unsigned int FactId; + unsigned int FactSize; + unsigned int Samples; + + unsigned int DataId; + unsigned int DataSize; +} PCMWavHeader; + +#define RIFF_ID 0x46464952 +#define WAVE_ID 0x45564157 +#define FMT__ID 0x20746d66 +#define DATA_ID 0x61746164 +#define FACT_ID 0x74636166 + + +extern int region_size; +extern float region_size_inverse; +extern float standard_deviation[64]; +extern float deviation_inverse[64]; +extern float region_power_table_boundary[63]; +extern int expected_bits_table[8]; +extern int vector_dimension[8]; +extern int number_of_vectors[8]; +extern float dead_zone[8]; +extern int max_bin[8]; +extern float step_size[8]; +extern float step_size_inverse[8]; + + + +extern void siren_init(); +extern int categorize_regions(int number_of_regions, int number_of_available_bits, int *absolute_region_power_index, int *power_categories, int *category_balance); +extern int GetSirenCodecInfo(int flag, int sample_rate, int *number_of_coefs, int *sample_rate_bits, int *rate_control_bits, int *rate_control_possibilities, int *checksum_bits, int *esf_adjustment, int *scale_factor, int *number_of_regions, int *sample_rate_code, int *bits_per_frame ); + + +#ifdef __BIG_ENDIAN__ + +#define POW_2_8 256 +#define POW_2_16 65536 +#define POW_2_24 16777216 + +#define IDX(val, i) ((unsigned int) ((unsigned char *) &val)[i]) + + + +#define ME_FROM_LE16(val) ( (unsigned short) ( IDX(val, 0) + IDX(val, 1) * 256 )) +#define ME_FROM_LE32(val) ( (unsigned int) (IDX(val, 0) + IDX(val, 1) * 256 + \ + IDX(val, 2) * 65536 + IDX(val, 3) * 16777216)) + + +#define ME_TO_LE16(val) ( (unsigned short) ( \ + (((unsigned short)val % 256) & 0xff) << 8 | \ + ((((unsigned short)val / POW_2_8) % 256) & 0xff) )) + +#define ME_TO_LE32(val) ( (unsigned int) ( \ + ((((unsigned int) val ) % 256) & 0xff) << 24 | \ + ((((unsigned int) val / POW_2_8 ) % 256) & 0xff) << 16| \ + ((((unsigned int) val / POW_2_16) % 256) & 0xff) << 8 | \ + ((((unsigned int) val / POW_2_24) % 256) & 0xff) )) + +#else + +#define ME_TO_LE16(val) ( (unsigned short) (val)) +#define ME_TO_LE32(val) ( (unsigned int) (val)) +#define ME_FROM_LE16(val) ( (unsigned short) (val)) +#define ME_FROM_LE32(val) ( (unsigned int) (val)) + + +#endif + + + +#endif /* _SIREN_COMMON_H */ + diff --git a/libs/libmsn/libsiren/dct4.cpp b/libs/libmsn/libsiren/dct4.cpp new file mode 100644 index 0000000..2fee488 --- /dev/null +++ b/libs/libmsn/libsiren/dct4.cpp @@ -0,0 +1,184 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "siren7.h" + + +#define PI 3.1415926 + +typedef struct { + float cos; + float msin; +} dct_table_type; + +static float dct_core_320[100]; +static float dct_core_640[100]; +static dct_table_type dct_table_5[5]; +static dct_table_type dct_table_10[10]; +static dct_table_type dct_table_20[20]; +static dct_table_type dct_table_40[40]; +static dct_table_type dct_table_80[80]; +static dct_table_type dct_table_160[160]; +static dct_table_type dct_table_320[320]; +static dct_table_type dct_table_640[640]; +static dct_table_type *dct_tables[8] = {dct_table_5, + dct_table_10, + dct_table_20, + dct_table_40, + dct_table_80, + dct_table_160, + dct_table_320, + dct_table_640}; + +static int dct4_initialized = 0; + +void siren_dct4_init() { + int i, j = 0; + double scale_320 = (float) sqrt(2.0/320); + double scale_640 = (float) sqrt(2.0/640); + double angle; + double scale; + + /* set up dct4 tables */ + for(i = 0; i < 10; i++) { + angle = (float) ((i + 0.5) * PI); + for (j = 0 ; j < 10; j++) { + dct_core_320[(i*10)+j] = (float) (scale_320 * cos((j + 0.5) * angle / 10)); + dct_core_640[(i*10)+j] = (float) (scale_640 * cos((j + 0.5) * angle / 10)); + } + } + + for(i = 0; i < 8; i++) { + scale = (float) (PI / ((5 << i) * 4)); + for (j = 0 ; j < (5 << i); j++) { + angle = (float) (j + 0.5) * scale; + dct_tables[i][j].cos = (float) cos(angle); + dct_tables[i][j].msin = (float) -sin(angle); + } + } + + dct4_initialized = 1; +} + + +void siren_dct4(float *Source, float *Destination, int dct_length) { + int log_length = 0; + float * dct_core = NULL; + dct_table_type ** dct_table_ptr_ptr = NULL; + dct_table_type * dct_table_ptr = NULL; + float OutBuffer1[640]; + float OutBuffer2[640]; + float *Out_ptr; + float *NextOut_ptr; + float *In_Ptr = NULL; + float *In_Ptr_low = NULL; + float *In_Ptr_high = NULL; + float In_val_low; + float In_val_high; + float *Out_ptr_low = NULL; + float *Out_ptr_high = NULL; + float mult1, mult2, mult3, mult4, mult5, mult6, mult7, mult8, mult9, mult10; + int i,j; + + if (dct4_initialized == 0) + siren_dct4_init(); + + if (dct_length == 640) { + log_length = 5; + dct_core = dct_core_640; + } else { + log_length = 4; + dct_core = dct_core_320; + } + + Out_ptr = OutBuffer1; + NextOut_ptr = OutBuffer2; + In_Ptr = Source; + for (i = 0; i <= log_length; i++) { + for (j = 0; j < (1 << i); j++) { + Out_ptr_low = Out_ptr + (j * (dct_length >> i)); + Out_ptr_high = Out_ptr + ( (j+1) * (dct_length >> i)); + do { + In_val_low = *In_Ptr++; + In_val_high = *In_Ptr++; + *Out_ptr_low++ = In_val_low + In_val_high; + *--Out_ptr_high = In_val_low - In_val_high; + } while (Out_ptr_low < Out_ptr_high); + } + + In_Ptr = Out_ptr; + Out_ptr = NextOut_ptr; + NextOut_ptr = In_Ptr; + } + + for (i = 0; i < (2 << log_length); i++) { + for (j = 0 ; j < 10 ; j ++) { + mult1 = In_Ptr[(i*10)] * dct_core[j*10]; + mult2 = In_Ptr[(i*10) + 1] * dct_core[(j*10) + 1]; + mult3 = In_Ptr[(i*10) + 2] * dct_core[(j*10) + 2]; + mult4 = In_Ptr[(i*10) + 3] * dct_core[(j*10) + 3]; + mult5 = In_Ptr[(i*10) + 4] * dct_core[(j*10) + 4]; + mult6 = In_Ptr[(i*10) + 5] * dct_core[(j*10) + 5]; + mult7 = In_Ptr[(i*10) + 6] * dct_core[(j*10) + 6]; + mult8 = In_Ptr[(i*10) + 7] * dct_core[(j*10) + 7]; + mult9 = In_Ptr[(i*10) + 8] * dct_core[(j*10) + 8]; + mult10 = In_Ptr[(i*10) + 9] * dct_core[(j*10) + 9]; + Out_ptr[(i*10)+j] = mult1 + mult2 + mult3 + mult4 + + mult5 + mult6 + mult7 + mult8 + + mult9 + mult10; + } + } + + + In_Ptr = Out_ptr; + Out_ptr = NextOut_ptr; + NextOut_ptr = In_Ptr; + dct_table_ptr_ptr = dct_tables; + for (i = log_length; i >= 0; i--) { + dct_table_ptr_ptr++; + for (j = 0; j < (1 << i); j++) { + dct_table_ptr = *dct_table_ptr_ptr; + if ( i == 0 ) + Out_ptr_low = Destination + (j * (dct_length >> i)); + else + Out_ptr_low = Out_ptr + (j * (dct_length >> i)); + + Out_ptr_high = Out_ptr_low + (dct_length >> i); + + In_Ptr_low = In_Ptr + (j * (dct_length >> i)); + In_Ptr_high = In_Ptr_low + (dct_length >> (i+1)); + do { + *Out_ptr_low++ = (*In_Ptr_low * (*dct_table_ptr).cos) - (*In_Ptr_high * (*dct_table_ptr).msin); + *--Out_ptr_high = (*In_Ptr_high++ * (*dct_table_ptr).cos) + (*In_Ptr_low++ * (*dct_table_ptr).msin); + dct_table_ptr++; + *Out_ptr_low++ = (*In_Ptr_low * (*dct_table_ptr).cos) + (*In_Ptr_high * (*dct_table_ptr).msin); + *--Out_ptr_high = (*In_Ptr_low++ * (*dct_table_ptr).msin) - (*In_Ptr_high++ * (*dct_table_ptr).cos); + dct_table_ptr++; + } while (Out_ptr_low < Out_ptr_high); + } + + In_Ptr = Out_ptr; + Out_ptr = NextOut_ptr; + NextOut_ptr = In_Ptr; + } + +} diff --git a/libs/libmsn/libsiren/dct4.h b/libs/libmsn/libsiren/dct4.h new file mode 100644 index 0000000..6394b13 --- /dev/null +++ b/libs/libmsn/libsiren/dct4.h @@ -0,0 +1,30 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _SIREN7_DCT4_H_ +#define _SIREN7_DCT4_H_ + +extern void siren_dct4_init(); +extern void siren_dct4(float *Source, float *Destination, int dct_length); + + +#endif /* _SIREN7_DCT4_H_ */ diff --git a/libs/libmsn/libsiren/decoder.cpp b/libs/libmsn/libsiren/decoder.cpp new file mode 100644 index 0000000..5a18389 --- /dev/null +++ b/libs/libmsn/libsiren/decoder.cpp @@ -0,0 +1,234 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "siren7.h" + +SirenDecoder Siren7_NewDecoder(int sample_rate) { + SirenDecoder decoder = (SirenDecoder) malloc(sizeof(struct stSirenDecoder)); + decoder->sample_rate = sample_rate; + + decoder->WavHeader.riff.RiffId = ME_TO_LE32(RIFF_ID); + decoder->WavHeader.riff.RiffSize = sizeof(PCMWavHeader) - 2*sizeof(int); + decoder->WavHeader.riff.RiffSize = ME_TO_LE32(decoder->WavHeader.riff.RiffSize); + decoder->WavHeader.WaveId = ME_TO_LE32(WAVE_ID); + + decoder->WavHeader.FmtId = ME_TO_LE32(FMT__ID); + decoder->WavHeader.FmtSize = ME_TO_LE32(sizeof(FmtChunk)); + + decoder->WavHeader.fmt.Format = ME_TO_LE16(0x01); + decoder->WavHeader.fmt.Channels = ME_TO_LE16(1); + decoder->WavHeader.fmt.SampleRate = ME_TO_LE32(16000); + decoder->WavHeader.fmt.ByteRate = ME_TO_LE32(32000); + decoder->WavHeader.fmt.BlockAlign = ME_TO_LE16(2); + decoder->WavHeader.fmt.BitsPerSample = ME_TO_LE16(16); + + decoder->WavHeader.FactId = ME_TO_LE32(FACT_ID); + decoder->WavHeader.FactSize = ME_TO_LE32(sizeof(int)); + decoder->WavHeader.Samples = ME_TO_LE32(0); + + decoder->WavHeader.DataId = ME_TO_LE32(DATA_ID); + decoder->WavHeader.DataSize = ME_TO_LE32(0); + + memset(decoder->context, 0, sizeof(decoder->context)); + memset(decoder->backup_frame, 0, sizeof(decoder->backup_frame)); + + decoder->dw1 = 1; + decoder->dw2 = 1; + decoder->dw3 = 1; + decoder->dw4 = 1; + + siren_init(); + return decoder; +} + +void Siren7_CloseDecoder(SirenDecoder decoder) { + free(decoder); +} + +int Siren7_DecodeFrame(SirenDecoder decoder, unsigned char *DataIn, unsigned char *DataOut) { + int number_of_coefs, + sample_rate_bits, + rate_control_bits, + rate_control_possibilities, + checksum_bits, + esf_adjustment, + scale_factor, + number_of_regions, + sample_rate_code, + bits_per_frame; + int decoded_sample_rate_code; + + static int absolute_region_power_index[28] = {0}; + static float decoder_standard_deviation[28] = {0}; + static int power_categories[28] = {0}; + static int category_balance[28] = {0}; + int ChecksumTable[4] = {0x7F80, 0x7878, 0x6666, 0x5555}; + int i, j; + + int dwRes = 0; + int envelope_bits = 0; + int rate_control = 0; + int number_of_available_bits; + int number_of_valid_coefs; + int frame_error = 0; + + int In[20]; + float coefs[320]; + float BufferOut[320]; + int sum; + int checksum; + int calculated_checksum; + int idx; + int temp1; + int temp2; + + for (i = 0; i < 20; i++) +#ifdef __BIG_ENDIAN__ + In[i] = ((short *) DataIn)[i]; +#else + In[i] = ((((short *) DataIn)[i] << 8) & 0xFF00) | ((((short *) DataIn)[i] >> 8) & 0x00FF); +#endif + + dwRes = GetSirenCodecInfo(1, decoder->sample_rate, &number_of_coefs, &sample_rate_bits, &rate_control_bits, &rate_control_possibilities, &checksum_bits, &esf_adjustment, &scale_factor, &number_of_regions, &sample_rate_code, &bits_per_frame ); + + if (dwRes != 0) + return dwRes; + + + set_bitstream(In); + + decoded_sample_rate_code = 0; + for (i = 0; i < sample_rate_bits; i++) { + decoded_sample_rate_code <<= 1; + decoded_sample_rate_code |= next_bit(); + } + + + if (decoded_sample_rate_code != sample_rate_code) + return 7; + + number_of_valid_coefs = region_size * number_of_regions; + number_of_available_bits = bits_per_frame - sample_rate_bits - checksum_bits ; + + + envelope_bits = decode_envelope(number_of_regions, decoder_standard_deviation, absolute_region_power_index, esf_adjustment); + + number_of_available_bits -= envelope_bits; + + for (i = 0; i < rate_control_bits; i++) { + rate_control <<= 1; + rate_control |= next_bit(); + } + + number_of_available_bits -= rate_control_bits; + + categorize_regions(number_of_regions, number_of_available_bits, absolute_region_power_index, power_categories, category_balance); + + for (i = 0; i < rate_control; i++) { + power_categories[category_balance[i]]++; + } + + number_of_available_bits = decode_vector(decoder, number_of_regions, number_of_available_bits, decoder_standard_deviation, power_categories, coefs, scale_factor); + + + frame_error = 0; + if (number_of_available_bits > 0) { + for (i = 0; i < number_of_available_bits; i++) { + if (next_bit() == 0) + frame_error = 1; + } + } else if (number_of_available_bits < 0 && rate_control + 1 < rate_control_possibilities) { + frame_error |= 2; + } + + for (i = 0; i < number_of_regions; i++) { + if (absolute_region_power_index[i] > 33 || absolute_region_power_index[i] < -31) + frame_error |= 4; + } + + if (checksum_bits > 0) { + bits_per_frame >>= 4; + checksum = In[bits_per_frame - 1] & ((1 << checksum_bits) - 1); + In[bits_per_frame - 1] &= ~checksum; + sum = 0; + idx = 0; + do { + sum ^= (In[idx] & 0xFFFF) << (idx % 15); + } while (++idx < bits_per_frame); + + sum = (sum >> 15) ^ (sum & 0x7FFF); + calculated_checksum = 0; + for (i = 0; i < 4; i++) { + temp1 = ChecksumTable[i] & sum; + for (j = 8; j > 0; j >>= 1) { + temp2 = temp1 >> j; + temp1 ^= temp2; + } + calculated_checksum <<= 1; + calculated_checksum |= temp1 & 1; + } + + if (checksum != calculated_checksum) + frame_error |= 8; + } + + if (frame_error != 0) { + for (i = 0; i < number_of_valid_coefs; i++) { + coefs[i] = decoder->backup_frame[i]; + decoder->backup_frame[i] = 0; + } + } else { + for (i = 0; i < number_of_valid_coefs; i++) + decoder->backup_frame[i] = coefs[i]; + } + + + for (i = number_of_valid_coefs; i < number_of_coefs; i++) + coefs[i] = 0; + + + dwRes = siren_rmlt_decode_samples(coefs, decoder->context, 320, BufferOut); + + + for (i = 0; i < 320; i++) { + if (BufferOut[i] > 32767.0) + ((short *)DataOut)[i] = (short) ME_TO_LE16((short) 32767); + else if (BufferOut[i] <= -32768.0) + ((short *)DataOut)[i] = (short) ME_TO_LE16((short) 32768); + else + ((short *)DataOut)[i] = (short) ME_TO_LE16((short) BufferOut[i]); + } + + decoder->WavHeader.Samples = ME_FROM_LE32(decoder->WavHeader.Samples); + decoder->WavHeader.Samples += 320; + decoder->WavHeader.Samples = ME_TO_LE32(decoder->WavHeader.Samples); + decoder->WavHeader.DataSize = ME_FROM_LE32(decoder->WavHeader.DataSize); + decoder->WavHeader.DataSize += 640; + decoder->WavHeader.DataSize = ME_TO_LE32(decoder->WavHeader.DataSize); + decoder->WavHeader.riff.RiffSize = ME_FROM_LE32(decoder->WavHeader.riff.RiffSize); + decoder->WavHeader.riff.RiffSize += 640; + decoder->WavHeader.riff.RiffSize = ME_TO_LE32(decoder->WavHeader.riff.RiffSize); + + + return 0; +} + diff --git a/libs/libmsn/libsiren/decoder.h b/libs/libmsn/libsiren/decoder.h new file mode 100644 index 0000000..58c1406 --- /dev/null +++ b/libs/libmsn/libsiren/decoder.h @@ -0,0 +1,52 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _SIREN_DECODER_H +#define _SIREN_DECODER_H + +#include +#include +#include +#include "dct4.h" +#include "rmlt.h" +#include "huffman.h" +#include "common.h" + + +typedef struct stSirenDecoder { + int sample_rate; + PCMWavHeader WavHeader; + float context[320]; + float backup_frame[320]; + int dw1; + int dw2; + int dw3; + int dw4; +} * SirenDecoder; + + +/* MUST be 16000 to be compatible with MSN Voice clips (I think) */ +extern SirenDecoder Siren7_NewDecoder(int sample_rate); +extern void Siren7_CloseDecoder(SirenDecoder decoder); +extern int Siren7_DecodeFrame(SirenDecoder decoder, unsigned char *DataIn, unsigned char *DataOut); + +#endif /* _SIREN_DECODER_H */ diff --git a/libs/libmsn/libsiren/encoder.cpp b/libs/libmsn/libsiren/encoder.cpp new file mode 100644 index 0000000..9d89eb5 --- /dev/null +++ b/libs/libmsn/libsiren/encoder.cpp @@ -0,0 +1,234 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + + +#include "siren7.h" + + +SirenEncoder Siren7_NewEncoder(int sample_rate) { + SirenEncoder encoder = (SirenEncoder) malloc(sizeof(struct stSirenEncoder)); + encoder->sample_rate = sample_rate; + + encoder->WavHeader.riff.RiffId = ME_TO_LE32(RIFF_ID); + encoder->WavHeader.riff.RiffSize = sizeof(SirenWavHeader) - 2*sizeof(int); + encoder->WavHeader.riff.RiffSize = ME_TO_LE32(encoder->WavHeader.riff.RiffSize); + encoder->WavHeader.WaveId = ME_TO_LE32(WAVE_ID); + + encoder->WavHeader.FmtId = ME_TO_LE32(FMT__ID); + encoder->WavHeader.FmtSize = ME_TO_LE32(sizeof(SirenFmtChunk)); + + encoder->WavHeader.fmt.fmt.Format = ME_TO_LE16(0x028E); + encoder->WavHeader.fmt.fmt.Channels = ME_TO_LE16(1); + encoder->WavHeader.fmt.fmt.SampleRate = ME_TO_LE32(16000); + encoder->WavHeader.fmt.fmt.ByteRate = ME_TO_LE32(2000); + encoder->WavHeader.fmt.fmt.BlockAlign = ME_TO_LE16(40); + encoder->WavHeader.fmt.fmt.BitsPerSample = ME_TO_LE16(0); + encoder->WavHeader.fmt.ExtraSize = ME_TO_LE16(2); + encoder->WavHeader.fmt.DctLength = ME_TO_LE16(320); + + encoder->WavHeader.FactId = ME_TO_LE32(FACT_ID); + encoder->WavHeader.FactSize = ME_TO_LE32(sizeof(int)); + encoder->WavHeader.Samples = ME_TO_LE32(0); + + encoder->WavHeader.DataId = ME_TO_LE32(DATA_ID); + encoder->WavHeader.DataSize = ME_TO_LE32(0); + + memset(encoder->context, 0, sizeof(encoder->context)); + + siren_init(); + return encoder; +} + +void Siren7_CloseEncoder(SirenEncoder encoder) { + free(encoder); +} + + + +int Siren7_EncodeFrame(SirenEncoder encoder, unsigned char *DataIn, unsigned char *DataOut) { + int number_of_coefs, + sample_rate_bits, + rate_control_bits, + rate_control_possibilities, + checksum_bits, + esf_adjustment, + scale_factor, + number_of_regions, + sample_rate_code, + bits_per_frame; + int sample_rate = encoder->sample_rate; + + static int absolute_region_power_index[28] = {0}; + static int power_categories[28] = {0}; + static int category_balance[28] = {0}; + static int drp_num_bits[30] = {0}; + static int drp_code_bits[30] = {0}; + static int region_mlt_bit_counts[28] = {0}; + static int region_mlt_bits[112] = {0}; + int ChecksumTable[4] = {0x7F80, 0x7878, 0x6666, 0x5555}; + int i, j; + + int dwRes = 0; + short out_word; + int bits_left; + int current_word_bits_left; + int region_bit_count; + unsigned int current_word; + unsigned int sum; + unsigned int checksum; + int temp1 = 0; + int temp2 = 0; + int region; + int idx = 0; + int envelope_bits = 0; + int rate_control; + int number_of_available_bits; + + float coefs[320]; + float In[320]; + short BufferOut[20]; + float *context = encoder->context; + + for (i = 0; i < 320; i++) + In[i] = (float) ((short) ME_FROM_LE16(((short *) DataIn)[i])); + + dwRes = siren_rmlt_encode_samples(In, context, 320, coefs); + + + if (dwRes != 0) + return dwRes; + + dwRes = GetSirenCodecInfo(1, sample_rate, &number_of_coefs, &sample_rate_bits, &rate_control_bits, &rate_control_possibilities, &checksum_bits, &esf_adjustment, &scale_factor, &number_of_regions, &sample_rate_code, &bits_per_frame ); + + if (dwRes != 0) + return dwRes; + + envelope_bits = compute_region_powers(number_of_regions, coefs, drp_num_bits, drp_code_bits, absolute_region_power_index, esf_adjustment); + + number_of_available_bits = bits_per_frame - rate_control_bits - envelope_bits - sample_rate_bits - checksum_bits ; + + categorize_regions(number_of_regions, number_of_available_bits, absolute_region_power_index, power_categories, category_balance); + + for(region = 0; region < number_of_regions; region++) { + absolute_region_power_index[region] += 24; + region_mlt_bit_counts[region] = 0; + } + + rate_control = quantize_mlt(number_of_regions, rate_control_possibilities, number_of_available_bits, coefs, absolute_region_power_index, power_categories, category_balance, region_mlt_bit_counts, region_mlt_bits); + + idx = 0; + bits_left = 16 - sample_rate_bits; + out_word = sample_rate_code << (16 - sample_rate_bits); + drp_num_bits[number_of_regions] = rate_control_bits; + drp_code_bits[number_of_regions] = rate_control; + for (region = 0; region <= number_of_regions; region++) { + i = drp_num_bits[region] - bits_left; + if (i < 0) { + out_word += drp_code_bits[region] << -i; + bits_left -= drp_num_bits[region]; + } else { + BufferOut[idx++] = out_word + (drp_code_bits[region] >> i); + bits_left += 16 - drp_num_bits[region]; + out_word = drp_code_bits[region] << bits_left; + } + } + + for (region = 0; region < number_of_regions && (16*idx) < bits_per_frame; region++) { + current_word_bits_left = region_bit_count = region_mlt_bit_counts[region]; + if (current_word_bits_left > 32) + current_word_bits_left = 32; + + current_word = region_mlt_bits[region*4]; + i = 1; + while(region_bit_count > 0 && (16*idx) < bits_per_frame) { + if (current_word_bits_left < bits_left) { + bits_left -= current_word_bits_left; + out_word += (current_word >> (32 - current_word_bits_left)) << bits_left; + current_word_bits_left = 0; + } else { + BufferOut[idx++] = (short) (out_word + (current_word >> (32 - bits_left))); + current_word_bits_left -= bits_left; + current_word <<= bits_left; + bits_left = 16; + out_word = 0; + } + if (current_word_bits_left == 0) { + region_bit_count -= 32; + current_word = region_mlt_bits[(region*4) + i++]; + current_word_bits_left = region_bit_count; + if (current_word_bits_left > 32) + current_word_bits_left = 32; + } + } + } + + + while ( (16*idx) < bits_per_frame) { + BufferOut[idx++] = (short) ((0xFFFF >> (16 - bits_left)) + out_word); + bits_left = 16; + out_word = 0; + } + + if (checksum_bits > 0) { + BufferOut[idx-1] &= (-1 << checksum_bits); + sum = 0; + idx = 0; + do { + sum ^= (BufferOut[idx] & 0xFFFF) << (idx % 15); + } while ((16*++idx) < bits_per_frame); + + sum = (sum >> 15) ^ (sum & 0x7FFF); + checksum = 0; + for (i = 0; i < 4; i++) { + temp1 = ChecksumTable[i] & sum; + for (j = 8; j > 0; j >>= 1) { + temp2 = temp1 >> j; + temp1 ^= temp2; + } + checksum <<= 1; + checksum |= temp1 & 1; + } + BufferOut[idx-1] |= ((1 << checksum_bits) -1) & checksum; + } + + + for (i = 0; i < 20; i++) +#ifdef __BIG_ENDIAN__ + ((short *) DataOut)[i] = BufferOut[i]; +#else + ((short *) DataOut)[i] = ((BufferOut[i] << 8) & 0xFF00) | ((BufferOut[i] >> 8) & 0x00FF); +#endif + + encoder->WavHeader.Samples = ME_FROM_LE32(encoder->WavHeader.Samples); + encoder->WavHeader.Samples += 320; + encoder->WavHeader.Samples = ME_TO_LE32(encoder->WavHeader.Samples); + encoder->WavHeader.DataSize = ME_FROM_LE32(encoder->WavHeader.DataSize); + encoder->WavHeader.DataSize += 40; + encoder->WavHeader.DataSize = ME_TO_LE32(encoder->WavHeader.DataSize); + encoder->WavHeader.riff.RiffSize = ME_FROM_LE32(encoder->WavHeader.riff.RiffSize); + encoder->WavHeader.riff.RiffSize += 40; + encoder->WavHeader.riff.RiffSize = ME_TO_LE32(encoder->WavHeader.riff.RiffSize); + + + return 0; +} + diff --git a/libs/libmsn/libsiren/encoder.h b/libs/libmsn/libsiren/encoder.h new file mode 100644 index 0000000..a53185c --- /dev/null +++ b/libs/libmsn/libsiren/encoder.h @@ -0,0 +1,47 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _SIREN_ENCODER_H +#define _SIREN_ENCODER_H + +#include +#include +#include +#include "dct4.h" +#include "rmlt.h" +#include "huffman.h" +#include "common.h" + + +typedef struct stSirenEncoder { + int sample_rate; + SirenWavHeader WavHeader; + float context[320]; +} * SirenEncoder; + +/* sample_rate MUST be 16000 to be compatible with MSN Voice clips (I think) */ +extern SirenEncoder Siren7_NewEncoder(int sample_rate); +extern void Siren7_CloseEncoder(SirenEncoder encoder); +extern int Siren7_EncodeFrame(SirenEncoder encoder, unsigned char *DataIn, unsigned char *DataOut); + + +#endif /* _SIREN_ENCODER_H */ diff --git a/libs/libmsn/libsiren/huffman.cpp b/libs/libmsn/libsiren/huffman.cpp new file mode 100644 index 0000000..35a4ae0 --- /dev/null +++ b/libs/libmsn/libsiren/huffman.cpp @@ -0,0 +1,382 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "siren7.h" +#include "huffman_consts.h" + + +static short current_word = 0; +static int bit_idx = 0; +static int *bitstream_ptr = NULL; + +int next_bit() { + if (bitstream_ptr == NULL) + return -1; + + if (bit_idx == 0) { + current_word = *bitstream_ptr++; + bit_idx = 16; + } + + return (current_word >> --bit_idx) & 1; +} + +void set_bitstream(int *stream) { + bitstream_ptr = stream; + current_word = *bitstream_ptr; + bit_idx = 0; +} + + +int compute_region_powers(int number_of_regions, float *coefs, int *drp_num_bits, int *drp_code_bits, int *absolute_region_power_index, int esf_adjustment) { + float region_power = 0; + int num_bits; + int idx; + int max_idx, min_idx; + int region, i; + + for (region = 0; region < number_of_regions; region++) { + region_power = 0.0f; + for (i = 0 ; i < region_size; i++) { + region_power += coefs[(region*region_size)+i] * coefs[(region*region_size)+i]; + } + region_power *= region_size_inverse; + + min_idx = 0; + max_idx = 64; + for (i = 0; i < 6; i++) { + idx = (min_idx + max_idx) / 2; + if (region_power_table_boundary[idx-1] <= region_power) { + min_idx = idx; + } else { + max_idx = idx; + } + } + absolute_region_power_index[region] = min_idx - 24; + + } + + for (region = number_of_regions-2; region >= 0; region--) { + if (absolute_region_power_index[region] < absolute_region_power_index[region+1] - 11) + absolute_region_power_index[region] = absolute_region_power_index[region+1] - 11; + } + + if (absolute_region_power_index[0] < (1-esf_adjustment)) + absolute_region_power_index[0] = (1-esf_adjustment); + + if (absolute_region_power_index[0] > (31-esf_adjustment)) + absolute_region_power_index[0] = (31-esf_adjustment); + + drp_num_bits[0] = 5; + drp_code_bits[0] = absolute_region_power_index[0] + esf_adjustment; + + + for(region = 1; region < number_of_regions; region++) { + if (absolute_region_power_index[region] < (-8 - esf_adjustment)) + absolute_region_power_index[region] = (-8 - esf_adjustment); + if (absolute_region_power_index[region] > (31-esf_adjustment)) + absolute_region_power_index[region] = (31-esf_adjustment); + } + + num_bits = 5; + + for(region = 0; region < number_of_regions-1; region++) { + idx = absolute_region_power_index[region+1] - absolute_region_power_index[region] + 12; + if (idx < 0) + idx = 0; + + absolute_region_power_index[region+1] = absolute_region_power_index[region] + idx - 12; + drp_num_bits[region+1] = differential_region_power_bits[region][idx]; + drp_code_bits[region+1] = differential_region_power_codes[region][idx]; + num_bits += drp_num_bits[region+1]; + } + + return num_bits; +} + + +int decode_envelope(int number_of_regions, float *decoder_standard_deviation, int *absolute_region_power_index, int esf_adjustment) { + int index; + int i; + int envelope_bits = 0; + + index = 0; + for (i = 0; i < 5; i++) + index = (index<<1) | next_bit(); + envelope_bits = 5; + + absolute_region_power_index[0] = index - esf_adjustment; + decoder_standard_deviation[0] = standard_deviation[absolute_region_power_index[0] + 24]; + + for (i = 1; i < number_of_regions; i++) { + index = 0; + do { + index = differential_decoder_tree[i-1][index][next_bit()]; + envelope_bits++; + } while (index > 0); + + absolute_region_power_index[i] = absolute_region_power_index[i-1] - index - 12; + decoder_standard_deviation[i] = standard_deviation[absolute_region_power_index[i] + 24]; + } + + return envelope_bits; +} + + + +static int huffman_vector(int category, int power_idx, float *mlts, int *out) { + int i, j; + float temp_value = deviation_inverse[power_idx] * step_size_inverse[category]; + int sign_idx, idx, non_zeroes, max, bits_available; + int current_word = 0; + int region_bits = 0; + + bits_available = 32; + for (i = 0; i < number_of_vectors[category]; i++) { + sign_idx = idx = non_zeroes = 0; + for (j = 0; j < vector_dimension[category]; j++) { + max = (int) ((fabs(*mlts) * temp_value) + dead_zone[category]); + if (max != 0) { + sign_idx <<= 1; + non_zeroes++; + if (*mlts > 0) + sign_idx++; + if (max > max_bin[category] || max < 0) + max = max_bin[category]; + + } + mlts++; + idx = (idx * (max_bin[category] + 1)) + max; + } + + region_bits += bitcount_tables[category][idx] + non_zeroes; + bits_available -= bitcount_tables[category][idx] + non_zeroes; + if (bits_available < 0) { + *out++ = current_word + (((code_tables[category][idx] << non_zeroes) + sign_idx) >> -bits_available); + bits_available += 32; + current_word = ((code_tables[category][idx] << non_zeroes) + sign_idx) << bits_available; + } else { + current_word += ((code_tables[category][idx] << non_zeroes) + sign_idx) << bits_available; + } + + } + + *out = current_word; + return region_bits; +} + +int quantize_mlt(int number_of_regions, int rate_control_possibilities, int number_of_available_bits, float *coefs, int *absolute_region_power_index, int *power_categories, int *category_balance, int *region_mlt_bit_counts, int *region_mlt_bits) { + int region; + int mlt_bits = 0; + int rate_control; + + for (rate_control = 0; rate_control < ((rate_control_possibilities >> 1) - 1); rate_control++) + power_categories[category_balance[rate_control]]++; + + for (region = 0; region < number_of_regions; region++) { + if (power_categories[region] > 6) + region_mlt_bit_counts[region] = 0; + else + region_mlt_bit_counts[region] = huffman_vector(power_categories[region], absolute_region_power_index[region], coefs + (region_size * region), + region_mlt_bits + (4*region)); + mlt_bits += region_mlt_bit_counts[region]; + } + + while (mlt_bits < number_of_available_bits && rate_control > 0) { + rate_control--; + region = category_balance[rate_control]; + power_categories[region]--; + + if (power_categories[region] < 0) + power_categories[region] = 0; + + mlt_bits -= region_mlt_bit_counts[region]; + + if (power_categories[region] > 6) + region_mlt_bit_counts[region] = 0; + else + region_mlt_bit_counts[region] = huffman_vector(power_categories[region], absolute_region_power_index[region], coefs + (region_size * region), + region_mlt_bits + (4*region)); + + mlt_bits += region_mlt_bit_counts[region]; + } + + while(mlt_bits > number_of_available_bits && rate_control < rate_control_possibilities) { + region = category_balance[rate_control]; + power_categories[region]++; + mlt_bits -= region_mlt_bit_counts[region]; + + if (power_categories[region] > 6) + region_mlt_bit_counts[region] = 0; + else + region_mlt_bit_counts[region] = huffman_vector(power_categories[region], absolute_region_power_index[region], coefs + (region_size * region), + region_mlt_bits + (4*region)); + + mlt_bits += region_mlt_bit_counts[region]; + + rate_control++; + } + + return rate_control; +} + +static int get_dw(SirenDecoder decoder) { + int ret = decoder->dw1 + decoder->dw4; + + if ((ret & 0x8000) != 0) + ret++; + + decoder->dw1 = decoder->dw2; + decoder->dw2 = decoder->dw3; + decoder->dw3 = decoder->dw4; + decoder->dw4 = ret; + + return ret; +} + + + + +int decode_vector(SirenDecoder decoder, int number_of_regions, int number_of_available_bits, float *decoder_standard_deviation, int *power_categories, float *coefs, int scale_factor) { + float *coefs_ptr; + float decoded_value; + float noise; + int *decoder_tree; + + int region; + int category; + int i, j; + int index; + int error; + int dw1; + int dw2; + + error = 0; + for (region = 0; region < number_of_regions; region++) { + category = power_categories[region]; + coefs_ptr = coefs + (region * region_size); + + if (category < 7) { + decoder_tree = decoder_tables[category]; + + for (i = 0; i < number_of_vectors[category]; i++) { + index = 0; + do { + if (number_of_available_bits <= 0) { + error = 1; + break; + } + + index = decoder_tree[index + next_bit()]; + number_of_available_bits--; + } while ((index & 1) == 0); + + index >>= 1; + + if (error == 0 && number_of_available_bits >= 0) { + for (j = 0; j < vector_dimension[category]; j++) { + decoded_value = mlt_quant[category][index & ((1 << index_table[category]) - 1)]; + index >>= index_table[category]; + + if (decoded_value != 0) { + if (next_bit() == 0) + decoded_value *= -decoder_standard_deviation[region]; + else + decoded_value *= decoder_standard_deviation[region]; + number_of_available_bits--; + } + + *coefs_ptr++ = decoded_value * scale_factor; + } + } else { + error = 1; + break; + } + } + + if (error == 1) { + for (j = region + 1; j < number_of_regions; j++) + power_categories[j] = 7; + category = 7; + } + } + + + coefs_ptr = coefs + (region * region_size); + + if (category == 5) { + i = 0; + for (j = 0; j < region_size; j++) { + if (*coefs_ptr != 0) { + i++; + if (fabs(*coefs_ptr) > 2.0 * decoder_standard_deviation[region]) { + i += 3; + } + } + coefs_ptr++; + } + + noise = decoder_standard_deviation[region] * noise_category5[i]; + } else if (category == 6) { + i = 0; + for (j = 0; j < region_size; j++) { + if (*coefs_ptr++ != 0) + i++; + } + + noise = decoder_standard_deviation[region] * noise_category6[i]; + } else if (category == 7) { + noise = decoder_standard_deviation[region] * noise_category7; + } else { + noise = 0; + } + + coefs_ptr = coefs + (region * region_size); + + if (category == 5 || category == 6 || category == 7) { + dw1 = get_dw(decoder); + dw2 = get_dw(decoder); + + for (j=0; j<10; j++) { + if (category == 7 || *coefs_ptr == 0) { + if ((dw1 & 1)) + *coefs_ptr = noise; + else + *coefs_ptr = -noise; + } + coefs_ptr++; + dw1 >>= 1; + + if (category == 7 || *coefs_ptr == 0) { + if ((dw2 & 1)) + *coefs_ptr = noise; + else + *coefs_ptr = -noise; + } + coefs_ptr++; + dw2 >>= 1; + } + } + } + + return error == 1 ? -1 : number_of_available_bits; +} diff --git a/libs/libmsn/libsiren/huffman.h b/libs/libmsn/libsiren/huffman.h new file mode 100644 index 0000000..196932d --- /dev/null +++ b/libs/libmsn/libsiren/huffman.h @@ -0,0 +1,35 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _SIREN7_HUFFMAN_H_ +#define _SIREN7_HUFFMAN_H_ + +#include "decoder.h" + +extern int compute_region_powers(int number_of_regions, float *coefs, int *drp_num_bits, int *drp_code_bits, int *absolute_region_power_index, int esf_adjustment); +extern int quantize_mlt(int number_of_regions, int rate_control_possibilities, int number_of_available_bits, float *coefs, int *absolute_region_power_index, int *power_categories, int *category_balance, int *region_mlt_bit_counts, int *region_mlt_bits); +extern int decode_envelope(int number_of_regions, float *decoder_standard_deviation, int *absolute_region_power_index, int esf_adjustment); +extern int decode_vector(SirenDecoder decoder, int number_of_regions, int number_of_available_bits, float *decoder_standard_deviation, int *power_categories, float *coefs, int scale_factor); + +extern void set_bitstream(int *stream); +extern int next_bit(); + +#endif /* _SIREN7_HUFFMAN_H_ */ diff --git a/libs/libmsn/libsiren/huffman_consts.h b/libs/libmsn/libsiren/huffman_consts.h new file mode 100644 index 0000000..0a38c0b --- /dev/null +++ b/libs/libmsn/libsiren/huffman_consts.h @@ -0,0 +1,528 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _HUFFMAN_CONSTS_H +#define _HUFFMAN_CONSTS_H + + +static int differential_region_power_bits[28][24] = { + {4, 6, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 5, 7, 8, 9, 11, 11, 12, 12, 12, 12}, + {10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 3, 3, 4, 5, 7, 9, 11, 12, 13, 15, 15, 15, 16, 16}, + {12, 10, 8, 6, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 5, 5, 7, 9, 11, 13, 14, 14}, + {13, 10, 9, 9, 7, 7, 5, 5, 4, 3, 3, 3, 3, 3, 4, 4, 4, 5, 7, 9, 11, 13, 13, 13}, + {12, 13, 10, 8, 6, 6, 5, 5, 4, 4, 3, 3, 3, 3, 3, 4, 5, 5, 6, 7, 9, 11, 14, 14}, + {12, 11, 9, 8, 8, 7, 5, 4, 4, 3, 3, 3, 3, 3, 4, 4, 5, 5, 7, 8, 10, 13, 14, 14}, + {15, 16, 15, 12, 10, 8, 6, 5, 4, 3, 3, 3, 2, 3, 4, 5, 5, 7, 9, 11, 13, 16, 16, 16}, + {14, 14, 11, 10, 9, 7, 7, 5, 5, 4, 3, 3, 2, 3, 3, 4, 5, 7, 9, 9, 12, 14, 15, 15}, + {9, 9, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 13}, + {14, 12, 10, 8, 6, 6, 5, 4, 3, 3, 3, 3, 3, 3, 4, 5, 6, 8, 8, 9, 11, 14, 14, 14}, + {13, 10, 9, 8, 6, 6, 5, 4, 4, 4, 3, 3, 2, 3, 4, 5, 6, 8, 9, 9, 11, 12, 14, 14}, + {16, 13, 12, 11, 9, 6, 5, 5, 4, 4, 4, 3, 2, 3, 3, 4, 5, 7, 8, 10, 14, 16, 16, 16}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14}, + {13, 14, 14, 14, 10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9, 11, 14, 14, 14} +}; + + +static int differential_region_power_codes[28][24] = { + {8, 38, 18, 10, 7, 6, 3, 2, 0, 1, 7, 6, 5, 4, 11, 78, 158, 318, 1278, 1279, 2552, 2553, 2554, 2555}, + {36, 8, 3, 5, 0, 1, 7, 6, 4, 3, 2, 5, 3, 4, 5, 19, 74, 150, 302, 1213, 1214, 1215, 2424, 2425}, + {2582, 644, 160, 41, 5, 11, 7, 5, 4, 1, 0, 6, 4, 7, 3, 6, 4, 21, 81, 323, 1290, 5167, 10332, 10333}, + {2940, 366, 181, 180, 47, 46, 27, 10, 8, 5, 1, 0, 3, 7, 4, 9, 12, 26, 44, 182, 734, 2941, 2942, 2943}, + {3982, 7967, 994, 249, 63, 26, 19, 18, 14, 8, 6, 1, 0, 2, 5, 7, 12, 30, 27, 125, 496, 1990, 15932, 15933}, + {3254, 1626, 407, 206, 202, 100, 30, 14, 3, 5, 3, 0, 2, 4, 2, 13, 24, 31, 102, 207, 812, 6511, 13020, 13021}, + {1110, 2216, 1111, 139, 35, 9, 3, 20, 11, 4, 2, 1, 3, 3, 1, 0, 21, 5, 16, 68, 276, 2217, 2218, 2219}, + {1013, 1014, 127, 62, 29, 6, 4, 16, 0, 1, 3, 2, 3, 1, 5, 9, 17, 5, 28, 30, 252, 1015, 2024, 2025}, + {381, 380, 372, 191, 94, 44, 16, 10, 7, 3, 1, 0, 2, 6, 9, 17, 45, 92, 187, 746, 1494, 2991, 5980, 5981}, + {3036, 758, 188, 45, 43, 10, 4, 3, 6, 4, 2, 0, 3, 7, 11, 20, 42, 44, 46, 95, 378, 3037, 3038, 3039}, + {751, 92, 45, 20, 26, 4, 12, 7, 4, 0, 4, 1, 3, 5, 5, 3, 27, 21, 44, 47, 186, 374, 1500, 1501}, + {45572, 5697, 2849, 1425, 357, 45, 23, 6, 10, 7, 2, 2, 3, 0, 4, 6, 7, 88, 179, 713, 11392, 45573, 45574, 45575}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021}, + {2511, 5016, 5018, 5017, 312, 79, 38, 36, 30, 14, 6, 0, 2, 1, 3, 5, 8, 31, 37, 157, 626, 5019, 5020, 5021} +}; + + +static int bitcount_table_category0[196] = { + 1, 4, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 11, 11, 4, 5, 6, 7, 7, 8, 8, + 9, 9, 9, 9, 10, 11, 11, 5, 6, 7, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, + 12, 6, 7, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 12, 13, 7, 7, 8, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 12, 13, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, + 11, 12, 13, 14, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 15, 8, + 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 14, 15, 9, 9, 9, 10, 10, 10, + 11, 11, 12, 13, 12, 14, 15, 16, 9, 9, 10, 10, 10, 10, 11, 12, 12, 14, + 14, 16, 16, 16, 9, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, + 10, 10, 10, 11, 11, 12, 12, 13, 15, 15, 16, 14, 15, 15, 11, 11, 11, 12, + 13, 13, 13, 15, 16, 16, 16, 16, 14, 15, 11, 11, 12, 13, 13, 14, 15, 16, + 16, 16, 16, 16, 16, 14}; + +static int code_table_category0[196] = { + 1, 2, 1, 24, 14, 51, 9, 68, 110, 26, 218, 54, 154, 761, 3, 10, 22, 8, 58, + 22, 71, 16, 30, 50, 213, 75, 94, 632, 15, 18, 52, 23, 107, 5, 54, 63, 239, + 46, 276, 271, 851, 252, 28, 10, 12, 1, 22, 133, 191, 55, 105, 278, 317, 554, + 310, 276, 32, 50, 94, 20, 187, 219, 13, 268, 473, 445, 145, 849, 1277, 623, + 1, 14, 0, 55, 238, 121, 120, 269, 318, 530, 639, 1117, 509, 556, 24, 78, 51, + 153, 62, 308, 16, 25, 68, 1058, 428, 277, 2233, 1114, 92, 108, 141, 223, 270, + 381, 24, 212, 760, 35, 1063, 279, 1717, 3439, 7, 21, 152, 73, 309, 310, 95, 944, + 1890, 2232, 1891, 5107, 10213, 4981, 61, 62, 9, 79, 474, 475, 848, 1059, 1056, 1716, + 139, 4978, 4983, 4983, 140, 186, 76, 444, 144, 633, 1057, 838, 2237, 4472, 4473, + 10212, 10212, 4983, 74, 78, 311, 213, 850, 1062, 1119, 508, 276, 277, 4982, 4473, + 10212, 10212, 208, 70, 555, 418, 68, 510, 2552, 1115, 4980, 4979, 4982, 4982, 4473, + 10212, 215, 71, 253, 511, 839, 1718, 2488, 6876, 6877, 4979, 4979, 4982, 4982, 4473}; + + +static int bitcount_table_category1[100] = { + 1, 4, 5, 6, 7, 8, 8, 9, 10, 10, 4, 5, 6, 7, 7, 8, 8, 9, 9, 11, 5, 5, 6, 7, 8, 8, 9, 9, + 10, 11, 6, 6, 7, 8, 8, 9, 9, 10, 11, 12, 7, 7, 8, 8, 9, 9, 10, 11, 11, 13, 8, 8, 8, + 9, 9, 10, 10, 11, 12, 14, 8, 8, 8, 9, 10, 11, 11, 12, 13, 15, 9, 9, 9, 10, 11, 12, + 12, 14, 14, 14, 9, 9, 9, 10, 11, 12, 14, 16, 14, 14, 10, 10, 11, 12, 13, 14, 16, 16, + 16, 14}; + +static int code_table_category1[100] = { + 1, 2, 11, 27, 31, 9, 120, 31, 275, 310, 1, 0, 12, 5, 33, 54, 102, 111, 246, 448, 10, 14, + 31, 39, 59, 100, 114, 202, 485, 969, 24, 26, 36, 52, 103, 30, 120, 242, 69, 1244, 35, + 32, 14, 61, 113, 117, 233, 486, 487, 2491, 13, 12, 69, 110, 149, 35, 495, 449, 1978, + 7751, 76, 75, 122, 136, 213, 68, 623, 930, 3959, 9961, 115, 16, 107, 225, 424, 850, + 1936, 7916, 4981, 4981, 148, 154, 243, 407, 988, 851, 7750, 19920, 7916, 4981, 406, 274, + 464, 931, 3874, 7917, 19921, 19920, 19920, 7916}; + + +static int bitcount_table_category2[64] = { + 1, 4, 5, 7, 8, 9, 10, 3, 4, 5, 7, 8, 9, 10, 5, 5, 6, 7, 8, 10, 10, 7, 6, 7, 8, 9, 10, 12, + 8, 8, 8, 9, 10, 12, 14, 8, 9, 9, 10, 11, 15, 16, 9, 10, 11, 12, 13, 16, 15, 1, 1, 1}; + +static int code_table_category2[52] = { + 1, 0, 10, 11, 28, 62, 363, 3, 2, 9, 8, 24, 53, 352, 7, 8, 13, 25, 89, 74, 355, 10, 23, 24, + 29, 55, 354, 1449, 25, 19, 30, 52, 108, 438, 5793, 91, 36, 63, 353, 725, 11584, 23170, 180, + 75, 218, 439, 2897, 23171, 11584}; + + +static int bitcount_table_category3[625] = { + 2, 4, 6, 8, 10, 5, 5, 6, 8, 10, 7, 8, 8, 10, 12, 9, 9, 10, 12, 15, 10, 11, 13, 16, 16, 5, 6, 8, + 10, 11, 5, 6, 8, 10, 12, 7, 7, 8, 10, 13, 9, 9, 10, 12, 15, 12, 11, 13, 16, 16, 7, 9, 10, 12, + 15, 7, 8, 10, 12, 13, 9, 9, 11, 13, 16, 11, 11, 12, 14, 16, 12, 12, 14, 16, 14, 9, 11, 12, 16, + 16, 9, 10, 13, 15, 16, 10, 11, 12, 16, 16, 13, 13, 16, 16, 16, 16, 16, 15, 16, 16, 11, 13, 16, + 16, 15, 11, 13, 15, 16, 16, 13, 13, 16, 16, 16, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 6, 8, + 10, 13, 6, 6, 8, 10, 13, 9, 8, 10, 12, 16, 10, 10, 11, 15, 16, 13, 12, 14, 16, 16, 5, 6, 8, 11, 13, + 6, 6, 8, 10, 13, 8, 8, 9, 11, 14, 10, 10, 12, 12, 16, 13, 12, 13, 15, 16, 7, 8, 9, 12, 16, 7, 8, + 10, 12, 14, 9, 9, 10, 13, 16, 11, 10, 12, 15, 16, 13, 13, 16, 16, 15, 9, 11, 13, 16, 16, 9, 10, + 12, 15, 16, 10, 11, 13, 16, 16, 13, 12, 16, 16, 16, 16, 16, 16, 16, 16, 11, 13, 16, 16, 16, 11, + 13, 16, 16, 16, 12, 13, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 6, 8, 11, 13, 16, 8, + 8, 10, 12, 16, 11, 10, 11, 13, 16, 12, 13, 13, 15, 16, 16, 16, 14, 16, 15, 6, 8, 10, 13, 16, 8, + 8, 10, 12, 16, 10, 10, 11, 13, 16, 13, 12, 13, 16, 16, 14, 14, 14, 16, 16, 8, 9, 11, 13, 16, 8, + 9, 11, 16, 14, 10, 10, 12, 15, 16, 12, 12, 13, 16, 16, 15, 16, 16, 16, 16, 10, 12, 15, 16, 16, + 10, 12, 12, 14, 16, 12, 12, 13, 16, 16, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 12, 15, 15, 16, + 16, 13, 13, 16, 16, 14, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 14, 15, 16, 16, 16, 8, 10, 13, + 15, 16, 10, 11, 13, 16, 16, 13, 13, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 10, + 11, 15, 16, 9, 10, 12, 16, 16, 12, 12, 15, 16, 16, 16, 14, 16, 16, 16, 16, 16, 16, 16, 16, 9, 11, + 14, 16, 16, 10, 11, 13, 16, 16, 14, 13, 14, 16, 16, 16, 15, 15, 16, 16, 16, 16, 16, 16, 16, 11, 13, + 16, 16, 16, 11, 13, 15, 16, 16, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 15, 16, + 16, 16, 16, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 9, 13, + 16, 16, 16, 11, 13, 16, 16, 16, 14, 15, 16, 16, 16, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 9, 13, + 15, 15, 16, 12, 13, 14, 16, 16, 16, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 13, + 15, 16, 16, 12, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 15, 15, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 15, 16, 16, 13, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + +static int code_table_category3[625] = { + 3, 8, 46, 145, 228, 4, 8, 47, 28, 455, 89, 2, 180, 5, 1335, 250, 12, 644, 1311, 139, 729, 251, 870, + 2172, 2211, 5, 23, 112, 334, 1469, 21, 3, 5, 111, 2014, 88, 79, 152, 124, 2685, 297, 48, 110, 1310, + 149, 501, 1231, 153, 2267, 2569, 57, 13, 653, 2587, 143, 75, 124, 118, 2611, 5242, 61, 50, 253, 3633, + 2216, 476, 39, 57, 1926, 2236, 2586, 1329, 1920, 2566, 1926, 296, 233, 2590, 2240, 2217, 253, 613, + 867, 144, 318, 614, 252, 2589, 2242, 2218, 872, 866, 2187, 2296, 2155, 2568, 2227, 150, 2567, 2296, + 199, 2686, 2160, 2290, 19145, 232, 2680, 128, 2192, 2212, 2684, 793, 2281, 2223, 2242, 1934, 2165, + 2146, 2291, 2296, 2222, 2189, 2187, 2296, 2296, 6, 4, 82, 725, 3632, 15, 21, 56, 599, 148, 3, 162, + 42, 411, 2301, 735, 654, 930, 137, 2586, 869, 1334, 1931, 2300, 2213, 9, 22, 146, 1290, 5240, 5, 12, + 53, 630, 875, 80, 9, 8, 86, 2002, 210, 117, 56, 2019, 2162, 146, 397, 868, 131, 2151, 77, 160, 365, + 2610, 2252, 59, 54, 41, 2591, 1928, 226, 14, 121, 5792, 2295, 1197, 728, 408, 130, 2157, 3635, 155, + 2573, 2587, 130, 314, 64, 144, 2173, 2176, 115, 30, 409, 153, 2590, 631, 26, 4787, 2221, 2174, 2683, + 1863, 2572, 319, 2150, 2177, 2194, 2571, 2257, 319, 65, 145, 2251, 2156, 2161, 909, 864, 2193, 2197, + 2246, 2588, 5797, 156, 2258, 2221, 2158, 2199, 2214, 2152, 319, 2188, 2264, 2572, 319, 319, 30, 117, + 219, 865, 2263, 147, 127, 239, 410, 2247, 27, 324, 1468, 2681, 2180, 1328, 5241, 147, 142, 2237, 2241, + 2245, 1921, 2262, 142, 41, 11, 505, 2682, 2591, 0, 26, 229, 2015, 2577, 464, 98, 87, 5243, 2166, 149, + 2016, 5244, 2190, 2198, 9573, 11598, 11599, 2235, 2190, 144, 298, 1004, 5245, 2277, 156, 104, 254, 2560, + 1922, 612, 325, 2017, 129, 2588, 2608, 1330, 871, 2144, 2145, 132, 2147, 2148, 2149, 2144, 119, 1331, + 133, 2153, 2154, 211, 58, 2609, 1923, 2159, 510, 163, 5246, 2163, 2164, 1924, 134, 2167, 2168, 2168, 2169, + 2170, 2171, 2168, 2168, 1332, 135, 136, 2175, 2153, 150, 873, 2178, 2179, 1923, 1925, 2181, 2182, 2183, + 2163, 2184, 2185, 2186, 2168, 2168, 1924, 134, 2167, 2168, 2168, 58, 326, 2687, 138, 2191, 31, 66, 874, + 2195, 2196, 151, 152, 1927, 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2205, 55, + 103, 1230, 140, 2215, 118, 15, 1333, 2219, 2220, 2018, 511, 141, 2224, 2225, 2226, 1929, 2228, 2229, 2230, + 2231, 2232, 2233, 2234, 2229, 366, 1005, 1930, 2238, 2239, 12, 1006, 5247, 2243, 2244, 1932, 3634, 1933, + 2248, 2249, 2250, 145, 146, 2253, 2253, 2254, 2255, 2256, 2253, 2253, 1291, 5793, 2259, 2260, 2261, 477, + 5794, 147, 2265, 2266, 5795, 2268, 2269, 2270, 2270, 2271, 2272, 2273, 2274, 2274, 2275, 2276, 2273, 2274, + 2274, 148, 2278, 2279, 2280, 2260, 1935, 2282, 2283, 2284, 2265, 2285, 2286, 2287, 2270, 2270, 2288, 2289, + 2273, 2274, 2274, 2271, 2272, 2273, 2274, 2274, 233, 5796, 2292, 2293, 2294, 1292, 3724, 2297, 2298, 2299, + 2000, 151, 2302, 2303, 2200, 152, 2561, 2562, 2563, 2205, 2564, 2565, 2204, 2205, 2205, 363, 154, 154, 155, + 2570, 59, 3725, 2001, 2574, 2575, 2576, 157, 2578, 2579, 2224, 2580, 2581, 2582, 2583, 2229, 2584, 2585, 2228, + 2229, 2229, 654, 5798, 158, 2589, 2238, 2392, 2003, 2592, 2593, 2243, 2594, 2595, 2596, 2597, 2248, 2598, 2599, + 2600, 2253, 2253, 2250, 145, 146, 2253, 2253, 2601, 2602, 2603, 2604, 2260, 2605, 2606, 2607, 6336, 2265, 6337, + 6338, 6339, 2270, 2270, 6340, 6341, 2273, 2274, 2274, 2271, 2272, 2273, 2274, 2274, 6342, 6343, 2259, 2260, + 2260, 38288, 38289, 147, 2265, 2265, 5795, 2268, 2269, 2270, 2270, 2271, 2272, 2273, 2274, 2274, 2271, 2272, + 2273, 2274, 2274}; + + +static int bitcount_table_category4[256] = { + 2, 4, 7, 10, 4, 5, 7, 10, 7, 8, 10, 14, 11, 11, 15, 15, 4, 5, 9, + 12, 5, 5, 8, 12, 8, 7, 10, 15, 11, 11, 15, 15, 7, 9, 12, 15, 8, 8, + 12, 15, 10, 10, 13, 15, 14, 14, 15, 13, 11, 13, 15, 15, 11, 13, 15, + 15, 14, 15, 15, 13, 15, 15, 13, 13, 4, 5, 9, 13, 5, 6, 9, 13, 9, 9, + 11, 15, 14, 13, 15, 15, 4, 6, 9, 12, 5, 6, 9, 13, 9, 8, 11, 15, 13, + 12, 15, 15, 7, 9, 12, 15, 7, 8, 11, 15, 10, 10, 14, 15, 14, 15, 15, + 14, 10, 12, 15, 15, 11, 13, 15, 15, 15, 15, 15, 14, 15, 15, 14, 14, + 6, 9, 13, 14, 8, 9, 12, 15, 12, 12, 15, 15, 15, 15, 15, 15, 7, 9, 13, + 15, 8, 9, 12, 15, 11, 12, 15, 15, 15, 15, 15, 15, 9, 11, 15, 15, 9, + 11, 15, 15, 14, 14, 15, 15, 15, 15, 15, 15, 14, 15, 15, 15, 14, 15, + 15, 15, 15, 15, 15, 15, 14, 14, 15, 15, 9, 12, 15, 15, 12, 13, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 10, 12, 15, 15, 12, 14, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, + 15, 15, 14, 14, 15, 15}; + +static int code_table_category4[256] = { + 1, 2, 4, 572, 10, 0, 69, 712, 91, 10, 46, 9182, 1426, 1430, 30172, 30194, + 9, 28, 22, 2258, 16, 25, 142, 2179, 15, 111, 719, 1521, 1131, 1437, 1520, + 30196, 88, 283, 3803, 30193, 13, 236, 2856, 30166, 545, 951, 5709, 1522, + 3241, 9180, 30179, 5709, 1088, 4356, 30410, 30175, 1146, 377, 30162, 30163, + 8715, 30176, 30165, 5709, 30197, 30184, 5709, 5709, 1, 23, 28, 5710, 26, 14, + 29, 7538, 102, 103, 1429, 1524, 3237, 7060, 30401, 30201, 15, 13, 470, 3768, + 24, 15, 281, 5747, 24, 181, 1128, 30206, 5711, 3531, 30156, 30158, 116, 100, + 2260, 30187, 119, 234, 1764, 30171, 716, 883, 9183, 30164, 3236, 1528, 30180, + 9183, 885, 2870, 1532, 30160, 1431, 5708, 30192, 30205, 30402, 30168, 30173, + 9183, 30157, 30161, 9183, 9183, 54, 25, 1621, 15211, 180, 287, 2261, 30198, 808, + 811, 30411, 30413, 30414, 22986, 22987, 30411, 24, 273, 376, 30159, 137, 280, + 2871, 1523, 1768, 2259, 1525, 30167, 1526, 30169, 30170, 1525, 443, 1434, 1527, + 30174, 474, 1769, 30177, 30178, 3238, 3239, 30181, 30181, 30182, 30183, 30181, + 30181, 3240, 30185, 30186, 1527, 9181, 30188, 30189, 30177, 30190, 30191, 30181, + 30181, 3238, 3239, 30181, 30181, 440, 2857, 1529, 30195, 2294, 7061, 1530, 30199, + 30200, 1531, 30202, 30411, 30203, 30204, 30411, 30411, 203, 2872, 30207, 30400, + 189, 11492, 30403, 30404, 30405, 30406, 30407, 1525, 30408, 30409, 1525, 1525, + 8714, 1533, 30412, 1527, 1534, 1535, 30415, 30177, 30416, 30417, 30181, 30181, + 3238, 3239, 30181, 30181, 30418, 30419, 1527, 1527, 30420, 30421, 30177, 30177, + 3238, 3239, 30181, 30181, 3238, 3239, 30181, 30181}; + + +static int bitcount_table_category5[256] = { + 2, 4, 8, 4, 5, 9, 9, 10, 14, 4, 6, 11, 5, 6, 12,10, 11, 15, 9, 11, 15, 10, 13, 15, + 14, 15, 6, 4, 6, 12, 6, 7, 12, 12, 12, 15, 5, 7, 13, 6, 7, 13, 12, 13, 15, 10, 12, + 15, 11, 13, 15, 15, 15, 7, 8, 13, 15, 11, 12, 15, 15, 15, 7, 10, 13, 15, 12, 15, 15, + 15, 15, 7, 15, 15, 7, 15, 15, 7, 6, 7, 7, 4, 5, 11, 5, 7, 12, 11, 12, 15, 6, 7, 13, 7, + 8, 14, 12, 14, 15, 11, 13, 15, 12, 13, 15, 15, 15, 8, 5, 6, 13, 7, 8, 15, 12, 14, 15, + 6, 8, 14, 7, 8, 15, 14, 15, 15, 12, 12, 15, 12, 13, 15, 15, 15, 8, 9, 13, 15, 12, 13, + 15, 15, 15, 8, 11, 13, 15, 13, 13, 15, 15, 15, 8, 14, 15, 8, 15, 15, 8, 7, 8, 8, 8, 10, + 15, 11, 12, 15, 15, 15, 7, 10, 12, 15, 12, 13, 15, 15, 15, 8, 14, 15, 7, 15, 15, 8, 7, + 8, 8, 8, 12, 15, 12, 13, 15, 15, 15, 8, 11, 13, 15, 13, 15, 15, 15, 15, 8, 15, 15, 8, + 15, 15, 8, 7, 8, 8, 14, 15, 6, 15, 15, 8, 7, 8, 8, 15, 15, 8, 15, 15, 8, 7, 8, 8, 6, + 8, 8, 7, 8, 8, 7, 8, 8}; + +static int code_table_category5[243] = { + 0, 5, 220, 10, 16, 443, 390, 391, 14333, 11, 26, 1566, 26, 54, 3135, 508, 1558, 28581, + 255, 1782, 28599, 885, 6208, 28578, 14335, 28579, 54, 9, 35, 3129, 27, 68, 3537, 1562, + 3568, 28610, 25, 62, 4078, 58, 118, 7763, 3107, 7758, 28563, 778, 3131, 28598, 780, 7123, + 28630, 28593, 28586, 118, 243, 6210, 28614, 1018, 3567, 28601, 28611, 28570, 68, 388, 6256, + 28619, 1559, 28562, 28606, 28565, 28591, 118, 28594, 28571, 62, 28618, 28590, 118, 58, + 118, 118, 4, 28, 1781, 31, 60, 3134, 1938, 3882, 28574, 25, 96, 7757, 49, 126, 14244, + 3883, 14334, 28613, 1769, 4077, 28602, 3106, 7756, 28582, 28621, 28566, 126, 14, 61, 4079, + 61, 138, 28491, 3536, 8153, 28573, 49, 96, 12442, 119, 240, 28490, 12443, 28560, 28561, 3111, + 3580, 28564, 3130, 7759, 28567, 28568, 28569, 240, 444, 6209, 28572, 3569, 6211, 28575, 28576, + 28577, 138, 778, 7760, 28580, 7761, 7762, 28583, 28584, 28585, 240, 14319, 28587, 96, 28588, 28589, + 240, 119, 240, 240, 139, 968, 28592, 1554, 3581, 28595, 28596, 28597, 60, 971, 3560, 28600,3582, + 7132, 28603, 28604, 28605, 126, 14332, 28607, 96, 28608, 28609, 126, 49, 126, 126, 241, 1558, 28612, + 1563, 6257, 28615, 28616, 28617, 138, 1559, 7133, 28620, 6220, 28622, 28623, 28624, 28625, 240, 28626, + 28627, 96, 28628, 28629, 240, 119, 240, 240, 8152, 28631, 61, 28632, 28633, 138, 61, 138, 138, 28634, + 28635, 96, 28636, 28637, 240, 119, 240, 240, 49, 96, 96, 119, 240, 240, 119, 240, 240}; + + +static int bitcount_table_category6[32] = {1, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 9, 8, 10, 4, 6, 7, 8, 6, 9, 8, 11, 6, 9, 8, 10, 8, 10, 9, 11}; + +static int code_table_category6[32] = {1, 2, 4, 2, 5, 29, 24, 101, 3, 31, 28, 105, 3, 5, 102, 424, 1, 30, 0, 107, 27, 200, 103, 806, 1, 4, 104, 402, 3, 425, 213, 807}; + + + +static int *bitcount_tables[7] = { + bitcount_table_category0, + bitcount_table_category1, + bitcount_table_category2, + bitcount_table_category3, + bitcount_table_category4, + bitcount_table_category5, + bitcount_table_category6}; + +static int *code_tables[7] = { + code_table_category0, + code_table_category1, + code_table_category2, + code_table_category3, + code_table_category4, + code_table_category5, + code_table_category6}; + + + + +static int differential_decoder_tree[27][24][2] = { + {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, -12}, {-11, -10}, {-8, -9}, {-7, -6}, {-13, 12}, {-5, -4}, {0, 13}, {-3, -14}, {-2, 14}, {-1, 15}, {-15, 16}, {-16, 17}, {-17, 18}, {19, 20}, {21, 22}, {-18, -19}, {-20, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {-10, -9}, {-8, -11}, {-7, -6}, {9, -5}, {10, -12}, {-4, 11}, {-13, -3}, {12, -2}, {13, -14}, {-1, 14}, {15, -15}, {0, 16}, {-16, 17}, {-17, 18}, {-18, 19}, {20, 21},{22, -19}, {-20, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {-12, 11}, {-11, -13}, {-10, -9}, {12, -14}, {-8, -7}, {-15, -6}, {13, -5}, {-16, -4}, {14, -17}, {15, -3}, {16, -18}, {-2, 17}, {18, -19}, {-1, 19}, {-20, 20}, {0, 21}, {22, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {-11, -10}, {7, -12}, {8, -9}, {9, -13}, {-14, 10}, {-8, -15}, {-16, 11}, {-7, 12}, {-17, -6}, {13, 14}, {-18, 15}, {-5, -4}, {16, 17}, {-3, -2}, {-19, 18}, {-1, 19}, {-20, 20}, {21, 22}, {0, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {-12, -11}, {-13, 7}, {8, -14}, {-10, 9}, {10, -15}, {-9, 11}, {-8, 12}, {-16, 13}, {-7, -6}, {-17, 14}, {-5, -18}, {15, -4}, {16, -19}, {17, -3}, {-20, 18}, {-2, 19}, {-21, 20}, {0, 21}, {22, -1}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {-11, 7}, {-12, -10}, {-13, -9}, {8, 9}, {-14, -8}, {10, -15}, {-7, 11}, {-16, 12}, {-6, -17}, {13, 14}, {-5, 15}, {-18, 16}, {-4, 17}, {-3, -19}, {18, -2}, {-20, 19}, {-1, 20}, {0, 21}, {22, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, -12}, {6, -11}, {-10, -13}, {-9, 7}, {8, -14}, {9, -8}, {-15, 10}, {-7, -16}, {11, -6}, {12, -17}, {13, -5}, {-18, 14}, {15, -4}, {-19, 16}, {17, -3}, {-20, 18}, {19, 20}, {21, 22}, {0, -2}, {-1, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, -12}, {6, -13}, {-11, -10}, {7, -14}, {8, -9}, {9, -15}, {-8, 10}, {-7, -16}, {11, 12}, {-6, -17}, {-5, 13}, {14, 15}, {-18, -4}, {-19, 16}, {-3, 17}, {18, -2}, {-20, 19}, {20, 21}, {22, 0}, {-1, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {-11, -10}, {-12, -9}, {7, 8}, {-13, -8}, {9, -14}, {-7, 10}, {-6, -15}, {11, 12}, {-5, -16}, {13, 14}, {-17, 15}, {-4, 16}, {17, -18}, {18, -3}, {-2, 19}, {-1, 0}, {-19, 20}, {-20, 21}, {22, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, 6}, {-11, 7}, {-10, -12}, {-9, 8}, {-8, -13}, {9, -7}, {10, -14}, {-6, 11}, {-15, 12}, {-5, 13}, {-16, -4}, {14, 15}, {-17, -3}, {-18, 16}, {17, -19}, {-2, 18}, {-20, 19}, {-1, 20}, {21, 22}, {0, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, -12}, {6, -11}, {7, 8}, {-10, -13}, {-9, 9}, {-8, -14}, {10, -7}, {11, -15}, {-6, 12}, {-5, 13}, {-4, -16}, {14, 15}, {-3, -17}, {16, 17}, {-18, -2}, {18, -19}, {-1, 19}, {-20, 20}, {-21, 21}, {22, 0}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {5, -12}, {-13, 6}, {-11, 7}, {-14, 8}, {-10, 9}, {-15, -9}, {-8, 10}, {-7, -16}, {11, -6}, {12, -5}, {-17, 13}, {14, -18}, {15, -4}, {16, -19}, {17, -3}, {18, -2}, {19, -1}, {-20, 20}, {21, 22}, {0, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}, + {{1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32}}}; + + + +static float mlt_quant[7][14] = { + { 0.0f, 0.392f, 0.761f, 1.120f, 1.477f, 1.832f, 2.183f, 2.541f, 2.893f, 3.245f, 3.598f, 3.942f, 4.288f, 4.724f}, + { 0.0f, 0.544f, 1.060f, 1.563f, 2.068f, 2.571f, 3.072f, 3.562f, 4.070f, 4.620f, 0.0f, 0.0f, 0.0f, 0.0f}, + { 0.0f, 0.746f, 1.464f, 2.180f, 2.882f, 3.584f, 4.316f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + { 0.0f, 1.006f, 2.000f, 2.993f, 3.985f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + { 0.0f, 1.321f, 2.703f, 3.983f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + { 0.0f, 1.657f, 3.491f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + { 0.0f, 1.964f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}; + + +static int decoder_tree0[360] = { + 2, 1, 4, 6, 8, 10, 12, 14, 16, 18, 33, 3, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 35, 40, 42, 44, 46, 5, 48, 65, 50, 52, 54, 56, 58, 60, + 62, 64, 37, 66, 67, 68, 97, 70, 72, 74, 7, 76, 78, 80, 82, 84, 86, 88, + 99, 90, 39, 92, 94, 96, 129, 98, 9, 100, 102, 104, 106, 108, 110, 112, 41, 161, + 69, 114, 116, 118, 131, 120, 122, 11, 124, 126, 128, 193, 130, 132, 71, 134, 43, 136, + 138, 140, 163, 101, 13, 142, 144, 146, 148, 150, 152, 154, 225, 156, 158, 195, 160, 162, + 45, 164, 15, 166, 73, 168, 170, 133, 47, 172, 257, 174, 176, 178, 75, 103, 180, 165, + 182, 17, 227, 184, 105, 49, 135, 186, 289, 188, 259, 190, 192, 194, 196, 198, 291, 77, + 200, 202, 197, 107, 204, 19, 51, 229, 206, 167, 208, 210, 212, 214, 21, 79, 81, 109, + 216, 218, 220, 222, 53, 137, 224, 199, 226, 323, 321, 169, 228, 111, 230, 232, 139, 261, + 234, 83, 236, 201, 238, 240, 293, 242, 353, 231, 141, 244, 246, 113, 23, 355, 85, 248, + 55, 115, 250, 263, 252, 254, 203, 171, 256, 258, 233, 235, 143, 357, 325, 260, 295, 262, + 173, 145, 177, 87, 264, 327, 267, 266, 268, 175, 270, 272, 117, 297, 274, 265, 147, 179, + 205, 276, 207, 237, 269, 278, 57, 59, 387, 209, 280, 282, 149, 329, 385, 284, 25, 286, + 239, 119, 288, 27, 290, 292, 299, 294, 359, 89, 296, 298, 419, 181, 300, 331, 271, 417, + 211, 361, 151, 389, 241, 302, 304, 303, 306, 308, 421, 91, 310, 312, 391, 314, 121, 316, + 333, 318, 275, 213, 301, 243, 183, 335, 320, 363, 322, 215, 324, 393, 273, 337, 153, 326, + 423, 365, 328, 367, 247, 395, 185, 123, 330, 425, 245, 155, 332, 334, 305, 397, 336, 277, + 217, 338, 340, 339, 427, 342, 344, 346, 307, 399, 187, 348, 309, 341, 350, 369, 279, 311, + 429, 249, 219, 352, 354, 356, 358, 431, 373, 401, 371, 313, 281, 433, 343, 403, 251, 283}; + + +static int decoder_tree1[188] = { + 2, 1, 4, 6, 8, 10, 12, 14, 16, 3, 33, 18, 20, 22, 24, 26, 35, 28, 30, + 32, 34, 36, 5, 65, 38, 40, 37, 42, 44, 46, 67, 48, 50, 52, 54, 56, 58, + 60, 7, 62, 39, 97, 64, 69, 66, 99, 68, 70, 72, 74, 76, 78, 80, 129, 41, + 131, 82, 9, 71, 84, 86, 101, 88, 90, 92, 94, 96, 161, 43, 11, 73, 98, 103, + 100, 163, 102, 104, 106, 108, 133, 110, 105, 112, 75, 114, 45, 13, 116, 165, 118, 195, + 135, 193, 120, 77, 122, 47, 124, 167, 225, 126, 79, 107, 227, 128, 137, 197, 15, 130, + 169, 199, 132, 109, 134, 17, 139, 49, 136, 229, 138, 140, 81, 259, 142, 144, 171, 146, + 141, 148, 111, 150, 201, 231, 152, 51, 257, 289, 154, 19, 113, 156, 261, 158, 203, 173, + 263, 143, 160, 291, 235, 83, 162, 233, 265, 164, 205, 166, 293, 145, 168, 175, 177, 237, + 115, 295, 170, 207, 172, 267, 174, 176, 297, 147, 178, 180, 269, 182, 271, 209, 299, 239, + 179, 184, 301, 241, 211, 0, 0}; + +static int decoder_tree2[96] = { + 2, 1, 4, 6, 8, 10, 12, 3, 17, 14, 19, 16, 18, 20, 22, 24, 26, 5, 21, + 35, 33, 28, 30, 32, 34, 36, 38, 37, 40, 23, 51, 42, 7, 49, 44, 46, 48, 50, + 39, 53, 52, 54, 56, 25, 67, 9, 58, 60, 65, 55, 41, 62, 64, 69, 66, 11, 27, + 68, 57, 83, 70, 71, 81, 43, 72, 74, 13, 76, 85, 29, 73, 78, 99, 59, 87, 101, + 80, 97, 45, 82, 84, 75, 89, 61, 86, 103, 88, 77, 90, 105, 91, 92, 107, 93, 0, 0}; + +static int decoder_tree3[1040] = { + 2, 4, 6, 8, 10, 1, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 3, 36, + 1025, 38, 40, 42, 44, 46, 48, 50, 129, 17, 52, 54, 1153, 19, 56, 58, 60, 62, 64, + 66, 68, 145, 70, 72, 74, 76, 78, 1169, 1027, 147, 80, 82, 1171, 84, 86, 131, 88, 1155, + 1043, 1041, 90, 92, 5, 94, 96, 98, 100, 102, 104, 21, 106, 108, 2049, 2177, 110, 112, 114, + 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 33, 144, 163, 146, 148, + 150, 152, 154, 161, 156, 35, 158, 1297, 160, 162, 273, 257, 164, 166, 149, 168, 1281, 170, 172, + 2193, 174, 176, 178, 1299, 180, 1045, 182, 184, 1173, 186, 3201, 188, 190, 192, 194, 2195, 1187, 23, + 2179, 196, 7, 198, 275, 200, 2051, 202, 2065, 204, 206, 1029, 1185, 208, 210, 1157, 37, 3073, 2067, + 133, 212, 214, 2321, 216, 165, 218, 1059, 220, 1283, 222, 2305, 224, 226, 228, 230, 259, 232, 234, + 2323, 236, 1409, 1057, 1315, 238, 240, 242, 244, 246, 1425, 248, 1313, 250, 252, 254, 256, 258, 260, + 289, 262, 264, 1189, 266, 268, 179, 151, 270, 272, 274, 276, 278, 291, 280, 282, 9, 385, 284, + 286, 177, 49, 401, 1061, 288, 290, 292, 51, 294, 296, 298, 300, 302, 304, 25, 306, 2083, 39, + 308, 310, 3329, 167, 312, 314, 1175, 316, 318, 1203, 135, 320, 322, 324, 326, 328, 2211, 2307, 330, + 1301, 332, 334, 1047, 336, 338, 2449, 3217, 340, 1427, 2209, 53, 342, 2339, 3345, 344, 346, 348, 403, + 181, 4097, 2197, 350, 2181, 1285, 1317, 1031, 352, 354, 356, 3089, 358, 360, 4225, 277, 362, 364, 366, + 368, 2069, 370, 3203, 293, 1201, 305, 372, 3219, 307, 2433, 374, 376, 378, 380, 2081, 1411, 382, 384, + 3075, 1443, 513, 386, 387, 388, 390, 1331, 261, 392, 394, 396, 398, 400, 1441, 1075, 67, 1159, 402, + 404, 406, 408, 410, 412, 414, 3347, 2325, 416, 65, 418, 420, 422, 424, 426, 2053, 193, 1073, 428, + 430, 432, 1537, 1329, 2337, 2213, 434, 417, 183, 41, 436, 438, 440, 442, 444, 446, 448, 450, 195, + 2435, 452, 2085, 1063, 1191, 454, 456, 458, 460, 419, 2071, 1553, 3091, 55, 137, 462, 464, 466, 468, + 470, 472, 474, 476, 478, 2309, 4113, 480, 482, 484, 486, 2451, 2465, 1205, 153, 488, 490, 492, 494, + 496, 498, 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 1333, 526, 1555, 2467, + 2227, 3205, 3331, 528, 530, 532, 534, 536, 538, 540, 542, 544, 546, 548, 529, 309, 1303, 3473, 3457, + 389, 1569, 1445, 1077, 69, 2199, 1539, 4353, 550, 552, 554, 556, 558, 560, 562, 1459, 4241, 3221, 1429, + 2341, 279, 3475, 169, 564, 545, 3105, 323, 2353, 2097, 3235, 421, 2229, 3107, 3233, 566, 568, 570, 572, + 574, 576, 578, 580, 582, 584, 586, 588, 590, 592, 594, 596, 2099, 1091, 531, 2437, 4227, 405, 197, + 263, 1287, 2577, 1049, 1571, 598, 600, 602, 604, 606, 608, 610, 612, 614, 616, 618, 620, 622, 624, 626, + 628, 630, 632, 634, 636, 638, 640, 642, 644, 646, 648, 650, 1345, 1219, 3077, 1457, 2225, 2579, 515, 2561, + 2469, 433, 1221, 2183, 4243, 652, 654, 656, 658, 660, 662, 664, 666, 668, 670, 1217, 3333, 3093, 435, 321, + 4369, 1089, 2055, 4099, 3361, 1319, 547, 1161, 1177, 672, 2355, 4115, 1413, 4257, 3349, 2453, 3109, 2357, 2215, 3363, + 1079, 1207, 311, 1033, 1347, 1065, 674, 676, 678, 680, 682, 684, 686, 688, 690, 692, 694, 696, 698, 700, + 702, 704, 706, 708, 710, 712, 714, 716, 718, 720, 722, 724, 726, 728, 730, 732, 734, 736, 738, 740, + 742, 744, 746, 748, 750, 752, 754, 756, 758, 760, 762, 764, 766, 768, 770, 772, 774, 776, 778, 780, + 782, 784, 786, 788, 790, 792, 794, 796, 798, 800, 802, 804, 806, 808, 810, 812, 814, 2593, 2565, 4261, + 3253, 437, 325, 3489, 2311, 4259, 1431, 2087, 2563, 295, 2343, 449, 199, 265, 2201, 4371, 1193, 816, 533, 1557, + 2581, 2241, 3365, 3491, 3603, 549, 2101, 1461, 1093, 2117, 3459, 3079, 4481, 3095, 2327, 3461, 4129, 3249, 1447, 2471, + 2231, 71, 4497, 2609, 1289, 393, 3251, 2073, 3097, 2371, 1305, 2089, 818, 820, 822, 824, 826, 828, 830, 832, + 834, 836, 838, 840, 842, 844, 846, 848, 850, 852, 854, 856, 858, 860, 862, 864, 866, 868, 870, 872, + 874, 876, 878, 880, 882, 884, 886, 888, 890, 892, 894, 896, 898, 900, 902, 904, 906, 908, 910, 912, + 914, 916, 918, 920, 922, 924, 926, 928, 930, 932, 934, 936, 938, 940, 942, 944, 946, 948, 950, 952, + 954, 956, 958, 960, 962, 964, 966, 968, 970, 972, 974, 976, 978, 980, 982, 984, 986, 988, 990, 992, + 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018, 1020, 1022, 1024, 1026, 1028, 1030, 1032, + 1034, 1036, 4161, 4273, 3507, 3493, 4517, 2497, 1573, 2597, 3621, 4531, 4627, 3523, 3125, 4149, 4529, 3139, 4515, 451, + 4277, 2113, 4163, 4499, 3381, 4405, 1473, 4373, 2485, 3509, 565, 1589, 2613, 3585, 3123, 4403, 3141, 4147, 563, 2245, + 3269, 4357, 1349, 2373, 3397, 453, 1477, 2501, 2481, 579, 1601, 3477, 4103, 3265, 2243, 1587, 3207, 4231, 3267, 4501, + 1475, 3335, 4359, 391, 1415, 2439, 3463, 4487, 519, 1543, 2567, 3591, 4609, 4289, 4611, 2499, 4119, 4385, 4145, 4401, + 3223, 4247, 3379, 577, 3393, 3351, 4375, 407, 1585, 2455, 3479, 4503, 535, 1559, 2583, 3607, 3605, 4513, 4485, 3111, + 4135, 3121, 517, 3377, 3239, 4263, 1541, 4291, 4229, 3367, 4391, 423, 2115, 4131, 3495, 551, 1575, 2599, 3635, 3395, + 2103, 3127, 4151, 3589, 4101, 1603, 3255, 4279, 3601, 1335, 2359, 3383, 439, 1463, 2487, 3511, 567, 1591, 4133, 1095, + 2119, 3143, 2369, 1223, 2247, 3271, 327, 1351, 2375, 455, 1479, 3137, 3521, 2057, 3081, 4105, 4387, 3505, 2185, 3209, + 4233, 3587, 4355, 2313, 3337, 3237, 1417, 2441, 3465, 521, 1545, 3617, 3633, 561, 4625, 4121, 2611, 2483, 2595, 3225, + 4249, 281, 4245, 2329, 3353, 409, 1433, 2457, 3481, 537, 1561, 4483, 3619, 4389, 3113, 4275, 4117, 2217, 3241, 297, + 1321, 2345, 3369, 425, 1449, 2473, 57, 1081, 2105, 3129, 185, 1209, 2233, 3257, 313, 1337, 2361, 441, 1465, 73, + 1097, 201, 1225, 0, 0}; + + +static int decoder_tree4[416] = { + 2, 4, 6, 1, 8, 10, 12, 14, 16, 18, 20, 22, 24, 3, 129, 26, 28, 9, 33, 30, 32, + 34, 36, 11, 161, 38, 40, 42, 41, 44, 46, 131, 43, 169, 35, 48, 137, 50, 52, 54, 56, 139, + 163, 171, 58, 60, 62, 64, 5, 66, 68, 70, 257, 72, 74, 76, 13, 78, 80, 289, 82, 84, 17, + 86, 88, 65, 90, 201, 19, 92, 94, 51, 193, 96, 98, 49, 100, 73, 102, 104, 106, 45, 108, 110, + 297, 112, 114, 116, 37, 203, 118, 120, 179, 122, 177, 124, 265, 126, 75, 133, 259, 291, 147, 128, 67, + 195, 130, 141, 173, 299, 132, 145, 134, 165, 136, 138, 140, 142, 7, 144, 146, 21, 267, 148, 53, 150, + 321, 152, 154, 15, 156, 81, 158, 160, 385, 162, 417, 164, 166, 168, 83, 170, 172, 329, 174, 211, 176, + 27, 178, 180, 182, 209, 184, 186, 188, 190, 25, 192, 331, 194, 196, 105, 57, 198, 97, 200, 202, 323, + 225, 59, 149, 204, 206, 233, 307, 208, 77, 181, 210, 212, 214, 216, 218, 220, 222, 47, 224, 226, 69, + 228, 230, 197, 232, 425, 393, 205, 275, 293, 39, 234, 236, 238, 305, 135, 155, 301, 143, 240, 242, 235, + 395, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 273, 269, 185, 264, 266, 268, 270, 272, 274, 276, + 261, 153, 278, 280, 282, 187, 337, 387, 107, 284, 427, 227, 167, 419, 286, 288, 290, 292, 294, 296, 298, + 300, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 115, + 99, 85, 213, 29, 113, 23, 89, 241, 61, 449, 339, 175, 340, 342, 344, 346, 348, 350, 352, 354, 356, + 358, 360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382, 384, 386, 388, 390, 392, 394, 396, 398, + 400, 402, 404, 406, 408, 410, 412, 414, 389, 361, 457, 465, 429, 451, 333, 109, 277, 243, 263, 295, 199, + 283, 151, 55, 183, 229, 357, 363, 123, 491, 397, 411, 251, 313, 441, 467, 345, 433, 461, 219, 237, 365, + 435, 353, 347, 405, 409, 217, 309, 437, 369, 371, 341, 117, 245, 249, 157, 285, 403, 189, 317, 93, 221, + 315, 401, 481, 391, 489, 121, 421, 423, 71, 483, 327, 103, 231, 443, 459, 271, 399, 355, 91, 303, 431, + 79, 207, 335, 111, 239, 281, 325, 279, 453, 101, 311, 87, 215, 31, 159, 63, 191}; + +static int decoder_tree5[384] = { + 2, 4, 1, 6, 8, 10, 12, 14, 16, 18, 20, 22, 3, 513, 24, 26, 28, 9, 129, 33, 30, + 32, 34, 36, 38, 40, 11, 42, 641, 44, 46, 41, 161, 48, 515, 50, 52, 131, 54, 35, 545, 137, + 56, 58, 60, 521, 62, 43, 673, 64, 169, 66, 68, 523, 70, 163, 643, 139, 553, 72, 649, 74, 547, + 76, 78, 80, 681, 171, 82, 84, 555, 86, 675, 88, 651, 5, 90, 92, 1025, 94, 96, 98, 683, 13, + 100, 17, 102, 104, 106, 65, 108, 110, 257, 112, 114, 1153, 19, 116, 118, 120, 122, 124, 49, 126, 128, + 769, 289, 130, 132, 134, 73, 136, 138, 140, 142, 193, 144, 146, 148, 150, 152, 154, 517, 156, 158, 37, + 51, 160, 201, 162, 145, 164, 166, 168, 133, 170, 801, 45, 172, 174, 1057, 176, 178, 67, 180, 1027, 577, + 182, 184, 186, 188, 190, 192, 194, 196, 198, 259, 200, 202, 204, 525, 177, 265, 141, 206, 208, 210, 212, + 195, 297, 214, 75, 216, 1033, 203, 585, 1155, 1185, 267, 1161, 549, 218, 220, 657, 777, 147, 222, 224, 226, + 228, 230, 232, 234, 236, 238, 240, 587, 645, 165, 242, 244, 246, 248, 250, 771, 291, 252, 579, 1065, 1035, + 705, 531, 529, 659, 173, 254, 561, 653, 256, 713, 677, 557, 258, 260, 262, 264, 266, 268, 270, 272, 274, + 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 707, 1059, 809, 715, 563, 179, 691, 1193, + 21, 779, 1067, 299, 1187, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, + 334, 336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358, 360, 362, 364, 366, 368, 370, 372, 374, + 376, 378, 380, 83, 69, 1281, 803, 321, 1195, 1163, 811, 1323, 689, 1321, 1099, 305, 835, 1227, 331, 843, 785, + 593, 1043, 1291, 1283, 1171, 275, 787, 1217, 833, 1075, 1313, 1219, 1203, 307, 819, 841, 595, 211, 723, 721, 817, + 1029, 329, 81, 1157, 261, 773, 1097, 1089, 1061, 1169, 1091, 1189, 293, 805, 1201, 581, 197, 709, 1289, 273, 1037, + 1315, 1041, 1165, 269, 781, 209, 1073, 1069, 323, 685, 1197, 301, 813, 77, 589, 205, 717, 1225, 533, 149, 661, + 53, 565, 181, 693, 0, 0}; + + +static int decoder_tree6[62] = { + 2, 1, 4, 6, 8, 10, 12, 14, 16, 3, + 33, 5, 17, 9, 18, 20, 22, 24, 26, 28, + 30, 32, 34, 7, 49, 13, 25, 36, 38, 11, + 21, 41, 35, 37, 19, 40, 42, 44, 46, 48, + 50, 15, 52, 57, 29, 27, 23, 53, 54, 51, + 39, 45, 43, 56, 58, 31, 55, 60, 61, 47, + 59, 63}; + +static int *decoder_tables[7] = { + decoder_tree0, + decoder_tree1, + decoder_tree2, + decoder_tree3, + decoder_tree4, + decoder_tree5, + decoder_tree6, +}; + +static float noise_category5[20] = {0.70711f, 0.6179f, 0.5005f, 0.3220f, + 0.17678f, 0.17678f, 0.17678f, 0.17678f, + 0.17678f, 0.17678f, 0.17678f, 0.17678f, + 0.17678f, 0.17678f, 0.17678f, 0.17678f, + 0.17678f, 0.17678f, 0.17678f, 0.17678f}; + +static float noise_category6[20] = {0.70711f, 0.5686f, 0.3563f, 0.25f, + 0.25f, 0.25f, 0.25f, 0.25f, + 0.25f, 0.25f, 0.25f, 0.25f, + 0.25f, 0.25f, 0.25f, 0.25f, + 0.25f, 0.25f, 0.25f, 0.25f}; + +static float noise_category7 = 0.70711f; + + +static int index_table[8] = {4, 4, 3, 3, 2, 2, 1, 0}; + +#endif /* _HUFFMAN_CONSTS_H */ diff --git a/libs/libmsn/libsiren/rmlt.cpp b/libs/libmsn/libsiren/rmlt.cpp new file mode 100644 index 0000000..229b910 --- /dev/null +++ b/libs/libmsn/libsiren/rmlt.cpp @@ -0,0 +1,133 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "siren7.h" + + +static int rmlt_initialized = 0; +static float rmlt_window_640[640]; +static float rmlt_window_320[320]; + +#define PI_2 1.57079632679489661923 + +void siren_rmlt_init() { + int i = 0; + float angle; + + for (i = 0; i < 640; i++) { + angle = (float) (((i + 0.5) * PI_2) / 640); + rmlt_window_640[i] = (float) sin(angle); + } + for (i = 0; i < 320; i++) { + angle = (float) (((i + 0.5) * PI_2) / 320); + rmlt_window_320[i] = (float) sin(angle); + } + + rmlt_initialized = 1; +} + +int siren_rmlt_encode_samples(float *samples, float *old_samples, int dct_length, float *rmlt_coefs) { + int half_dct_length = dct_length / 2; + float *old_ptr = old_samples + half_dct_length; + float *coef_high = rmlt_coefs + half_dct_length; + float *coef_low = rmlt_coefs + half_dct_length; + float *samples_low = samples; + float *samples_high = samples + dct_length; + float *window_low = NULL; + float *window_high = NULL; + int i = 0; + + if (rmlt_initialized == 0) + siren_rmlt_init(); + + if (dct_length == 320) + window_low = rmlt_window_320; + else if (dct_length == 640) + window_low = rmlt_window_640; + else + return 4; + + window_high = window_low + dct_length; + + + for (i = 0; i < half_dct_length; i++) { + *--coef_low = *--old_ptr; + *coef_high++ = (*samples_low * *--window_high) - (*--samples_high * *window_low); + *old_ptr = (*samples_high * *window_high) + (*samples_low++ * *window_low++); + } + siren_dct4(rmlt_coefs, rmlt_coefs, dct_length); + + return 0; +} + + + +int siren_rmlt_decode_samples(float *coefs, float *old_coefs, int dct_length, float *samples) { + int half_dct_length = dct_length / 2; + float *old_low = old_coefs; + float *old_high = old_coefs + half_dct_length; + float *samples_low = samples ; + float *samples_high = samples + dct_length; + float *samples_middle_low = samples + half_dct_length; + float *samples_middle_high = samples + half_dct_length; + float *window_low = NULL; + float *window_high = NULL; + float *window_middle_low = NULL; + float *window_middle_high = NULL; + float sample_low_val; + float sample_high_val; + float sample_middle_low_val; + float sample_middle_high_val; + int i = 0; + + if (rmlt_initialized == 0) + siren_rmlt_init(); + + if (dct_length == 320) + window_low = rmlt_window_320; + else if (dct_length == 640) + window_low = rmlt_window_640; + else + return 4; + + + window_high = window_low + dct_length; + window_middle_low = window_low + half_dct_length; + window_middle_high = window_low + half_dct_length; + + siren_dct4(coefs, samples, dct_length); + + for (i = 0; i < half_dct_length; i+=2) { + sample_low_val = *samples_low; + sample_high_val = *--samples_high; + sample_middle_low_val = *--samples_middle_low; + sample_middle_high_val = *samples_middle_high; + *samples_low++ = (*old_low * *--window_high) + (sample_middle_low_val * *window_low); + *samples_high = (sample_middle_low_val * *window_high) - (*old_low * *window_low++); + *samples_middle_high++ = (sample_low_val * *window_middle_high) - (*--old_high * *--window_middle_low); + *samples_middle_low = (*old_high * *window_middle_high++) + (sample_low_val * *window_middle_low); + *old_low++ = sample_middle_high_val; + *old_high = sample_high_val; + } + + return 0; +} diff --git a/libs/libmsn/libsiren/rmlt.h b/libs/libmsn/libsiren/rmlt.h new file mode 100644 index 0000000..a855dd0 --- /dev/null +++ b/libs/libmsn/libsiren/rmlt.h @@ -0,0 +1,30 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _SIREN7_RMLT_H_ +#define _SIREN7_RMLT_H_ + +extern void siren_rmlt_init(); +extern int siren_rmlt_encode_samples(float *samples, float *old_samples, int dct_length, float *rmlt_coefs); +extern int siren_rmlt_decode_samples(float *coefs, float *old_coefs, int dct_length, float *samples); + +#endif /* _SIREN7_RMLT_H_ */ diff --git a/libs/libmsn/libsiren/siren7.h b/libs/libmsn/libsiren/siren7.h new file mode 100644 index 0000000..9a89811 --- /dev/null +++ b/libs/libmsn/libsiren/siren7.h @@ -0,0 +1,30 @@ +/* + * Siren Encoder/Decoder library + * + * @author: Youness Alaoui + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef _SIREN7_H +#define _SIREN7_H + +#include "encoder.h" +#include "decoder.h" + + +#endif /* _SIREN7_H */ diff --git a/libs/libmsn/md5.cpp b/libs/libmsn/md5.cpp new file mode 100644 index 0000000..473d695 --- /dev/null +++ b/libs/libmsn/md5.cpp @@ -0,0 +1,392 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/*$Id: md5.c,v 1.5 2002/10/11 08:04:56 jtownsend Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +main() +{ + static const char *const test[7] = { + "", /*d41d8cd98f00b204e9800998ecf8427e*/ + "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/ + "abc", /*900150983cd24fb0d6963f7d28e17f72*/ + "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ + "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + /*d174ab98d277d9f5a5611c2c9f419d9f*/ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ + }; + int i; + + for (i = 0; i < 7; ++i) { + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); + md5_finish(&state, digest); + printf("MD5 (\"%s\") = ", test[i]); + for (di = 0; di < 16; ++di) + printf("%02x", digest[di]); + printf("\n"); + } + return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include +main() +{ + int i; + for (i = 1; i <= 64; ++i) { + unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); + printf("#define T%d 0x%08lx\n", i, v); + } + return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + + /* + * On big-endian machines, we must arrange the bytes in the right + * order. (This also works on machines of unknown byte order.) + */ + md5_word_t X[16]; + const md5_byte_t *xp = data; + int i; + + for (i = 0; i < 16; ++i, xp += 4) + X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else /* !ARCH_IS_BIG_ENDIAN */ + + /* + * On little-endian machines, we can process properly aligned data + * without copying it. + */ + md5_word_t xbuf[16]; + const md5_word_t *X; + + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = 0xefcdab89; + pms->abcd[2] = 0x98badcfe; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/libs/libmsn/md5.h b/libs/libmsn/md5.h new file mode 100644 index 0000000..711d673 --- /dev/null +++ b/libs/libmsn/md5.h @@ -0,0 +1,94 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/*$Id: md5.h,v 1.3 2002/10/11 06:49:22 jtownsend Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints. Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +#ifdef P1 +void md5_init(P1(md5_state_t *pms)); +#else +void md5_init(md5_state_t *pms); +#endif + +/* Append a string to the message. */ +#ifdef P3 +void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes)); +#else +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +#endif + +/* Finish the message and return the digest. */ +#ifdef P2 +void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16])); +#else +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +#endif + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/libs/libmsn/message.cpp b/libs/libmsn/message.cpp new file mode 100644 index 0000000..4b606a0 --- /dev/null +++ b/libs/libmsn/message.cpp @@ -0,0 +1,338 @@ +/* + * message.cpp + * libmsn + * + * Created by Mark Rowe on Wed Mar 17 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "message.h" +#include "errorcodes.h" +#include "util.h" +#include +#include + +namespace MSN +{ + Message::Message(std::string body_, std::string header_) + : body(body_), header(header_) + { + } + + std::string Message::asString() const + { + return this->header.asString() + this->body; + } + + std::string Message::operator[](const std::string header_) const + { + assert(header_ != ""); + return this->header[header_]; + } + + std::string Message::Headers::asString() const + { + return this->rawContents; + } + + std::map Message::getFormatInfo() const throw (std::runtime_error) + { + std::map formatInfo; + std::string formatHeader = (*this)["X-MMS-IM-Format"]; + if (formatHeader.empty()) + return formatInfo; + + std::vector parameters = splitString(formatHeader, ";"); + std::vector::iterator i = parameters.begin(); + for (; i != parameters.end(); i++) + { + if (i->at(0) == ' ') + i->erase(0, 1); + + std::vector pair = splitString(*i, "="); + if (pair.size() == 2) + formatInfo[decodeURL(pair[0])] = decodeURL(pair[1]); + else if (pair.size() == 1) + formatInfo[decodeURL(pair[0])] = ""; + else + throw std::runtime_error("Incorrectly specified message format!"); + } + + return formatInfo; + } + + void Message::setFormatInfo(std::map & info) + { + std::string value; + std::map::iterator i = info.begin(); + + if (info.find("FN") != info.end()) + { + value += "FN="; + value += encodeURL(info["FN"]); + value += "; "; + } + + for (; i != info.end(); i++) + { + if ((*i).first == "FN") + continue; + + value += encodeURL((*i).first); + value += "="; + value += encodeURL((*i).second); + value += "; "; + } + if (value == "") + return; + + assert(value.size() >= 2); + value = value.substr(0, value.size() - 2); + this->header.setHeader("X-MMS-IM-Format", value); + } + + std::string Message::getFontName() const + { + return this->getFormatInfo()["FN"]; + } + + void Message::setFontName(const std::string & fontName) + { + std::map info = this->getFormatInfo(); + info["FN"] = fontName; + this->setFormatInfo(info); + } + + std::vector Message::getColor() const + { + std::string color = this->getFormatInfo()["CO"]; + assert(color.size() <= 6 && color.size() >= 0); + color.insert(0UL, 6 - color.size(), '0'); + int r = 0, g = 0, b = 0; + + b = strtol(color.substr(0, 2).c_str(), NULL, 16); + g = strtol(color.substr(2, 2).c_str(), NULL, 16); + r = strtol(color.substr(4, 2).c_str(), NULL, 16); + + std::vector out; + out.push_back(r); + out.push_back(g); + out.push_back(b); + return out; + } + + std::string Message::getColorAsHTMLString() const + { + std::vector color = this->getColor(); + std::ostringstream s; + s << std::hex << std::setfill('0') << std::setw(2) << color[0]; + s << std::hex << std::setfill('0') << std::setw(2) << color[1]; + s << std::hex << std::setfill('0') << std::setw(2) << color[2]; + + assert(s.str().size() == 6); + return s.str(); + } + + void Message::setColor(std::vector color) + { + std::map info = this->getFormatInfo(); + assert(color.size() == 3); + + std::ostringstream s; + s << std::hex << std::setfill('0') << std::setw(2) << color[2]; + s << std::hex << std::setfill('0') << std::setw(2) << color[1]; + s << std::hex << std::setfill('0') << std::setw(2) << color[0]; + + assert(s.str().size() == 6); + info["CO"] = s.str(); + this->setFormatInfo(info); + } + + void Message::setColor(std::string color) + { + color.insert(0UL, 6 - color.size(), '0'); + int r = 0, g = 0, b = 0; + + r = strtol(color.substr(0, 2).c_str(), NULL, 16); + g = strtol(color.substr(2, 2).c_str(), NULL, 16); + b = strtol(color.substr(4, 2).c_str(), NULL, 16); + + std::vector v; + v.push_back(r); + v.push_back(g); + v.push_back(b); + this->setColor(v); + } + + void Message::setColor(int red, int green, int blue) + { + std::vector v; + v.push_back(red); + v.push_back(green); + v.push_back(blue); + this->setColor(v); + } + + + int Message::getFontEffects() const + { + int retVal = 0; + std::string fontEffects = this->getFormatInfo()["EF"]; + + if (fontEffects.find("B") != std::string::npos) + retVal |= BOLD_FONT; + + if (fontEffects.find("I") != std::string::npos) + retVal |= ITALIC_FONT; + + if (fontEffects.find("U") != std::string::npos) + retVal |= UNDERLINE_FONT; + + if (fontEffects.find("S") != std::string::npos) + retVal |= STRIKETHROUGH_FONT; + + return retVal; + } + + void Message::setFontEffects(int fontEffects) + { + std::string effects; + std::map info = this->getFormatInfo(); + + if (fontEffects & BOLD_FONT) + effects += "B"; + + if (fontEffects & ITALIC_FONT) + effects += "I"; + + if (fontEffects & UNDERLINE_FONT) + effects += "U"; + + if (fontEffects & STRIKETHROUGH_FONT) + effects += "S"; + + info["EF"] = effects; + this->setFormatInfo(info); + } + + Message::CharacterSet Message::getFontCharacterSet() const + { + std::string fontCharacterSet = this->getFormatInfo()["CS"]; + int c = strtol(fontCharacterSet.c_str(), NULL, 16); + return (Message::CharacterSet) c; + } + + void Message::setFontCharacterSet(CharacterSet cs) + { + std::map info = this->getFormatInfo(); + std::ostringstream s; + + s << std::hex << (int) cs; + info["CS"] = s.str(); + + this->setFormatInfo(info); + } + + Message::FontFamily Message::getFontFamily() const + { + std::string fontFamily = this->getFormatInfo()["PF"]; + if (fontFamily.size() < 1) + return (Message::FontFamily) 0; + int family = decimalFromString(fontFamily.substr(0, 1)); + return (Message::FontFamily) family; + } + + Message::FontPitch Message::getFontPitch() const + { + std::string fontPitch = this->getFormatInfo()["PF"]; + if (fontPitch.size() < 2) + return (Message::FontPitch) 0; + int pitch = decimalFromString(fontPitch.substr(1, 1)); + return (Message::FontPitch) pitch; + } + + void Message::setFontFamilyAndPitch(Message::FontFamily fontFamily, Message::FontPitch fontPitch) + { + std::map info = this->getFormatInfo(); + std::ostringstream s; + + s << fontFamily << fontPitch; + info["PF"] = s.str(); + + this->setFormatInfo(info); + } + + bool Message::isRightAligned() const + { + return this->getFormatInfo()["RL"] == "1"; + } + + void Message::Headers::setHeader(const std::string header, const std::string value) + { + if ((*this)[header] == "") + { + assert(this->rawContents.size() >= 2); + this->rawContents.insert(this->rawContents.size() - 2, header + ": " + value + "\r\n"); + } + else + { + size_t position = this->rawContents.find(header + ": "); + assert(position != std::string::npos); + + size_t eol = this->rawContents.find("\r\n", position); + if (eol == std::string::npos) + eol = this->rawContents.size(); + + this->rawContents.erase(position, eol - position + 2); + this->rawContents.insert(position, header + ": " + value + "\r\n"); + } + } + + std::string Message::Headers::operator[](const std::string header_) const + { + std::string retval; + std::string::iterator i; + + if (this->rawContents.substr(0U, header_.size()) == header_) + { + retval = this->rawContents; + } else { + std::string tmp = "\r\n" + header_; + size_t position = this->rawContents.find(tmp); + if (position == std::string::npos) + return ""; + + retval = this->rawContents.substr(position + 2); + } + + retval = retval.substr(retval.find(':') + 1); + while (isspace(retval[0])) + retval.erase(retval.begin()); + + for (i = retval.begin(); i != retval.end(); i++) + { + if (*i == '\r') + { + return retval.substr(0, std::distance(retval.begin(), i)); + } + } + return ""; + } +} diff --git a/libs/libmsn/message.h b/libs/libmsn/message.h new file mode 100644 index 0000000..79c31d5 --- /dev/null +++ b/libs/libmsn/message.h @@ -0,0 +1,211 @@ +#ifndef __msn_message_h__ +#define __msn_message_h__ + +/* + * message.h + * libmsn + * + * Created by Mark Rowe on Wed Mar 17 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef WIN32 +#include +#endif +#include +#include +#include +#include + +#include "libmsn_export.h" + +#ifdef _MSC_VER +#pragma warning( disable : 4290 ) +#endif + +namespace MSN +{ + + /** This class represents an MSN message + * + * It may or may not represent an @e instant @e message. + * + * @todo Complete read/write support for formatting messages. + */ + class LIBMSN_EXPORT Message + { +public: + enum FontEffects + { + BOLD_FONT = 1, + ITALIC_FONT = 2, + UNDERLINE_FONT = 4, + STRIKETHROUGH_FONT = 8 + }; + +#ifdef WIN32 + typedef int CharacterSet; + typedef int FontFamily; + typedef int FontPitch; +#else + enum CharacterSet + { + ANSI_CHARSET = 0x00, + DEFAULT_CHARSET = 0x01, + SYMBOL_CHARSET = 0x02, + MAC_CHARSET = 0x4d, + SHIFTJIS_CHARSET = 0x80, + HANGEUL_CHARSET = 0x81, + JOHAB_CHARSET = 0x82, + GB2312_CHARSET = 0x86, + CHINESEBIG5_CHARSET = 0x88, + GREEK_CHARSET = 0xa1, + TURKISH_CHARSET = 0xa2, + VIETNAMESE_CHARSET = 0xa3, + HEBREW_CHARSET = 0xb1, + ARABIC_CHARSET = 0xb2, + BALTIC_CHARSET = 0xba, + RUSSIAN_CHARSET_DEFAULT = 0xcc, + THAI_CHARSET = 0xde, + EASTEUROPE_CHARSET = 0xee, + OEM_DEFAULT = 0xff + }; + + enum FontFamily + { + FF_DONTCARE = 0, + FF_ROMAN = 1, + FF_SWISS = 2, + FF_MODERN = 3, + FF_SCRIPT = 4, + FF_DECORATIVE = 5 + }; + + enum FontPitch + { + DEFAULT_PITCH = 0, + FIXED_PITCH = 1, + VARIABLE_PITCH = 2 + }; +#endif + + class Headers + { +public: + Headers(const std::string & rawContents_) : rawContents(rawContents_) {}; + Headers() : rawContents("") {}; + std::string asString() const; + std::string operator[](const std::string header) const; + void setHeader(const std::string header, const std::string value); + +private: + std::string rawContents; + }; + +private: + std::string body; + Message::Headers header; + +public: + /** Create a message with the specified @a body and @a mimeHeader. + */ + Message(std::string body, std::string mimeHeader="MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n"); + + /** Convert the Message into a string. + * + * This returns a string containing the MIME headers separated from the + * message body by a blank line. + */ + std::string asString() const; + + /** Return the value of the MIME header named @a header. + * + * @return The value of the MIME header if present, or "" if not found. + */ + std::string operator[](const std::string header) const; + void setHeader(const std::string name, const std::string value) { header.setHeader(name, value); }; + + /** Return the body portion of this Message. + */ + std::string getBody() const { return body; }; + + /** Return the font name used in this Message. + * + * @return The font name used for this Message, or "" if none specified. + */ + std::string getFontName() const; + + /** Set font name for use in this Message. + */ + void setFontName(const std::string & fontName); + + /** Get the color used in this Message. + */ + std::vector getColor() const; + std::string getColorAsHTMLString() const; + + /** Set the color used in this Message. + */ + void setColor(std::vector color); + void setColor(std::string color); + void setColor(int red, int green, int blue); + + /** Return the font effects used in this Message. + * + * @return An integer that is a bitwise-or of Message::FontEffects members. + */ + int getFontEffects() const; + + /** Set the font effects for use in this Message. + * + * @param fontEffects Bitwise-or of Message::FontEffects members. + */ + void setFontEffects(int fontEffects); + + /** Return the character set that the font uses in this Message. + */ + CharacterSet getFontCharacterSet() const; + + /** Set the character set that the font should use for this Message. + */ + void setFontCharacterSet(CharacterSet cs); + + /** Return the font family used in this Message. + */ + FontFamily getFontFamily() const; + + /** Return the font pitch used in this Message. + */ + FontPitch getFontPitch() const; + + /** Set the font family and pitch to be used for this Message. + */ + void setFontFamilyAndPitch(Message::FontFamily fontFamily, Message::FontPitch fontPitch); + + /** Is the Message to be right-aligned? + */ + bool isRightAligned() const; + +private: + std::map getFormatInfo() const throw (std::runtime_error); + void setFormatInfo(std::map & info); + }; + +} +#endif diff --git a/libs/libmsn/msn.h b/libs/libmsn/msn.h new file mode 100644 index 0000000..f90f0a7 --- /dev/null +++ b/libs/libmsn/msn.h @@ -0,0 +1,38 @@ +#ifndef __msn_h__ +#define __msn_h__ + +/* + * msn.h + * libmsn + * + * Created by Mark Rowe on Mon Apr 17 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "authdata.h" +#include "connection.h" +#include "errorcodes.h" +#include "externals.h" +#include "message.h" +#include "notificationserver.h" +#include "switchboardserver.h" +#include "util.h" +#include "soap.h" + +#endif diff --git a/libs/libmsn/msnobject.cpp b/libs/libmsn/msnobject.cpp new file mode 100644 index 0000000..801e938 --- /dev/null +++ b/libs/libmsn/msnobject.cpp @@ -0,0 +1,185 @@ +/* + * msnobject.cpp + * libmsn + * + * Created by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "util.h" +#include +#include "msnobject.h" +#include "xmlParser.h" +#include +#include + +namespace MSN +{ + void MSNObject::addMSNObject(std::string filename, int Type) + { + std::ifstream::pos_type size; + char * memblock; + SHA_CTX ctx; + unsigned char digest[SHA_DIGEST_LENGTH]; + + MSNObjectUnit msnobj; + msnobj.Creator = this->Creator; + msnobj.Size = FileSize(filename.c_str()); + msnobj.Type = Type; + + if(Type!=11) + { + msnobj.Location = toStr(++current_id); + msnobj.Location += ".tmp"; + } + else + { + msnobj.Location = "0"; + // encode wav to siren + libmsn_Siren7_EncodeVoiceClip(filename); + msnobj.Size = FileSize(filename.c_str()); + } + + msnobj.realLocation = filename; + if(Type!=5 && Type!=8) + msnobj.Friendly="AAA="; + + std::ifstream file (filename.c_str(), std::ios::in|std::ios::binary|std::ios::ate); + if (!file.is_open()) + { + return; + } + + size = file.tellg(); + memblock = new char [size]; + file.seekg (0, std::ios::beg); + file.read (memblock, size); + file.close(); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, memblock, size); + SHA1_Final(digest, &ctx); + + delete[] memblock; + + msnobj.SHA1D = b64_encode((const char*)digest,20); + std::string all_fields("Creator" + msnobj.Creator + + "Size" + toStr(msnobj.Size) + + "Type" + toStr(msnobj.Type) + + "Location" + msnobj.Location + + "Friendly" + msnobj.Friendly + + "SHA1D" + msnobj.SHA1D); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, all_fields.c_str(), all_fields.length()); + SHA1_Final(digest, &ctx); + msnobj.SHA1C = b64_encode((const char*)digest,20); + XMLNode msnObject = XMLNode::createXMLTopNode("msnobj"); + msnObject.addAttribute("Creator", this->Creator.c_str()); + msnObject.addAttribute("Size", toStr(msnobj.Size).c_str()); + msnObject.addAttribute("Type", toStr(msnobj.Type).c_str()); + msnObject.addAttribute("Location", msnobj.Location.c_str()); + msnObject.addAttribute("Friendly", msnobj.Friendly.c_str()); + msnObject.addAttribute("SHA1D", msnobj.SHA1D.c_str()); + + if(Type!=11) // voice does not have this field + msnObject.addAttribute("SHA1C", msnobj.SHA1C.c_str()); + + char *xml = msnObject.createXMLString(false); + msnobj.XMLString = xml; + free(xml); + msnObjects.push_front(msnobj); + } + + void MSNObject::setCreator(std::string creator) + { + this->Creator = creator; + } + + bool MSNObject::getMSNObjectXML(std::string filename, int Type, std::string & msnobj) + { + if(msnObjects.empty()) return false; + + std::list::iterator i = msnObjects.begin(); + for(; i!=msnObjects.end();i++) + { + if((*i).realLocation == filename && + (*i).Type == Type) + { + msnobj = (*i).XMLString; + return true; + } + } + return false; + } + + bool MSNObject::delMSNObjectByType(int Type) + { + bool deleted=false; + if(msnObjects.empty()) return false; + + std::list::iterator i = msnObjects.begin(); + std::list::iterator d; + for(;i!=msnObjects.end();i++) + { + if((*i).Type == Type) + { + d=i; + deleted=true; + } + } + if(deleted) + msnObjects.erase(d); + return deleted; + } + + bool MSNObject::getMSNObjectXMLByType(int Type, std::string & xml) + { + if(msnObjects.empty()) return false; + std::list::iterator i = msnObjects.begin(); + + for( ; i!=msnObjects.end();i++) + { + if((*i).Type == Type) + { + xml = (*i).XMLString; + return true; + } + } + return false; + } + + bool MSNObject::getMSNObjectRealPath(std::string xml, std::string & realpath) + { + if(msnObjects.empty()) return false; + XMLNode msnObject = XMLNode::parseString(xml.c_str()); + std::string SHA1D = msnObject.getAttribute("SHA1D",0); + + std::list::iterator i = msnObjects.begin(); + + for( ; i!=msnObjects.end();i++) + { + // using SHA1D to ensure if we have this file, even if the name is different + if((*i).SHA1D == SHA1D) + { + realpath = (*i).realLocation; + return true; + } + } + return false; + } +} + diff --git a/libs/libmsn/msnobject.h b/libs/libmsn/msnobject.h new file mode 100644 index 0000000..a86bd36 --- /dev/null +++ b/libs/libmsn/msnobject.h @@ -0,0 +1,64 @@ +#ifndef __msn_msnobj_h__ +#define __msn_msnobj_h__ + +/* + * msnobject.h + * libmsn + * + * Created by Tiago Salem Herrmann on Mon Ago 22 2007. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "libmsn_export.h" + +namespace MSN +{ + class LIBMSN_EXPORT MSNObject + { +private: + unsigned int current_id; + std::string Creator; + typedef struct + { + std::string Creator; + unsigned long long Size; + int Type; + std::string Location; + std::string realLocation; + std::string Friendly; + std::string SHA1D; + std::string SHA1C; + std::string XMLString; + } MSNObjectUnit; + + std::list msnObjects; +public: + MSNObject() : current_id(0) {}; + ~MSNObject() {}; + void setCreator(std::string creator); + void addMSNObject(std::string filename, int Type); + bool getMSNObjectXML(std::string filename, int Type, std::string & msnobj); + bool getMSNObject(std::string filename, int Type, MSNObjectUnit & msnobj); + bool delMSNObjectByType(int Type); + bool getMSNObjectXMLByType(int Type, std::string & xml); + bool getMSNObjectRealPath(std::string xml, std::string & realpath); + }; +} +#endif diff --git a/libs/libmsn/msntest.cpp b/libs/libmsn/msntest.cpp new file mode 100644 index 0000000..d6b8727 --- /dev/null +++ b/libs/libmsn/msntest.cpp @@ -0,0 +1,1375 @@ +/* + * msntest.cpp + * libmsn + * + * Created by Meredydd Luff. + * Refactored by Tiago Salem Herrmann + * Copyright (c) 2004 Meredydd Luff. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msn.h" +#include +#include + +class Callbacks : public MSN::Callbacks +{ + + virtual void registerSocket(void *s, int read, int write, bool isSSL); + + virtual void unregisterSocket(void *s); + + virtual void closeSocket(void *s); + + virtual void showError(MSN::Connection * conn, std::string msg); + + virtual void buddyChangedStatus(MSN::NotificationServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::BuddyStatus state, unsigned int clientID, std::string msnobject); + + virtual void buddyOffline(MSN::NotificationServerConnection * conn, MSN::Passport buddy); + + virtual void log(int writing, const char* buf); + + virtual void buddyChangedPersonalInfo(MSN::NotificationServerConnection * conn, MSN::Passport fromPassport, MSN::personalInfo); + + virtual void gotFriendlyName(MSN::NotificationServerConnection * conn, std::string friendlyname); + + virtual void gotBuddyListInfo(MSN::NotificationServerConnection * conn, MSN::ListSyncInfo * data); + + virtual void gotLatestListSerial(MSN::NotificationServerConnection * conn, std::string lastChange); + + virtual void gotGTC(MSN::NotificationServerConnection * conn, char c); + + virtual void gotBLP(MSN::NotificationServerConnection * conn, char c); + + virtual void addedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport buddy, std::string friendlyname); + + virtual void removedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport buddy); + + virtual void addedGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupName, std::string groupID); + + virtual void removedGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupID); + + virtual void renamedGroup(MSN::NotificationServerConnection * conn, bool renamed, std::string newGroupName, std::string groupID); + + virtual void addedContactToGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupId, std::string contactId); + + virtual void removedContactFromGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupId, std::string contactId); + + virtual void addedContactToAddressBook(MSN::NotificationServerConnection * conn, bool added, std::string passport, std::string displayName, std::string guid); + + virtual void removedContactFromAddressBook(MSN::NotificationServerConnection * conn, bool removed, std::string contactId, std::string passport); + + virtual void enabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool enabled, std::string contactId, std::string passport); + virtual void disabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool disabled, std::string contactId); + + virtual void gotSwitchboard(MSN::SwitchboardServerConnection * conn, const void * tag); + + virtual void buddyJoinedConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, int is_initial); + + virtual void buddyLeftConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy); + + virtual void gotInstantMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::Message * msg); + + virtual void gotMessageSentACK(MSN::SwitchboardServerConnection * conn, int trID); + + virtual void gotEmoticonNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string alias, std::string msnobject); + + virtual void failedSendingMessage(MSN::Connection * conn); + + virtual void buddyTyping(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname); + + virtual void gotNudge(MSN::SwitchboardServerConnection * conn, MSN::Passport from); + + virtual void gotVoiceClipNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport from, std::string msnobject); + + virtual void gotWinkNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport from, std::string msnobject); + + virtual void gotInk(MSN::SwitchboardServerConnection * conn, MSN::Passport from, std::string image); + + virtual void gotVoiceClipFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file); + + virtual void gotEmoticonFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string alias, std::string file); + + virtual void gotWinkFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file); + + virtual void gotActionMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string message); + + virtual void gotInitialEmailNotification(MSN::NotificationServerConnection * conn, int msgs_inbox, int unread_inbox, int msgs_folders, int unread_folders); + + virtual void gotNewEmailNotification(MSN::NotificationServerConnection * conn, std::string from, std::string subject); + + virtual void fileTransferProgress(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, long long unsigned transferred, long long unsigned total); + + virtual void fileTransferFailed(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, MSN::fileTransferError error); + + virtual void fileTransferSucceeded(MSN::SwitchboardServerConnection * conn, unsigned int sessionID); + + virtual void fileTransferInviteResponse(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, bool response); + + virtual void gotNewConnection(MSN::Connection * conn); + + virtual void gotOIMList(MSN::NotificationServerConnection * conn, std::vector OIMs); + + virtual void gotOIM(MSN::NotificationServerConnection * conn, bool success, std::string id, std::string message); + + virtual void gotOIMSendConfirmation(MSN::NotificationServerConnection * conn, bool success, int id); + + virtual void gotOIMDeleteConfirmation(MSN::NotificationServerConnection * conn, bool success, std::string id); + + virtual void gotContactDisplayPicture(MSN::SwitchboardServerConnection * conn, MSN::Passport passport, std::string filename ); + + virtual void closingConnection(MSN::Connection * conn); + + virtual void changedStatus(MSN::NotificationServerConnection * conn, MSN::BuddyStatus state); + + virtual void * connectToServer(std::string server, int port, bool *connected, bool isSSL); + + virtual void connectionReady(MSN::Connection * conn); + + virtual void askFileTransfer(MSN::SwitchboardServerConnection *conn, MSN::fileTransferInvite ft); + + virtual int listenOnPort(int port); + + virtual std::string getOurIP(); + + virtual int getSocketFileDescriptor (void *sock); + + virtual size_t getDataFromSocket (void *sock, char *data, size_t size); + + virtual size_t writeDataToSocket (void *sock, char *data, size_t size); + + virtual std::string getSecureHTTPProxy(); + virtual void gotInboxUrl (MSN::NotificationServerConnection *conn, MSN::hotmailInfo info); +}; + +struct pollfd mySockets[21]; +struct ssl { + bool isSSL; + bool isConnected; + SSL *ssl; + SSL_CTX *ctx; +} mySocketsSsl[21]; + +void handle_command(MSN::NotificationServerConnection &); +int countsocks(void); +std::string myFriendlyName; +std::string myUsername; +//std::string lastObject; + +// should be random +unsigned int sessionID = 123456; + +int main() +{ + for (int i = 1; i < 20; i++) + { + mySockets[i].fd = -1; + mySockets[i].events = POLLIN; + mySockets[i].revents = 0; + mySocketsSsl[i].isSSL = false; + mySocketsSsl[i].isConnected = false; + mySocketsSsl[i].ctx = NULL; + mySocketsSsl[i].ssl = NULL; + } + + mySockets[0].fd = 0; + mySockets[0].events = POLLIN; + mySockets[0].revents = 0; + + Callbacks cb; + MSN::Passport uname; + char *pass = NULL; + while (1) + { + fprintf(stderr, "Enter your login name: "); + fflush(stdout); + try + { + std::cin >> uname; + myUsername = uname; + break; + } + catch (MSN::InvalidPassport & e) + { + std::cout << e.what() << std::endl; + } + } + + pass = getpass("Enter your password: "); + fprintf(stderr, "Connecting to the MSN Messenger service...\n"); + + MSN::NotificationServerConnection mainConnection(uname, pass, cb); + mainConnection.connect("messenger.hotmail.com", 1863); + fprintf(stderr, "> "); + fflush(stderr); + while (1) + { + poll(mySockets, 20, -1); + for (int i = 1; i < 20; i++) + { + if (mySockets[i].fd == -1) + break; + if (mySockets[i].revents & POLLHUP) { + mySockets[i].revents = 0; + continue; + } + if (mySockets[i].revents & (POLLIN | POLLOUT | POLLPRI)) + { + MSN::Connection *c; + + // Retrieve the connection associated with the + // socket's file handle on which the event has + // occurred. + c = mainConnection.connectionWithSocket((void*)mySockets[i].fd); + + // if this is a libmsn socket + if (c != NULL) + { + // If we aren't connected yet, a socket event means that + // our connection attempt has completed. + if(mySocketsSsl[i].isSSL && !mySocketsSsl[i].isConnected) + { + BIO *bio_socket_new; + SSL_METHOD *meth=NULL; + meth=const_cast(SSLv23_client_method()); + SSLeay_add_ssl_algorithms(); + mySocketsSsl[i].ctx = SSL_CTX_new(meth); + mySocketsSsl[i].ssl = SSL_new(mySocketsSsl[i].ctx); + bio_socket_new = BIO_new_socket(mySockets[i].fd, BIO_CLOSE); + if(!mySocketsSsl[i].ssl) + break; + BIO_set_nbio(bio_socket_new, 0); + SSL_set_bio(mySocketsSsl[i].ssl, bio_socket_new, bio_socket_new); + SSL_set_mode(mySocketsSsl[i].ssl, SSL_MODE_AUTO_RETRY); + + // TODO - fix-me - not async and buggy + int ret = SSL_connect(mySocketsSsl[i].ssl); + mySocketsSsl[i].isConnected = true; + } + if (c->isConnected() == false) + c->socketConnectionCompleted(); + + // If this event is due to new data becoming available + if (mySockets[i].revents & POLLIN) + { + if(mySocketsSsl[i].isSSL && mySocketsSsl[i].isConnected) + { + if(SSL_want_read(mySocketsSsl[i].ssl)) + { + mySockets[i].revents = 0; + continue; + } + } + c->dataArrivedOnSocket(); + } + + // If this event is due to the socket becoming writable + if (mySockets[i].revents & POLLOUT) + { + c->socketIsWritable(); + } + } + } + + if (mySockets[i].revents & (POLLERR | POLLNVAL)) + { + printf("Dud socket (%d)! Code %x (ERR=%x, INVAL=%x)\n", mySockets[i].fd, mySockets[i].revents, POLLERR, POLLNVAL); + + MSN::Connection *c; + + // Retrieve the connection associated with the + // socket's file handle on which the event has + // occurred. + c = mainConnection.connectionWithSocket((void*)mySockets[i].fd); + + // if this is a libmsn socket + if (c != NULL) + { + // Delete the connection. This will cause the resources + // that are being used to be freed. + delete c; + } + + mySockets[i].fd = -1; + mySockets[i].revents = 0; + continue; + } + } + + if (mySockets[0].revents & POLLIN) + { + handle_command(mainConnection); + mySockets[0].revents = 0; + } + } +} + +void handle_command(MSN::NotificationServerConnection & mainConnection) +{ + char command[40]; + + if (scanf(" %s", command) == EOF) + { + printf("\n"); + exit(0); + } + + if (!strcmp(command, "quit")) + { + exit(0); + } else if (!strcmp(command, "sendoim")) { + char rcpt[80]; + char msg[1024]; + + scanf(" %s", rcpt); + + fgets(msg, 1024, stdin); + + msg[strlen(msg)-1] = '\0'; + + const std::string rcpt_ = rcpt; + const std::string msg_ = msg; + + MSN::Soap::OIM oim; + oim.myFname = myFriendlyName; + oim.toUsername = rcpt; + oim.message = msg; + oim.myUsername = myUsername; + oim.id = 1; + + mainConnection.send_oim(oim); + } else if (!strcmp(command, "addtogroup")) { + char gid[80]; + char uid[80]; + + scanf(" %s", gid); + + fgets(uid, 80, stdin); + + uid[strlen(uid)-1] = '\0'; + + const std::string uid_ = uid; + const std::string gid_ = gid; + + mainConnection.addToGroup(gid,uid); + } else if (!strcmp(command, "delfromgroup")) { + char gid[80]; + char uid[80]; + + scanf(" %s", gid); + + fgets(uid, 80, stdin); + + uid[strlen(uid)-1] = '\0'; + + const std::string uid_ = uid; + const std::string gid_ = gid; + + mainConnection.removeFromGroup(gid,uid); + } else if (!strcmp(command, "renamegroup")) { + char gid[80]; + char name[80]; + + scanf(" %s", gid); + + fgets(name, 80, stdin); + + name[strlen(name)-1] = '\0'; + + const std::string name_ = name; + const std::string gid_ = gid; + + mainConnection.renameGroup(gid,name); + } else if (!strcmp(command, "getoim")) { + char id[80]; + + scanf(" %s", id); + + const std::string _id = id; + mainConnection.get_oim(id,false); + } else if (!strcmp(command, "addgroup")) { + char gname[80]; + + scanf(" %s", gname); + + const std::string _gname = gname; + mainConnection.addGroup(_gname); + } else if (!strcmp(command, "delgroup")) { + char gname[80]; + + scanf(" %s", gname); + + const std::string _gname = gname; + mainConnection.removeGroup(_gname); + } else if (!strcmp(command, "deloim")) { + char id[80]; + + scanf(" %s", id); + + const std::string _id = id; + mainConnection.delete_oim(id); + + } else if (!strcmp(command, "msg")) { + char rcpt[80]; + char msg[1024]; + + scanf(" %s", rcpt); + + fgets(msg, 1024, stdin); + + msg[strlen(msg)-1] = '\0'; + + const std::string rcpt_ = rcpt; + const std::string msg_ = msg; + const std::pair *ctx = new std::pair(rcpt_, msg_); + mainConnection.requestSwitchboardConnection(ctx); + } else if (!strcmp(command, "status")) { + char state[10]; + + scanf(" %s", state); + mainConnection.change_DisplayPicture("/tmp/global-photo.png"); + uint clientid=0; + + clientid += MSN::MSNC7; + clientid += MSN::MSNC6; + clientid += MSN::MSNC5; + clientid += MSN::MSNC4; + clientid += MSN::MSNC3; + clientid += MSN::MSNC2; + clientid += MSN::MSNC1; + clientid += MSN::SupportWinks; + clientid += MSN::VoiceClips; + clientid += MSN::InkGifSupport; + clientid += MSN::SIPInvitations; + clientid += MSN::SupportMultiPacketMessaging; + + mainConnection.setState(MSN::buddyStatusFromString(state), clientid); + } else if (!strcmp(command, "friendlyname")) { + char fn[256]; + + fgets(fn, 256, stdin); + + fn[strlen(fn)-1] = '\0'; + + mainConnection.setFriendlyName(fn); + } else if (!strcmp(command, "addtolist")) { + MSN::ContactList list; + char user[128]; + + scanf(" %d %s", (int*)&list, user); + + mainConnection.addToList(list, user); + } else if (!strcmp(command, "add")) { + char user[128]; + char nick[128]; + + scanf(" %s %s", user, nick); + + mainConnection.addToAddressBook(user, nick); + } else if (!strcmp(command, "block")) { + char user[128]; + + scanf(" %s", user); + + mainConnection.blockContact(user); + } else if (!strcmp(command, "unblock")) { + char user[128]; + + scanf(" %s", user); + + mainConnection.unblockContact(user); + } else if (!strcmp(command, "delfromlist")) { + MSN::ContactList list; + char user[128]; + + scanf(" %d %s", (int*)&list, user); + + mainConnection.removeFromList(list, user); + } else if (!strcmp(command, "del")) { + char contactid[128]; + char passport[128]; + + scanf(" %s %s", contactid, passport); + + mainConnection.delFromAddressBook(contactid, passport); + } else if (!strcmp(command, "enable")) { + char contactid[128]; + char passport[128]; + + scanf(" %s %s", contactid, passport); + + mainConnection.enableContactOnAddressBook(contactid,passport); + } else if (!strcmp(command, "disable")) { + char contactid[128]; + char passport[128]; + + scanf(" %s %s", contactid, passport); + + mainConnection.disableContactOnAddressBook(contactid,passport); + } else if (!strcmp(command, "reconnect")) { + if (mainConnection.connectionState() != MSN::NotificationServerConnection::NS_DISCONNECTED) + mainConnection.disconnect(); + + mainConnection.connect("messenger.hotmail.com", 1863); + } else if (!strcmp(command, "disconnect")) { + mainConnection.disconnectNS(); + } else if (!strcmp(command, "inboxurl")) { + mainConnection.getInboxUrl(); + } else { + fprintf(stderr, "\nBad command \"%s\"", command); + } + + fprintf(stderr, "\n> "); + fflush(stderr); +} + +int countsocks(void) +{ + int retval = 0; + + for (int a = 0; a < 20; a++) + { + if (mySockets[a].fd == -1) + break; + + retval++; + } + return retval; +} + +void Callbacks::registerSocket(void *s, int reading, int writing, bool isSSL) +{ + for (int a = 1; a < 20; a++) + { + if (mySockets[a].fd == -1 || mySockets[a].fd == getSocketFileDescriptor(s)) + { + if(mySockets[a].fd == -1) + mySockets[a].events = 0; + if (reading) + mySockets[a].events |= POLLIN; + + if (writing) + mySockets[a].events |= POLLOUT; + + mySockets[a].fd = getSocketFileDescriptor(s); + if(isSSL) + mySocketsSsl[a].isSSL = true; + return; + } + } +} + +int +Callbacks::getSocketFileDescriptor (void *sock) +{ + long a = (long)sock; + return (int)a; +} + +void Callbacks::closeSocket(void *s) +{ + for (int a = 1; a < 20; a++) + { + if (mySockets[a].fd == getSocketFileDescriptor(s)) + { + close(getSocketFileDescriptor(s)); + if(mySocketsSsl[a].isSSL) + { + if(mySocketsSsl[a].ssl) + { + if(mySocketsSsl[a].ssl) SSL_set_shutdown(mySocketsSsl[a].ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + if(mySocketsSsl[a].ssl) SSL_free(mySocketsSsl[a].ssl); + if(mySocketsSsl[a].ctx) SSL_CTX_free(mySocketsSsl[a].ctx); + mySocketsSsl[a].ssl=NULL; + mySocketsSsl[a].ctx=NULL; + } + } + for (int b = a; b < 19; b++) + { + mySockets[b].fd = mySockets[b + 1].fd; + mySockets[b].revents = mySockets[b + 1].revents; + mySockets[b].events = mySockets[b + 1].events; + mySocketsSsl[b].isSSL = mySocketsSsl[b + 1].isSSL; + mySocketsSsl[b].isConnected = mySocketsSsl[b + 1].isConnected; + mySocketsSsl[b].ssl = mySocketsSsl[b + 1].ssl; + mySocketsSsl[b].ctx = mySocketsSsl[b + 1].ctx; + } + mySockets[19].fd = -1; + mySocketsSsl[19].isConnected = false; + mySocketsSsl[19].isSSL = false; + mySocketsSsl[19].ssl = NULL; + mySocketsSsl[19].ctx = NULL; + } + } +} + +void Callbacks::unregisterSocket(void *s) +{ + for (int a = 1; a < 20; a++) + { + if (mySockets[a].fd == getSocketFileDescriptor(s)) + { + mySockets[a].events = 0; + } + } +} + +void Callbacks::gotFriendlyName(MSN::NotificationServerConnection * conn, std::string friendlyname) +{ + myFriendlyName = friendlyname.c_str(); + printf("Your friendlyname is now: %s\n", friendlyname.c_str()); +} + +void Callbacks::gotBuddyListInfo(MSN::NotificationServerConnection * conn, MSN::ListSyncInfo * info) +{ + // IMPORTANT + // Here you need to fill a vector with all your contacts + // both received by the server and previous ones. + // Next pass this vector to the function completeConnection() + // if you dont call completeConnection(), the service will + // not work. + std::map::iterator i = info->contactList.begin(); + std::map allContacts; + + for (; i != info->contactList.end(); i++) + { + MSN::Buddy *contact = (*i).second; + if(contact->lists & MSN::LST_AB ) // only if it is the address book + { + allContacts[contact->userName.c_str()]=0; + allContacts[contact->userName.c_str()] |= MSN::LST_AB; + printf("-AB %s (%s)\n Id: %s\n", contact->friendlyName.c_str(), contact->userName.c_str(), contact->properties["contactId"].c_str()); + + if(contact->properties["isMessengerUser"]=="false") + printf(" Not Messenger User\n"); + + std::list::iterator pns = contact->phoneNumbers.begin(); + std::list::iterator g = contact->groups.begin(); + + for (; g != contact->groups.end(); g++) + { + printf(" G: %s\n", (*g)->name.c_str()); + } + + for (; pns != contact->phoneNumbers.end(); pns++) + { + printf(" %s: %s (%d)\n", (*pns).title.c_str(), (*pns).number.c_str(), (*pns).enabled); + } + } + if(contact->lists & MSN::LST_AL ) + { + allContacts[contact->userName.c_str()] |= MSN::LST_AL; + printf("-AL %s \n", contact->userName.c_str()); + } + + if(contact->lists & MSN::LST_BL ) + { + allContacts[contact->userName.c_str()] |= MSN::LST_BL; + printf("-BL %s \n", contact->userName.c_str()); + } + + if(contact->lists & MSN::LST_RL ) + { + printf("-RL %s \n", contact->userName.c_str()); + } + if(contact->lists & MSN::LST_PL ) + { + printf("-PL %s \n", contact->userName.c_str()); + } + } + printf("Available Groups:\n"); + std::map::iterator g = info->groups.begin(); + + for (; g != info->groups.end(); g++) + { + printf(" %s: %s\n", (*g).second.groupID.c_str(), (*g).second.name.c_str()); + } + + std::map::iterator b = allContacts.begin(); + + // this will send the ADL command to the server + // It is necessary. Dont forget to add *all* your contacts to allContacts, + // (both Forward, allow and block lists) or you probably will + // loose someone. + // A contact cannot be present both on allow and block lists or the + // server will return an error, so you need to let your application + // choose the better list to put it in. + conn->completeConnection(allContacts,info); +} + +void Callbacks::gotLatestListSerial(MSN::NotificationServerConnection * conn, std::string lastChange) +{ + // The application needs to track this number to not ask for the whole contact + // list every login + printf("The latest change number is: %s\n", lastChange.c_str()); +} + +void Callbacks::gotGTC(MSN::NotificationServerConnection * conn, char c) +{ + printf("Your GTC value is now %c\n", c); +} + +void Callbacks::gotOIMDeleteConfirmation(MSN::NotificationServerConnection * conn, bool success, std::string id) +{ + if(success) + std::cout << "OIM "<< id << " removed sucessfully." << std::endl; + else + std::cout << "OIM "<< id << " not removed sucessfully." << std::endl; + +} + +void Callbacks::gotOIMSendConfirmation(MSN::NotificationServerConnection * conn, bool success, int id) +{ + if(success) + std::cout << "OIM " << id << " sent sucessfully." << std::endl; + else + std::cout << "OIM " << id << " not sent sucessfully." << std::endl; +} + +void Callbacks::gotOIM(MSN::NotificationServerConnection * conn, bool success, std::string id, std::string message) +{ + if(success) + std::cout << "ID: " << id << std::endl << "\t" << message << std::endl; + else + std::cout << "Error retreiving OIM " << id << std::endl; +} + +void Callbacks::gotOIMList(MSN::NotificationServerConnection * conn, std::vector OIMs) +{ + if(OIMs.size()==0) + { + printf("No Offline messages\n"); + return; + } + std::vector::iterator i = OIMs.begin(); + for(; i *ctx = static_cast *>(tag); + conn->inviteUser(ctx->first); + } +} + +void Callbacks::buddyJoinedConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string friendlyname, int is_initial) +{ + printf("%s (%s) is now in the session\n", friendlyname.c_str(), username.c_str()); + if (conn->auth.tag) + { + const std::pair *ctx = static_cast *>(conn->auth.tag); + // Example of sending a custom emoticon +// conn->sendEmoticon("(EMOTICON)", filename); + + int trid = conn->sendMessage(ctx->second); + std::cout << "Message " << trid << " queued" << std::endl; + delete ctx; + conn->auth.tag = NULL; + + //Example of sending a file +// MSN::fileTransferInvite ft; +// ft.filename = "/tmp/filetosend.txt"; +// ft.friendlyname = "filetosend2.txt"; +// ft.sessionId = sessionID++; +// ft.type = MSN::FILE_TRANSFER_WITHOUT_PREVIEW; +// conn->sendFile(ft); + +// conn->sendNudge(); +// conn->sendAction("Action message here"); + + // Exemple of requesting a display picture. +// std::string filename2("/tmp/displayPicture.bin"+MSN::toStr(sessionID)); + // lastObject is the msnobject received on each contact status change + // you should generate a random sessionID +// conn->requestDisplayPicture(sessionID++, filename2, lastObject); + + // Example of sending a voice clip +// conn->myNotificationServer()->msnobj.addMSNObject("/tmp/voiceclip.wav",11); +// std::string obj; +// conn->myNotificationServer()->msnobj.getMSNObjectXML("/tmp/voiceclip.wav", 11, obj); +// conn->sendVoiceClip(obj); + // exemple of sending an ink +// std::string ink("base64 data here..."); +// conn->sendInk(ink); + } +} + +void Callbacks::buddyLeftConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport username) +{ + printf("%s has now left the session\n", username.c_str()); +} + +void Callbacks::gotInstantMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string friendlyname, MSN::Message * msg) +{ + printf("--- Message from %s (%s) in font %s:\n%s\n", friendlyname.c_str(), username.c_str(), msg->getFontName().c_str(), msg->getBody().c_str()); +} + +void Callbacks::gotEmoticonNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string alias, std::string msnobject) +{ + std::string filename2("/tmp/emoticon.bin"+MSN::toStr(sessionID)); + printf("--- Emoticon '%s' from %s -> %s\n", alias.c_str(), username.c_str(), msnobject.c_str()); + conn->requestEmoticon(sessionID++, filename2, msnobject, alias); +} + +void Callbacks::failedSendingMessage(MSN::Connection * conn) +{ + printf("**************************************************\n"); + printf("ERROR: Your last message failed to send correctly\n"); + printf("**************************************************\n"); +} + +void Callbacks::buddyTyping(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string friendlyname) +{ + printf("\t%s (%s) is typing...\n", friendlyname.c_str(), username.c_str()); + +} + +void Callbacks::gotNudge(MSN::SwitchboardServerConnection * conn, MSN::Passport username) +{ + printf("\t%s sent you a nudge ...\n", username.c_str()); +} + +void Callbacks::gotVoiceClipNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string msnobject) +{ + printf("\t%s sent you a voice clip...\n", username.c_str()); + std::string filename2("/tmp/voiceclip.bin"+MSN::toStr(sessionID)); + conn->requestVoiceClip(sessionID++, filename2, msnobject); +} + +void Callbacks::gotWinkNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string msnobject) +{ + printf("\t%s sent you a Wink...\n", username.c_str()); + std::string filename2("/tmp/wink.bin"+MSN::toStr(sessionID)); + // you should generate a random sessionID number + conn->requestWink(sessionID++, filename2, msnobject); +} + +void Callbacks::gotInk(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string image) +{ + // image variable is the base64 encoded gif file + printf("\t%s sent you an Ink...\n", username.c_str()); +// std::string filename2("/tmp/ink.bin"+MSN::toStr(sessionID)); + // you should generate a random sessionID number +// conn->requestFile(sessionID++, filename2, msnobject); +} + +void Callbacks::gotContactDisplayPicture(MSN::SwitchboardServerConnection * conn, MSN::Passport passport, std::string filename ) +{ + printf("Received display picture from %s. File: %s\n", passport.c_str(), filename.c_str()); +} + +void Callbacks::gotActionMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string message) +{ + printf("\t%s sent you an action message: %s\n", username.c_str(), message.c_str()); +} + +void Callbacks::gotInitialEmailNotification(MSN::NotificationServerConnection * conn, int msgs_inbox, int unread_inbox, int msgs_folders, int unread_folders) +{ + if (unread_inbox > 0) + printf("You have %d new messages in your Inbox. Total: %d\n", unread_inbox, msgs_inbox); + + if (unread_folders > 0) + printf("You have %d new messages in other folders. Total: %d\n", unread_folders, msgs_folders); +} + +void Callbacks::gotNewEmailNotification(MSN::NotificationServerConnection * conn, std::string from, std::string subject) +{ + printf("New e-mail has arrived from %s.\nSubject: %s\n", from.c_str(), subject.c_str()); +} + +void Callbacks::fileTransferInviteResponse(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, bool response) +{ + if(response) + printf("Session accepted %d: \n", sessionID); + else + printf("Session not accepted %d: \n", sessionID); +} + +void Callbacks::fileTransferProgress(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, unsigned long long transferred, unsigned long long total) +{ + printf("File transfer: session %d\t(%llu/%llu bytes sent/received)\n", sessionID, transferred, total); +} + +void Callbacks::fileTransferFailed(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, MSN::fileTransferError error) +{ + if( error == MSN::FILE_TRANSFER_ERROR_USER_CANCELED) + printf("The other user canceled the file transfer failed. Session: %d\n", sessionID); + if( error == MSN::FILE_TRANSFER_ERROR_UNKNOWN) + printf("File transfer failed. Session: %d\n", sessionID); +} + +void Callbacks::fileTransferSucceeded(MSN::SwitchboardServerConnection * conn, unsigned int sessionID) +{ + printf("File transfer successfully completed. session: %d\n", sessionID); +} + +void Callbacks::gotNewConnection(MSN::Connection * conn) +{ + if (dynamic_cast(conn)) + dynamic_cast(conn)->synchronizeContactList(); +} + +void Callbacks::buddyChangedPersonalInfo(MSN::NotificationServerConnection * conn, MSN::Passport fromPassport, MSN::personalInfo pInfo) +{ + // MSN::personalInfo shows all the data you can grab from the contact + printf("User %s Personal Message: %s\n", fromPassport.c_str(),pInfo.PSM.c_str()); +} + +void Callbacks::closingConnection(MSN::Connection * conn) +{ + printf("Closed connection with socket %d\n", conn->sock); +} + +void Callbacks::changedStatus(MSN::NotificationServerConnection * conn, MSN::BuddyStatus state) +{ + printf("Your state is now: %s\n", MSN::buddyStatusToString(state).c_str()); + + MSN::personalInfo pInfo; + pInfo.PSM="my personal message"; + pInfo.mediaType="Music"; + pInfo.mediaIsEnabled=1; + pInfo.mediaFormat="{0} - {1}"; + pInfo.mediaLines.push_back("Artist"); + pInfo.mediaLines.push_back("Song"); + conn->setPersonalStatus(pInfo); +} + +void * Callbacks::connectToServer(std::string hostname, int port, bool *connected, bool isSSL) +{ + struct sockaddr_in sa; + struct hostent *hp; + int s; + + if ((hp = gethostbyname(hostname.c_str())) == NULL) { + errno = ECONNREFUSED; + return (void*)-1; + } + + memset(&sa,0,sizeof(sa)); + memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */ + sa.sin_family = hp->h_addrtype; + sa.sin_port = htons((u_short)port); + + if ((s = socket(hp->h_addrtype,SOCK_STREAM,0)) < 0) /* get socket */ + return (void*)-1; + + int oldfdArgs = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, oldfdArgs | O_NONBLOCK); + + if (connect(s,(struct sockaddr *)&sa,sizeof sa) < 0) + { + if (errno != EINPROGRESS) + { + close(s); + return (void*)-1; + } + *connected = false; + } + else + *connected = true; + return (void*)s; +} + +int Callbacks::listenOnPort(int port) +{ + int s; + struct sockaddr_in addr; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + if (bind(s, (sockaddr *)(&addr), sizeof(addr)) < 0 || listen(s, 1) < 0) + { + close(s); + return -1; + } + + return s; +} + +std::string Callbacks::getOurIP(void) +{ + struct hostent * hn; + char buf2[1024]; + + gethostname(buf2,1024); + hn = gethostbyname(buf2); + + return inet_ntoa( *((struct in_addr*)hn->h_addr)); +} + +void Callbacks::log(int i, const char *s) +{ + +} + +std::string Callbacks::getSecureHTTPProxy() +{ + return ""; +} + +void Callbacks::gotMessageSentACK(MSN::SwitchboardServerConnection * conn, int trID) +{ + std::cout << "Message " << trID << " delivered" << std::endl; +} + +void Callbacks::askFileTransfer(MSN::SwitchboardServerConnection * conn, MSN::fileTransferInvite ft) +{ + std::string filename2("/tmp/"+ft.filename); + switch(ft.type) + { + case MSN::FILE_TRANSFER_BACKGROUND_SHARING: + printf("User %s wants to share with you a background file named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize); + break; + case MSN::FILE_TRANSFER_BACKGROUND_SHARING_CUSTOM: + printf("User %s wants to share with you a *custom background file named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize); + break; + case MSN::FILE_TRANSFER_WITH_PREVIEW: + printf("User %s wants to send you a file *with preview named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize); + // ft.preview has the base64 encoded png file + break; + case MSN::FILE_TRANSFER_WITHOUT_PREVIEW: + printf("User %s wants to send you a file *without preview named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize); + break; + default: + printf("Unknown filetransfer type from %s..\n", ft.userPassport.c_str()); + + } + conn->fileTransferResponse(ft.sessionId, filename2, true); +} + +void Callbacks::addedContactToGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupId, std::string contactId) +{ + if(added) + printf("User Id (%s) added to group Id (%s)\n", contactId.c_str(),groupId.c_str()); + else + printf("User Id (%s) NOT added to group Id (%s)\n", contactId.c_str(),groupId.c_str()); +} + +void Callbacks::removedContactFromGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupId, std::string contactId) +{ + if(removed) + printf("User Id (%s) removed from group Id (%s)\n", contactId.c_str(),groupId.c_str()); + else + printf("User Id (%s) NOT removed from group Id (%s)\n", contactId.c_str(),groupId.c_str()); +} + +void Callbacks::addedContactToAddressBook(MSN::NotificationServerConnection * conn, bool added, std::string passport, std::string displayName, std::string guid) +{ + if(added) + printf("User (%s - %s) added to AddressBook. Guid (%s)\n", passport.c_str(),displayName.c_str(), guid.c_str()); + else + printf("User (%s - %s) NOT added to AddressBook.\n", passport.c_str(),displayName.c_str()); +} + +void Callbacks::removedContactFromAddressBook(MSN::NotificationServerConnection * conn, bool removed, std::string contactId, std::string passport) +{ + if(removed) + printf("User %s removed from AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str()); + else + printf("User %s NOT removed from AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str()); +} + +void Callbacks::enabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool enabled, std::string contactId, std::string passport) +{ + // this is used to enable a contact previously disabled from msn, but not fully removed + if(enabled) + printf("User (%s) enabled on AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str()); + else + printf("User (%s) NOT enabled on AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str()); +} + +void Callbacks::disabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool disabled, std::string contactId) +{ + // this is used when you have disabled this user from msn, but not deleted from hotmail + // I suggest to delete the contact instead of disable, since I haven't tested this too much yet + if(disabled) + printf("User disabled on AddressBook. Guid (%s)\n", contactId.c_str()); + else + printf("User NOT disabled on AddressBook. Guid (%s)\n", contactId.c_str()); +} + +size_t Callbacks::getDataFromSocket (void *sock, char *data, size_t size) +{ + int fd; + int idx=0; + bool ssl_done = false; + int amountRead = 0; + int newAmountRead = 0; + for (int i = 1; i < 20; i++) + { + if(mySockets[i].fd == getSocketFileDescriptor(sock)) + { + idx = i; + break; + } + } + if(!idx) + return 0; + + if(!mySocketsSsl[idx].isSSL) + { + int newWritten = ::recv(getSocketFileDescriptor(sock), data, size, 0); + if (errno == EAGAIN) + return -1; + return newWritten; + } + + if (!mySocketsSsl[idx].isConnected) + return 0; + + while(!ssl_done) + { + if(!mySocketsSsl[idx].ssl) break; + newAmountRead = SSL_read(mySocketsSsl[idx].ssl, data, size); + switch(SSL_get_error(mySocketsSsl[idx].ssl,newAmountRead)) + { + case SSL_ERROR_NONE: + return newAmountRead; + case SSL_ERROR_ZERO_RETURN: + return 0; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + ssl_done=false; + break; + case SSL_ERROR_SSL: + return 0; + break; + case SSL_ERROR_SYSCALL: + return 0; + break; + default: + return newAmountRead; + } + } + return 0; +} + +size_t Callbacks::writeDataToSocket (void *sock, char *data, size_t size) +{ + size_t written = 0; + int idx; + for (int i = 1; i < 20; i++) + { + if(mySockets[i].fd == getSocketFileDescriptor(sock)) + { + idx = i; + break; + } + } + + while (written < size) + { + int newWritten; + if (mySocketsSsl[idx].isSSL) + { + if(!mySocketsSsl[idx].isConnected) + return 0; + newWritten = SSL_write(mySocketsSsl[idx].ssl, data, (int) (size - written)); + int error = SSL_get_error(mySocketsSsl[idx].ssl,newWritten); + switch(error) + { + case SSL_ERROR_NONE: + written += newWritten; + data+=newWritten; + break; + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + continue; + case SSL_ERROR_ZERO_RETURN: + break; + case SSL_ERROR_SYSCALL: + default: + break; + } + } + else + { + newWritten = ::send(getSocketFileDescriptor(sock), data, (int)(size - written), 0); + + if (newWritten <= 0) + { + if (errno == EAGAIN) + continue; + else + break; + } + written += newWritten; + data+=newWritten; + } + } + if (written != size) + { + // TODO: return an error + showError(NULL, "Error on socket"); + unregisterSocket(sock); + closeSocket(sock); + return written; + } + return written; +} + +void Callbacks::gotVoiceClipFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file) +{ + printf("--- Voice clip file '%s'\n", file.c_str()); + +} + +void Callbacks::gotEmoticonFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string alias, std::string file) +{ + printf("--- Voice emoticon file '%s' for '%s'\n", file.c_str(), alias.c_str()); +} + +void Callbacks::gotWinkFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file) +{ + printf("--- Voice Wink file '%s'\n", file.c_str()); +} + +void Callbacks::gotInboxUrl(MSN::NotificationServerConnection *conn, MSN::hotmailInfo info) +{ + std::vector::const_iterator i; + + std::cout << "--- Inbox URL data :" << std::endl; + std::cout << "sid: " << info.sid << std::endl; + std::cout << "kv: " << info.kv << std::endl; + std::cout << "id: " << info.id << std::endl; + std::cout << "sl: " << info.sl << std::endl; + std::cout << "rru: " << info.rru << std::endl; + std::cout << "auth: " << info.MSPAuth << std::endl; + std::cout << "creds: " << info.creds << std::endl; +} diff --git a/libs/libmsn/notificationserver.cpp b/libs/libmsn/notificationserver.cpp new file mode 100644 index 0000000..93371c6 --- /dev/null +++ b/libs/libmsn/notificationserver.cpp @@ -0,0 +1,1738 @@ +/* + * notificationserver.cpp + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "notificationserver.h" +#include "errorcodes.h" +#include "externals.h" +#include "md5.h" +#include "util.h" +#include "soap.h" +#include +#include +#include + +#ifndef WIN32 +#include +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include + +#include "xmlParser.h" + +namespace MSN +{ + std::map &)> NotificationServerConnection::commandHandlers; + std::map &, std::string, std::string)> NotificationServerConnection::messageHandlers; + + NotificationServerConnection::NotificationServerConnection(Passport username_, std::string password_, Callbacks & cb_) + : Connection(), auth(username_, password_), myPassport(username_), m_clientId(0), externalCallbacks(cb_), _connectionState(NS_DISCONNECTED), generatingLockkey(false), removingOIM(false), bplSetting('B') + { + msnobj.setCreator(username_); + registerHandlers(); + } + + NotificationServerConnection::~NotificationServerConnection() + { + if (this->connectionState() != NS_DISCONNECTED) + this->disconnect(); + } + + Connection *NotificationServerConnection::connectionWithSocket(void *sock) + { + if (this->sock == sock) + return this; + + std::vector & list = _switchboardConnections; + std::vector::iterator i = list.begin(); + + for (; i != list.end(); i++) + { + Connection *c = (*i)->connectionWithSocket(sock); + if (c) + return c; + } + + std::vector & list2 = _SoapConnections; + std::vector::iterator d = list2.begin(); + + for (; d != list2.end(); d++) + { + if((*d)->sock == sock ) + return (*d); + } + + + return NULL; + } + + SwitchboardServerConnection *NotificationServerConnection::switchboardWithOnlyUser(Passport username) + { + if (this->connectionState() >= NS_CONNECTED) + { + std::vector & list = _switchboardConnections; + std::vector::iterator i = list.begin(); + + for (; i != list.end(); i++) + { + if ((*i)->users.size() == 1 && + *((*i)->users.begin()) == username) + return *i; + } + } + return NULL; + } + + const std::vector & NotificationServerConnection::switchboardConnections() + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + return _switchboardConnections; + } + + void NotificationServerConnection::addSwitchboardConnection(SwitchboardServerConnection *c) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + _switchboardConnections.push_back(c); + } + + void NotificationServerConnection::addSoapConnection(Soap *s) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + _SoapConnections.push_back(s); + } + + void NotificationServerConnection::removeSwitchboardConnection(SwitchboardServerConnection *c) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::vector::iterator it; + for(it = _switchboardConnections.begin(); it != _switchboardConnections.end(); it++) + { + if((*it) == c) + { + _switchboardConnections.erase(it); + return; + } + } + } + + void NotificationServerConnection::removeSoapConnection(Soap *s) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::vector::iterator it; + for(it = _SoapConnections.begin(); it != _SoapConnections.end(); it++) + { + if((*it) == s) + { + _SoapConnections.erase(it); + return; + } + } + } + + void NotificationServerConnection::addCallback(NotificationServerCallback callback, + int trid, void *data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTING); + this->callbacks[trid] = std::make_pair(callback, data); + } + + void NotificationServerConnection::removeCallback(int trid) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTING); + this->callbacks.erase(trid); + } + + void NotificationServerConnection::registerHandlers() + { + if (commandHandlers.size() == 0) + { + commandHandlers["OUT"] = &NotificationServerConnection::handle_OUT; + commandHandlers["RML"] = &NotificationServerConnection::handle_RML; + commandHandlers["BLP"] = &NotificationServerConnection::handle_BLP; + commandHandlers["CHG"] = &NotificationServerConnection::handle_CHG; + commandHandlers["CHL"] = &NotificationServerConnection::handle_CHL; + commandHandlers["ILN"] = &NotificationServerConnection::handle_ILN; + commandHandlers["NLN"] = &NotificationServerConnection::handle_NLN; + commandHandlers["FLN"] = &NotificationServerConnection::handle_FLN; + commandHandlers["MSG"] = &NotificationServerConnection::handle_MSG; + commandHandlers["PRP"] = &NotificationServerConnection::handle_PRP; + commandHandlers["UBX"] = &NotificationServerConnection::handle_UBX; + commandHandlers["GCF"] = &NotificationServerConnection::handle_GCF; + commandHandlers["ADL"] = &NotificationServerConnection::handle_ADL; + commandHandlers["UBN"] = &NotificationServerConnection::handle_UBN; + commandHandlers["FQY"] = &NotificationServerConnection::handle_FQY; + } + + if (messageHandlers.size() == 0) + { + messageHandlers["text/x-msmsgsinitialemailnotification"] = &NotificationServerConnection::message_initial_email_notification; + messageHandlers["text/x-msmsgsinitialmdatanotification"] = &NotificationServerConnection::message_initialmdatanotification; + messageHandlers["text/x-msmsgsemailnotification"] = &NotificationServerConnection::message_email_notification; + messageHandlers["text/x-msmsgsprofile"] = &NotificationServerConnection::message_msmsgsprofile; + messageHandlers["text/x-msmsgsoimnotification"] = &NotificationServerConnection::message_oimnotification; + } + } + + void NotificationServerConnection::dispatchCommand(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::map &)>::iterator i = commandHandlers.find(args[0]); + if (i != commandHandlers.end()) + (this->*commandHandlers[args[0]])(args); + } + + void NotificationServerConnection::disconnectNS() + { + std::ostringstream buf_; + buf_ << "OUT\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + disconnect(); + } + + void NotificationServerConnection::handle_OUT(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + if (args.size() > 1) + { + if (args[1] == "OTH") + { + this->myNotificationServer()->externalCallbacks.showError(this, "You have logged onto MSN twice at once. Your MSN session will now terminate."); + } + else if (args[1] == "SSD") + { + this->myNotificationServer()->externalCallbacks.showError(this, "This MSN server is going down for maintenance. Your MSN session will now terminate."); + } else { + this->myNotificationServer()->externalCallbacks.showError(this, (std::string("The MSN server has terminated the connection with an unknown reason code. Please report this code: ") + + args[1]).c_str()); + } + } + this->disconnect(); + } + + void NotificationServerConnection::handle_RML(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + int msglen; + std::string msg; + + if(args[2] != "OK" && args[2] != "OK") + return; // TODO - raise an error + + msglen = decimalFromString(args[2]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + + XMLNode rml_data = XMLNode::parseString( msg.c_str() ); + + int nDomains = rml_data.nChildNode("d"); + for(int i=0; imyNotificationServer()->externalCallbacks.removedListEntry(this, list_number, passport); + } + } + } + + void NotificationServerConnection::handle_BLP(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + this->myNotificationServer()->externalCallbacks.gotBLP(this, args[3][0]); + } + + void NotificationServerConnection::handle_CHG(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + this->myNotificationServer()->externalCallbacks.changedStatus(this, buddyStatusFromString(args[2])); + } + + void NotificationServerConnection::handle_CHL(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + std::ostringstream buf_; + buf_ << "QRY " << this->trID++ << " " << szClientID << " 32\r\n"; + if (write(buf_) != buf_.str().size()) + return; + + char b[33]; + memset(&b,0,33); + DoMSNP11Challenge(args[2].c_str(),b); + // send the md5 + std::string a(b); + write(a, false); + } + + void NotificationServerConnection::handle_ILN(std::vector & args) + { + this->assertConnectionStateIs(NS_CONNECTED); + if(args.size() > 7) + this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[3], decodeURL(args[5]), buddyStatusFromString(args[2]), decimalFromString(args[6]), decodeURL(args[7])); + else + this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[3], decodeURL(args[5]), buddyStatusFromString(args[2]), decimalFromString(args[6]), ""); + } + + void NotificationServerConnection::handle_NLN(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + if(args.size() > 6) + this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[2], decodeURL(args[4]), buddyStatusFromString(args[1]), decimalFromString(args[5]), decodeURL(args[6])); + else + this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[2], decodeURL(args[4]), buddyStatusFromString(args[1]), decimalFromString(args[5]), ""); + } + + void NotificationServerConnection::handle_FLN(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + this->myNotificationServer()->externalCallbacks.buddyOffline(this, args[1]); + } + + void NotificationServerConnection::handle_UBN(std::vector & args) + { + // TODO - UBN is a way to exchange data through notification server + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + int msglen; + std::string msg; + + msglen = decimalFromString(args[3]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + } + + void NotificationServerConnection::handle_FQY(std::vector & args) + { + // TODO - I dont know what it means yet + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + int msglen; + std::string msg; + msglen = decimalFromString(args[2]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + } + + void NotificationServerConnection::callback_URL(std::vector & args, int trid, void *data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + char b[33]; + MSN::hotmailInfo info; + info.rru = args[2]; + info.url = args[3]; + info.id = args[4]; + info.sl = toStr(time(NULL) - decimalFromString(login_time)); + info.MSPAuth = MSPAuth; + info.sid = sid; + info.kv = kv; + + // calculate creds + std::string creds_tmp = MSPAuth + info.sl + this->auth.password; + memset(&b,0,33); + + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)creds_tmp.c_str(), creds_tmp.size()); + md5_finish(&state, digest); + // convert to string + for (di = 0; di < 16; ++di) + sprintf(&b[2*di], "%02x", digest[di]); + + std::string creds(b); + info.creds = creds; + + this->myNotificationServer()->externalCallbacks.gotInboxUrl(this, info); + } + + void NotificationServerConnection::handle_MSG(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + int msglen; + std::string msg; + std::string mime; + std::string body; + size_t tmp; + + msglen = decimalFromString(args[3]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + + body = msg.substr(msg.find("\r\n\r\n") + 4); + mime = msg.substr(0, msg.size() - body.size()); + + std::string contentType; + Message::Headers headers = Message::Headers(mime); + contentType = headers["Content-Type"]; + + if ((tmp = contentType.find("; charset")) != std::string::npos) + contentType = contentType.substr(0, tmp); + + std::map &, std::string, std::string)>::iterator i = messageHandlers.find(contentType); + if (i != messageHandlers.end()) + (this->*(messageHandlers[contentType]))(args, mime, body); + } + + void NotificationServerConnection::message_initial_email_notification(std::vector & args, std::string mime, std::string body) + { + std::string unreadInbox; + std::string unreadFolder; + int unreadInboxCount = 0, unreadFolderCount = 0; + + // Initial email notifications body is a set of MIME headers + Message::Headers headers = Message::Headers(body); + + unreadInbox = headers["Inbox-Unread"]; + unreadFolder = headers["Folders-Unread"]; + if (! unreadInbox.empty()) + unreadInboxCount = decimalFromString(unreadInbox); + + if (! unreadFolder.empty()) + unreadFolderCount = decimalFromString(unreadFolder); + +// this->myNotificationServer()->externalCallbacks.gotInitialEmailNotification(this, unreadInboxCount, unreadFolderCount); + } + + void NotificationServerConnection::message_email_notification(std::vector & args, std::string mime, std::string body) + { + // New email notifications body is a set of MIME headers + Message::Headers headers = Message::Headers(body); + + std::string from = headers["From-Addr"]; + std::string subject = headers["Subject"]; + + this->myNotificationServer()->externalCallbacks.gotNewEmailNotification(this, from, subject); + } + + void NotificationServerConnection::message_msmsgsprofile(std::vector & args, std::string mime, std::string body) + { + direct_connection=false; + Message::Headers headers = Message::Headers(mime); + server_reported_ip = headers["ClientIP"]; + server_reported_port = headers["ClientPort"]; + login_time = headers["LoginTime"]; + MSPAuth = headers["MSPAuth"]; + sid = headers["sid"]; + kv = headers["kv"]; + + if (login_time.empty()) //IN MSNP9 there is no logintime it seems, so set it manualy + { + time_t actualTime; + std::stringstream os; + time(&actualTime); + os << actualTime; + login_time = os.str(); + } + + this->myNotificationServer()->externalCallbacks.gotNewConnection(this); + // TODO - test portability to windows and mac, probably solved by ifdefs, + // or with an external callback to user application + + // search on local machine the ip reported by the server + /* int s = socket (PF_INET, SOCK_STREAM, 0); + for (int i=1;;i++) + { + struct ifreq ifr; + struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + char *ip; + + ifr.ifr_ifindex = i; + if (ioctl (s, SIOCGIFNAME, &ifr) < 0) + break; + + if (ioctl (s, SIOCGIFADDR, &ifr) < 0) + continue; + + ip = inet_ntoa (sin->sin_addr); + std::string ip2(ip); + if(ip2==server_reported_ip) + direct_connection=true; + } + close (s); + */ + } + + void NotificationServerConnection::message_initialmdatanotification(std::vector & args, std::string mime, std::string body) + { + Message::Headers headers = Message::Headers(body); + + std::string maildata = headers["Mail-Data"]; + XMLNode domTree = XMLNode::parseString( maildata.c_str() ); + + //Mail-Data: 2110409600204800 + //Mail-Data: 3220409600204800 + //Mail-Data: 3120409600204800 + //empty inbox + //Mail-Data: 0050409600204800 + int emails = domTree.nChildNode("E"); + + if (emails) + { + XMLNode curr_element = domTree.getChildNode("E",0); + int inbox_msgs = decimalFromString(curr_element.getChildNode("I").getText()); + int inbox_unread = decimalFromString(curr_element.getChildNode("IU").getText()); + int other_folders = decimalFromString(curr_element.getChildNode("O").getText()); + int other_folders_unread = decimalFromString(curr_element.getChildNode("OU").getText()); + + this->myNotificationServer()->externalCallbacks. gotInitialEmailNotification(this, inbox_msgs, inbox_unread, other_folders, other_folders_unread); + } + // try to get OIM information + message_oimnotification(args, mime, body); + } + + void NotificationServerConnection::message_oimnotification(std::vector & args, std::string mime, std::string body) + { + Message::Headers headers = Message::Headers(body); + + std::string maildata = headers["Mail-Data"]; + + if(maildata == "too-large") + { + // more than 25 OIM's + // request OIM list through soap + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->getMailData(); + return; + } + // process maildata + gotMailData(maildata); + } + + void NotificationServerConnection::gotSoapMailData(Soap & soapConnection, std::string maildata) + { + gotMailData(maildata); + } + + void NotificationServerConnection::gotMailData(std::string maildata) + { + std::vector messages; + XMLNode domTree = XMLNode::parseString( maildata.c_str() ); + + int oims = domTree.nChildNode("M"); + + if (oims) + { + for(int i=0;i friendlyName; + // if we do not have '?', it is just the email + if(temp_oim.fromFN.find("?") != std::string::npos) + { + friendlyName=splitString(temp_oim.fromFN,"?"); + // TODO - handle the encoding (friendlyName[1]) + if(friendlyName[2]=="B") + { + temp_oim.fromFN=b64_decode(friendlyName[3].c_str()); + } + if(friendlyName[2]=="Q") + { + // Quoted-Printable, is similar to URL encoding, + // but uses "=" instead of "%". + std::string change = friendlyName[3]; + // changes the = by % + for(unsigned int a=0;amyNotificationServer()->externalCallbacks.gotOIMList(this, messages); + } + domTree.deleteNodeContent('Y'); + } + + void NotificationServerConnection::handle_RNG(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + SwitchboardServerConnection::AuthData auth = SwitchboardServerConnection::AuthData(this->auth.username, + args[1], + args[4]); + SwitchboardServerConnection *newSBconn = new SwitchboardServerConnection(auth, *this); + this->addSwitchboardConnection(newSBconn); + std::pair server_address = splitServerAddress(args[2]); + newSBconn->connect(server_address.first, server_address.second); + } + + void NotificationServerConnection::handle_PRP(std::vector & args) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + if(this->_connectionState == NS_SYNCHRONISING) + { + this->myNotificationServer()->externalCallbacks.gotFriendlyName(this, decodeURL(args[3])); + this->myDisplayName = decodeURL(args[3]); + this->myNotificationServer()->externalCallbacks.connectionReady(this); + // the initial process ends here + this->setConnectionState(NS_CONNECTED); + return; + } + if ( args[2] == "MFN") //when you set manually your FriendlyName + { + this->myNotificationServer()->externalCallbacks.gotFriendlyName(this, decodeURL(args[3])); + this->myDisplayName = decodeURL(args[3]); + + }// TODO - Implement other PRP commands: MBE WWE + } + + void NotificationServerConnection::handle_GCF(std::vector & args) + { + int msglen; + std::string msg; + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + // we do not use it so far. throw it away + msglen = decimalFromString(args[2]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + } + + void NotificationServerConnection::handle_ADL(std::vector & args) + { + int msglen; + std::string msg; + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + if(args[2] =="OK" && (this->_connectionState == NS_SYNCHRONISING)) + { + if(adl_packets.empty()) + { + // no more adl packets to send! + // now you need the change the nickname and set the status + // to complete the initial connection process + // it is not possible to use setFriendlyName + // because we cannot send soap requests at this time + std::ostringstream buf_; + if(this->myDisplayName.empty()) + this->myDisplayName = myPassport; + if(server_email_verified != "0") + { + // our email is verified + buf_ << "PRP " << this->trID++ << " MFN " << encodeURL(this->myDisplayName) << "\r\n"; + write(buf_); + } + else + { + // not verified, so we cant change our displayName + this->myNotificationServer()->externalCallbacks.connectionReady(this); + // the initial process ends here + this->setConnectionState(NS_CONNECTED); + } + return; + } + else + { + // send each adl packet at a time + std::string adl_payload = adl_packets.front(); + adl_packets.pop_front(); + std::ostringstream buf_; + buf_ << "ADL " << this->trID++ << " " << adl_payload.length() << "\r\n"; + buf_ << adl_payload; + if (write(buf_) != buf_.str().size()) + return; + } + } + // I reach here when ADL has payload + msglen = decimalFromString(args[2]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + // ADL 0 70 + // + // + + XMLNode adl_data = XMLNode::parseString( msg.c_str() ); + + int nDomains = adl_data.nChildNode("d"); + for(int i=0; imyNotificationServer()->externalCallbacks.addedListEntry(this, list_number, passport, fname); + } + } + } + + void NotificationServerConnection::handle_UBX(std::vector & args) + { + int msglen; + personalInfo pInfo; + std::string msg,media,psm; + MSN::Passport fromPassport = args[1]; + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + msglen = decimalFromString(args[3]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + + // some buggy clients send no data in UBX command + if( msg.length() < 10 ) return; + + XMLNode ubx_data = XMLNode::parseString( msg.c_str() ); + + const char *a = ubx_data.getChildNode("PSM").getText(); + if(a) + { + psm = a; + pInfo.PSM = psm; + } + + const char *m = ubx_data.getChildNode("CurrentMedia").getText(); + if(m) + { + media = m; + // the splitString will drop the first NULL position. + // we need it to keep the order of the following fields. + std::vector media1 = splitString(media, "\\0"); + if(media1.size()>=4) // at least 4 fields. Type, Enabled, format, data + { // if we have some field, so ... + int i=0; + if (media.find("\\0")==0) + { // if starts with \0 there is no + // App field. It is optional. + pInfo.mediaApp = ""; + } + else + { + pInfo.mediaApp = media1[i++]; + } + pInfo.mediaType = media1[i++]; + pInfo.mediaIsEnabled = decimalFromString(media1[i++]); + + if(pInfo.mediaIsEnabled) + { + pInfo.mediaFormat = media1[i++]; + for(unsigned int b=i; b < media1.size(); b++) + { + pInfo.mediaLines.push_back(media1[i++]); + } + } + } + } + this->myNotificationServer()->externalCallbacks.buddyChangedPersonalInfo(this, fromPassport, pInfo); + } + + void NotificationServerConnection::setState(BuddyStatus state, uint clientID) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::ostringstream buf_; + std::string xml; + + if(msnobj.getMSNObjectXMLByType(3,xml)) + buf_ << "CHG " << this->trID++ << " " << + buddyStatusToString(state) << " "<< unsignedToStr(clientID) << " " << encodeURL(xml) << "\r\n"; + else + buf_ << "CHG " << this->trID++ << " " << buddyStatusToString(state) << " " << unsignedToStr(clientID) << "\r\n"; + write(buf_); + } + + void NotificationServerConnection::setBLP(char setting) + { + if (setting != 'A' || setting != 'B') + return; + + if(this->_connectionState == NS_CONNECTED) + { + std::ostringstream buf_; + this->bplSetting = setting; + buf_ << "BLP " << this->trID++ << " " << setting << "L\r\n"; + write(buf_); + } + else + { + this->bplSetting = setting; + } + } + + void NotificationServerConnection::setFriendlyName(std::string friendlyName, bool updateServer) throw (std::runtime_error) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + if(friendlyName.empty()) + return; + if (friendlyName.size() > 387) + throw std::runtime_error("Friendly name too long!"); + + if(updateServer) + { + // update nickname on server + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->changeDisplayName(friendlyName); + } + else + { + this->myDisplayName = friendlyName; + std::ostringstream buf_; + buf_ << "PRP " << this->trID++ << " MFN " << encodeURL(friendlyName) << "\r\n"; + write(buf_); + } + } + + void NotificationServerConnection::setPersonalStatus(personalInfo pInfo) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::string tempMedia; + XMLNode data = XMLNode::createXMLTopNode("Data"); + XMLNode PSM = XMLNode::createXMLTopNode("PSM"); + XMLNode CurrentMedia = XMLNode::createXMLTopNode("CurrentMedia"); + XMLNode MachineGuid = XMLNode::createXMLTopNode("MachineGuid"); + + PSM.addText( pInfo.PSM.c_str() ); + if(pInfo.mediaIsEnabled) + { + tempMedia = pInfo.mediaApp +"\\0"+ + pInfo.mediaType+"\\0"+ + toStr(pInfo.mediaIsEnabled)+"\\0"+ + pInfo.mediaFormat +"\\0"; + + std::vector::iterator i = pInfo.mediaLines.begin(); + for(;i!=pInfo.mediaLines.end();i++) + { + tempMedia+=(*i); + tempMedia+="\\0"; + } + } + CurrentMedia.addText( tempMedia.c_str() ); + + data.addChild(PSM); + data.addChild(CurrentMedia); + + char *payload1=data.createXMLString(false); + + std::string payload(payload1); + free(payload1); + std::ostringstream buf_; + buf_ << "UUX " << this->trID++ << " " << payload.length() << "\r\n"; + buf_ << payload; + write(buf_); + } + + void NotificationServerConnection::blockContact(Passport buddyName) + { + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->removeContactFromList(buddyName,LST_AL); + + soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->addContactToList(buddyName,LST_BL); + } + + void NotificationServerConnection::unblockContact(Passport buddyName) + { + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->removeContactFromList(buddyName,LST_BL); + + soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->addContactToList(buddyName,LST_AL); + } + + void NotificationServerConnection::addToAddressBook(Passport buddyName, std::string displayName) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->addContactToAddressBook(buddyName, displayName); + } + + void NotificationServerConnection::enableContactOnAddressBook(std::string contactId, std::string passport) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->enableContactOnAddressBook(contactId, passport, this->myDisplayName); + } + + void NotificationServerConnection::disableContactOnAddressBook(std::string contactId, std::string passport) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->disableContactFromAddressBook(contactId,passport); + } + + void NotificationServerConnection::delFromAddressBook(std::string contactId, std::string passport) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->delContactFromAddressBook(contactId,passport); + } + + void NotificationServerConnection::addToList(MSN::ContactList list, Passport buddyName) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->addContactToList(buddyName,list); + } + + void NotificationServerConnection::removeFromList(MSN::ContactList list, Passport buddyName) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->removeContactFromList(buddyName,list); + } + + void NotificationServerConnection::addToGroup(std::string groupId, std::string contactId) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->addContactToGroup(groupId, contactId); + } + + void NotificationServerConnection::removeFromGroup(std::string groupId, std::string contactId) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->delContactFromGroup(groupId, contactId); + } + + void NotificationServerConnection::addGroup(std::string groupName) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->addGroup(groupName); + } + + void NotificationServerConnection::removeGroup(std::string groupID) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->delGroup(groupID); + } + + void NotificationServerConnection::renameGroup(std::string groupID, std::string newGroupName) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->renameGroup(groupID, newGroupName); + } + + void NotificationServerConnection::synchronizeContactList(std::string lastChange) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + this->assertConnectionStateIsNot(NS_SYNCHRONISING); + + // we are synchronizing through soap requests now + this->setConnectionState(NS_SYNCHRONISING); + + listInfo = new ListSyncInfo(lastChange); + if(!listInfo) + return; // TODO - raise an error + + if(!lastChange.length()) lastChange = "0"; + + listInfo->lastChange = lastChange; + + Soap *soapConnection; + soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->getLists(listInfo); + } + + void NotificationServerConnection::gotLists(Soap &soapConnection) + { + if(!listInfo) + return; // TODO - raise an error + + Soap *soapConnection1; + soapConnection1 = new Soap(*this, this->sitesToAuthList); + + // ask for Address book + soapConnection1->getAddressBook(this->listInfo); + } + + void NotificationServerConnection::gotAddressBook(Soap &soapConnection) + { + // TODO - sorry, I dont have choice. I need this here because the initial setFriendlyName + // is called by handle_ADL(), which does not have access to info variable. + this->myDisplayName = listInfo->myDisplayName; + + std::ostringstream buf_; + + // TODO - see what is the user choice: BL or AL + // A value of 'AL' indicates that users that are neither on the client's Allow List or Buddy List will be allowed to see the client's online status and open a switchboard session with the client. A value of 'BL' indicates that these users will see the client as offline and will not be allowed to open a switchboard session. + // http://msnpiki.msnfanatic.com/index.php/Command:BLP + + buf_ << "BLP " << this->trID << " " << this->bplSetting << "L\r\n"; + if (write(buf_) != buf_.str().size()) + return; + + this->addCallback(&NotificationServerConnection::callback_initialBPL, this->trID++, (void *)NULL); + } + + void NotificationServerConnection::callback_initialBPL(std::vector & args, int trid, void *data) + { + this->assertConnectionStateIs(NS_SYNCHRONISING); + this->removeCallback(trid); + + this->myNotificationServer()->externalCallbacks.gotBuddyListInfo(this, this->listInfo); + delete this->listInfo; + } + + void NotificationServerConnection::completeConnection(std::map & allContacts, void *info) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + + // FIXME - handle the errors + std::map > domains; + + std::string tempADL; + + // contains which number is. (i.e. 1 for FL, 2 for AL, 4 for BL) or the sum + std::map tempList; + + std::map::iterator i; + for (i = allContacts.begin(); i != allContacts.end(); i++) + { + std::vector parts = splitString((*i).first,"@"); + if(tempList[(*i).first]==0) + domains[parts[1]].push_back(parts[0]); + + // it does not allow LST_AL and LST_BL, as the server will refuse with a 241 error + int privacyListMask = MSN::LST_AL | MSN::LST_BL; + if ( ((*i).second & privacyListMask) == privacyListMask ) + tempList[(*i).first] = (*i).second & ~MSN::LST_AL; + else + tempList[(*i).first] = (*i).second; + } + + // deleting buddy information + std::map::iterator d = listInfo->contactList.begin(); + for(d; d != listInfo->contactList.end(); d++) + { + delete (*d).second; + } + + // adding domains and users to xml: + // The max payload of ADL command is about 7500 bytes. + // so the code below should do that. + // not using xmlParser due to the complex algorithm + // TODO - What to do when there are no contacts? + std::map >::iterator cur = domains.begin(); + + tempADL = ""; + // for each domain + for(; cur != domains.end(); cur++) + { + do + { + tempADL += ""; + // for each user + while(domains[(*cur).first].size()!=0) + { + std::string a((*cur).second[0]+"@"+(*cur).first); + tempADL += ""; + (*cur).second.erase((*cur).second.begin()); + + if (tempADL.length()>7400) + break; + } + tempADL += ""; + + if (tempADL.length()>7400) + { + adl_packets.push_back("" + tempADL + "" ); + tempADL = ""; + } + } while(domains[(*cur).first].size()!=0); + } + adl_packets.push_back("" + tempADL + "" ); + + // send the first one + std::string adl_payload = adl_packets.front(); + adl_packets.pop_front(); + + std::ostringstream buf_; + buf_ << "ADL " << this->trID++ << " " << adl_payload.length() << "\r\n"; + buf_ << adl_payload; + if (write(buf_) != buf_.str().size()) + return; + } + + + void NotificationServerConnection::sendPing() + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::string a("PNG\r\n"); + write(a); + } + + void NotificationServerConnection::requestSwitchboardConnection(const void *tag) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + SwitchboardServerConnection::AuthData *auth = new SwitchboardServerConnection::AuthData(this->auth.username, tag); + std::ostringstream buf_; + buf_ << "XFR " << this->trID << " SB\r\n"; + if (write(buf_) != buf_.str().size()) + return; + + this->addCallback(&NotificationServerConnection::callback_TransferToSwitchboard, this->trID++, (void *)auth); + } + + template + class _sameUserName + { + Buddy buddy; +public: + _sameUserName(const _Tp &__u) : buddy(__u) {}; + bool operator()(const _Tp &__x) { return __x.userName == buddy.userName; } + }; + + void NotificationServerConnection::socketConnectionCompleted() + { + this->assertConnectionStateIs(NS_CONNECTING); + this->setConnectionState(NS_CONNECTED); + + Connection::socketConnectionCompleted(); + + // If an error occurs in Connection::socketConnectionCompleted, we + // will be disconnected before we get here. + if (this->connectionState() != NS_DISCONNECTED) + { + this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock); + this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 1, 0, false); + } + } + + void NotificationServerConnection::connect(const std::string & hostname, unsigned int port) + { + this->assertConnectionStateIs(NS_DISCONNECTED); + connectinfo *info = new connectinfo(this->auth.username, this->auth.password); + this->info = info; + + if ((this->sock = this->myNotificationServer()->externalCallbacks.connectToServer(hostname, port, &this->connected)) == NULL) + { + this->myNotificationServer()->externalCallbacks.showError(this, "Could not connect to MSN server"); + this->myNotificationServer()->externalCallbacks.closingConnection(this); + return; + } + this->setConnectionState(NS_CONNECTING); + this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 0, 1, false); + if (this->connected) + this->socketConnectionCompleted(); + + std::ostringstream buf_; + buf_ << "VER " << this->trID << " MSNP15 CVR0\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + + this->addCallback(&NotificationServerConnection::callback_NegotiateCVR, this->trID++, (void *)info); + } + + void NotificationServerConnection::connect(const std::string & hostname, unsigned int port, const Passport & username, const std::string & password) + { + this->auth.username = username; + this->auth.password = password; + this->connect(hostname, port); + } + + void NotificationServerConnection::disconnect() + { + if (this->connectionState() == NS_DISCONNECTED) + return; + + std::vector list = _switchboardConnections; + std::vector::iterator i = list.begin(); + for (; i != list.end(); ++i) + { + delete *i; + } + std::vector list2 = _SoapConnections; + std::vector::iterator d = list2.begin(); + + for (; d != list2.end(); ++d) + { + delete *d; + } + + this->callbacks.clear(); + this->sitesToAuthList.erase(sitesToAuthList.begin(), sitesToAuthList.end()); + SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end()); + this->setConnectionState(NS_DISCONNECTED); + this->myNotificationServer()->externalCallbacks.closingConnection(this); + Connection::disconnect(); + } + + void NotificationServerConnection::disconnectForTransfer() + { + this->assertConnectionStateIsNot(NS_DISCONNECTED); + this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock); + this->myNotificationServer()->externalCallbacks.closeSocket(this->sock); + this->setConnectionState(NS_DISCONNECTED); + } + + void NotificationServerConnection::handleIncomingData() + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + while (this->isWholeLineAvailable()) + { + std::vector args = this->getLine(); + if(!args.size()) continue; + if (args[0] == "MSG" || args[0] == "NOT" || + args[0] == "IPG" || args[0] == "GCF" || + args[0] == "UBX" || args[0] == "ADL" || + args[0] == "RML") + { + int dataLength; + if (args[0] == "MSG" || args[0] == "UBX") + dataLength = decimalFromString(args[3]); + else if(args[0] == "GCF" || args[0] == "ADL" || args[0] == "RML") + dataLength = decimalFromString(args[2]); + else + dataLength = decimalFromString(args[1]); + + if (this->readBuffer.find("\r\n") + 2 + dataLength > this->readBuffer.size()) + return; + } + this->readBuffer = this->readBuffer.substr(this->readBuffer.find("\r\n") + 2); + int trid = 0; + + if (args.size() >= 6 && args[0] == "XFR" && args[2] == "NS") + { + // XFR TrID NS NotificationServerIP:Port 0 ThisServerIP:Port + // 0 1 2 3 4 5 + this->callbacks.clear(); // delete the callback data + + this->disconnectForTransfer(); + + std::pair server_address = splitServerAddress(args[3]); + this->connect(server_address.first, server_address.second); + return; + } + + if (args.size() >= 7 && args[0] == "RNG") + { + // RNG SessionID SwitchboardServerIP:Port CKI AuthString InvitingUser InvitingDisplayName + // 0 1 2 3 4 5 6 + this->handle_RNG(args); + return; + } + + if (args.size() >= 2 && args[0] == "QNG") + { + // QNG seconds + // 0 1 + + // ping response, ignore + return; + } + + if ((args.size() >= 3 && args[0] == "LST" ) || + (args.size() >= 2 && (args[0] == "GTC" )) || + (args.size() >= 3 && (args[0] == "BPR" || args[0] == "LSG" )) + ) + { + // LST N=UserName F=FriendlyName C=GUID param groupID + // 0 1 2 3 4 5 + // + // or + // (GTC|BLP) [TrID] [ListVersion] Setting + // 0 1 2 4 + + if (this->synctrid) + { + trid = this->synctrid; + } + else + { + trid = decimalFromString(args[1]); + } + } + else if (args.size() > 1) + { + try + { + trid = decimalFromString(args[1]); + } + catch (...) + { + } + } + + if (!this->callbacks.empty() && trid >= 0) + { + if (this->callbacks.find(trid) != this->callbacks.end()) + { + (this->*(this->callbacks[trid].first))(args, trid, this->callbacks[trid].second); + continue; + } + } + + if (isdigit(args[0][0])) + this->showError(decimalFromString(args[0])); + else + this->dispatchCommand(args); + } + } + + void NotificationServerConnection::callback_NegotiateCVR(std::vector & args, int trid, void *data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + connectinfo * info = (connectinfo *) data; + this->removeCallback(trid); + if (args.size() >= 3 && args[0] != "VER" || args[2] != "MSNP15") // if either *differs*... + { + this->myNotificationServer()->externalCallbacks.showError(NULL, "Protocol negotiation failed"); + this->disconnect(); + return; + } + + std::ostringstream buf_; + buf_ << "CVR " << this->trID << " 0x0409 winnt 5.1 i386 MSG80BETA 8.1.0178.00 MSMSGS " << info->username << "\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + this->addCallback(&NotificationServerConnection::callback_RequestUSR, this->trID++, (void *) data); + } + + void NotificationServerConnection::callback_TransferToSwitchboard(std::vector & args, int trid, void *data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + SwitchboardServerConnection::AuthData *auth = static_cast(data); + this->removeCallback(trid); + + if (args[0] != "XFR") + { + this->showError(decimalFromString(args[0])); + this->disconnect(); + delete auth; + return; + } + + auth->cookie = args[5]; + auth->sessionID = ""; + + SwitchboardServerConnection *newconn = new SwitchboardServerConnection(*auth, *this); + + this->addSwitchboardConnection(newconn); + std::pair server_address = splitServerAddress(args[3]); + newconn->connect(server_address.first, server_address.second); + + delete auth; + } + + void NotificationServerConnection::callback_RequestUSR(std::vector & args, int trid, void *data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + connectinfo *info = (connectinfo *)data; + this->removeCallback(trid); + + if (args.size() > 1 && args[0] != "CVR") // if*differs*... + { + this->myNotificationServer()->externalCallbacks.showError(NULL, "Protocol negotiation failed"); + this->disconnect(); + return; + } + + std::ostringstream buf_; + buf_ << "USR " << this->trID << " SSO I " << info->username << "\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + + this->addCallback(&NotificationServerConnection::callback_PassportAuthentication, this->trID++, (void *) data); + } + + void NotificationServerConnection::callback_PassportAuthentication(std::vector & args, int trid, void * data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + connectinfo * info; + + info=(connectinfo *)data; + this->removeCallback(trid); + + if (isdigit(args[0][0])) + { + this->showError(decimalFromString(args[0])); + this->disconnect(); + return; + } + + if (args.size() >= 4 && args[4].empty()) { + this->disconnect(); + return; + } + + this->myNotificationServer()->externalCallbacks.getSecureHTTPProxy(); + + Soap *soapConnection = new Soap(*this); + + this->mdi = args[5]; + soapConnection->setMBI(args[4]); + soapConnection->getTickets(info->username,info->password,args[4]); + delete info; + info=NULL; + } + + void NotificationServerConnection::gotTickets(Soap & soapConnection, std::vector sitesToAuthList) + { + std::ostringstream buf_; + this->sitesToAuthList = sitesToAuthList; + std::string token = sitesToAuthList[1].BinarySecurityToken; + std::string binarysecret = sitesToAuthList[1].BinarySecret; + this->token = token; + + buf_ << "USR " << this->trID << " SSO S " << token << " " << mdi_encrypt(binarysecret, mdi) << "\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + this->addCallback(&NotificationServerConnection::callback_AuthenticationComplete, this->trID++, NULL); + } + + void NotificationServerConnection::callback_AuthenticationComplete(std::vector & args, int trid, void * data) + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + this->removeCallback(trid); + + if (isdigit(args[0][0])) + { + this->showError(decimalFromString(args[0])); + this->disconnect(); + return; + } + server_email_verified = args[4]; + } + + void NotificationServerConnection::get_oim(std::string id, bool markAsRead) + { + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->getOIM(id,markAsRead); + } + + void NotificationServerConnection::delete_oim(std::string id) + { + if(this->removingOIM) + { + DeletedQueuedOIMs.push_back(id); + return; + } + this->removingOIM = true; + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->deleteOIM(id); + } + + void NotificationServerConnection::send_oim(Soap::OIM oim) + { + // do not generate two lockkeys at the same time + if(this->generatingLockkey) + { + SentQueuedOIMs.push_back(oim); + return; + } + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + SentQueuedOIMs.push_back(oim); + this->generatingLockkey=true; + soapConnection->generateLockkey(oim); + } + + void NotificationServerConnection::gotOIM(Soap & soapConnection, bool success, std::string id, std::string message) + { + this->myNotificationServer()->externalCallbacks.gotOIM(this, success, id, message); + } + + void NotificationServerConnection::gotOIMLockkey(Soap & soapConnection, std::string lockkey) + { + this->lockkey = lockkey; + this->generatingLockkey = false; + if(this->lockkey.empty()) + { + std::vector::iterator i = SentQueuedOIMs.begin(); + for(; i != SentQueuedOIMs.end(); i++) + { + this->myNotificationServer()->externalCallbacks.gotOIMSendConfirmation(this, false, (*i).id); + } + SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end()); + return; + } + sendQueuedOIMs(); + } + + void NotificationServerConnection::gotOIMDeleteConfirmation(Soap & soapConnection, std::string id, bool deleted) + { + this->myNotificationServer()->externalCallbacks.gotOIMDeleteConfirmation(this, deleted, id); + if(this->DeletedQueuedOIMs.empty()) + { + removingOIM = false; + return; + } + else + { + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + soapConnection->deleteOIM(DeletedQueuedOIMs.back()); + DeletedQueuedOIMs.pop_back(); + } + } + + void NotificationServerConnection::gotOIMSendConfirmation(Soap & soapConnection, int id, bool sent) + { + if(!sent) + this->lockkey.clear(); + + this->myNotificationServer()->externalCallbacks.gotOIMSendConfirmation(this, sent, id); + } + + void NotificationServerConnection::sendQueuedOIMs() + { + std::vector::iterator i = SentQueuedOIMs.begin(); + for(; i != SentQueuedOIMs.end(); i++) + { + Soap *soapConnection = new Soap(*this, this->sitesToAuthList); + + soapConnection->sendOIM((*i), this->lockkey); + } + SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end()); + } + + bool NotificationServerConnection::change_DisplayPicture(std::string filename) + { + msnobj.delMSNObjectByType(3); + if(!filename.empty()) + msnobj.addMSNObject(filename,3); + + return true; + } + + void NotificationServerConnection::gotChangeDisplayNameConfirmation(Soap & soapConnection, std::string displayName, bool changed) + { + if(changed) + { + this->myDisplayName = displayName; + // server update OK, now change to the current session + std::ostringstream buf_; + buf_ << "PRP " << this->trID++ << " MFN " << encodeURL(displayName) << "\r\n"; + write(buf_); + } + // TODO - raise an error if not changed + } + + void NotificationServerConnection::gotAddContactToGroupConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string groupId, std::string contactId) + { + this->myNotificationServer()->externalCallbacks.addedContactToGroup(this, added, groupId, contactId); + } + + void NotificationServerConnection::gotDelContactFromGroupConfirmation(Soap & soapConnection, bool removed, std::string newVersion, std::string groupId, std::string contactId) + { + this->myNotificationServer()->externalCallbacks.removedContactFromGroup(this, removed, groupId, contactId); + } + + void NotificationServerConnection::gotAddGroupConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string groupName, std::string groupId) + { + this->myNotificationServer()->externalCallbacks.addedGroup(this, added, groupName, groupId); + } + + void NotificationServerConnection::gotDelGroupConfirmation(Soap & soapConnection, bool removed, std::string newVersion, std::string groupId) + { + this->myNotificationServer()->externalCallbacks.removedGroup(this, removed, groupId); + } + void NotificationServerConnection::gotRenameGroupConfirmation(Soap & soapConnection, bool renamed, std::string newVersion, std::string newGroupName, std::string groupId) + { + this->myNotificationServer()->externalCallbacks.renamedGroup(this, renamed, newGroupName, groupId); + } + + void NotificationServerConnection::gotAddContactToAddressBookConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string passport, std::string displayName, std::string guid) + { + this->myNotificationServer()->externalCallbacks.addedContactToAddressBook(this, added, passport, displayName, guid); + if(added) + { + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + + // TODO - use xmlParser + std::string payload3(""); + std::ostringstream buf_3; + buf_3 << "ADL " << this->trID++ << " " << payload3.length() << "\r\n"; + buf_3 << payload3; + write(buf_3); + + std::string payload2(""); + std::ostringstream buf_2; + buf_2 << "ADL " << this->trID++ << " " << payload2.length() << "\r\n"; + buf_2 << payload2; + write(buf_2); + + // the official client sends FQY + std::string payload4(""); + std::ostringstream buf_4; + buf_4 << "FQY " << this->trID++ << " " << payload4.length() << "\r\n"; + buf_4 << payload4; + write(buf_4); + } + } + + void NotificationServerConnection::gotDelContactFromAddressBookConfirmation(Soap & soapConnection, bool removed, std::string newVersion, std::string contactId, std::string passport) + { + this->myNotificationServer()->externalCallbacks.removedContactFromAddressBook(this, removed, contactId, passport); + if(removed) + { + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + + // TODO - use xmlParser + std::string payload2(""); + std::ostringstream buf_2; + buf_2 << "RML " << this->trID++ << " " << payload2.length() << "\r\n"; + buf_2 << payload2; + write(buf_2); + + } + } + + void NotificationServerConnection::gotEnableContactOnAddressBookConfirmation(Soap & soapConnection, bool disabled, std::string newVersion, std::string contactId, std::string passport) + { + this->myNotificationServer()->externalCallbacks.enabledContactOnAddressBook(this, disabled, contactId, passport); + if(disabled) + { + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + + // TODO - use xmlParser + std::string payload3(""); + std::ostringstream buf_3; + buf_3 << "ADL " << this->trID++ << " " << payload3.length() << "\r\n"; + buf_3 << payload3; + write(buf_3); + } + } + + void NotificationServerConnection::gotDisableContactOnAddressBookConfirmation(Soap & soapConnection, bool disabled, std::string newVersion, std::string contactId, std::string passport) + { + this->myNotificationServer()->externalCallbacks.disabledContactOnAddressBook(this, disabled, contactId); + if(disabled) + { + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + + // TODO - use xmlParser + std::string payload2(""); + std::ostringstream buf_2; + buf_2 << "RML " << this->trID++ << " " << payload2.length() << "\r\n"; + buf_2 << payload2; + write(buf_2); + } + } + + void NotificationServerConnection::gotAddContactToListConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string passport, MSN::ContactList list) + { + if(added) + { + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + + // TODO - use xmlParser + std::string payload2(""); + std::ostringstream buf_2; + buf_2 << "ADL " << this->trID++ << " " << payload2.length() << "\r\n"; + buf_2 << payload2; + write(buf_2); + this->myNotificationServer()->externalCallbacks.addedListEntry(this, list, passport, ""); + } + } + void NotificationServerConnection::gotDelContactFromListConfirmation(Soap & soapConnection, bool deleted, std::string newVersion, std::string passport, MSN::ContactList list) + { + if(deleted) + { + std::vector passport2 = splitString(passport, "@"); + std::string user = passport2[0]; + std::string domain = passport2[1]; + // TODO - use XMLParser + std::string payload(""); + + std::ostringstream buf_; + buf_ << "RML " << this->trID++ << " " << payload.length() << "\r\n"; + buf_ << payload; + write(buf_); + this->myNotificationServer()->externalCallbacks.removedListEntry(this, list, passport); + } + } + + void NotificationServerConnection::setCapabilities(uint m_clientId) + { + this->m_clientId = m_clientId; + } + + void NotificationServerConnection::getInboxUrl() + { + this->assertConnectionStateIsAtLeast(NS_CONNECTED); + std::ostringstream buf_; + buf_ << "URL " << this->trID << " INBOX\r\n"; + write(buf_); + this->addCallback(&NotificationServerConnection::callback_URL, this->trID++, NULL); + } +} diff --git a/libs/libmsn/notificationserver.h b/libs/libmsn/notificationserver.h new file mode 100644 index 0000000..ed32d0e --- /dev/null +++ b/libs/libmsn/notificationserver.h @@ -0,0 +1,549 @@ +#ifndef __msn_notificationserver_h__ +#define __msn_notificationserver_h__ + +/* + * notificationserver.h + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "connection.h" +#include "authdata.h" +#include "errorcodes.h" +#include "buddy.h" +#include "passport.h" +#include +#include "externals.h" +#include "msnobject.h" +#include "soap.h" +#include +#include + +#include "libmsn_export.h" + +#ifdef _WIN32 +typedef unsigned uint; +#endif + +namespace MSN +{ + class SwitchboardServerConnection; + + /** Contains information about synchronising the contact list with the server. + */ + class LIBMSN_EXPORT ListSyncInfo + { +public: + /** Constants specifying the status of the list synchronisation. + */ + enum SyncProgress + { + LST_AB = 1, /**< Address book has been received. */ + LST_AL = 2, /**< Allow list has been received. */ + LST_BL = 4, /**< Block list has been received. */ + LST_RL = 8, /**< Reverse list has been received. */ + LST_PL = 16, /**< Pending list has been received. */ + COMPLETE_BLP = 32 /**< @c BLP has been received. */ + }; + + + /** Treat unknown users as if appearing on this list when they attempt + * to initiate a switchboard session. + */ + enum PrivacySetting + { + ALLOW = 'A', /**< Any user can see our status, except those in Block List. */ + BLOCK = 'B' /**< Nobody can see our status, except those in Allow List. */ + }; + + /** Action to take when a new user appears on our reverse list. + */ + enum NewReverseListEntryAction + { + PROMPT = 'A', + DONT_PROMPT = 'N' + }; + + + /** A list of people who's statuses we are wish to be notified about. + */ + std::map contactList; + + std::string myDisplayName; + + std::map groups; + + /** The progress of the sync operation. + */ + unsigned int progress; + + unsigned int usersRemaining, groupsRemaining; + + /** Last change on Address Book and lists timestamp + */ + std::string lastChange; + + /** Specifies the default list for non-buddies to be treated as + * appearing in when attempting to invite you into a switchboard setting. + * + * This corresponds to the @c BLP command in the MSN protocol. + */ + char privacySetting; + + /** Specifies whether the user should be prompted when a new buddy + * appears on their reverse list. + * + * This corresponds to the @c GTC command in the MSN protocol. + */ + char reverseListPrompting; + + ListSyncInfo(std::string lastChange_) : + progress(0), lastChange(lastChange_), + privacySetting(ListSyncInfo::ALLOW), reverseListPrompting(ListSyncInfo::PROMPT) {}; + }; + + // Intermediate steps in connection: + class LIBMSN_EXPORT connectinfo + { +public: + Passport username; + std::string password; + std::string cookie; + + connectinfo(const Passport & username_, const std::string & password_) : username(username_), password(password_), cookie("") {}; + }; + + /** Represents a connection to a MSN notification server. + * + * The MSN notification server is responsible for dealing with the contact + * list, online status, and dispatching message requests or invitations to + * or from switchboard servers. + */ + class LIBMSN_EXPORT NotificationServerConnection : public Connection + { +private: + typedef void (NotificationServerConnection::*NotificationServerCallback)(std::vector & args, int trid, void *); + + std::string token; + class AuthData : public ::MSN::AuthData + { +public: + std::string password; + + AuthData(const Passport & passport_, + const std::string & password_) : + ::MSN::AuthData(passport_), password(password_) {} ; + }; + NotificationServerConnection::AuthData auth; + int synctrid; + +public: + MSNObject msnobj; + + /** Create a NotificationServerConnection with the specified @a username and + * @a password. + */ + NotificationServerConnection(Passport username, std::string password, Callbacks & cb); + + virtual ~NotificationServerConnection(); + virtual void dispatchCommand(std::vector & args); + + /** Return a list of SwitchboardServerConnection's that have been started + * from this NotificationServerConnection. + */ + const std::vector & switchboardConnections(); + + /* Add a SwitchboardServerConnection to the list of connections that have + * been started from this connection. + */ + void addSwitchboardConnection(SwitchboardServerConnection *); + + /* Add the @p Soap object to the list of connections that have + * been started from this connection. + */ + void addSoapConnection(Soap *); + + /* Remove a SwitchboardServerConnection from the list of connections that have + * been started from this connection. + */ + void removeSwitchboardConnection(SwitchboardServerConnection *); + + /* Remove the @p Soap object from the list of connections that have + * been started from this connection. + */ + void removeSoapConnection(Soap *); + + /** Return a connection that is associated with @a fd. + * + * If @a fd is equal to @p sock, @c this is returned. Otherwise + * connectionWithSocket is sent to each SwitchboardServerConnection + * or Soap connections until a match is found. + * + * @return The matching connection, if found. Otherwise, @c NULL. + */ + Connection *connectionWithSocket(void *sock); + + /** Return a SwitchboardServerConnection that has exactly one user whose + * username is @a username. + * + * @return The matching SwitchboardServerConnection, or @c NULL. + */ + SwitchboardServerConnection *switchboardWithOnlyUser(Passport username); + + /** @name Action Methods + * + * These methods all perform actions on the notification server. + */ + /** @{ */ + + /** Set our capabilities. This is the sum of MSNClientInformationFields enum fields + */ + void setCapabilities(uint m_clientId); + + void disconnectNS(); + + /** Set our online state to @a state and our capabilites to @a clientID. + */ + void setState(BuddyStatus state, uint clientID); + + /** Set Black List policy + */ + void setBLP(char setting); + + /** It must be called to complete the connection. Add all your contacts + * to allContacts, (both Forward, allow and block lists). The integer value + * of allContacts is the sum of all lists this contact is present. For example: + * If the contact is both on your allow and forward list, the number must be + * 3 (2+1). + */ + void completeConnection(std::map & allContacts, void *data); + + /** Set our friendly name to @a friendlyName. + * + * @param friendlyName Maximum allowed length is 387 characters after URL-encoding. + */ + void setFriendlyName(std::string friendlyName, bool updateServer = false) throw (std::runtime_error); + + /** Points to our display picture. It must be a png file, size: 96x96. + * @param filename Path to the PNG file to be sent as avatar of this contact + */ + bool change_DisplayPicture(std::string filename); + + /** Sets our personal info. Current media, personal message... + */ + void setPersonalStatus(personalInfo pInfo); + + /** Add buddy named @a buddyName to the list named @a list. + */ + void addToList(MSN::ContactList list, Passport buddyName); + + /** Remove buddy named @a buddyName from the list named @a list. + */ + void removeFromList(MSN::ContactList list, Passport buddyName); + + /** Add contact @a buddyName to address book with @a displayName + * Automatically adds to allow list too. + */ + void addToAddressBook(Passport buddyName, std::string displayName); + + /** Delete contact @a passport with @a contactId from address book. + */ + void delFromAddressBook(std::string contactId, std::string passport); + + /** Enable a contact on address book. You will use it when you have disabled + * a contact before. This contact is still on your Address Book, but it is not + * a messenger contact. This function will turn this contact into a messenger + * contact again. + */ + void enableContactOnAddressBook(std::string contactId, std::string passport); + + /** Do not delete the contact, just disable it. + */ + void disableContactOnAddressBook(std::string contactId, std::string passport); + + /** Block a contact. This user won't be able to see your status anymore. + */ + void blockContact(Passport buddyName); + + /** Unblock a contact. This user will be able to see your status again. + */ + void unblockContact(Passport buddyName); + + /** Add a contact @a contactId to the group @a groupId . + */ + void addToGroup(std::string groupId, std::string contactId); + + /** Remove a contact from a group. + */ + void removeFromGroup(std::string groupId, std::string contactId); + + /** Add a new group. + */ + void addGroup(std::string groupName); + + /** Remove a group. + */ + void removeGroup(std::string groupId); + + /** Rename a group. + */ + void renameGroup(std::string groupId, std::string newGroupName); + + /** Request the server side buddy list. + * + * @param lastChange if @a version is specified the server will respond with + * the changes necessary to update the list to the latest + * version. Otherwise the entire list will be sent. + */ + void synchronizeContactList(std::string lastChange="0"); + + /** Send a 'keep-alive' ping to the server. + */ + void sendPing(); + + /** Request a switchboard connection. + */ + void requestSwitchboardConnection(const void *tag); + + /** Retrieve the Offline Instant Message identified by id + */ + void get_oim(std::string id, bool markAsRead); + + /** Erase the Offline Instant Message identified by id + */ + void delete_oim(std::string id); + + /** Send an Offline Instant Message + */ + void send_oim(Soap::OIM oim); + + void getInboxUrl(); + + /* when we have to send more than 1 ADL command, we need to keep this here to track */ + std::list adl_packets; + + /* Our current Display Name */ + std::string myDisplayName; + + /* Our passport */ + std::string myPassport; + + /* Sum of capabilities of the user */ + uint m_clientId; + + char bplSetting; + + /* Our IP number reported by notification server */ + std::string server_reported_ip; + + /* Our TCP source port reported by notification server */ + std::string server_reported_port; + + std::string login_time; + + std::string MSPAuth; + + std::string sid; + + std::string kv; + + /* 1 if our email is verified, 0 if not */ + std::string server_email_verified; + + /* Says if we are direct connected based on server's report */ + bool direct_connection; + + virtual void connect(const std::string & hostname, unsigned int port); + + virtual void connect(const std::string & hostname, unsigned int port, + const Passport & username, + const std::string & password); + + virtual void disconnect(); + + virtual void addCallback(NotificationServerCallback cb, int trid, void *data); + + virtual void removeCallback(int trid); + + virtual void socketConnectionCompleted(); + + enum NotificationServerState + { + NS_DISCONNECTED, + NS_CONNECTING, + NS_CONNECTED, + NS_SYNCHRONISING, + NS_ONLINE + }; + + connectinfo *info; + NotificationServerState connectionState() const { return this->_connectionState; }; + Callbacks & externalCallbacks; + virtual NotificationServerConnection *myNotificationServer() { return this; }; + void gotTickets(Soap & soapConnection, std::vector sitesToAuthList); + void gotLists(Soap &soapConnection); + void gotAddressBook(Soap &soapConnection); + void gotOIM(Soap & soapConnection, bool success, std::string id, std::string message); + void gotOIMLockkey(Soap & soapConnection, std::string lockkey); + void gotOIMSendConfirmation(Soap & soapConnection, int id, bool sent); + void gotOIMDeleteConfirmation(Soap & soapConnection, std::string id, bool deleted); + void gotSoapMailData(Soap & soapConnection, std::string maildata); + void gotChangeDisplayNameConfirmation(Soap & soapConnection, std::string displayName, bool changed); + void gotDelContactFromGroupConfirmation(Soap & soapConnection, + bool deleted, + std::string newVersion, + std::string groupId, + std::string contactId); + + void gotAddContactToGroupConfirmation(Soap & soapConnection, + bool added, + std::string newVersion, + std::string groupId, + std::string contactId); + + void gotAddGroupConfirmation(Soap & soapConnection, + bool added, + std::string newVersion, + std::string groupName, + std::string groupId); + + void gotDelGroupConfirmation(Soap & soapConnection, + bool removed, + std::string newVersion, + std::string groupId); + + void gotRenameGroupConfirmation(Soap & soapConnection, + bool renamed, + std::string newVersion, + std::string newGroupName, + std::string groupId); + + void gotAddContactToAddressBookConfirmation(Soap & soapConnection, + bool added, + std::string newVersion, + std::string passport, + std::string displayName, + std::string guid); + + void gotDelContactFromAddressBookConfirmation(Soap & soapConnection, + bool removed, + std::string newVersion, + std::string contactId, + std::string passport); + + void gotEnableContactOnAddressBookConfirmation(Soap & soapConnection, + bool enabled, + std::string newVersion, + std::string contactId, + std::string passport); + + void gotDisableContactOnAddressBookConfirmation(Soap & soapConnection, + bool disabled, + std::string newVersion, + std::string contactId, + std::string passport); + + void gotAddContactToListConfirmation(Soap & soapConnection, + bool added, + std::string newVersion, + std::string passport, + MSN::ContactList list); + + void gotDelContactFromListConfirmation(Soap & soapConnection, + bool deleted, + std::string newVersion, + std::string passport, + MSN::ContactList list); + +protected: + virtual void handleIncomingData(); + NotificationServerState _connectionState; + + void setConnectionState(NotificationServerState s) { this->_connectionState = s; }; + void assertConnectionStateIs(NotificationServerState s) { assert(this->_connectionState == s); }; + void assertConnectionStateIsNot(NotificationServerState s) { assert(this->_connectionState != s); }; + void assertConnectionStateIsAtLeast(NotificationServerState s) { assert(this->_connectionState >= s); }; +private: + std::vector _switchboardConnections; + std::vector _SoapConnections; + std::map > callbacks; + + ListSyncInfo *listInfo; + + std::vector sitesToAuthList; + std::vector SentQueuedOIMs; + std::vector DeletedQueuedOIMs; + + std::string lockkey; + bool generatingLockkey; + bool removingOIM; + + void sendQueuedOIMs(); + + // mdi value got by tweener + std::string mdi; + + virtual void disconnectForTransfer(); + + static std::map &)> commandHandlers; + static std::map &, std::string, std::string)> messageHandlers; + + void registerHandlers(); + void handle_OUT(std::vector & args); + void handle_RML(std::vector & args); + void handle_BLP(std::vector & args); + void handle_CHG(std::vector & args); + void handle_CHL(std::vector & args); + void handle_ILN(std::vector & args); + void handle_NLN(std::vector & args); + void handle_FLN(std::vector & args); + void handle_MSG(std::vector & args); + void handle_RNG(std::vector & args); + void handle_PRP(std::vector & args); + void handle_UBX(std::vector & args); + void handle_GCF(std::vector & args); + void handle_ADL(std::vector & args); + void handle_UBN(std::vector & args); + void handle_FQY(std::vector & args); + + void callback_NegotiateCVR(std::vector & args, int trid, void *data); + void callback_TransferToSwitchboard(std::vector & args, int trid, void *data); + void callback_RequestUSR(std::vector & args, int trid, void *data); + void callback_PassportAuthentication(std::vector & args, int trid, void * data); + void callback_AuthenticationComplete(std::vector & args, int trid, void * data); + void callback_initialBPL(std::vector & args, int trid, void *data); + void callback_URL(std::vector & args, int trid, void *data); + + + void message_initial_email_notification(std::vector & args, std::string mime, std::string body); + void message_email_notification(std::vector & args, std::string mime, std::string body); + void message_msmsgsprofile(std::vector & args, std::string mime, std::string body); + void message_initialmdatanotification(std::vector & args, std::string mime, std::string body); + void message_oimnotification(std::vector & args, std::string mime, std::string body); + + void gotMailData(std::string maildata); + + }; + +} + + +#endif diff --git a/libs/libmsn/p2p.cpp b/libs/libmsn/p2p.cpp new file mode 100644 index 0000000..d84d226 --- /dev/null +++ b/libs/libmsn/p2p.cpp @@ -0,0 +1,1413 @@ +/* + * p2p.cpp + * libmsn + * + * Created by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "notificationserver.h" +#include "errorcodes.h" +#include "externals.h" +#include "util.h" +#include "p2p.h" +#include "xmlParser.h" + +#include +#include +#include +#include + +#include + +namespace MSN { + + P2P::P2P() + { + rand_helper = 1; + } + P2P::~P2P() + { + } + + + void P2P::handleP2Pmessage(MSN::SwitchboardServerConnection &conn, std::vector & args, std::string mime, std::string body) + { + Message::Headers headers = Message::Headers(mime); + p2pPacket packet; + // not for me, ignore it + if(headers["P2P-Dest"] != conn.myNotificationServer()->myPassport) + return; + + std::istringstream header(body,std::ios::binary); + + header.read((char*)&packet.p2pHeader.sessionID, sizeof(packet.p2pHeader.sessionID)); + header.read((char*)&packet.p2pHeader.identifier, sizeof(packet.p2pHeader.identifier)); + header.read((char*)&packet.p2pHeader.dataOffset, sizeof(packet.p2pHeader.dataOffset)); + header.read((char*)&packet.p2pHeader.totalDataSize, sizeof(packet.p2pHeader.totalDataSize)); + header.read((char*)&packet.p2pHeader.messageLength, sizeof(packet.p2pHeader.messageLength)); + header.read((char*)&packet.p2pHeader.flag, sizeof(packet.p2pHeader.flag)); + header.read((char*)&packet.p2pHeader.ackID, sizeof(packet.p2pHeader.ackID)); + header.read((char*)&packet.p2pHeader.ackUID, sizeof(packet.p2pHeader.ackUID)); + header.read((char*)&packet.p2pHeader.ackDataSize, sizeof(packet.p2pHeader.ackDataSize)); + char *c = new char[packet.p2pHeader.messageLength]; + header.read(c, packet.p2pHeader.messageLength); + std::string content(c, packet.p2pHeader.messageLength); + packet.body = content; + delete [] c; + header.read((char*)&packet.p2pFooter.appID, sizeof(packet.p2pFooter.appID)); + + if(packet.p2pHeader.flag==FLAG_ACK) + { + handle_p2pACK(conn, packet); + return; + } + + if(packet.p2pHeader.sessionID == 0x40 && + little2big_endian(packet.p2pFooter.appID)==3) // INK, oh god! + { + p2pSession session; + session.to = args[1]; + if(!packet.p2pHeader.dataOffset) // first packet + { + session.ink = packet.body; + startedSessions[packet.p2pHeader.sessionID] = session; + return; + } + else + { + if (packet.p2pHeader.dataOffset+packet.p2pHeader.messageLength + == packet.p2pHeader.totalDataSize) + { + session = startedSessions[packet.p2pHeader.sessionID]; + session.ink += packet.body; + sendACK(conn, packet, session); + startedSessions.erase(packet.p2pHeader.sessionID); + // TODO - THIS IS UGLY! + U8 *d1 = new U8[packet.p2pHeader.totalDataSize]; + U8 *a = new U8[packet.p2pHeader.totalDataSize]; + a[0]='\0'; + a++; + const char *f = session.ink.c_str(); + memcpy(a,f,session.ink.size()); + a--; + _ucs2_utf8(d1, a, session.ink.size()); + char *c = new char[packet.p2pHeader.totalDataSize+2]; + char *g = new char[packet.p2pHeader.totalDataSize+2]; + sprintf(c,"%s",(char*)d1); + sprintf(g,"%s",(char*)(d1+strlen((char*)d1)+1)); + + std::string d2(c); + std::string d3(g); + delete [] a; + delete [] c; + delete [] d1; + delete [] g; + + conn.message_ink(args, d2, d3); + return; + } + if (packet.p2pHeader.dataOffset+packet.p2pHeader.messageLength + < packet.p2pHeader.totalDataSize) + { + session = startedSessions[packet.p2pHeader.sessionID]; + session.ink += packet.body; + startedSessions[packet.p2pHeader.sessionID] = session; + return; + } + } + return; + } + + // in these conditions, the packet is data, receive it to a file + if(packet.p2pHeader.sessionID && + (packet.p2pHeader.flag == FLAG_FILE_DATA || + packet.p2pHeader.flag == FLAG_FILE_DATA2 || + packet.p2pHeader.flag == FLAG_DATA_EMOTICONS)) + { + // we need to ensure we have a started session + if(!startedSessions.count(packet.p2pHeader.sessionID)) + return; + + startedSessions[packet.p2pHeader.sessionID].step = STEP_RECEIVING; + receiveP2PData(conn,packet); + return; + } + + if(packet.p2pHeader.sessionID && packet.p2pFooter.appID) + { + // we need to ensure we have a started session + if(!startedSessions.count(packet.p2pHeader.sessionID)) + return; + + // data preparation, always? + p2pSession session = startedSessions[packet.p2pHeader.sessionID]; + sendACK(conn, packet, session); + return; + } + + if(packet.p2pFooter.appID==APP_NONE) // when 0, assembly all the data before processing + { + // reassembly the packet + if(packet.p2pHeader.messageLength < packet.p2pHeader.totalDataSize) // just part of the packet + { + if(pendingP2PMsg.count(packet.p2pHeader.identifier)==0) // it is the first part + { + pendingP2PMsg[packet.p2pHeader.identifier]=packet; + return; + } + p2pPacket pkt_part = pendingP2PMsg[packet.p2pHeader.identifier]; + + // not the first part + pkt_part.body+=packet.body; + + if(packet.p2pHeader.messageLength+packet.p2pHeader.dataOffset < + packet.p2pHeader.totalDataSize) + { + pendingP2PMsg[packet.p2pHeader.identifier]=pkt_part; + // this is not the last part, wait for the last one + return; + } + + // shouldn't be reached + if(packet.p2pHeader.messageLength+packet.p2pHeader.dataOffset > + packet.p2pHeader.totalDataSize) + { + pendingP2PMsg.erase(packet.p2pHeader.identifier); + return; + } + if(packet.p2pHeader.messageLength+packet.p2pHeader.dataOffset == + packet.p2pHeader.totalDataSize) + { + packet=pkt_part; + pendingP2PMsg.erase(packet.p2pHeader.identifier); + } + } + } + + if(!packet.body.find("INVITE")) + { + handle_INVITE(conn,packet); + } + else if (!packet.body.find("MSNSLP/1.0 200 OK")) + { + handle_200OK(conn,packet); + } + else if (!packet.body.find("BYE")) + { + handle_BYE(conn,packet); + } + else if(!packet.body.find("MSNSLP/1.0 603 Decline") || !packet.body.find("MSNSLP/1.0 603 DECLINE")) + { + handle_603Decline(conn,packet); + } +/* std::cout << "session id: " << packet.p2pHeader.sessionID << std::endl; + std::cout << "identifier: " << packet.p2pHeader.identifier << std::endl; + std::cout << "dataOffset: " << packet.p2pHeader.dataOffset << std::endl; + std::cout << "totalDataSize: " << packet.p2pHeader.totalDataSize << std::endl; + std::cout << "messageLength: " << packet.p2pHeader.messageLength << std::endl; + std::cout << "flag: " << packet.p2pHeader.flag << std::endl; + std::cout << "ackID: " << packet.p2pHeader.ackID << std::endl; + std::cout << "ackUID: " << packet.p2pHeader.ackUID << std::endl; + std::cout << "ackDataSize: " << packet.p2pHeader.ackDataSize << std::endl; + std::cout << "footer: " << packet.p2pFooter.appID << std::endl << std::endl; +*/ + } + + void P2P::sendACK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session) + { + p2pPacket ack_pkt; + + std::ostringstream msghdr; + std::ostringstream footer; + std::ostringstream header; + std::ostringstream full_msg; + + session.currentIdentifier++; + if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier + session.currentIdentifier++; + + // assembly the ack packet + ack_pkt.p2pHeader.sessionID = packet.p2pHeader.sessionID; + ack_pkt.p2pHeader.identifier = session.currentIdentifier; + ack_pkt.p2pHeader.dataOffset = 0; + ack_pkt.p2pHeader.totalDataSize = packet.p2pHeader.totalDataSize; + ack_pkt.p2pHeader.messageLength = 0; + ack_pkt.p2pHeader.flag = FLAG_ACK; + ack_pkt.p2pHeader.ackID = packet.p2pHeader.identifier; + ack_pkt.p2pHeader.ackUID = packet.p2pHeader.ackID; + ack_pkt.p2pHeader.ackDataSize = packet.p2pHeader.totalDataSize; + + ack_pkt.p2pFooter.appID = APP_NONE; + + msghdr <<"MIME-Version: 1.0\r\n" + "Content-Type: application/x-msnmsgrp2p\r\n" + "P2P-Dest: " << conn.users.front() << "\r\n\r\n"; + + header.write((char*)&ack_pkt.p2pHeader.sessionID, sizeof(ack_pkt.p2pHeader.sessionID)); + header.write((char*)&ack_pkt.p2pHeader.identifier, sizeof(ack_pkt.p2pHeader.identifier)); + header.write((char*)&ack_pkt.p2pHeader.dataOffset, sizeof(ack_pkt.p2pHeader.dataOffset)); + header.write((char*)&ack_pkt.p2pHeader.totalDataSize, sizeof(ack_pkt.p2pHeader.totalDataSize)); + header.write((char*)&ack_pkt.p2pHeader.messageLength, sizeof(ack_pkt.p2pHeader.messageLength)); + header.write((char*)&ack_pkt.p2pHeader.flag, sizeof(ack_pkt.p2pHeader.flag)); + header.write((char*)&ack_pkt.p2pHeader.ackID, sizeof(ack_pkt.p2pHeader.ackID)); + header.write((char*)&ack_pkt.p2pHeader.ackUID, sizeof(ack_pkt.p2pHeader.ackUID)); + header.write((char*)&ack_pkt.p2pHeader.ackDataSize, sizeof(ack_pkt.p2pHeader.ackDataSize)); + + footer.write((char*)&ack_pkt.p2pFooter.appID,sizeof(ack_pkt.p2pFooter.appID)); + + full_msg << msghdr.str() << header.str() << footer.str(); + + std::ostringstream buf_; + buf_ << "MSG " << conn.trID++ << " D " << full_msg.str().size() << "\r\n"; + buf_ << full_msg.str(); + + if (conn.write(buf_) != buf_.str().size()) + return; +/* std::cout << "session id: " << ack_pkt.p2pHeader.sessionID << std::endl; + std::cout << "identifier: " << ack_pkt.p2pHeader.identifier << std::endl; + std::cout << "dataOffset: " << ack_pkt.p2pHeader.dataOffset << std::endl; + std::cout << "totalDataSize: " << ack_pkt.p2pHeader.totalDataSize << std::endl; + std::cout << "messageLength: " << ack_pkt.p2pHeader.messageLength << std::endl; + std::cout << "flag: " << ack_pkt.p2pHeader.flag << std::endl; + std::cout << "ackID: " << ack_pkt.p2pHeader.ackID << std::endl; + std::cout << "ackUID: " << ack_pkt.p2pHeader.ackUID << std::endl; + std::cout << "ackDataSize: " << ack_pkt.p2pHeader.ackDataSize << std::endl; + std::cout << "footer: " << ack_pkt.p2pFooter.appID << std::endl << std::endl; +*/ + + } + + void P2P::receiveP2PData(MSN::SwitchboardServerConnection &conn, p2pPacket &packet) + { + // check if there is no session + if(!startedSessions.count(packet.p2pHeader.sessionID)) + return; + + p2pSession session = startedSessions[packet.p2pHeader.sessionID]; + if(!session.in_stream && STEP_RECEIVING_FINISHED) + return; + + if(!session.in_stream->is_open()) + { + startedSessions[packet.p2pHeader.sessionID].totalDataSize = packet.p2pHeader.totalDataSize; + session.in_stream->open(session.filename.c_str(), std::ios::binary); + } + + if(packet.body.length()) + session.in_stream->write(packet.body.c_str(), packet.body.length()); + + // notify upper layer the current progress + if(session.appID == APP_FILE_TRANSFER) + conn.myNotificationServer()->externalCallbacks.fileTransferProgress(&conn, session.sessionID,session.in_stream->tellp(), packet.p2pHeader.totalDataSize); + + if((unsigned int)packet.p2pHeader.totalDataSize <= session.in_stream->tellp()) + { + session.in_stream->close(); + session.step = STEP_RECEIVING_FINISHED; + delete session.in_stream; // end of line + session.in_stream=NULL; + sendACK(conn, packet, session); + startedSessions[packet.p2pHeader.sessionID]=session; + + if(session.appID == APP_DISPLAY_PICTURE || + session.appID == APP_DISPLAY_PICTURE2) + { + conn.myNotificationServer()->externalCallbacks.gotContactDisplayPicture(&conn, conn.users.front(), session.filename ); + } + else + { + switch(session.typeTransfer) + { + case APP_VOICE_CLIP: + libmsn_Siren7_DecodeVoiceClip(session.filename); + conn.myNotificationServer()->externalCallbacks.gotVoiceClipFile(&conn, session.sessionID, session.filename); + break; + case APP_EMOTICON: + conn.myNotificationServer()->externalCallbacks.gotEmoticonFile(&conn, session.sessionID, session.emoticonAlias, session.filename); + break; + case APP_WINK: + conn.myNotificationServer()->externalCallbacks.gotWinkFile(&conn, session.sessionID, session.filename); + break; + } + if(session.appID == APP_FILE_TRANSFER) + conn.myNotificationServer()->externalCallbacks.fileTransferSucceeded(&conn, session.sessionID); + } + + if(session.appID != APP_FILE_TRANSFER) + { + send_BYE(conn, packet, session); + this->addCallback(&P2P::handle_BYEACK, session.sessionID, packet.p2pHeader.ackID); + } + } + } + + void P2P::sendP2PData(MSN::SwitchboardServerConnection &conn, p2pSession &session, p2pPacket &packet) + { + p2pPacket pkt_part = session.tempPacket; + char part[1202]; + std::ostringstream msghdr; + std::ostringstream footer; + std::ostringstream header; + std::ostringstream full_msg; + + msghdr <<"MIME-Version: 1.0\r\n" + "Content-Type: application/x-msnmsgrp2p\r\n" + "P2P-Dest: " << conn.users.front() << "\r\n\r\n"; + if(session.tempPacket.p2pHeader.ackID==0) // it means.. first packet + { + session.currentIdentifier++; + if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier + session.currentIdentifier++; + + session.tempPacket.p2pHeader.sessionID = session.sessionID; + session.tempPacket.p2pHeader.identifier = session.currentIdentifier; + + if(session.appID == APP_FILE_TRANSFER) + session.tempPacket.p2pHeader.flag = FLAG_FILE_DATA; + else + session.tempPacket.p2pHeader.flag = FLAG_DATA_PICTURE; + + session.tempPacket.p2pHeader.dataOffset = 0; + session.tempPacket.p2pHeader.totalDataSize = FileSize(session.filename.c_str()); + session.tempPacket.p2pHeader.messageLength = 0; + session.tempPacket.p2pHeader.ackUID = 0; + session.tempPacket.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + session.tempPacket.p2pHeader.ackDataSize=0; + // swap to big endian + session.tempPacket.p2pFooter.appID = little2big_endian(session.appID); + // scheduling the action to do when it is finished + this->addCallback(&P2P::handle_DataACK, session.sessionID, session.tempPacket.p2pHeader.ackID); + } + + pkt_part = session.tempPacket; + if(!session.out_stream) + return; + + if(!session.out_stream->is_open()) + session.out_stream->open(session.filename.c_str(), std::ios::binary); + + pkt_part.p2pHeader.dataOffset = session.out_stream->tellg(); + session.out_stream->read(part, 1100); + + if(!session.out_stream->gcount())// nothing to read, go away + { + session.out_stream->close(); + delete session.out_stream; + session.out_stream = NULL; + startedSessions[session.sessionID]=session; + if(session.appID == APP_FILE_TRANSFER) + conn.myNotificationServer()->externalCallbacks.fileTransferSucceeded(&conn, session.sessionID); + return; + } + + pkt_part.p2pHeader.messageLength = session.out_stream->gcount(); + + if(session.appID == APP_FILE_TRANSFER) + conn.myNotificationServer()->externalCallbacks.fileTransferProgress(&conn, session.sessionID, pkt_part.p2pHeader.dataOffset, pkt_part.p2pHeader.totalDataSize); + + std::string a(part, pkt_part.p2pHeader.messageLength); + std::istringstream temp_msg(a); + + header.write((char*)&pkt_part.p2pHeader.sessionID, sizeof(pkt_part.p2pHeader.sessionID)); + header.write((char*)&pkt_part.p2pHeader.identifier, sizeof(pkt_part.p2pHeader.identifier)); + header.write((char*)&pkt_part.p2pHeader.dataOffset, sizeof(pkt_part.p2pHeader.dataOffset)); + header.write((char*)&pkt_part.p2pHeader.totalDataSize, sizeof(pkt_part.p2pHeader.totalDataSize)); + header.write((char*)&pkt_part.p2pHeader.messageLength, sizeof(pkt_part.p2pHeader.messageLength)); + header.write((char*)&pkt_part.p2pHeader.flag, sizeof(pkt_part.p2pHeader.flag)); + header.write((char*)&pkt_part.p2pHeader.ackID, sizeof(pkt_part.p2pHeader.ackID)); + header.write((char*)&pkt_part.p2pHeader.ackUID, sizeof(pkt_part.p2pHeader.ackUID)); + header.write((char*)&pkt_part.p2pHeader.ackDataSize, sizeof(pkt_part.p2pHeader.ackDataSize)); + + footer.write((char*)&pkt_part.p2pFooter.appID,sizeof(pkt_part.p2pFooter.appID)); + + full_msg << msghdr.str() << header.str() << temp_msg.str() << footer.str(); + + std::ostringstream buf_; + buf_ << "MSG " << conn.trID << " D " << full_msg.str().size() << "\r\n"; + buf_ << full_msg.str(); + + if (conn.write(buf_) != buf_.str().size()) + return; + + session.tempPacket = pkt_part; + + startedSessions[session.sessionID]=session; + // call callback_continueTransfer when ack for this packet is received + conn.addP2PCallback(&SwitchboardServerConnection::callback_continueTransfer, conn.trID++, session.sessionID); + } + + void P2P::sendP2PPacket(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session) + { + std::ostringstream msghdr; + std::istringstream msg(packet.body); + std::ostringstream footer; + + if(session.to.empty()) + session.to = conn.users.front(); + + msghdr <<"MIME-Version: 1.0\r\n" + "Content-Type: application/x-msnmsgrp2p\r\n" + "P2P-Dest: " << conn.users.front() << "\r\n\r\n"; + + footer.write((char*)&packet.p2pFooter.appID,sizeof(packet.p2pFooter.appID)); + + session.currentIdentifier++; + if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier + session.currentIdentifier++; + + packet.p2pHeader.identifier = session.currentIdentifier; + + // split big messages in many packets + char temp_buf[1201]; + while(!msg.eof()) + { + std::ostringstream header; + std::ostringstream full_msg; + packet.p2pHeader.dataOffset = msg.tellg(); + msg.read(temp_buf,1200); + + if(msg.gcount()==0)// nothing to read, go away + break; + + packet.p2pHeader.totalDataSize = msg.str().size(); + packet.p2pHeader.messageLength = msg.gcount(); + + std::string a(temp_buf, msg.gcount()); + std::istringstream temp_msg(a); + + header.write((char*)&packet.p2pHeader.sessionID, sizeof(packet.p2pHeader.sessionID)); + header.write((char*)&packet.p2pHeader.identifier, sizeof(packet.p2pHeader.identifier)); + header.write((char*)&packet.p2pHeader.dataOffset, sizeof(packet.p2pHeader.dataOffset)); + header.write((char*)&packet.p2pHeader.totalDataSize, sizeof(packet.p2pHeader.totalDataSize)); + header.write((char*)&packet.p2pHeader.messageLength, sizeof(packet.p2pHeader.messageLength)); + header.write((char*)&packet.p2pHeader.flag, sizeof(packet.p2pHeader.flag)); + header.write((char*)&packet.p2pHeader.ackID, sizeof(packet.p2pHeader.ackID)); + header.write((char*)&packet.p2pHeader.ackUID, sizeof(packet.p2pHeader.ackUID)); + header.write((char*)&packet.p2pHeader.ackDataSize, sizeof(packet.p2pHeader.ackDataSize)); + + full_msg << msghdr.str() << header.str() << temp_msg.str() << footer.str(); + + std::ostringstream buf_; + buf_ << "MSG " << conn.trID++ << " D " << full_msg.str().size() << "\r\n"; + buf_ << full_msg.str(); + + if (conn.write(buf_) != buf_.str().size()) + return; + } + } + + void P2P::handle_session_changes(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session) + { + std::string body; + std::vector msg = splitString(packet.body, "\r\n\r\n"); + msg[1]+="\r\n"; // it is really needed :( + Message::Headers header_slp = Message::Headers(msg[0]); + Message::Headers header_app = Message::Headers(msg[1]); + switch(session.appID) + { + case APP_FILE_TRANSFER: + { + session.CSeq = decimalFromString(header_slp["CSeq"]); + session.Bridges = header_app["Bridges"]; + session.NetID = decimalFromString(header_app["NetID"]); + session.ConnType = header_app["Conn-Type"]; + session.ICF = header_app["ICF"]; + session.UPnPNat = header_app["UPnPNat"]; + session.Listening = header_app["Listening"]; + + session.IPv4InternalAddrs = header_app["IPv4Internal-Addrs"]; + session.IPv4InternalPort = header_app["IPv4Internal-Port"]; + session.IPv4ExternalAddrs = header_app["IPv4External-Addrs"]; + session.IPv4ExternalPort = header_app["IPv4External-Port"]; + + if(session.step == STEP_RECEIVING) + { + return; + } + + // direct connection, sending client is waiting connection. + if(session.Listening == "true") + { +/* // TODO - implement this part + bool a; + unsigned int port = decimalFromString(session.IPv4ExternalPort); + session.fileTransfer = new FileTransferConnectionP2P(conn, session); + if(!session.fileTransfer) + return; + + session.fileTransfer->setDirection(FileTransferConnectionP2P::MSNFTP_RECV); + session.fileTransfer->setPerspective(FileTransferConnectionP2P::MSNFTP_CLIENT); + session.fileTransfer->sock = conn.myNotificationServer()->externalCallbacks.connectToServer(session.IPv4ExternalAddrs, port, &a); + conn.addFileTransferConnectionP2P(session.fileTransfer); + + if (session.fileTransfer->sock < 0) + { + // if not possible to connect, stop + return; + } + std::ostringstream body2; + char b=4; + body2.write(&b,1); + body2.write("foo\0",4); + + conn.myNotificationServer()->externalCallbacks.registerSocket(session.fileTransfer->sock,1,1); + session.fileTransfer->write(body2.str());*/ + } + else if (conn.myNotificationServer()->direct_connection) + { + // direct connection, we are waiting connection. + // direct_connection means we are directly connected + // to the internet + body= "Bridge: TCPv1\r\n" + "Listening: true\r\n" + "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"; + } + else + { + // switchboard file transfer, the slowest mode + body= "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"; + } + + send_200OK(conn, session, body); + } + } + } + + void P2P::handle_INVITE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet) + { + p2pSession session; + std::vector msg = splitString(packet.body, "\r\n\r\n"); + msg[1]+="\r\n"; // this is really needed :( + Message::Headers header_slp = Message::Headers(msg[0]); + Message::Headers header_app = Message::Headers(msg[1]); + + session.to = header_slp["From"]; + session.to = splitString(header_slp["From"], ":")[1]; + session.to = splitString(session.to, ">")[0]; + session.from = header_slp["To"]; + session.from = splitString(header_slp["To"], ":")[1]; + session.from = splitString(session.from, ">")[0]; + + session.Via = header_slp["Via"]; + session.CSeq = decimalFromString(header_slp["CSeq"]); + session.CallID = header_slp["Call-ID"]; + session.ContentType = header_slp["Content-Type"]; + + std::map::iterator i = startedSessions.begin(); + for(; i != startedSessions.end(); i++) + { + if((*i).second.CallID == session.CallID) + { + // this isn't a new session, since I already have this callid + p2pSession old_session = (*i).second; + sendACK(conn, packet, session); + old_session.Via = session.Via; + old_session.CSeq = session.CSeq; + old_session.ContentType = session.ContentType; + // handle the changes received in this invitation packet + handle_session_changes(conn, packet, old_session); + return; + } + } + //session.fromPassport = packet.fromPassport; + session.sessionID = decimalFromString(header_app["SessionID"]); + session.appID = decimalFromString(header_app["AppID"]); + session.Context = header_app["Context"]; + + // new connection goes below + session.out_stream = new std::ifstream; + session.tempPacket.p2pHeader.ackID=0; + session.in_stream = NULL; + session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++; + session.baseIdentifier = session.currentIdentifier; // we need to keep this one + sendACK(conn, packet, session); + session.baseIdentifier++; + session.step = STEP_ACK_INVITATION_SENT; + + switch(session.appID) + { + case APP_WEBCAM: + { + if(header_app["EUF-GUID"] == "{4BD96FC0-AB17-4425-A14A-439185962DC8}") + { + //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID); + std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n"); + send_200OK(conn, session, body); + } + if(header_app["EUF-GUID"] == "{1C9AA97E-9C05-4583-A3BD-908A196F1E92}") + { + //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID); + } + break; + } + case APP_EMOTICON: + case APP_DISPLAY_PICTURE: + case APP_DISPLAY_PICTURE2: + case APP_VOICE_CLIP: + { + // I dont know why, just following the rules. + // I think this is the better place to do this + session.currentIdentifier-=4; + + std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n"); + send_200OK(conn,session,body); // always accept display picture + break; + } + case APP_FILE_TRANSFER: + { + session.currentIdentifier-=1; + session.sending=false; + startedSessions[session.sessionID]=session; + std::istringstream context(b64_decode(session.Context.c_str()), std::ios::binary); + + std::string preview; + unsigned int header_size; + unsigned int type; + bool has_preview =true; + + context.read((char*)&header_size, sizeof(unsigned int)); + + context.seekg(0, std::ios::beg); + context.seekg(16); + context.read((char*)&type, sizeof(unsigned int)); + + context.seekg(0, std::ios::end); + // type == 1 means no preview + if(header_size != context.tellg() && type != 1) + has_preview=true; + + context.seekg(0, std::ios::beg); + context.seekg(19); + + U8 *filenameutf16 = new U8[520]; + U8 *filenameutf8 = new U8[520]; + context.read((char*)filenameutf16, 520); + _ucs2_utf8(filenameutf8, filenameutf16, 520); + std::string filename((char*)filenameutf8); + delete [] filenameutf16; + delete [] filenameutf8; + + unsigned long long filesize; + context.seekg(8); + context.read((char*)&filesize, sizeof(unsigned long long)); + + if(has_preview) + { + context.seekg(header_size); + int size2 = b64_decode(session.Context.c_str()).size() - header_size; + char *a = new char[size2]; + context.read((char*)a, size2); + preview = b64_encode(a,size2); + delete [] a; + } + fileTransferInvite ft; + ft.type = type; + ft.sessionId = session.sessionID; + ft.userPassport = conn. users.front(); + ft.filename = filename; + ft.filesize = filesize; + ft.preview = preview; + + conn.myNotificationServer()->externalCallbacks.askFileTransfer(&conn, ft); + return; + } + } + } + + void P2P::handle_200OK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet) + { + p2pSession session; + std::vector msg = splitString(packet.body, "\r\n\r\n"); + msg[1]+="\r\n"; // this is really needed :( + Message::Headers header_slp = Message::Headers(msg[0]); + Message::Headers header_app = Message::Headers(msg[1]); + + unsigned int tmp_session = decimalFromString(header_app["SessionID"]); + + if(!tmp_session) + return; + + // we need to ensure we have a started session + if(!startedSessions.count(tmp_session)) + return; + + session = startedSessions[tmp_session]; + + sendACK(conn, packet, session); + if(session.appID == APP_FILE_TRANSFER) + { + sendP2PData(conn, session, packet); + conn.myNotificationServer()->externalCallbacks.fileTransferInviteResponse(&conn, tmp_session, true); + } + } + + void P2P::handle_603Decline(MSN::SwitchboardServerConnection &conn, p2pPacket &packet) + { + p2pSession session; + std::vector msg = splitString(packet.body, "\r\n\r\n"); + msg[1]+="\r\n"; // this is really needed :( + Message::Headers header_slp = Message::Headers(msg[0]); + Message::Headers header_app = Message::Headers(msg[1]); + + unsigned int tmp_session = decimalFromString(header_app["SessionID"]); + + if(!tmp_session) + return; + + // we need to ensure we have a started session + if(!startedSessions.count(tmp_session)) + return; + + session = startedSessions[tmp_session]; + + conn.myNotificationServer()->externalCallbacks.fileTransferInviteResponse(&conn, tmp_session, false); + } + + void P2P::handle_BYE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet) + { + p2pSession session; + std::vector msg = splitString(packet.body, "\r\n\r\n"); + if (msg.size() < 2) + { + std::cout << "P2P::handle_BYE ERROR size: " << msg.size() << " < 2" << std::endl; + std::cout << "\'" << packet.body << "\'" << std::endl; + return; + } + msg[1]+="\r\n"; // this is really needed :( + Message::Headers header_slp = Message::Headers(msg[0]); + Message::Headers header_app = Message::Headers(msg[1]); + + session.to = header_slp["From"]; + session.to = splitString(header_slp["From"], ":")[1]; + session.to = splitString(session.to, ">")[0]; + session.from = header_slp["To"]; + session.from = splitString(header_slp["To"], ":")[1]; + session.from = splitString(session.from, ">")[0]; + + session.CSeq = decimalFromString(header_slp["CSeq"]); + session.CallID = header_slp["Call-ID"]; + session.Via = header_slp["Via"]; + + session.sessionID = decimalFromString(header_app["SessionID"]); + session.appID = decimalFromString(header_app["AppID"]); + session.Context = header_app["Context"]; + + std::map::iterator i = startedSessions.begin(); + for(; i != startedSessions.end(); i++) + { + if((*i).second.CallID == session.CallID) + { + sendACK(conn, packet, (*i).second); + if((*i).second.in_stream && + (unsigned int)(*i).second.totalDataSize > (*i).second.in_stream->tellp()) + { + if((*i).second.appID == APP_FILE_TRANSFER) + conn.myNotificationServer()->externalCallbacks.fileTransferFailed(&conn, (*i).second.sessionID, MSN::FILE_TRANSFER_ERROR_USER_CANCELED); + } + if(!(*i).second.in_stream && (*i).second.appID == APP_FILE_TRANSFER) + { + if((*i).second.appID == APP_FILE_TRANSFER) + conn.myNotificationServer()->externalCallbacks.fileTransferFailed(&conn, (*i).second.sessionID, MSN::FILE_TRANSFER_ERROR_USER_CANCELED); + } + if((*i).second.in_stream) + { + if((*i).second.in_stream->is_open()) + (*i).second.in_stream->close(); + + delete (*i).second.in_stream; + (*i).second.in_stream = NULL; + } + startedSessions.erase((*i).second.sessionID); + return; + } + } + } + + void P2P::handle_p2pACK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet) + { + + if (!this->callbacks.empty() && packet.p2pHeader.ackUID > 0) + { + if (this->callbacks.find(packet.p2pHeader.ackUID) != this->callbacks.end()) + { + (this->*(this->callbacks[packet.p2pHeader.ackUID].first))(conn,this->callbacks[packet.p2pHeader.ackUID].second, packet); + } + } + } + + void P2P::send_200OK(MSN::SwitchboardServerConnection &conn, p2pSession &session, std::string body) + { + p2pPacket packet; + + std::ostringstream body2; + body2.write("\0",1); + std::string body_final("\r\n"+body+body2.str()); + + if(session.ContentType == "application/x-msnmsgr-transreqbody") + session.ContentType="application/x-msnmsgr-transrespbody"; + + std::string content("MSNSLP/1.0 200 OK\r\n" + "To: \r\n" + "From: \r\n" + "Via: "+session.Via+"\r\n" + "CSeq: "+ toStr(++session.CSeq) + "\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: "+session.ContentType +"\r\n" + "Content-Length: "+ toStr(body_final.length())+ "\r\n" + +body_final); + + packet.p2pHeader.sessionID = 0; + packet.p2pHeader.identifier = session.currentIdentifier; + packet.p2pHeader.flag = FLAG_NOP; + packet.p2pHeader.dataOffset = 0; + packet.p2pHeader.totalDataSize = content.length(); + packet.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + packet.p2pHeader.ackUID = 0; + packet.p2pHeader.ackDataSize=0; + + packet.body = content; + + packet.p2pFooter.appID = APP_NONE; + + sendP2PPacket(conn, packet, session); + + session.step = STEP_200OK_SENT; + + startedSessions[session.sessionID]=session; + + this->addCallback(&P2P::handle_200OKACK, session.sessionID, packet.p2pHeader.ackID); + } + + void P2P::send_BYE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session) + { + std::ostringstream body; + body.write("\r\n\0",3); + std::string content("BYE MSNMSGR:"+session.to+" MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: "+session.Via+"\r\n" + "CSeq: 0\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionclosebody\r\n" + "Content-Length: "+ toStr(body.str().size())+ "\r\n" + +body.str()); + + packet.p2pHeader.sessionID = 0; + packet.p2pHeader.identifier = session.currentIdentifier; + packet.p2pHeader.flag = FLAG_NOP; + packet.p2pHeader.dataOffset = 0; + packet.p2pHeader.totalDataSize = content.length(); + packet.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + packet.p2pHeader.ackUID = 0; + packet.p2pHeader.ackDataSize=0; + + packet.body = content; + + packet.p2pFooter.appID = APP_NONE; + + sendP2PPacket(conn, packet, session); + + session.step = STEP_BYE_SENT; + + startedSessions[session.sessionID]=session; + +// this->addCallback(&P2P::handle_200OKACK, session.sessionID, packet.p2pHeader.ackID); + } + + + void P2P::send_603Decline(MSN::SwitchboardServerConnection &conn, p2pSession &session) + { + p2pPacket packet; + + std::ostringstream body2; + body2.write("\0",1); + std::string body("\r\nSessionID: "+ toStr(session.sessionID)+"\r\n"+body2.str()); + std::string content("MSNSLP/1.0 603 Decline\r\n" + "To: \r\n" + "From: \r\n" + "Via: "+session.Via+"\r\n" + "CSeq: "+ toStr(++session.CSeq) + "\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionreqbody\r\n" + "Content-Length: "+ toStr(body.length())+ "\r\n" + +body); + + packet.p2pHeader.sessionID = 0; + packet.p2pHeader.identifier = session.currentIdentifier; + packet.p2pHeader.flag = FLAG_NOP; + packet.p2pHeader.dataOffset = 0; + packet.p2pHeader.totalDataSize = content.length(); + packet.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + packet.p2pHeader.ackUID = 0; + packet.p2pHeader.ackDataSize=0; + + packet.body = content; + + packet.p2pFooter.appID = APP_NONE; + + sendP2PPacket(conn, packet, session); + + session.step = STEP_603DECLINE_SENT; + + startedSessions[session.sessionID]=session; + + this->addCallback(&P2P::handle_603DeclineACK, session.sessionID, packet.p2pHeader.ackID); + } + + + void P2P::addCallback(P2PCallbacks callback, unsigned int sessionID, unsigned int ackID) + { + this->callbacks[ackID] = std::make_pair(callback,sessionID); + } + + void P2P::removeCallback(unsigned int ackID) + { + this->callbacks.erase(ackID); + } + + void P2P::handle_603DeclineACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet) + { + this->removeCallback(packet.p2pHeader.ackUID); + startedSessions.erase(sessionID); + } + + void P2P::handle_200OKACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet) + { + p2pPacket pkt_prep; + + this->removeCallback(packet.p2pHeader.ackUID); + if (startedSessions.find(sessionID) == startedSessions.end()) + { + return; + } + p2pSession session = startedSessions[sessionID]; + session.step = STEP_200OK_ACK_SENT; + + switch(session.appID) + { + case APP_EMOTICON: + case APP_WEBCAM: + case APP_DISPLAY_PICTURE: + case APP_DISPLAY_PICTURE2: + case APP_VOICE_CLIP: + { + pkt_prep.p2pHeader.sessionID = sessionID; + pkt_prep.p2pHeader.flag = FLAG_NOP; + pkt_prep.p2pHeader.identifier = session.currentIdentifier; + pkt_prep.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + pkt_prep.p2pHeader.ackUID = 0; + pkt_prep.p2pHeader.ackDataSize = 0; + // big endian + pkt_prep.p2pFooter.appID = little2big_endian(session.appID); + + std::ostringstream content; + content.write("\0\0\0\0",4); + pkt_prep.body = content.str(); + + sendP2PPacket(conn, pkt_prep, session); + session.step = STEP_DATA_PREPARATION_SENT; + session.typeTransfer = (p2pTransferObj)session.appID; + + startedSessions[sessionID]=session; + + this->addCallback(&P2P::handle_DataPreparationACK, session.sessionID, pkt_prep.p2pHeader.ackID); + break; + } + case APP_FILE_TRANSFER: + { + // great, on 200OK from file transfer the packet does not + // have appID + // We should wait now an inviting for direct connection + } + } + } + + void P2P::handle_DataPreparationACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet) + { + this->removeCallback(packet.p2pHeader.ackUID); + p2pSession session = startedSessions[sessionID]; + session.step = STEP_SENDING; + std::string filepath; + filepath += b64_decode(session.Context.c_str()); // prevents empty context + if(filepath.length()) + { + if(!conn.myNotificationServer()->msnobj.getMSNObjectRealPath(b64_decode(session.Context.c_str()), session.filename)) + { + send_603Decline(conn,session); + return; + } + } + else + { + send_603Decline(conn,session); + return; + } + sendP2PData(conn, session, packet); + } + + void P2P::handle_MSGACKReceived(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string fromPassport) + { + p2pPacket packet; + if(startedSessions.find(sessionID) == startedSessions.end()) + return; + + p2pSession session = startedSessions[sessionID]; + sendP2PData(conn, session, packet); + } + + void P2P::handle_fileTransferResponse(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, bool response) + { + p2pSession session = startedSessions[sessionID]; + session.filename = filename; + if(response) // user accepted + { + session.in_stream = new std::ofstream; + std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n"); + send_200OK(conn, session, body); + } + else // user rejected + { + // I dont want to receive your file, blergh + send_603Decline(conn,session); + } + } + + void P2P::handle_DataACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet) + { + this->removeCallback(packet.p2pHeader.ackUID); + + p2pPacket pkt_bye; + + std::string branch = new_branch(); + + p2pSession session = startedSessions[sessionID]; + session.step = STEP_DATA_TRANSFER_ACK; + + std::ostringstream body; + body.write("\r\n\0",3); + std::string content("BYE MSNMSGR:"+session.to+" MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: MSNSLP/1.0/TLP ;branch="+branch+"\r\n" + "CSeq: 0\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionclosebody\r\n" + "Content-Length: "+ toStr(body.str().length())+ "\r\n" + +body.str()); + + pkt_bye.p2pHeader.sessionID = 0; + pkt_bye.p2pHeader.identifier = session.currentIdentifier; + pkt_bye.p2pHeader.flag = FLAG_NOP; + pkt_bye.p2pHeader.dataOffset = 0; + pkt_bye.p2pHeader.totalDataSize = content.length(); + pkt_bye.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + pkt_bye.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + pkt_bye.p2pHeader.ackUID = 0; + pkt_bye.p2pHeader.ackDataSize=0; + + pkt_bye.body = content; + + pkt_bye.p2pFooter.appID = APP_NONE; + + sendP2PPacket(conn, pkt_bye, session); + + session.step = STEP_BYE_SENT; + + startedSessions[session.sessionID]=session; + + this->addCallback(&P2P::handle_BYEACK, session.sessionID, pkt_bye.p2pHeader.ackID); + } + + void P2P::handle_BYEACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet) + { + this->removeCallback(packet.p2pHeader.ackUID); + } + + void P2P::requestDisplayPicture(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject) + { + p2pSession session; + session.Context = b64_encode(msnobject.c_str(), msnobject.size()); + session.CSeq = 0; + session.sessionID = sessionID; + session.filename = filename; + session.CallID = new_branch(); + session.to = conn.users.front() ; + session.from = conn.myNotificationServer()->myPassport; + session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++; + session.baseIdentifier = session.currentIdentifier; + session.Via = "MSNSLP/1.0/TLP ;branch="; + session.Via+= new_branch(); + session.appID = APP_DISPLAY_PICTURE2; + p2pPacket packet; + std::ostringstream body2; + body2.write("\0",1); + std::string body("\r\n" + "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n" + "SessionID: "+ toStr(session.sessionID)+"\r\n" + "AppID: 1\r\n" + "Context: " + session.Context + "\r\n"+body2.str()); + std::string content("INVITE MSNMSGR:"+ session.to +" MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: "+session.Via+"\r\n" + "CSeq: "+ toStr(session.CSeq++) + "\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionreqbody\r\n" + "Content-Length: "+ toStr(body.length())+ "\r\n" + +body); + + packet.p2pHeader.sessionID = 0; + packet.p2pHeader.identifier = session.currentIdentifier; + packet.p2pHeader.flag = FLAG_NOP; + packet.p2pHeader.dataOffset = 0; + packet.p2pHeader.totalDataSize = content.length(); + packet.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + packet.p2pHeader.ackUID = 0; + packet.p2pHeader.ackDataSize=0; + + packet.body = content; + + packet.p2pFooter.appID = APP_NONE; + + session.in_stream = new std::ofstream; + + sendP2PPacket(conn, packet, session); + + session.currentIdentifier = session.currentIdentifier - 3; + + startedSessions[session.sessionID]=session; + } + + void P2P::requestFile(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject, p2pTransferObj obj) + { + p2pSession session; + if(startedSessions.find(sessionID) != startedSessions.end()) + session = startedSessions[sessionID]; + + session.Context = b64_encode(msnobject.c_str(), msnobject.size()); + session.CSeq = 0; + session.sessionID = sessionID; + session.filename = filename; + session.CallID = new_branch(); + session.to = conn.users.front() ; + session.from = conn.myNotificationServer()->myPassport; + session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++; + session.baseIdentifier = session.currentIdentifier; + session.Via = "MSNSLP/1.0/TLP ;branch="; + session.Via+= new_branch(); + session.typeTransfer = obj; + + p2pPacket packet; + std::ostringstream body2; + body2.write("\0",1); + std::string body("\r\n" + "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n" + "SessionID: "+ toStr(session.sessionID)+"\r\n" + "AppID: 1\r\n" + "Context: " + session.Context + "\r\n"+body2.str()); + std::string content("INVITE MSNMSGR:"+ session.to +" MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: "+session.Via+"\r\n" + "CSeq: "+ toStr(session.CSeq++) + "\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionreqbody\r\n" + "Content-Length: "+ toStr(body.length())+ "\r\n" + +body); + + packet.p2pHeader.sessionID = 0; + packet.p2pHeader.identifier = session.currentIdentifier; + packet.p2pHeader.flag = FLAG_NOP; + packet.p2pHeader.dataOffset = 0; + packet.p2pHeader.totalDataSize = content.length(); + packet.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + packet.p2pHeader.ackUID = 0; + packet.p2pHeader.ackDataSize=0; + + packet.body = content; + + packet.p2pFooter.appID = APP_NONE; + + session.in_stream = new std::ofstream; + + sendP2PPacket(conn, packet, session); + + session.currentIdentifier = session.currentIdentifier - 3; + + startedSessions[session.sessionID]=session; + } + + void P2P::requestEmoticon(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject, std::string alias) + { + p2pSession session; + session.emoticonAlias = alias; + startedSessions[sessionID]=session; + requestFile(conn, sessionID, filename, msnobject, APP_EMOTICON); + } + + void P2P::requestVoiceClip(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject) + { + requestFile(conn, sessionID, filename, msnobject, APP_VOICE_CLIP); + } + + void P2P::requestWink(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject) + { + requestFile(conn, sessionID, filename, msnobject, APP_WINK); + } + + void P2P::handle_INVITE_ACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet) + { + } + + std::string build_file_transfer_context(MSN::fileTransferInvite ft) + { + std::ostringstream context; + unsigned int hlength=0x27E; + unsigned int msnversion=0x03; + unsigned long long filesize = FileSize(ft.filename.c_str()); + unsigned int type = ft.type; + char filename[520]; + char unknown1[30]; + unsigned int unknown2 = (ft.type == FILE_TRANSFER_BACKGROUND_SHARING) || + (ft.type == FILE_TRANSFER_BACKGROUND_SHARING_CUSTOM) ? 0xFFFFFE : 0xFFFFFF ; + char unknown3[64]; + + memset(&filename,0,520); + memset(&unknown1,0,30); + memset(&unknown3,0,64); + + // TODO - convert filename to ucs2 + U8 *filenameutf8 = new U8[520]; + U8 *filenameutf16 = new U8[521]; + memset(filenameutf8, 0, 520); + memset(filenameutf16, 0, 521); + memcpy(filenameutf8, ft.friendlyname.c_str(), ft.friendlyname.size()); + _utf8_ucs2(filenameutf16, filenameutf8); + filenameutf16++; + + context.write((char*)&hlength, sizeof(unsigned int)); + context.write((char*)&msnversion, sizeof(unsigned int)); + context.write((char*)&filesize, sizeof(unsigned long long)); + context.write((char*)&type, sizeof(unsigned int)); + context.write((char*)filenameutf16, 520); + context.write((char*)&unknown1, 30); + context.write((char*)&unknown2, sizeof(unsigned int)); + context.write((char*)&unknown3, 64); + + filenameutf16--; + delete [] filenameutf16; + delete [] filenameutf8; + + if(ft.type == FILE_TRANSFER_WITH_PREVIEW && ft.preview.size()) + context.write((char*)b64_decode(ft.preview.c_str()).c_str(), b64_decode(ft.preview.c_str()).size()); + + return b64_encode(context.str().c_str(), context.str().size()); + } + + void P2P::sendFile(MSN::SwitchboardServerConnection &conn, MSN::fileTransferInvite ft) + { + p2pSession session; + session.Context = build_file_transfer_context(ft); + session.CSeq = 0; + session.sessionID = ft.sessionId; + session.filename = ft.filename; + session.CallID = new_branch(); + session.to = conn.users.front(); + session.from = conn.myNotificationServer()->myPassport; + session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++; + session.baseIdentifier = session.currentIdentifier; + session.Via = "MSNSLP/1.0/TLP ;branch="; + session.Via+= new_branch(); + session.tempPacket.p2pHeader.ackID=0; + session.in_stream = NULL; + + p2pPacket packet; + std::ostringstream body2; + body2.write("\0",1); + std::string other_passport = conn.users.front(); + std::string body("\r\n" + "EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}\r\n" + "SessionID: "+ toStr(session.sessionID)+"\r\n" + "SChannelState: 0\r\n" + "Capabilities-Flags: 1\r\n" + "AppID: 2\r\n" + "Context: " + session.Context + "\r\n"+body2.str()); + std::string content("INVITE MSNMSGR:"+ other_passport +" MSNSLP/1.0\r\n" + "To: \r\n" + "From: \r\n" + "Via: "+session.Via+"\r\n" + "CSeq: "+ toStr(session.CSeq++) + "\r\n" + "Call-ID: "+session.CallID+"\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: application/x-msnmsgr-sessionreqbody\r\n" + "Content-Length: "+ toStr(body.length())+ "\r\n" + +body); + + packet.p2pHeader.sessionID = 0; + packet.p2pHeader.identifier = session.currentIdentifier; + packet.p2pHeader.flag = FLAG_NOP; + packet.p2pHeader.dataOffset = 0; + packet.p2pHeader.totalDataSize = content.length(); + packet.p2pHeader.messageLength = 0; // filled inside sendP2PPacket() + packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++; + packet.p2pHeader.ackUID = 0; + packet.p2pHeader.ackDataSize=0; + + packet.body = content; + + packet.p2pFooter.appID = APP_NONE; + session.appID = APP_FILE_TRANSFER; + + session.out_stream = new std::ifstream; + + sendP2PPacket(conn, packet, session); + + startedSessions[session.sessionID]=session; + } + + void P2P::sendInk(MSN::SwitchboardServerConnection &conn, std::string image) + { + std::list::iterator i = conn.users.begin(); + // for each user + + } + void P2P::cancelTransfer(MSN::SwitchboardServerConnection &conn, unsigned int sessionID) + { + p2pSession session; + p2pPacket packet; + if(startedSessions.find(sessionID) == startedSessions.end()) + return; + + session = startedSessions[sessionID]; + send_BYE(conn, packet, session); + startedSessions.erase(sessionID); + } +} diff --git a/libs/libmsn/p2p.h b/libs/libmsn/p2p.h new file mode 100644 index 0000000..356f3f8 --- /dev/null +++ b/libs/libmsn/p2p.h @@ -0,0 +1,322 @@ +#ifndef __msn_p2p_h__ +#define __msn_p2p_h__ +/* + * p2p.h + * libmsn + * + * Crated by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "connection.h" +#include "authdata.h" +#include "errorcodes.h" +#include "buddy.h" +#include "passport.h" +#include "util.h" +#include + +#include +#include +#include + +#include "libmsn_export.h" + +namespace MSN +{ + class SwitchboardServerConnection; + class FileTransferConnectionP2P; + + /** Manages all p2p communication. + */ + class LIBMSN_EXPORT P2P + { + public: + P2P(); + virtual ~P2P(); + unsigned int rand_helper; + enum { + DIRECTION_SENDING = 0, + DIRECTION_RECEIVING = 1 + }; + enum { + STEP_INVITATION_SENT, + STEP_ACK_INVITATION_SENT, + STEP_200OK_SENT, + STEP_200OK_ACK_SENT, + STEP_603DECLINE_SENT, + STEP_603DECLINE_ACK_SENT, + STEP_DC_INVITE_SENT, // direct connection + STEP_DC_INVITE_ACK_SENT, // direct connection + STEP_DC_200OK_SENT, + STEP_DC_200OKACK_SENT, + STEP_DATA_PREPARATION_SENT, + STEP_DATA_PREPARATION_ACK, + STEP_SENDING, + STEP_RECEIVING, + STEP_RECEIVING_FINISHED, + STEP_DATA_TRANSFER_ACK, + STEP_BYE_SENT, + STEP_BYE_ACK + }; + typedef enum { + APP_NONE = 0, + APP_WEBCAM = 4, + APP_FILE_TRANSFER = 2, + APP_DISPLAY_PICTURE = 1, + APP_EMOTICON = 11, + APP_DISPLAY_PICTURE2 = 12, // MSNP15 uses 12 instead 1 + APP_VOICE_CLIP = 20, // MSNP15 uses 12 instead 1 + APP_WINK = 98, // non standard + APP_INK = 99 // non standard + } p2pTransferObj; + enum { + FLAG_NOP = 0x0, + FLAG_ACK = 0x2, + FLAG_ERROR = 0x8, + FLAG_DATA_EMOTICONS = 0x20, + FLAG_DATA_PICTURE = 0x20, + FLAG_FILE_DATA = 0x01000030, + FLAG_FILE_DATA2 = 0x01000020 + }; + + struct p2pPacket { + struct { + unsigned int sessionID; + unsigned int identifier; + unsigned long long dataOffset; + unsigned long long totalDataSize; + unsigned int messageLength; + unsigned int flag; + unsigned int ackID; + unsigned int ackUID; + unsigned long long ackDataSize; + }p2pHeader; + std::string body; + struct { + unsigned int appID; + }p2pFooter; + + p2pPacket() { + p2pHeader.sessionID = 0; + p2pHeader.identifier = 0; + p2pHeader.dataOffset = 0; + p2pHeader.totalDataSize = 0; + p2pHeader.messageLength = 0; + p2pHeader.flag = 0; + p2pHeader.ackID = 0; + p2pHeader.ackUID = 0; + p2pHeader.ackDataSize = 0; + p2pFooter.appID = 0; + } + }; + + struct p2pSession { + bool sending; // sending or receiving, if sending, so true + unsigned long long totalDataSize; + unsigned int step; // step at the moment + unsigned int currentIdentifier; + unsigned int baseIdentifier; // baseIdentifier + unsigned int CSeq; + unsigned int sessionID; + unsigned int appID; + MSN::FileTransferConnectionP2P *fileTransfer; + std::string from; + std::string to; + std::string CallID; + std::string Via; + std::string ContentType; + std::string Context; // can be the file preview or msnobject + std::string filename; // filename to transfer + std::ifstream *out_stream; // file to send + std::ofstream *in_stream; // file to receive + std::string ConnType; + std::string Bridges; + std::string NetID; + std::string UPnPNat; + std::string Listening; + std::string ICF; + std::string IPv4InternalAddrs; + std::string IPv4InternalPort; + std::string IPv4ExternalAddrs; + std::string IPv4ExternalPort; + p2pTransferObj typeTransfer; + std::string emoticonAlias; + + p2pPacket tempPacket; // this is used for general purposes + std::string ink; + + p2pSession() { + sending = false; + totalDataSize = 0; + step = 0; + currentIdentifier = 0; + baseIdentifier = 0; + CSeq = 0; + sessionID = 0; + appID = 0; + fileTransfer = 0; + out_stream = 0; + in_stream = 0; + typeTransfer = APP_NONE; + } + }; + + typedef void (P2P::*P2PCallbacks)(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + std::map > callbacks; + + virtual void addCallback(P2PCallbacks, unsigned int sessionID, + unsigned int ackID); + + virtual void removeCallback(unsigned int ackID); + + std::map pendingP2PMsg; + std::map startedSessions; + + void sendFile(MSN::SwitchboardServerConnection &conn, + MSN::fileTransferInvite ft); + + void handleP2Pmessage(MSN::SwitchboardServerConnection &conn, + std::vector & args, + std::string mime, std::string body); + + void sendACK(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet, + p2pSession &session); + + void sendP2PPacket(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet, + p2pSession &session); + + void sendP2PData(MSN::SwitchboardServerConnection &conn, + p2pSession &session, + p2pPacket &packet); + + void receiveP2PData(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void handle_negotiation(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void handle_INVITE(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void handle_603Decline(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void handle_INVITE_ACK(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + void handle_200OK(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void handle_BYE(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void send_200OK(MSN::SwitchboardServerConnection &conn, + p2pSession &session, + std::string body); + + void send_BYE(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet, + p2pSession &session); + + void send_603Decline(MSN::SwitchboardServerConnection &conn, + p2pSession &session); + + void handle_p2pACK(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet); + + void handle_200OKACK(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + void handle_603DeclineACK(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + void handle_DataPreparationACK(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + void handle_DataACK(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + void handle_BYEACK(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + p2pPacket &packet); + + void handle_MSGACKReceived(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string fromPassport); + + void handle_fileTransferResponse(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + bool response); + + void handle_session_changes(MSN::SwitchboardServerConnection &conn, + p2pPacket &packet, + p2pSession &session); + + void requestFile(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + std::string msnobject, + p2pTransferObj obj); + + void requestDisplayPicture(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + std::string msnobject); + + void sendInk(MSN::SwitchboardServerConnection &conn, + std::string image); + + void cancelTransfer(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID); + + void requestEmoticon(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + std::string msnobject, + std::string alias); + + void requestVoiceClip(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + std::string msnobject); + + void requestWink(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + std::string msnobject); + + void requestInk(MSN::SwitchboardServerConnection &conn, + unsigned int sessionID, + std::string filename, + std::string msnobject); + }; +} + +#endif diff --git a/libs/libmsn/passport.cpp b/libs/libmsn/passport.cpp new file mode 100644 index 0000000..14ca0bb --- /dev/null +++ b/libs/libmsn/passport.cpp @@ -0,0 +1,68 @@ +/* + * passport.cpp + * libmsn + * + * Created by Mark Rowe on Thu May 20 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "passport.h" +#include + +namespace MSN +{ + void Passport::validate() + { + if (email.find(" ") != std::string::npos) + throw InvalidPassport("Passport must not contain any spaces!"); + + if (email.find("@") == std::string::npos || email.find("@") != email.rfind("@")) + throw InvalidPassport("Passport must contain exactly one '@' character!"); + + if (email.find("@") == 0) + throw InvalidPassport("Passport must have at least one character before the '@'!"); + + if (email.find(".", email.find("@")) == std::string::npos) + throw InvalidPassport("Passport must have at least one '.' after the '@'!"); + + if (email.find(".", email.find("@")) - email.find("@") < 2) + throw InvalidPassport("Passport must have at least one character between the '@' and the '.'!"); + + if (email[email.size() - 1] == '.') + throw InvalidPassport("Passport must not end with a '.' character!"); + + if (email.size() < 5) + throw InvalidPassport("Passport must contain at least 5 characters!"); + } + + Passport::operator std::string() const + { + return email; + } + + const char *Passport::c_str() const + { + return email.c_str(); + } +} + +std::ostream & operator <<(std::ostream & os, const MSN::Passport & passport) +{ + return os << std::string(passport); +} diff --git a/libs/libmsn/passport.h b/libs/libmsn/passport.h new file mode 100644 index 0000000..c5c3b47 --- /dev/null +++ b/libs/libmsn/passport.h @@ -0,0 +1,74 @@ +#ifndef __msn_passport_h__ +#define __msn_passport_h__ + +/* + * passport.h + * libmsn + * + * Created by Mark Rowe on Thu May 20 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "libmsn_export.h" + +namespace MSN +{ + /** An InvalidPassport exception will be thrown whenever + * a malformed passport is passed to a function that requires + * a valid address. + */ + class InvalidPassport : public std::runtime_error + { +public: + InvalidPassport(std::string err) : std::runtime_error(err) {}; + }; + + /** A Passport represents a passport address. It is used to + * validate these addresses for functions that require it. + * + * @todo Document validation rules. + * @todo Investigate subclassing std::string to reduce code duplication. + */ + class LIBMSN_EXPORT Passport + { +public: + Passport(std::string email_) : email(email_) { validate(); }; + Passport(const char *email_) : email(std::string(email_)) { validate(); }; + Passport() : email("") {}; + + operator std::string() const; + const char *c_str() const; + bool operator ==(const Passport & other) const { return this->email == other.email; }; + + friend bool operator ==(const Passport & p, const std::string & other) { return p.email == other; }; + friend bool operator ==(const std::string & other, const Passport & p) { return p.email == other; }; + friend std::istream& operator >>(std::istream & is, Passport & p) { is >> p.email; p.validate(); return is; } + friend std::ostream& operator <<(std::ostream & os, Passport & p) { os << p.email; p.validate(); return os; } +private: + void validate(); + std::string email; + }; +} + +std::ostream & operator << (std::ostream & os, const MSN::Passport& passport); +#endif diff --git a/libs/libmsn/soap.cpp b/libs/libmsn/soap.cpp new file mode 100644 index 0000000..f9fc4df --- /dev/null +++ b/libs/libmsn/soap.cpp @@ -0,0 +1,2711 @@ +/* + * soap.cpp + * libmsn + * + * Crated by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + +#include "notificationserver.h" +#include "errorcodes.h" +#include "externals.h" +#include "md5.h" +#include "util.h" +#include "soap.h" + +#include +#include +#include +#include + +#include "xmlParser.h" + +namespace MSN { + std::map Soap::actionDomains; + std::map Soap::actionPOSTURLs; + std::map Soap::actionURLs; + + Soap::Soap(NotificationServerConnection & _myNotificationServer) : + Connection(), + notificationServer(_myNotificationServer) + { + fillURLs(); + } + + Soap::Soap(NotificationServerConnection & _myNotificationServer, std::vector _sitesToAuthList) : + Connection(), + notificationServer(_myNotificationServer), + sitesToAuthList(_sitesToAuthList) + { + fillURLs(); + } + + void Soap::requestSoapAction(soapAction action,std::string xml_body, std::string &xml_response) + { + this->action = action; + + std::string full_msg; + + full_msg.append(""); + + full_msg.append(this->request_body); + + std::string http_header("POST "+actionPOSTURLs[action]+" HTTP/1.1\r\n"); + if(action != AUTH) + { + http_header.append("SOAPAction: "+actionURLs[action]+"\r\n"); + } + http_header.append( + "Accept: */*\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Cache-Control: no-cache\r\n" + "Proxy-Connection: Keep-Alive\r\n" + "Connection: Keep-Alive\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Windows Live Messenger 8.1.0178)\r\n" + "Host: "+actionDomains[action]+"\r\n" + "Content-Length: " +toStr(full_msg.length()) +"\r\n\r\n" + ); + + if ((this->sock = this->myNotificationServer()->externalCallbacks.connectToServer(actionDomains[action], 443, &this->connected, true)) == NULL) + { + this->myNotificationServer()->externalCallbacks.showError(this, "Could not connect to server"); + return; + } + this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 0, 1, true); + + if (this->connected) + this->socketConnectionCompleted(); + + std::ostringstream buf_; + buf_ << http_header << full_msg; + if(this->write(buf_) != buf_.str().size()) + return; + + this->myNotificationServer()->addSoapConnection(this); + + } + + void Soap::setMBI(std::string MBI) + { + this->mbi = MBI; + for(unsigned int d=0; d< sitesToAuthList.size(); d++) + { + // we just receive this MBI at connection time + if(sitesToAuthList[d].url=="messengerclear.live.com") + sitesToAuthList[d].URI=MBI; + } + } + + void Soap::fillURLs() + { + sitesToAuth sta; + + actionDomains[AUTH] = "login.live.com"; + actionDomains[GET_LISTS] = "by5.omega.contacts.msn.com"; + actionDomains[GET_ADDRESS_BOOK] = "by5.omega.contacts.msn.com"; + actionDomains[ADD_CONTACT_TO_LIST] = "by5.omega.contacts.msn.com"; + actionDomains[DEL_CONTACT_FROM_LIST] = "by5.omega.contacts.msn.com"; + actionDomains[DEL_CONTACT_FROM_ADDRESSBOOK] = "by5.omega.contacts.msn.com"; + actionDomains[ADD_CONTACT_TO_ADDRESSBOOK] = "by5.omega.contacts.msn.com"; + actionDomains[DISABLE_CONTACT_ON_ADDRESSBOOK] = "by5.omega.contacts.msn.com"; + actionDomains[ENABLE_CONTACT_ON_ADDRESSBOOK] = "by5.omega.contacts.msn.com"; + actionDomains[ADD_GROUP] = "by5.omega.contacts.msn.com"; + actionDomains[DEL_GROUP] = "by5.omega.contacts.msn.com"; + actionDomains[RENAME_GROUP] = "by5.omega.contacts.msn.com"; + actionDomains[BLOCK_CONTACT] = ""; + actionDomains[UNBLOCK_CONTACT] = ""; + actionDomains[ADD_CONTACT_TO_GROUP] = "by5.omega.contacts.msn.com"; + actionDomains[DEL_CONTACT_FROM_GROUP] ="by5.omega.contacts.msn.com"; + actionDomains[UPDATE_GROUP] = ""; + actionDomains[GENERATE_LOCKKEY] = "ows.messenger.msn.com"; + actionDomains[RETRIEVE_OIM_MAIL_DATA] = "rsi.hotmail.com"; + actionDomains[RETRIEVE_OIM] = "rsi.hotmail.com"; + actionDomains[DELETE_OIM] = "rsi.hotmail.com"; + actionDomains[SEND_OIM] = "ows.messenger.msn.com"; + actionDomains[CHANGE_DISPLAYNAME] = "by5.omega.contacts.msn.com"; + + actionPOSTURLs[AUTH] = "/RST.srf"; + actionPOSTURLs[GET_LISTS] = "/abservice/SharingService.asmx"; + actionPOSTURLs[GET_ADDRESS_BOOK] = "/abservice/abservice.asmx"; + actionPOSTURLs[ADD_CONTACT_TO_LIST] = "/abservice/SharingService.asmx"; + actionPOSTURLs[DEL_CONTACT_FROM_LIST] = "/abservice/SharingService.asmx"; + actionPOSTURLs[DEL_CONTACT_FROM_ADDRESSBOOK] = "/abservice/abservice.asmx"; + actionPOSTURLs[ADD_CONTACT_TO_ADDRESSBOOK] = "/abservice/abservice.asmx"; + actionPOSTURLs[DISABLE_CONTACT_ON_ADDRESSBOOK] = "/abservice/abservice.asmx"; + actionPOSTURLs[ENABLE_CONTACT_ON_ADDRESSBOOK] = "/abservice/abservice.asmx"; + actionPOSTURLs[ADD_GROUP] = "/abservice/abservice.asmx"; + actionPOSTURLs[DEL_GROUP] = "/abservice/abservice.asmx"; + actionPOSTURLs[RENAME_GROUP] = "/abservice/abservice.asmx"; + actionPOSTURLs[BLOCK_CONTACT] = ""; + actionPOSTURLs[UNBLOCK_CONTACT] = ""; + actionPOSTURLs[ADD_CONTACT_TO_GROUP] = "/abservice/abservice.asmx"; + actionPOSTURLs[DEL_CONTACT_FROM_GROUP] ="/abservice/abservice.asmx"; + actionPOSTURLs[UPDATE_GROUP] = ""; + actionPOSTURLs[GENERATE_LOCKKEY] = "/OimWS/oim.asmx"; + actionPOSTURLs[RETRIEVE_OIM_MAIL_DATA] = "/rsi/rsi.asmx"; + actionPOSTURLs[RETRIEVE_OIM] = "/rsi/rsi.asmx"; + actionPOSTURLs[DELETE_OIM] = "/rsi/rsi.asmx"; + actionPOSTURLs[SEND_OIM] = "/OimWS/oim.asmx"; + actionPOSTURLs[CHANGE_DISPLAYNAME] = "/abservice/abservice.asmx"; + + actionURLs[AUTH] = '\0'; + actionURLs[GET_LISTS] = "http://www.msn.com/webservices/AddressBook/FindMembership"; + actionURLs[GET_ADDRESS_BOOK] = "http://www.msn.com/webservices/AddressBook/ABFindAll"; + actionURLs[ADD_CONTACT_TO_LIST] = "http://www.msn.com/webservices/AddressBook/AddMember"; + actionURLs[DEL_CONTACT_FROM_LIST] = "http://www.msn.com/webservices/AddressBook/DeleteMember"; + actionURLs[DEL_CONTACT_FROM_ADDRESSBOOK] = "http://www.msn.com/webservices/AddressBook/ABContactDelete"; + actionURLs[ADD_CONTACT_TO_ADDRESSBOOK] = "http://www.msn.com/webservices/AddressBook/ABContactAdd"; + actionURLs[DISABLE_CONTACT_ON_ADDRESSBOOK] = "http://www.msn.com/webservices/AddressBook/ABContactUpdate"; + actionURLs[ENABLE_CONTACT_ON_ADDRESSBOOK] = "http://www.msn.com/webservices/AddressBook/ABContactUpdate"; + actionURLs[DEL_CONTACT_FROM_LIST] = "http://www.msn.com/webservices/AddressBook/DeleteMember"; + actionURLs[ADD_GROUP] = "http://www.msn.com/webservices/AddressBook/ABGroupAdd"; + actionURLs[DEL_GROUP] = "http://www.msn.com/webservices/AddressBook/ABGroupDelete"; + actionURLs[RENAME_GROUP] = "http://www.msn.com/webservices/AddressBook/ABGroupUpdate"; + actionURLs[BLOCK_CONTACT] = ""; + actionURLs[UNBLOCK_CONTACT] = ""; + actionURLs[ADD_CONTACT_TO_GROUP] = "http://www.msn.com/webservices/AddressBook/ABGroupContactAdd"; + actionURLs[DEL_CONTACT_FROM_GROUP] ="http://www.msn.com/webservices/AddressBook/ABGroupContactDelete"; + actionURLs[UPDATE_GROUP] = ""; + actionURLs[GENERATE_LOCKKEY] = "http://messenger.live.com/ws/2006/09/oim/Store2"; + actionURLs[RETRIEVE_OIM_MAIL_DATA] = "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata"; + actionURLs[RETRIEVE_OIM] = "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage"; + actionURLs[DELETE_OIM] = "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages"; + actionURLs[SEND_OIM] = "http://messenger.live.com/ws/2006/09/oim/Store2"; + actionURLs[CHANGE_DISPLAYNAME] = "http://www.msn.com/webservices/AddressBook/ABContactUpdate"; + + sta.url = "http://Passport.NET/tb"; + sitesToAuthList.push_back(sta); + sta.url = "messengerclear.live.com"; + sta.URI = ""; // this is filled later. + sitesToAuthList.push_back(sta); + sta.url = "messenger.msn.com"; + sta.URI = "?id=507"; + sitesToAuthList.push_back(sta); + sta.url = "contacts.msn.com"; + sta.URI = "MBI"; + sitesToAuthList.push_back(sta); + sta.url = "messengersecure.live.com"; + sta.URI = "MBI_SSL"; + sitesToAuthList.push_back(sta); + sta.url = "spaces.live.com"; + sta.URI = "MBI"; + sitesToAuthList.push_back(sta); + sta.url = "storage.msn.com"; + sta.URI = "MBI"; + sitesToAuthList.push_back(sta); + + } + + void Soap::getTickets(std::string Passport, std::string password, std::string policy) + { + this->passport = Passport; + this->password = password; + this->policy = policy; + XMLNode temp; //to general use + XMLNode envelope = XMLNode::createXMLTopNode("Envelope"); + envelope.addAttribute("xmlns", "http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:wsse", "http://schemas.xmlsoap.org/ws/2003/06/secext"); + envelope.addAttribute("xmlns:saml", "urn:oasis:names:tc:SAML:1.0:assertion"); + envelope.addAttribute("xmlns:wsp", "http://schemas.xmlsoap.org/ws/2002/12/policy"); + envelope.addAttribute("xmlns:wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); + envelope.addAttribute("xmlns:wsa", "http://schemas.xmlsoap.org/ws/2004/03/addressing"); + envelope.addAttribute("xmlns:wssc","http://schemas.xmlsoap.org/ws/2004/04/sc"); + envelope.addAttribute("xmlns:wst","http://schemas.xmlsoap.org/ws/2004/04/trust"); + XMLNode header = XMLNode::createXMLTopNode("Header"); + XMLNode authinfo = XMLNode::createXMLTopNode("ps:AuthInfo"); + authinfo.addAttribute("xmlns:ps","http://schemas.microsoft.com/Passport/SoapServices/PPCRL"); + authinfo.addAttribute("Id","PPAuthInfo"); + temp = XMLNode::createXMLTopNode("ps:HostingApp"); + temp.addText("{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}"); + authinfo.addChild(temp); + temp = XMLNode::createXMLTopNode("ps:BinaryVersion"); + temp.addText("4"); + authinfo.addChild(temp); + temp = XMLNode::createXMLTopNode("ps:UIVersion"); + temp.addText("1"); + authinfo.addChild(temp); + temp = XMLNode::createXMLTopNode("ps:Cookies"); + temp.addText(""); + authinfo.addChild(temp); + temp = XMLNode::createXMLTopNode("ps:RequestParams"); + temp.addText("AQAAAAIAAABsYwQAAAAxMDMz"); + authinfo.addChild(temp); + header.addChild(authinfo); + + XMLNode security = XMLNode::createXMLTopNode("wsse:Security"); + XMLNode username = XMLNode::createXMLTopNode("wsse:UsernameToken"); + username.addAttribute("Id","user"); + temp = XMLNode::createXMLTopNode("wsse:Username"); + temp.addText( Passport.c_str() ); + username.addChild(temp); + temp = XMLNode::createXMLTopNode( "wsse:Password" ); + temp.addText( password.c_str() ); + username.addChild(temp); + security.addChild(username); + header.addChild(security); + envelope.addChild( header ); + + // BODY + XMLNode body = XMLNode::createXMLTopNode( "Body" ); + XMLNode multipletokens = XMLNode::createXMLTopNode( "ps:RequestMultipleSecurityTokens" ); + multipletokens.addAttribute("xmlns:ps","http://schemas.microsoft.com/Passport/SoapServices/PPCRL"); + multipletokens.addAttribute("Id","RSTS"); + XMLNode securitytoken; + XMLNode endpr; + XMLNode address; + + // request tokens for each site + for (unsigned int i=0; irequest_body = temp2; + + requestSoapAction(AUTH, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseGetTicketsResponse(std::string response) + { + XMLNode domTree = XMLNode::parseString( response.c_str() ); + if(http_response_code == "301" ) + { + const char *preferredHostName = domTree.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[AUTH] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->getTickets(this->passport, this->password, this->policy); + } + return; + } + + // get the header information from the DOM + XMLNode tokens = domTree.getChildNode("S:Envelope").getChildNode("S:Body").getChildNode("wst:RequestSecurityTokenResponseCollection"); + const char *reason = domTree.getChildNode("S:Envelope").getChildNode("S:Fault").getChildNode("faultcode").getText(); + if(reason) + { + std::string reason1(reason); + if(reason1 == "wsse:FailedAuthentication") + { + this->myNotificationServer()->externalCallbacks.showError(this, "Wrong Password"); + this->myNotificationServer()->removeSoapConnection(this); + this->myNotificationServer()->disconnect(); + return; + } + if(reason1 == "psf:Redirect") + { + const char *newurl = domTree.getChildNode("S:Envelope").getChildNode("S:Fault").getChildNode("psf:redirectUrl").getText(); + Soap *soapConnection = new Soap(notificationServer); + + std::string newurl1(newurl); + std::vector a = splitString(newurl1, "/"); + std::string newdomain = splitString(a[1], "/")[0]; + soapConnection->actionDomains[AUTH] = newdomain; + std::vector postpath = splitString(newurl1, newdomain); + soapConnection->actionPOSTURLs[AUTH] = postpath[1]; + soapConnection->setMBI(mbi); + + soapConnection->getTickets(passport,password,policy); + return; + } + } + int nItems = tokens.nChildNode("wst:RequestSecurityTokenResponse"); + + // fill sitesToAuthList the strucutre with tokens and binary secrets + for(int site=0; site< nItems; site++) + { + XMLNode a = tokens.getChildNode("wst:RequestSecurityTokenResponse",site); + + const char *reason = a.getChildNode("S:Fault").getChildNode("faultcode").getText(); + if(reason) + { + std::string reason1(reason); + if(reason1 == "wsse:FailedAuthentication") + { + const char *reasonString = a.getChildNode("S:Fault").getChildNode("faultstring").getText(); + std::string reasonString1 = (reasonString) ? reasonString : "Authentication Failed"; + this->myNotificationServer()->externalCallbacks.showError(this, reasonString1); + this->myNotificationServer()->removeSoapConnection(this); + this->myNotificationServer()->disconnect(); + return; + } + } + const char *token1 = a.getChildNode("wst:RequestedProofToken").getChildNode("wst:BinarySecret").getText(); + if(token1) + { + std::string c(token1); + sitesToAuthList[site].BinarySecret = c; + } + const char *token2 = a.getChildNode("wst:RequestedSecurityToken").getChildNode("wsse:BinarySecurityToken").getText(); + if(token2) + { + std::string b(token2); + sitesToAuthList[site].BinarySecurityToken = b; + } + } + this->myNotificationServer()->gotTickets(*this, sitesToAuthList); + } + + void Soap::enableContactOnAddressBook(std::string contactId, std::string passport, std::string myDisplayName) + { + this->contactId = contactId; + this->tempPassport = passport; + this->myDisplayName = myDisplayName; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("ContactSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABContactUpdate = XMLNode::createXMLTopNode( "ABContactUpdate" ); + ABContactUpdate.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABContactUpdate.addChild(abId); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + Contact.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode contactId1 = XMLNode::createXMLTopNode( "contactId" ); + contactId1.addText(contactId.c_str()); + XMLNode contactInfo = XMLNode::createXMLTopNode( "contactInfo" ); + XMLNode displayName = XMLNode::createXMLTopNode( "displayName" ); + displayName.addText(passport.c_str()); + XMLNode isMessengerUser = XMLNode::createXMLTopNode( "isMessengerUser" ); + isMessengerUser.addText("true"); + XMLNode MessengerMemberInfo = XMLNode::createXMLTopNode( "MessengerMemberInfo" ); + XMLNode DisplayName = XMLNode::createXMLTopNode( "DisplayName" ); + DisplayName.addText(myDisplayName.c_str()); + MessengerMemberInfo.addChild(DisplayName); + contactInfo.addChild(displayName); + contactInfo.addChild(isMessengerUser); + contactInfo.addChild(MessengerMemberInfo); + XMLNode propertiesChanged = XMLNode::createXMLTopNode( "propertiesChanged" ); + propertiesChanged.addText("DisplayName IsMessengerUser MessengerMemberInfo"); + Contact.addChild(contactId1); + Contact.addChild(contactInfo); + Contact.addChild(propertiesChanged); + contacts.addChild(Contact); + ABContactUpdate.addChild(contacts); + body.addChild(ABContactUpdate); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(ENABLE_CONTACT_ON_ADDRESSBOOK, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseEnableContactOnAddressBookResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[ENABLE_CONTACT_ON_ADDRESSBOOK] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->enableContactOnAddressBook(this->contactId, this->tempPassport, this->myDisplayName); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotEnableContactOnAddressBookConfirmation(*this, true, newVersion, this->contactId, this->tempPassport); + } + else + { + this->myNotificationServer()->gotEnableContactOnAddressBookConfirmation(*this, false, "", this->contactId, this->tempPassport); + } + response1.deleteNodeContent(); + } + + void Soap::delContactFromAddressBook(std::string contactId, std::string passport) + { + this->contactId = contactId; + this->tempPassport = passport; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("Timer"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABContactDelete = XMLNode::createXMLTopNode( "ABContactDelete" ); + ABContactDelete.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABContactDelete.addChild(abId); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + XMLNode contactId1 = XMLNode::createXMLTopNode( "contactId" ); + contactId1.addText(contactId.c_str()); + Contact.addChild(contactId1); + contacts.addChild(Contact); + ABContactDelete.addChild(contacts); + body.addChild(ABContactDelete); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(DEL_CONTACT_FROM_ADDRESSBOOK, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + + } + + void Soap::parseDelContactFromAddressBookResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[DEL_CONTACT_FROM_ADDRESSBOOK] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->delContactFromAddressBook(this->contactId, this->tempPassport); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotDelContactFromAddressBookConfirmation(*this, true, newVersion, this->contactId, this->tempPassport); + } + else + { + this->myNotificationServer()->gotDelContactFromAddressBookConfirmation(*this, false, "", this->contactId, this->tempPassport); + } + response1.deleteNodeContent(); + + } + + void Soap::disableContactFromAddressBook(std::string contactId, std::string passport) + { + this->contactId = contactId; + this->tempPassport = passport; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("Timer"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABContactUpdate = XMLNode::createXMLTopNode( "ABContactUpdate" ); + ABContactUpdate.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABContactUpdate.addChild(abId); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + Contact.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode contactId1 = XMLNode::createXMLTopNode( "contactId" ); + contactId1.addText(contactId.c_str()); + XMLNode contactInfo = XMLNode::createXMLTopNode( "contactInfo" ); + XMLNode displayName = XMLNode::createXMLTopNode( "displayName" ); + XMLNode isMessengerUser = XMLNode::createXMLTopNode( "isMessengerUser" ); + isMessengerUser.addText("false"); + contactInfo.addChild(displayName); + contactInfo.addChild(isMessengerUser); + XMLNode propertiesChanged = XMLNode::createXMLTopNode( "propertiesChanged" ); + propertiesChanged.addText("DisplayName IsMessengerUser"); + Contact.addChild(contactId1); + Contact.addChild(contactInfo); + Contact.addChild(propertiesChanged); + contacts.addChild(Contact); + ABContactUpdate.addChild(contacts); + body.addChild(ABContactUpdate); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(DISABLE_CONTACT_ON_ADDRESSBOOK, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseDisableContactFromAddressBookResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[DISABLE_CONTACT_ON_ADDRESSBOOK] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->disableContactFromAddressBook(this->contactId, this->tempPassport); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotDisableContactOnAddressBookConfirmation(*this, true, newVersion, this->contactId, this->tempPassport); + } + else + { + this->myNotificationServer()->gotDisableContactOnAddressBookConfirmation(*this, false, "", this->contactId, this->tempPassport); + } + response1.deleteNodeContent(); + } + + void Soap::addContactToAddressBook(std::string passport, std::string displayName) + { + this->tempPassport = passport; + this->tempDisplayName = displayName; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("ContactSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABContactAdd = XMLNode::createXMLTopNode( "ABContactAdd" ); + ABContactAdd.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABContactAdd.addChild(abId); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + Contact.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode contactInfo = XMLNode::createXMLTopNode( "contactInfo" ); + XMLNode contactType = XMLNode::createXMLTopNode( "contactType" ); + contactType.addText("Regular"); + XMLNode passportName = XMLNode::createXMLTopNode( "passportName" ); + passportName.addText(passport.c_str()); + XMLNode isMessengerUser = XMLNode::createXMLTopNode( "isMessengerUser" ); + isMessengerUser.addText("true"); + XMLNode MessengerMemberInfo = XMLNode::createXMLTopNode( "MessengerMemberInfo" ); + XMLNode DisplayName = XMLNode::createXMLTopNode( "DisplayName" ); + DisplayName.addText(displayName.c_str()); + MessengerMemberInfo.addChild(DisplayName); + contactInfo.addChild(contactType); + contactInfo.addChild(passportName); + contactInfo.addChild(isMessengerUser); + contactInfo.addChild(MessengerMemberInfo); + Contact.addChild(contactInfo); + contacts.addChild(Contact); + ABContactAdd.addChild(contacts); + XMLNode options = XMLNode::createXMLTopNode( "options" ); + XMLNode EnableAllowListManagement = XMLNode::createXMLTopNode( "EnableAllowListManagement" ); + EnableAllowListManagement.addText("true"); + options.addChild(EnableAllowListManagement); + ABContactAdd.addChild(options); + body.addChild(ABContactAdd); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(ADD_CONTACT_TO_ADDRESSBOOK, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseAddContactToAddressBookResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[ADD_CONTACT_TO_ADDRESSBOOK] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->addContactToAddressBook(this->tempPassport, this->tempDisplayName); + } + return; + } + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + const char *guid = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("ABContactAddResponse").getChildNode("ABContactAddResult").getChildNode("guid").getText(); + if(guid) + { + std::string newVersion(ver); + std::string newGuid(guid); + this->myNotificationServer()->gotAddContactToAddressBookConfirmation(*this, true, newVersion, this->tempPassport, this->tempDisplayName, newGuid); + } + } + else + { + this->myNotificationServer()->gotAddContactToAddressBookConfirmation(*this, false, "", this->tempPassport, this->tempDisplayName, ""); + } + response1.deleteNodeContent(); + } + + void Soap::addContactToGroup(std::string groupId, std::string contactId) + { + this->groupId = groupId; + this->contactId = contactId; + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("GroupSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABGroupContactAdd = XMLNode::createXMLTopNode( "ABGroupContactAdd" ); + ABGroupContactAdd.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABGroupContactAdd.addChild(abId); + XMLNode groupFilter = XMLNode::createXMLTopNode( "groupFilter" ); + XMLNode groupIds = XMLNode::createXMLTopNode( "groupIds" ); + XMLNode guid = XMLNode::createXMLTopNode( "guid" ); + guid.addText(groupId.c_str()); + groupIds.addChild(guid); + groupFilter.addChild(groupIds); + ABGroupContactAdd.addChild(groupFilter); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + XMLNode contactId1 = XMLNode::createXMLTopNode( "contactId" ); + contactId1.addText(contactId.c_str()); + Contact.addChild(contactId1); + contacts.addChild(Contact); + ABGroupContactAdd.addChild(contacts); + body.addChild(ABGroupContactAdd); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(ADD_CONTACT_TO_GROUP, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseAddContactToGroupResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[ADD_CONTACT_TO_GROUP] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->addContactToGroup(this->groupId, this->contactId); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotAddContactToGroupConfirmation(*this, true, newVersion, this->groupId, this->contactId); + } + else + { + this->myNotificationServer()->gotAddContactToGroupConfirmation(*this, false, "", this->groupId, this->contactId); + } + response1.deleteNodeContent(); + } + + void Soap::addGroup(std::string groupName) + { + this->groupName = groupName; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("GroupSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABGroupAdd = XMLNode::createXMLTopNode( "ABGroupAdd" ); + ABGroupAdd.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABGroupAdd.addChild(abId); + XMLNode groupAddOptions = XMLNode::createXMLTopNode( "groupAddOptions" ); + XMLNode fRenameOnMsgrConflict = XMLNode::createXMLTopNode( "fRenameOnMsgrConflict" ); + fRenameOnMsgrConflict.addText("false"); + groupAddOptions.addChild(fRenameOnMsgrConflict); + ABGroupAdd.addChild(groupAddOptions); + XMLNode groupInfo = XMLNode::createXMLTopNode( "groupInfo" ); + XMLNode GroupInfo = XMLNode::createXMLTopNode( "GroupInfo" ); + XMLNode name = XMLNode::createXMLTopNode( "name" ); + name.addText(groupName.c_str()); + GroupInfo.addChild(name); + XMLNode groupType = XMLNode::createXMLTopNode( "groupType" ); + groupType.addText("C8529CE2-6EAD-434d-881F-341E17DB3FF8"); + GroupInfo.addChild(groupType); + XMLNode fMessenger = XMLNode::createXMLTopNode( "fMessenger" ); + fMessenger.addText("false"); + GroupInfo.addChild(fMessenger); + XMLNode annotations = XMLNode::createXMLTopNode( "annotations" ); + XMLNode Annotation = XMLNode::createXMLTopNode( "Annotation" ); + XMLNode Name = XMLNode::createXMLTopNode( "Name" ); + Name.addText("MSN.IM.Display"); + XMLNode Value = XMLNode::createXMLTopNode( "Value" ); + Value.addText("1"); + Annotation.addChild(Name); + Annotation.addChild(Value); + annotations.addChild(Annotation); + GroupInfo.addChild(annotations); + groupInfo.addChild(GroupInfo); + ABGroupAdd.addChild(groupInfo); + body.addChild(ABGroupAdd); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(ADD_GROUP, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseAddGroupResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[ADD_GROUP] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->addGroup(this->groupName); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + const char *guid = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("ABGroupAddResponse").getChildNode("ABGroupAddResult").getChildNode("guid").getText(); + if(guid) + { + std::string newVersion(ver); + std::string newGuid(guid); + this->myNotificationServer()->gotAddGroupConfirmation(*this, true, newVersion, this->groupName, newGuid); + } + } + else + { + this->myNotificationServer()->gotAddGroupConfirmation(*this, false, "", this->groupName, ""); + } + response1.deleteNodeContent(); + } + + void Soap::delGroup(std::string groupId) + { + this->groupId = groupId; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("GroupSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABGroupDelete = XMLNode::createXMLTopNode( "ABGroupDelete" ); + ABGroupDelete.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABGroupDelete.addChild(abId); + XMLNode groupFilter = XMLNode::createXMLTopNode( "groupFilter" ); + XMLNode groupIds = XMLNode::createXMLTopNode( "groupIds" ); + XMLNode guid = XMLNode::createXMLTopNode( "guid" ); + guid.addText(groupId.c_str()); + groupIds.addChild(guid); + groupFilter.addChild(groupIds); + ABGroupDelete.addChild(groupFilter); + body.addChild(ABGroupDelete); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(DEL_GROUP, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseDelGroupResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[DEL_GROUP] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->delGroup(this->groupId); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotDelGroupConfirmation(*this, true, newVersion, this->groupId); + } + else + { + this->myNotificationServer()->gotDelGroupConfirmation(*this, false, "", this->groupId); + } + + response1.deleteNodeContent(); + } + + void Soap::renameGroup(std::string groupId, std::string newGroupName) + { + this->groupId = groupId; + this->groupName = newGroupName; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("GroupSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABGroupUpdate = XMLNode::createXMLTopNode( "ABGroupUpdate" ); + ABGroupUpdate.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABGroupUpdate.addChild(abId); + XMLNode groups = XMLNode::createXMLTopNode( "groups" ); + XMLNode Group = XMLNode::createXMLTopNode( "Group" ); + XMLNode groupId1 = XMLNode::createXMLTopNode( "groupId" ); + groupId1.addText(groupId.c_str()); + XMLNode groupInfo = XMLNode::createXMLTopNode( "groupInfo" ); + XMLNode name = XMLNode::createXMLTopNode( "name" ); + name.addText(newGroupName.c_str()); + groupInfo.addChild(name); + XMLNode propertiesChanged = XMLNode::createXMLTopNode( "propertiesChanged" ); + propertiesChanged.addText("GroupName"); + Group.addChild(groupId1); + Group.addChild(groupInfo); + Group.addChild(propertiesChanged); + groups.addChild(Group); + ABGroupUpdate.addChild(groups); + body.addChild(ABGroupUpdate); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(RENAME_GROUP, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseRenameGroupResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[RENAME_GROUP] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->renameGroup(this->groupId, this->groupName); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotRenameGroupConfirmation(*this, true, newVersion, this->groupName, this->groupId); + } + else + { + this->myNotificationServer()->gotRenameGroupConfirmation(*this, false, "", this->groupName, this->groupId); + } + response1.deleteNodeContent(); + } + + + void Soap::delContactFromGroup(std::string groupId, std::string contactId) + { + this->groupId = groupId; + this->contactId = contactId; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("GroupSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABGroupContactDelete = XMLNode::createXMLTopNode( "ABGroupContactDelete" ); + ABGroupContactDelete.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText( "00000000-0000-0000-0000-000000000000" ); + ABGroupContactDelete.addChild(abId); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + XMLNode contactId1 = XMLNode::createXMLTopNode( "contactId" ); + contactId1.addText(contactId.c_str()); + Contact.addChild(contactId1); + contacts.addChild(Contact); + ABGroupContactDelete.addChild(contacts); + XMLNode groupFilter = XMLNode::createXMLTopNode( "groupFilter" ); + XMLNode groupIds = XMLNode::createXMLTopNode( "groupIds" ); + XMLNode guid = XMLNode::createXMLTopNode( "guid" ); + guid.addText(groupId.c_str()); + groupIds.addChild(guid); + groupFilter.addChild(groupIds); + ABGroupContactDelete.addChild(groupFilter); + body.addChild(ABGroupContactDelete); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(DEL_CONTACT_FROM_GROUP, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseDelContactFromGroupResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[DEL_CONTACT_FROM_GROUP] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->delContactFromGroup(this->groupId, this->contactId); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotDelContactFromGroupConfirmation(*this, true, newVersion, this->groupId, this->contactId); + } + else + { + this->myNotificationServer()->gotDelContactFromGroupConfirmation(*this, false, "", this->groupId, this->contactId); + } + response1.deleteNodeContent(); + } + + void Soap::addContactToList(MSN::Passport passport, MSN::ContactList list) + { + this->tempPassport = passport; + this->tempList = list; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("ContactSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode AddMember = XMLNode::createXMLTopNode( "AddMember" ); + AddMember.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode serviceHandle = XMLNode::createXMLTopNode( "serviceHandle" ); + XMLNode Id = XMLNode::createXMLTopNode( "Id" ); + Id.addText( "0" ); + XMLNode Type = XMLNode::createXMLTopNode( "Type" ); + Type.addText( "Messenger" ); + XMLNode ForeignId = XMLNode::createXMLTopNode( "ForeignId" ); + ForeignId.addText( "" ); + serviceHandle.addChild(Id); + serviceHandle.addChild(Type); + serviceHandle.addChild(ForeignId); + AddMember.addChild(serviceHandle); + XMLNode memberships = XMLNode::createXMLTopNode( "memberships" ); + XMLNode Membership = XMLNode::createXMLTopNode( "Membership" ); + XMLNode MemberRole = XMLNode::createXMLTopNode( "MemberRole" ); + switch(list) + { + case LST_AL: + MemberRole.addText("Allow"); + break; + case LST_BL: + MemberRole.addText("Block"); + break; + case LST_RL: + MemberRole.addText("Reverse"); + break; + default: + return; // TODO - raise an error + } + XMLNode Members = XMLNode::createXMLTopNode( "Members" ); + XMLNode Member = XMLNode::createXMLTopNode( "Member" ); + Member.addAttribute("xsi:type","PassportMember"); + Member.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + XMLNode Type2 = XMLNode::createXMLTopNode("Type"); + Type2.addText("Passport"); + XMLNode State = XMLNode::createXMLTopNode("State"); + State.addText("Accepted"); + XMLNode PassportName = XMLNode::createXMLTopNode("PassportName"); + PassportName.addText(passport.c_str()); + Member.addChild(Type2); + Member.addChild(State); + Member.addChild(PassportName); + Members.addChild(Member); + Membership.addChild(MemberRole); + Membership.addChild(Members); + memberships.addChild(Membership); + AddMember.addChild(memberships); + body.addChild(AddMember); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(ADD_CONTACT_TO_LIST, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseAddContactToListResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[ADD_CONTACT_TO_LIST] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->addContactToList(this->tempPassport, this->tempList); + } + return; + } + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotAddContactToListConfirmation(*this, true, newVersion, this->tempPassport, this->tempList); + } + else + { + this->myNotificationServer()->gotAddContactToListConfirmation(*this, false, "", this->tempPassport, this->tempList); + } + response1.deleteNodeContent(); + } + + void Soap::removeContactFromList(MSN::Passport passport, MSN::ContactList list) + { + this->tempPassport = passport; + this->tempList = list; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false") ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText ("ContactSave"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode DeleteMember = XMLNode::createXMLTopNode( "DeleteMember" ); + DeleteMember.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode serviceHandle = XMLNode::createXMLTopNode( "serviceHandle" ); + XMLNode Id = XMLNode::createXMLTopNode( "Id" ); + Id.addText( "0" ); + XMLNode Type = XMLNode::createXMLTopNode( "Type" ); + Type.addText( "Messenger" ); + XMLNode ForeignId = XMLNode::createXMLTopNode( "ForeignId" ); + Type.addText( "" ); + serviceHandle.addChild(Id); + serviceHandle.addChild(Type); + serviceHandle.addChild(ForeignId); + DeleteMember.addChild(serviceHandle); + XMLNode memberships = XMLNode::createXMLTopNode( "memberships" ); + XMLNode Membership = XMLNode::createXMLTopNode( "Membership" ); + XMLNode MemberRole = XMLNode::createXMLTopNode( "MemberRole" ); + switch(list) + { + case LST_AL: + MemberRole.addText("Allow"); + break; + case LST_BL: + MemberRole.addText("Block"); + break; + case LST_PL: + MemberRole.addText("Pending"); + break; + default: + return; // TODO - raise an error + } + XMLNode Members = XMLNode::createXMLTopNode( "Members" ); + XMLNode Member = XMLNode::createXMLTopNode( "Member" ); + Member.addAttribute("xsi:type","PassportMember"); + Member.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + XMLNode Type2 = XMLNode::createXMLTopNode("Type"); + Type2.addText("Passport"); + XMLNode State = XMLNode::createXMLTopNode("State"); + State.addText("Accepted"); + XMLNode PassportName = XMLNode::createXMLTopNode("PassportName"); + PassportName.addText(passport.c_str()); + Member.addChild(Type2); + Member.addChild(State); + Member.addChild(PassportName); + Members.addChild(Member); + Membership.addChild(MemberRole); + Membership.addChild(Members); + memberships.addChild(Membership); + DeleteMember.addChild(memberships); + body.addChild(DeleteMember); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(DEL_CONTACT_FROM_LIST, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + + } + + void Soap::parseRemoveContactFromListResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[DEL_CONTACT_FROM_LIST] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->removeContactFromList(this->tempPassport, this->tempList); + } + return; + } + + + XMLNode version = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("Version"); + const char *ver = version.getText(); + if(ver) + { + std::string newVersion(ver); + this->myNotificationServer()->gotDelContactFromListConfirmation(*this, true, newVersion, this->tempPassport, this->tempList); + } + else + { + this->myNotificationServer()->gotDelContactFromListConfirmation(*this, false, "", this->tempPassport, this->tempList); + } + response1.deleteNodeContent(); + } + + void Soap::getLists(ListSyncInfo *data) + { + // info is used to to fill the lists with memebers + this->listInfo = data; + + XMLNode envelope = XMLNode::createXMLTopNode( "soap:Envelope" ); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode( "soap:Header" ); + header.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode abapphdr = XMLNode::createXMLTopNode( "ABApplicationHeader" ); + abapphdr.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode( "ApplicationId" ); + appid.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + appid.addText( "996CDE1E-AA53-4477-B943-2BE802EA6166" ); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode( "IsMigration" ); + ismigration.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + ismigration.addText( "false" ) ; + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode( "PartnerScenario" ); + scenario.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + scenario.addText ("Initial" ); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode( "ABAuthHeader" ); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode( "ManagedGroupRequest" ); + ManagedGroupRequest.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + ManagedGroupRequest.addText( "false" ); + XMLNode TicketToken = XMLNode::createXMLTopNode( "TicketToken" ); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + body.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode FindMembership = XMLNode::createXMLTopNode( "FindMembership" ); + FindMembership.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode serviceFilter = XMLNode::createXMLTopNode( "serviceFilter" ); + XMLNode Types = XMLNode::createXMLTopNode( "Types" ); + XMLNode ServiceType = XMLNode::createXMLTopNode( "ServiceType" ); + ServiceType.addText( "Messenger" ); + Types.addChild(ServiceType); + ServiceType = XMLNode::createXMLTopNode( "ServiceType" ); + ServiceType.addText( "Invitation" ); + Types.addChild(ServiceType); + ServiceType = XMLNode::createXMLTopNode( "ServiceType" ); + ServiceType.addText( "SocialNetwork" ); + Types.addChild(ServiceType); + ServiceType = XMLNode::createXMLTopNode( "ServiceType" ); + ServiceType.addText( "Space" ); + Types.addChild(ServiceType); + ServiceType = XMLNode::createXMLTopNode( "ServiceType" ); + ServiceType.addText( "Profile" ); + Types.addChild(ServiceType); + serviceFilter.addChild(Types); + if(data->lastChange != "0") + { + XMLNode View = XMLNode::createXMLTopNode( "View" ); + View.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + View.addText("Full"); + XMLNode deltasOnly = XMLNode::createXMLTopNode( "deltasOnly" ); + deltasOnly.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + deltasOnly.addText("true"); + XMLNode lastChange = XMLNode::createXMLTopNode( "lastChange" ); + lastChange.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + lastChange.addText(data->lastChange.c_str()); + FindMembership.addChild(View); + FindMembership.addChild(deltasOnly); + FindMembership.addChild(lastChange); + } + FindMembership.addChild(serviceFilter); + body.addChild(FindMembership); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(GET_LISTS, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseGetListsResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[GET_LISTS] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->getLists(this->listInfo); + } + return; + } + + + XMLNode Services = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("FindMembershipResponse").getChildNode("FindMembershipResult").getChildNode("Services"); + + int nServices = Services.nChildNode("Service"); + for(int d=0;dcontactList[a]) + listInfo->contactList[a]=new Buddy(a); + + if(MemberRole == "Allow") + { + listInfo->contactList[a]->lists |= MSN::LST_AL; + } + else if(MemberRole == "Block") + { + listInfo->contactList[a]->lists |= MSN::LST_BL; + } + else if(MemberRole == "Reverse") + { + listInfo->contactList[a]->lists |= MSN::LST_RL; + } + else if(MemberRole == "Pending") + { + listInfo->contactList[a]->lists |= MSN::LST_PL; + } + + } + } + } + listInfo->progress |= ListSyncInfo::LST_RL | ListSyncInfo::LST_AL | ListSyncInfo::LST_BL | ListSyncInfo::LST_PL; + response1.deleteNodeContent(); + this->myNotificationServer()->gotLists(*this); + } + + void Soap::getAddressBook(ListSyncInfo *info) + { + this->listInfo = info; + // info is used to to fill the lists with memebers + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode abapphdr = XMLNode::createXMLTopNode("ABApplicationHeader"); + abapphdr.addAttribute("xmlns", "http://www.msn.com/webservices/AddressBook"); + XMLNode appid = XMLNode::createXMLTopNode("ApplicationId"); + appid.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + abapphdr.addChild(appid); + XMLNode ismigration = XMLNode::createXMLTopNode("IsMigration"); + ismigration.addText("false"); + abapphdr.addChild(ismigration); + XMLNode scenario = XMLNode::createXMLTopNode("PartnerScenario"); + scenario.addText("Initial"); + abapphdr.addChild(scenario); + header.addChild(abapphdr); + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns", "http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode("ManagedGroupRequest"); + ManagedGroupRequest.addText("false"); + XMLNode TicketToken = XMLNode::createXMLTopNode("TicketToken"); + // TODO - change this - maybe one day the position can be changed + TicketToken.addText( sitesToAuthList[3].BinarySecurityToken.c_str() ); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + header.addChild(ABAuthHeader); + envelope.addChild(header); + + XMLNode body = XMLNode::createXMLTopNode("soap:Body"); + XMLNode ABFindAll = XMLNode::createXMLTopNode("FindMembership"); + ABFindAll.addAttribute("xmlns", "http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode("abId"); + abId.addText("00000000-0000-0000-0000-000000000000"); + ABFindAll.addChild(abId); + XMLNode abView = XMLNode::createXMLTopNode("abView"); + abView.addText("Full"); + ABFindAll.addChild(abView); + XMLNode deltasOnly = XMLNode::createXMLTopNode("deltasOnly"); + if(info->lastChange != "0") + { + // receive all the list + deltasOnly.addText("true"); + } + else + { + // receive only the changes since lastChange + deltasOnly.addText("false"); + } + ABFindAll.addChild(deltasOnly); + XMLNode lastChange_ = XMLNode::createXMLTopNode("lastChange"); + if(info->lastChange == "0") + { + lastChange_.addText("0001-01-01T00:00:00.0000000-08:00"); + } + else + { + lastChange_.addText( info->lastChange.c_str() ); + } + ABFindAll.addChild(lastChange_); + body.addChild(ABFindAll); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(GET_ADDRESS_BOOK, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseGetAddressBookResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[GET_ADDRESS_BOOK] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->getAddressBook(this->listInfo); + } + return; + } + + XMLNode groups = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("ABFindAllResponse").getChildNode("ABFindAllResult").getChildNode("groups"); + int nItems = groups.nChildNode("Group"); + + for(int i=0;igroups[groupId] = g; + } + XMLNode contacts = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("ABFindAllResponse").getChildNode("ABFindAllResult").getChildNode("contacts"); + + nItems = contacts.nChildNode("Contact"); + + for(int a=0;amyDisplayName = displayName; + continue; + } + // TODO - grab all the fields + // passportName isn't ever present + std::string passportName; + std::string contactId; + const char *passportName_cstr = thisContact.getChildNode("passportName").getText(); + const char *contactId_cstr = contacts.getChildNode("Contact", a).getChildNode("contactId").getText(); + + if(passportName_cstr) + passportName = passportName_cstr; + else + continue; + + if(contactId_cstr) + contactId = contactId_cstr; + else + continue; + + if(passportName.empty()) + continue; + + // + // FIXED 20090823 by José Agustín Terol Sanchis (agus3985@gmail.com): + // + // These attributes couldn't be founded at the server response and + // the method "getText()" will return NULL. This NULL value will + // throw a exception at the std::string constructor that will + // abort the address book parsing and, by the way, the connection + // to the service. + std::string displayName; + const char *displayName_cstr = thisContact.getChildNode("displayName").getText(); + if (displayName_cstr) + { + displayName = displayName_cstr; + } + else + { + // We can use the passport name if we haven't received the + // displayName of the current contact instead of ignore it + displayName = passportName; +// continue; + } + + std::string isMobileIMEnabled; + const char *isMobileIMEnabled_cstr = thisContact.getChildNode("isMobileIMEnabled").getText(); + if (isMobileIMEnabled_cstr) + isMobileIMEnabled = isMobileIMEnabled_cstr; + else + continue; // We could use here a default string such as "false" + + std::string isMessengerUser; + const char *isMessengerUser_cstr = thisContact.getChildNode("isMessengerUser").getText(); + if (isMessengerUser_cstr) + isMessengerUser = isMessengerUser_cstr; + else + continue; // We could use here a default string such as "true" + + std::string contactType1; + const char *contactType1_cstr = thisContact.getChildNode("contactType").getText(); + if (contactType1_cstr) + contactType1 = contactType1_cstr; + else + continue; // We could use here a default string such as "Regular" + + transform (passportName.begin(), passportName.end(), passportName.begin(), tolower); + + // we cant add this contact or we will receive a 205 error + // see http://trac.adiumx.com/ticket/11126 + if( passportName == "messenger@microsoft.com") + continue; + + if(!listInfo->contactList[passportName]) + listInfo->contactList[passportName]=new Buddy(passportName,displayName); + MSN::Buddy *contact = listInfo->contactList[passportName]; + + contact->properties["contactId"] = contactId; + contact->properties["passportName"] = passportName; + contact->properties["displayName"] = displayName; + contact->properties["isMobileIMEnabled"] = isMobileIMEnabled; + contact->properties["isMessengerUser"] = isMessengerUser; + contact->properties["contactType"] = contactType1; + contact->userName = contact->properties["passportName"]; + contact->friendlyName = contact->properties["displayName"]; + contact->lists |= MSN::LST_AB; + + XMLNode groupIds = thisContact.getChildNode("groupIds"); + int nItems2 = groupIds.nChildNode("guid"); + for(int b=0;bgroups[groupID].buddies.push_back(contact); + listInfo->contactList[contact->userName]->groups.push_back(&listInfo->groups[groupID]); + } + } + listInfo->progress |= ListSyncInfo::LST_AB; + std::string lastChange; + const char *clastChange = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("ABFindAllResponse").getChildNode("ABFindAllResult").getChildNode("ab").getChildNode("lastChange").getText(); + if(clastChange) + lastChange = clastChange; + else + lastChange = "0"; + + this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this->myNotificationServer(), lastChange); + + response1.deleteNodeContent(); + this->myNotificationServer()->gotAddressBook(*this); + } + + void Soap::getOIM(std::string id, bool markAsRead) + { + this->oim_id = id; + this->markAsRead = markAsRead; + // index 2 is messenger.msn.com + std::string token = sitesToAuthList[2].BinarySecurityToken; + std::string t = token.substr(token.find("t=") + 2, token.find("&p=")-2); + std::string p = token.substr(token.find("&p=") + 3, -1); + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode PassportCookie = XMLNode::createXMLTopNode("PassportCookie"); + PassportCookie.addAttribute("xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); + XMLNode t1 = XMLNode::createXMLTopNode("t"); + t1.addText(t.c_str()); + XMLNode p1 = XMLNode::createXMLTopNode("p"); + p1.addText(p.c_str()); + PassportCookie.addChild(t1); + PassportCookie.addChild(p1); + header.addChild(PassportCookie); + envelope.addChild(header); + XMLNode body = XMLNode::createXMLTopNode("soap:Body"); + XMLNode GetMessage = XMLNode::createXMLTopNode("GetMessage"); + GetMessage.addAttribute("xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); + XMLNode messageId = XMLNode::createXMLTopNode("messageId"); + messageId.addText(id.c_str()); + XMLNode alsoMarkAsRead = XMLNode::createXMLTopNode("alsoMarkAsRead"); + if(markAsRead) + alsoMarkAsRead.addText("true"); + else + alsoMarkAsRead.addText("false"); + GetMessage.addChild(messageId); + GetMessage.addChild(alsoMarkAsRead); + body.addChild(GetMessage); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(RETRIEVE_OIM, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseGetOIMResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[RETRIEVE_OIM] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->getOIM(this->oim_id, this->markAsRead); + } + return; + } + + + const char* msg = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("GetMessageResponse").getChildNode("GetMessageResult").getText(); + if(msg) + { + std::string message1(msg); + // TODO - extract all the fields. create a struct to carry this data + message1 = message1.substr(message1.find("\r\n\r\n")+4,-1).c_str(); + std::vector a = splitString(message1, "\r\n"); + message1=""; + std::vector::iterator i = a.begin(); + for(;i!=a.end(); i++) + { + message1+=(*i); + } + message1 = b64_decode(message1.c_str()); + response1.deleteNodeContent(); + // yes, new oim + this->myNotificationServer()->gotOIM(*this, true, this->oim_id, message1); + return; + } + // no oim with this id + this->myNotificationServer()->gotOIM(*this, false, this->oim_id, ""); + } + + void Soap::deleteOIM(std::string id) + { + this->oim_id=id; + // index 2 is messenger.msn.com + std::string token = sitesToAuthList[2].BinarySecurityToken; + std::string t = token.substr(token.find("t=") + 2, token.find("&p=")-2); + std::string p = token.substr(token.find("&p=") + 3, -1); + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode PassportCookie = XMLNode::createXMLTopNode("PassportCookie"); + PassportCookie.addAttribute("xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); + XMLNode t1 = XMLNode::createXMLTopNode("t"); + t1.addText(t.c_str()); + XMLNode p1 = XMLNode::createXMLTopNode("p"); + p1.addText(p.c_str()); + PassportCookie.addChild(t1); + PassportCookie.addChild(p1); + header.addChild(PassportCookie); + envelope.addChild(header); + XMLNode body = XMLNode::createXMLTopNode("soap:Body"); + XMLNode DeleteMessages = XMLNode::createXMLTopNode("DeleteMessages"); + DeleteMessages.addAttribute("xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); + XMLNode messageIds = XMLNode::createXMLTopNode("messageIds"); + XMLNode messageId = XMLNode::createXMLTopNode("messageId"); + messageId.addText(id.c_str()); + messageIds.addChild(messageId); + DeleteMessages.addChild(messageIds); + body.addChild(DeleteMessages); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(DELETE_OIM, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseDeleteOIMResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[DELETE_OIM] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->deleteOIM(this->oim_id); + } + return; + } + + if(http_response_code == "200" ) + { + this->myNotificationServer()->gotOIMDeleteConfirmation(*this, oim_id, true); + return; + } + this->myNotificationServer()->gotOIMDeleteConfirmation(*this, oim_id, false); + } + + void Soap::getMailData() + { + // index 2 is messenger.msn.com + std::string token = sitesToAuthList[2].BinarySecurityToken; + std::string t = token.substr(token.find("t=") + 2, token.find("&p=")-2); + std::string p = token.substr(token.find("&p=") + 3, -1); + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode PassportCookie = XMLNode::createXMLTopNode("PassportCookie"); + PassportCookie.addAttribute("xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); + XMLNode t1 = XMLNode::createXMLTopNode("t"); + t1.addText(t.c_str()); + XMLNode p1 = XMLNode::createXMLTopNode("p"); + p1.addText(p.c_str()); + PassportCookie.addChild(t1); + PassportCookie.addChild(p1); + header.addChild(PassportCookie); + envelope.addChild(header); + XMLNode body = XMLNode::createXMLTopNode("soap:Body"); + XMLNode GetMetadata = XMLNode::createXMLTopNode("GetMetadata"); + GetMetadata.addAttribute("xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); + body.addChild(GetMetadata); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(RETRIEVE_OIM_MAIL_DATA, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseGetMailDataResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[RETRIEVE_OIM_MAIL_DATA] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->getMailData(); + } + return; + } + + + // oh my god! xml text as a field of a xml node! I cant believe it! + std::string maildata = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("GetMetadataResponse").getChildNode("MD").createXMLString(false); + if(maildata.empty()) + return; // TODO - raise an error + + response1.deleteNodeContent(); + this->myNotificationServer()->gotSoapMailData(*this, maildata); + } + + + void Soap::generateLockkey(OIM oim) + { + //almost equal to send oim + + this->oim = oim; + // index 4 is messengersecure.live.com + std::string token = sitesToAuthList[4].BinarySecurityToken; + + // encoding to the required form + oim.myFname = "=?utf-8?B?" + b64_encode(oim.myFname.c_str(), oim.myFname.length()) + "?="; + + // TODO - find an email library to handle this part + std::string b64_message = b64_encode(oim.message.c_str(), oim.message.length()); + oim.message=""; + for(unsigned int i=0; ioim.full_msg = oim.full_msg; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode From = XMLNode::createXMLTopNode("From"); + From.addAttribute("memberName", oim.myUsername.c_str()); + From.addAttribute("friendlyName", oim.myFname.c_str()); + From.addAttribute("xml:lang", "pt-BR"); + From.addAttribute("proxy", "MSNMSGR"); + From.addAttribute("xmlns", "http://messenger.msn.com/ws/2004/09/oim/"); + From.addAttribute("msnpVer", "MSNP15"); + From.addAttribute("buildVer", "8.1.0178"); + XMLNode To = XMLNode::createXMLTopNode("To"); + To.addAttribute("memberName", oim.toUsername.c_str()); + To.addAttribute("xmlns", "http://messenger.msn.com/ws/2004/09/oim/"); + XMLNode Ticket = XMLNode::createXMLTopNode("Ticket"); + Ticket.addAttribute("passport", decodeURL(token).c_str()); + Ticket.addAttribute("appid", szClientID); + Ticket.addAttribute("lockkey", ""); + Ticket.addAttribute("xmlns", "http://messenger.msn.com/ws/2004/09/oim/"); + XMLNode Sequence = XMLNode::createXMLTopNode("Sequence"); + Sequence.addAttribute("xmlns", "http://schemas.xmlsoap.org/ws/2003/03/rm"); + XMLNode Identifier = XMLNode::createXMLTopNode( "Identifier" ); + Identifier.addAttribute("xmlns", "http://schemas.xmlsoap.org/ws/2002/07/utility"); + Identifier.addText("http://messenger.msn.com"); + XMLNode MessageNumber = XMLNode::createXMLTopNode("MessageNumber"); + MessageNumber.addText("1"); + Sequence.addChild(Identifier); + Sequence.addChild(MessageNumber); + header.addChild(From); + header.addChild(To); + header.addChild(Ticket); + header.addChild(Sequence); + envelope.addChild(header); + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode MessageType = XMLNode::createXMLTopNode( "MessageType" ); + MessageType.addAttribute("xmlns","http://messenger.msn.com/ws/2004/09/oim/"); + MessageType.addText("text"); + XMLNode Content = XMLNode::createXMLTopNode( "Content" ); + Content.addAttribute("xmlns","http://messenger.msn.com/ws/2004/09/oim/"); + Content.addText(oim.full_msg.c_str()); + body.addChild(MessageType); + body.addChild(Content); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(GENERATE_LOCKKEY, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseGenerateLockkeyResponse(std::string response) + { + OIM oim = this->oim; + // probably we need to generate a new lockkey + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[GENERATE_LOCKKEY] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->generateLockkey(this->oim); + } + return; + } + + XMLNode LockKeyChallenge = response1.getChildNode("soap:Envelope").getChildNode("soap:Body").getChildNode("soap:Fault").getChildNode("detail").getChildNode("LockKeyChallenge"); + + const char * lockkey1 = LockKeyChallenge.getText(); + if(!lockkey1) + { + this->myNotificationServer()->gotOIMLockkey(*this, ""); + return; + } + std::string lockkey = lockkey1; + char b[33]; + memset(&b,0,33); + DoMSNP11Challenge(lockkey.c_str(),b); + std::string new_lockkey(b); + this->lockkey = new_lockkey; + + this->myNotificationServer()->gotOIMLockkey(*this, this->lockkey); + } + + void Soap::sendOIM(OIM oim, std::string lockkey) + { + this->oim = oim; + this->lockkey = lockkey; + // index 4 is messengersecure.live.com + std::string token = sitesToAuthList[4].BinarySecurityToken; + + // encoding to the required form + oim.myFname = "=?utf-8?B?" + b64_encode(oim.myFname.c_str(), oim.myFname.length()) + "?="; + + // TODO - find an email library to handle this part + std::string b64_message = b64_encode(oim.message.c_str(), oim.message.length()); + oim.message=""; + for(unsigned int i=0; ioim.full_msg = oim.full_msg; + + XMLNode envelope = XMLNode::createXMLTopNode("soap:Envelope"); + envelope.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode From = XMLNode::createXMLTopNode("From"); + From.addAttribute("memberName", oim.myUsername.c_str()); + From.addAttribute("friendlyName", oim.myFname.c_str()); + From.addAttribute("xml:lang", "pt-BR"); + From.addAttribute("proxy", "MSNMSGR"); + From.addAttribute("xmlns", "http://messenger.msn.com/ws/2004/09/oim/"); + From.addAttribute("msnpVer", "MSNP15"); + From.addAttribute("buildVer", "8.1.0178"); + XMLNode To = XMLNode::createXMLTopNode("To"); + To.addAttribute("memberName", oim.toUsername.c_str()); + To.addAttribute("xmlns", "http://messenger.msn.com/ws/2004/09/oim/"); + XMLNode Ticket = XMLNode::createXMLTopNode("Ticket"); + Ticket.addAttribute("passport", decodeURL(token).c_str()); + Ticket.addAttribute("appid", szClientID); + Ticket.addAttribute("lockkey", lockkey.c_str()); + Ticket.addAttribute("xmlns", "http://messenger.msn.com/ws/2004/09/oim/"); + XMLNode Sequence = XMLNode::createXMLTopNode("Sequence"); + Sequence.addAttribute("xmlns", "http://schemas.xmlsoap.org/ws/2003/03/rm"); + XMLNode Identifier = XMLNode::createXMLTopNode( "Identifier" ); + Identifier.addAttribute("xmlns", "http://schemas.xmlsoap.org/ws/2002/07/utility"); + Identifier.addText("http://messenger.msn.com"); + XMLNode MessageNumber = XMLNode::createXMLTopNode("MessageNumber"); + MessageNumber.addText("1"); + Sequence.addChild(Identifier); + Sequence.addChild(MessageNumber); + header.addChild(From); + header.addChild(To); + header.addChild(Ticket); + header.addChild(Sequence); + envelope.addChild(header); + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode MessageType = XMLNode::createXMLTopNode( "MessageType" ); + MessageType.addAttribute("xmlns","http://messenger.msn.com/ws/2004/09/oim/"); + MessageType.addText("text"); + XMLNode Content = XMLNode::createXMLTopNode( "Content" ); + Content.addAttribute("xmlns","http://messenger.msn.com/ws/2004/09/oim/"); + Content.addText(oim.full_msg.c_str()); + body.addChild(MessageType); + body.addChild(Content); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(SEND_OIM, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + } + + void Soap::parseSendOIMResponse(std::string response) + { + OIM oim = this->oim; + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[SEND_OIM] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->sendOIM(this->oim, this->lockkey); + } + return; + } + + if(http_response_code == "200" ) + { + this->myNotificationServer()->gotOIMSendConfirmation(*this, oim.id, true); + return; + } + this->myNotificationServer()->gotOIMSendConfirmation(*this, oim.id, false); + } + + void Soap::changeDisplayName(std::string newDisplayName) + { + this->tempDisplayName = newDisplayName; + XMLNode envelope = XMLNode::createXMLTopNode( "soap:Envelope" ); + envelope.addAttribute("xmlns:soap","http://schemas.xmlsoap.org/soap/envelope/"); + envelope.addAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + envelope.addAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema"); + envelope.addAttribute("xmlns:soapenc","http://schemas.xmlsoap.org/soap/encoding/"); + XMLNode header = XMLNode::createXMLTopNode("soap:Header"); + XMLNode ABApplicationHeader = XMLNode::createXMLTopNode( "ABApplicationHeader" ); + ABApplicationHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ApplicationId = XMLNode::createXMLTopNode( "ApplicationId" ); + ApplicationId.addText("996CDE1E-AA53-4477-B943-2BE802EA6166"); + XMLNode IsMigration = XMLNode::createXMLTopNode("IsMigration"); + IsMigration.addText("false"); + XMLNode PartnerScenario = XMLNode::createXMLTopNode("PartnerScenario"); + PartnerScenario.addText("Timer"); + ABApplicationHeader.addChild(ApplicationId); + ABApplicationHeader.addChild(IsMigration); + ABApplicationHeader.addChild(PartnerScenario); + + XMLNode ABAuthHeader = XMLNode::createXMLTopNode("ABAuthHeader"); + ABAuthHeader.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode ManagedGroupRequest = XMLNode::createXMLTopNode("ManagedGroupRequest"); + ManagedGroupRequest.addText("false"); + XMLNode TicketToken = XMLNode::createXMLTopNode("TicketToken"); + TicketToken.addText(sitesToAuthList[3].BinarySecurityToken.c_str()); + ABAuthHeader.addChild(ManagedGroupRequest); + ABAuthHeader.addChild(TicketToken); + + header.addChild(ABApplicationHeader); + header.addChild(ABAuthHeader); + envelope.addChild(header); + XMLNode body = XMLNode::createXMLTopNode( "soap:Body" ); + XMLNode ABContactUpdate = XMLNode::createXMLTopNode( "ABContactUpdate" ); + ABContactUpdate.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode abId = XMLNode::createXMLTopNode( "abId" ); + abId.addText("00000000-0000-0000-0000-000000000000"); + XMLNode contacts = XMLNode::createXMLTopNode( "contacts" ); + XMLNode Contact = XMLNode::createXMLTopNode( "Contact" ); + Contact.addAttribute("xmlns","http://www.msn.com/webservices/AddressBook"); + XMLNode contactInfo = XMLNode::createXMLTopNode( "contactInfo" ); + XMLNode contactType = XMLNode::createXMLTopNode( "contactType" ); + contactType.addText("Me"); + XMLNode displayName = XMLNode::createXMLTopNode( "displayName" ); + displayName.addText(newDisplayName.c_str()); + contactInfo.addChild(contactType); + contactInfo.addChild(displayName); + XMLNode propertiesChanged = XMLNode::createXMLTopNode( "propertiesChanged" ); + propertiesChanged.addText("DisplayName"); + Contact.addChild(contactInfo); + Contact.addChild(propertiesChanged); + contacts.addChild(Contact); + ABContactUpdate.addChild(abId); + ABContactUpdate.addChild(contacts); + body.addChild(ABContactUpdate); + envelope.addChild(body); + + std::string xml_response; + char *xml_request = envelope.createXMLString(false); + std::string temp2 = xml_request; + this->request_body = temp2; + + requestSoapAction(CHANGE_DISPLAYNAME, xml_request, xml_response); + + free(xml_request); + envelope.deleteNodeContent(); + + } + + void Soap::parseChangeDisplayNameResponse(std::string response) + { + XMLNode response1 = XMLNode::parseString(response.c_str()); + if(http_response_code == "301" ) + { + const char *preferredHostName = response1.getChildNode("soap:Envelope").getChildNode("soap:Header").getChildNode("ServiceHeader").getChildNode("PreferredHostName").getText(); + if(preferredHostName) + { + Soap *soapConnection = new Soap(notificationServer, sitesToAuthList); + + std::string newdomain(preferredHostName); + soapConnection->actionDomains[CHANGE_DISPLAYNAME] = newdomain; + soapConnection->setMBI(mbi); + soapConnection->changeDisplayName(this->tempDisplayName); + } + return; + } + + if(http_response_code == "200" ) + { + this->myNotificationServer()->gotChangeDisplayNameConfirmation(*this, this->tempDisplayName, true); + return; + } + this->myNotificationServer()->gotChangeDisplayNameConfirmation(*this, this->tempDisplayName, false); + } + + void Soap::socketConnectionCompleted() + { + Connection::socketConnectionCompleted(); + this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock); + this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 1, 0, true); + } + + void Soap::handleIncomingData() + { + // grab http header + if(this->http_header_response.empty()) + { + if (this->readBuffer.find("\r\n\r\n") == std::string::npos ) + return; + + http_header_response = this->readBuffer.substr(0,this->readBuffer.find("\r\n\r\n") + 4); + Message::Headers headers = Message::Headers(http_header_response); + this->response_length = decimalFromString(headers["Content-Length"]); + + this->http_response_code = splitString(http_header_response.substr(0,http_header_response.find("\r\n"))," ")[1]; + + // drop http from buffer + this->readBuffer = this->readBuffer.substr(this->readBuffer.find("\r\n\r\n") + 4); + } + + if(this->readBuffer.length() < this->response_length) + return; // wait for the full response + + this->response_body = this->readBuffer; + + this->readBuffer.clear(); + + this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock); + + switch(this->action) + { + case AUTH: + parseGetTicketsResponse(this->response_body); + break; + case GET_LISTS: + parseGetListsResponse(this->response_body); + break; + case GET_ADDRESS_BOOK: + parseGetAddressBookResponse(this->response_body); + break; + case SEND_OIM: + parseSendOIMResponse(this->response_body); + break; + case GENERATE_LOCKKEY: + parseGenerateLockkeyResponse(this->response_body); + break; + case RETRIEVE_OIM: + parseGetOIMResponse(this->response_body); + break; + case DELETE_OIM: + parseDeleteOIMResponse(this->response_body); + break; + case RETRIEVE_OIM_MAIL_DATA: + parseGetMailDataResponse(this->response_body); + break; + case ADD_CONTACT_TO_LIST: + parseAddContactToListResponse(this->response_body); + break; + case DEL_CONTACT_FROM_LIST: + parseRemoveContactFromListResponse(this->response_body); + break; + case CHANGE_DISPLAYNAME: + parseChangeDisplayNameResponse(this->response_body); + break; + case ADD_CONTACT_TO_GROUP: + parseAddContactToGroupResponse(this->response_body); + break; + case DEL_CONTACT_FROM_GROUP: + parseDelContactFromGroupResponse(this->response_body); + break; + case ADD_GROUP: + parseAddGroupResponse(this->response_body); + break; + case DEL_GROUP: + parseDelGroupResponse(this->response_body); + break; + case RENAME_GROUP: + parseRenameGroupResponse(this->response_body); + break; + case DISABLE_CONTACT_ON_ADDRESSBOOK: + parseDisableContactFromAddressBookResponse(this->response_body); + break; + case ENABLE_CONTACT_ON_ADDRESSBOOK: + parseEnableContactOnAddressBookResponse(this->response_body); + break; + case ADD_CONTACT_TO_ADDRESSBOOK: + parseAddContactToAddressBookResponse(this->response_body); + break; + case DEL_CONTACT_FROM_ADDRESSBOOK: + parseDelContactFromAddressBookResponse(this->response_body); + break; + + } + delete this; + } + + void Soap::disconnect() + { + } + + Soap::~Soap() + { + Connection::disconnect(); + if(this->myNotificationServer()->connectionState() != MSN::NotificationServerConnection::NS_DISCONNECTED) + this->myNotificationServer()->removeSoapConnection(this); + } +} diff --git a/libs/libmsn/soap.h b/libs/libmsn/soap.h new file mode 100644 index 0000000..797212c --- /dev/null +++ b/libs/libmsn/soap.h @@ -0,0 +1,221 @@ +#ifndef __msn_soap_h__ +#define __msn_soap_h__ +/* + * soap.h + * libmsn + * + * Crated by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "connection.h" +#include "authdata.h" +#include "errorcodes.h" +#include "buddy.h" +#include "passport.h" +#include +#include "externals.h" + +#include +#include +#include + +#include "libmsn_export.h" + +namespace MSN +{ + class NotificationServerConnection; + + /** Represents a Soap Connection made by NotificationServerConnection + */ + class LIBMSN_EXPORT Soap : public Connection + { +private: + NotificationServerConnection & notificationServer; + std::string request_body; + std::string http_header_response; + std::string response_body; + int action; + unsigned int response_length; + ListSyncInfo *listInfo; + std::string oim_id; + std::string http_response_code; + std::string tempDisplayName; + std::string tempPassport; + std::string contactId; + std::string groupId; + std::string groupName; + MSN::ContactList tempList; + std::string passport; + std::string password; + std::string policy; + std::string mbi; + std::string myDisplayName; + bool markAsRead; + +public: + struct sitesToAuthTAG + { + std::string url; + std::string URI; + std::string BinarySecurityToken; + std::string BinarySecret; + }; + + typedef sitesToAuthTAG sitesToAuth; + + struct OIMTAG + { + int id; + std::string toUsername; + std::string myUsername; + std::string myFname; + std::string message; + std::string full_msg; // includes b64 body + }; + + typedef OIMTAG OIM; + + MSN::Soap::OIM oim; + Soap(NotificationServerConnection & _myNotificationServer); + Soap(NotificationServerConnection & _myNotificationServer, std::vector sitesToAuthList); + ~Soap(); + std::string body; + std::string ticket_token; + std::string lockkey; + + enum memberRoles { // the lists + ALLOW_LIST = 2, + BLOCK_LIST = 4, + REVERSE_LIST = 8, + PENDING_LIST = 16 + }; + + typedef enum { + AUTH, + GET_LISTS, + GET_ADDRESS_BOOK, + ADD_CONTACT_TO_LIST, + DEL_CONTACT_FROM_LIST, + ADD_CONTACT_TO_ADDRESSBOOK, + DEL_CONTACT_FROM_ADDRESSBOOK, + ENABLE_CONTACT_ON_ADDRESSBOOK, + DISABLE_CONTACT_ON_ADDRESSBOOK, + ADD_GROUP, + DEL_GROUP, + RENAME_GROUP, + BLOCK_CONTACT, + UNBLOCK_CONTACT, + ADD_CONTACT_TO_GROUP, + DEL_CONTACT_FROM_GROUP, + UPDATE_GROUP, + GENERATE_LOCKKEY, + RETRIEVE_OIM_MAIL_DATA, + RETRIEVE_OIM, + DELETE_OIM, + SEND_OIM, + CHANGE_DISPLAYNAME + } soapAction; + + static std::map actionDomains; + static std::map actionPOSTURLs; + static std::map actionURLs; + std::vector sitesToAuthList; + + void fillURLs(); + void setMBI(std::string MBI); + void requestSoapAction(soapAction action, std::string xml_body, std::string & xml_response); + + void getTickets(std::string Passport, + std::string password, + std::string policy); + void parseGetTicketsResponse(std::string response); + + void getLists(ListSyncInfo* data); + void parseGetListsResponse(std::string response); + + void getAddressBook(ListSyncInfo *info); + void parseGetAddressBookResponse(std::string response); + + void getOIM(std::string id, bool markAsRead); + void parseGetOIMResponse(std::string response); + + void deleteOIM(std::string id); + void parseDeleteOIMResponse(std::string response); + + void getMailData(); + void parseGetMailDataResponse(std::string response); + + void sendOIM(OIM oim, std::string lockkey); + void parseSendOIMResponse(std::string response); + + void addContactToList(MSN::Passport passport, MSN::ContactList list); + void parseAddContactToListResponse(std::string response); + + void addContactToAddressBook(std::string passport, std::string displayName); + void parseAddContactToAddressBookResponse(std::string response); + + void delContactFromAddressBook(std::string contactId, std::string passport); + void parseDelContactFromAddressBookResponse(std::string response); + + void enableContactOnAddressBook(std::string contactId, + std::string passport, + std::string myDisplayName); + + void parseEnableContactOnAddressBookResponse(std::string response); + + void disableContactFromAddressBook(std::string contactId, std::string passport); + void parseDisableContactFromAddressBookResponse(std::string response); + + void addContactToGroup(std::string groupId, std::string contactId); + void parseAddContactToGroupResponse(std::string response); + + void delContactFromGroup(std::string groupId, std::string contactId); + void parseDelContactFromGroupResponse(std::string response); + + void removeContactFromList(MSN::Passport passport, MSN::ContactList list); + void parseRemoveContactFromListResponse(std::string response); + + void addGroup(std::string groupName); + void parseAddGroupResponse(std::string response); + + void delGroup(std::string groupId); + void parseDelGroupResponse(std::string response); + + void renameGroup(std::string groupId, std::string newGroupName); + void parseRenameGroupResponse(std::string response); + + void generateLockkey(OIM oim); + void parseGenerateLockkeyResponse(std::string response); + + void changeDisplayName(std::string newDisplayName); + void parseChangeDisplayNameResponse(std::string); + + virtual void dispatchCommand(std::vector &) {}; + virtual void connect(const std::string &, unsigned int) {}; + virtual void disconnect(); + virtual void sendMessage(const Message *) {}; + virtual void sendMessage(const std::string &) {}; + virtual void socketConnectionCompleted(); + virtual void handleIncomingData(); + virtual NotificationServerConnection *myNotificationServer() { return ¬ificationServer; }; + + }; + +} +#endif diff --git a/libs/libmsn/sstream_fix.h b/libs/libmsn/sstream_fix.h new file mode 100644 index 0000000..e9a263e --- /dev/null +++ b/libs/libmsn/sstream_fix.h @@ -0,0 +1,74 @@ +/* + * This file is one big nasty kludge because the older + * libstdc++ libraries (before v3) have the old strstream + * but I want to use the new, and much improved sstream. + * - Barnaby + * + * Copyright (C) 2001 Barnaby Gray + * + * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_SSTREAM +# include +#elif HAVE_STRSTREAM +# define USE_STRSTREAM_WRAPPERS +#else +# error "No sstream/strstream implementation" +#endif + +#if defined(USE_STRSTREAM_WRAPPERS) && !defined(SSTREAM_FIX_H) +#define SSTREAM_FIX_H + +#include +#include + +#include "libmsn_export.h" + +namespace std +{ + + /* + * Only limited functionality from ostringstream + * is implemented + */ + class ostringstream : public ostrstream { + public: + string str() { + char *cstr = ostrstream::str(); + freeze(0); + if (cstr == 0) return string(); + return string(cstr,pcount()); + } + }; + + /* + * Only limited functionality from istringstream + * is implemented + */ + class istringstream : public istrstream { + public: + istringstream(const string& str) + : istrstream(str.c_str()) { } + }; + +} + +#endif diff --git a/libs/libmsn/switchboardserver.cpp b/libs/libmsn/switchboardserver.cpp new file mode 100644 index 0000000..d79f94c --- /dev/null +++ b/libs/libmsn/switchboardserver.cpp @@ -0,0 +1,749 @@ +/* + * switchboardserver.cpp + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "switchboardserver.h" +#include "notificationserver.h" +#include "errorcodes.h" +#include "externals.h" +#include "util.h" + +#include +#include +#include +#include +#include +#include + +namespace MSN +{ + std::map &)> SwitchboardServerConnection::commandHandlers; + std::map &, std::string, std::string)> SwitchboardServerConnection::messageHandlers; + + SwitchboardServerConnection::SwitchboardServerConnection(AuthData & auth_, NotificationServerConnection & n) + : Connection(), auth(auth_), _connectionState(SB_DISCONNECTED), notificationServer(n) + { + registerCommandHandlers(); + registerMessageHandlers(); + } + + SwitchboardServerConnection::~SwitchboardServerConnection() + { + if (this->connectionState() != SB_DISCONNECTED) + this->disconnect(); + } + + Connection *SwitchboardServerConnection::connectionWithSocket(void * sock) + { + if (this->sock == sock) + return this; + +/* std::list & list2 = _fileTransferConnectionsP2P; + std::list::iterator j = list2.begin(); + + for (; j != list2.end(); j++) + { + if ((*j)->sock == fd) + return *i; + } + */ + return NULL; + } + + void SwitchboardServerConnection::sendEmoticon(std::string alias, std::string file) + { + this->assertConnectionStateIsAtLeast(SB_READY); + + myNotificationServer()->msnobj.addMSNObject(file,2); + std::string msnobject; + myNotificationServer()->msnobj.getMSNObjectXML(file, 2, msnobject); + + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: text/x-mms-emoticon\r\n\r\n"; + msg_ << alias << "\t" << msnobject << "\t"; + size_t msg_length = msg_.str().size(); + + buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str(); + write(buf_); + } + + void SwitchboardServerConnection::sendFile(MSN::fileTransferInvite ft) + { + this->assertConnectionStateIsAtLeast(SB_READY); + p2p.sendFile(*this,ft); + } + + void SwitchboardServerConnection::addFileTransferConnectionP2P(FileTransferConnectionP2P *c) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + _fileTransferConnectionsP2P.push_back(c); + } + + void SwitchboardServerConnection::removeFileTransferConnectionP2P(FileTransferConnectionP2P *c) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + _fileTransferConnectionsP2P.remove(c); + } + + template + class hasCookieOf + { + const std::string & cookie; +public: + hasCookieOf(const std::string & __i) : cookie(__i) {}; + bool operator()(const _Tp &__x) { return __x->cookie == cookie; } + }; + + void SwitchboardServerConnection::addCallback(SwitchboardServerCallback callback, + int trid, void *data) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTING); + this->callbacks[trid] = std::make_pair(callback, data); + } + + void SwitchboardServerConnection::removeCallback(int trid) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTING); + this->callbacks.erase(trid); + } + + void SwitchboardServerConnection::addP2PCallback(SwitchboardServerCallback2 callback, + int trid, unsigned int sessionID) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTING); + this->callbacks2[trid] = std::make_pair(callback, sessionID); + } + + void SwitchboardServerConnection::removeP2PCallback(int trid) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTING); + this->callbacks2.erase(trid); + } + + void SwitchboardServerConnection::registerMessageHandlers() + { + if (messageHandlers.size() == 0) + { + messageHandlers["text/plain"] = &SwitchboardServerConnection::message_plain; + messageHandlers["text/x-msmsgsinvite"] = &SwitchboardServerConnection::message_invitation; + messageHandlers["text/x-msmsgscontrol"] = &SwitchboardServerConnection::message_typing_user; + messageHandlers["application/x-msnmsgrp2p"] = &SwitchboardServerConnection::message_p2p; + messageHandlers["text/x-msnmsgr-datacast"] = &SwitchboardServerConnection::message_datacast; + messageHandlers["text/x-mms-emoticon"] = &SwitchboardServerConnection::message_emoticon; + messageHandlers["text/x-mms-animemoticon"] = &SwitchboardServerConnection::message_emoticon; + messageHandlers["image/gif"] = &SwitchboardServerConnection::message_ink; + messageHandlers["application/x-ms-ink"] = &SwitchboardServerConnection::message_ink; + } + } + + void SwitchboardServerConnection::registerCommandHandlers() + { + if (commandHandlers.size() == 0) + { + commandHandlers["BYE"] = &SwitchboardServerConnection::handle_BYE; + commandHandlers["JOI"] = &SwitchboardServerConnection::handle_JOI; + commandHandlers["NAK"] = &SwitchboardServerConnection::handle_NAK; + commandHandlers["MSG"] = &SwitchboardServerConnection::handle_MSG; + } + } + + void SwitchboardServerConnection::message_plain(std::vector & args, std::string mime, std::string body) + { + Message msg = Message(body, mime); + + this->myNotificationServer()->externalCallbacks.gotInstantMessage(this,args[1], decodeURL(args[2]), &msg); + } + + void SwitchboardServerConnection::message_emoticon(std::vector & args, std::string mime, std::string body) + { + std::vector emoticons = splitString(body,"\t"); + for(unsigned int i=0; i < emoticons.size(); ) + { + // avoid errors with clients which do not respect the protocol + if(i+2 > emoticons.size()) return; + this->myNotificationServer()->externalCallbacks.gotEmoticonNotification(this,args[1], emoticons[i] , emoticons[i+1]); + i+=2; // move to the next one + } + } + + void SwitchboardServerConnection::message_invitation(std::vector & args, std::string mime, std::string body) + { + } + + void SwitchboardServerConnection::message_typing_user(std::vector & args, std::string mime, std::string body) + { + this->myNotificationServer()->externalCallbacks.buddyTyping(this, args[1], decodeURL(args[2])); + } + + void SwitchboardServerConnection::message_ink(std::vector & args, std::string mime, std::string body) + { + std::string image = body.substr(body.find("base64:")+7); + this->myNotificationServer()->externalCallbacks.gotInk(this, args[1], image); + } + + void SwitchboardServerConnection::message_p2p(std::vector & args, std::string mime, std::string body) + { + p2p.handleP2Pmessage(*this,args,mime,body); + } + void SwitchboardServerConnection::message_datacast(std::vector & args, std::string mime, std::string body) + { + Message::Headers headers = Message::Headers(body); + int id = decimalFromString(headers["ID"]); + switch(id) + { + case 1: + this->myNotificationServer()->externalCallbacks.gotNudge(this, args[1]); + break; + case 2: + this->myNotificationServer()->externalCallbacks.gotWinkNotification(this, args[1], headers["Data"]); + break; + case 3: + this->myNotificationServer()->externalCallbacks.gotVoiceClipNotification(this, args[1], headers["Data"]); + break; + case 4: + this->myNotificationServer()->externalCallbacks.gotActionMessage(this, args[1], headers["Data"]); + break; + } + } + + void SwitchboardServerConnection::dispatchCommand(std::vector & args) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + std::map &)>::iterator i = commandHandlers.find(args[0]); + if (i != commandHandlers.end()) + (this->*commandHandlers[args[0]])(args); + } + + int SwitchboardServerConnection::sendMessage(const Message *msg) + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::string s = msg->asString(); + + std::ostringstream buf_; + buf_ << "MSG " << this->trID << " A " << (int) s.size() << "\r\n" << s; + this->write(buf_); + this->addCallback(&SwitchboardServerConnection::callback_messageACK, this->trID, NULL); + return this->trID++; + } + + int SwitchboardServerConnection::sendMessage(const std::string & body) + { + Message msg(body, "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n"); + return this->sendMessage(&msg); + } + + void SwitchboardServerConnection::sendKeepAlive() + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::string s("MIME-Version: 1.0\r\nContent-Type: text/x-keepalive\r\n\r\n"); + std::ostringstream buf_; + buf_ << "MSG " << this->trID++ << " U " << (int) s.size() << "\r\n" << s; + this->write(buf_); + } + + void SwitchboardServerConnection::handle_BYE(std::vector & args) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + std::list & list = this->users; + std::list::iterator i; + + this->myNotificationServer()->externalCallbacks.buddyLeftConversation(this, args[1]); + + for (i = list.begin(); i != list.end(); i++) + { + if (*i == args[1]) + { + list.remove(*i); + break; + } + } + + if (this->users.empty() || (args.size() > 3 && args[3] == "1")) + { + this->disconnect(); + } + } + + void SwitchboardServerConnection::handle_JOI(std::vector & args) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + if (args[1] == this->auth.username) + return; + + if (this->auth.sessionID.empty() && this->connectionState() == SB_WAITING_FOR_USERS) + this->setConnectionState(SB_READY); + + this->users.push_back(args[1]); + this->myNotificationServer()->externalCallbacks.buddyJoinedConversation(this, args[1], decodeURL(args[2]), 0); + } + + void SwitchboardServerConnection::handle_NAK(std::vector & args) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + this->myNotificationServer()->externalCallbacks.failedSendingMessage(this); + } + + void SwitchboardServerConnection::handle_MSG(std::vector & args) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + int msglen; + std::string msg; + std::string mime; + std::string body; + size_t tmp; + + msglen = decimalFromString(args[3]); + msg = this->readBuffer.substr(0, msglen); + this->readBuffer = this->readBuffer.substr(msglen); + + body = msg.substr(msg.find("\r\n\r\n") + 4); + mime = msg.substr(0, msg.size() - body.size()); + + std::string contentType; + Message::Headers headers = Message::Headers(mime); + + std::string chunks = headers["Chunks"]; + if(!chunks.empty()) + { + // First packet of MultiPacket Data; + MultiPacketSession session; + std::string messageID = headers["Message-ID"]; + session.chunks = decimalFromString(chunks); + session.mime = mime; + session.receivedChunks = 1; + session.body += body; + if(session.chunks != 1) + { + MultiPacketSessions[messageID] = session; + return; + } + + } + else + { + std::string chunk = headers["Chunk"]; + if(!chunk.empty()) + { + // part of multipacket data + std::string messageID = headers["Message-ID"]; + MultiPacketSession session = MultiPacketSessions[messageID]; + session.body += body; + session.receivedChunks++; + if(session.chunks != session.receivedChunks) + { + MultiPacketSessions[messageID] = session; + return; + } + else + { + MultiPacketSessions.erase(messageID); + body = session.body; + mime = session.mime; + headers = Message::Headers(mime); + } + } + } + contentType = headers["Content-Type"]; + + if ((tmp = contentType.find("; charset")) != std::string::npos) + contentType = contentType.substr(0, tmp); + + std::map &, std::string, std::string)>::iterator i = messageHandlers.find(contentType); + if (i != messageHandlers.end()) + (this->*(messageHandlers[contentType]))(args, mime, body); + } + + void SwitchboardServerConnection::sendAction(std::string action) + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n"; + msg_ << "ID: 4\r\n"; + msg_ << "Data: " << action << "\r\n" ; + size_t msg_length = msg_.str().size(); + + buf_ << "MSG " << this->trID++ << " U " << (int) msg_length << "\r\n" << msg_.str(); + + write(buf_); + } + + void SwitchboardServerConnection::sendVoiceClip(std::string msnobject) + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n"; + msg_ << "ID: 3\r\n"; + msg_ << "Data: " << msnobject << "\r\n\r\n"; + size_t msg_length = msg_.str().size(); + + buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str(); + + write(buf_); + } + + void SwitchboardServerConnection::sendWink(std::string msnobject) + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n"; + msg_ << "ID: 2\r\n"; + msg_ << "Data: " << msnobject << "\r\n\r\n"; + size_t msg_length = msg_.str().size(); + + buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str(); + + write(buf_); + } + + void SwitchboardServerConnection::sendNudge() + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n"; + msg_ << "ID: 1\r\n"; + size_t msg_length = msg_.str().size(); + + buf_ << "MSG " << this->trID++ << " U " << (int) msg_length << "\r\n" << msg_.str(); + + write(buf_); + } + + void SwitchboardServerConnection::sendInk(std::string image) + { + this->assertConnectionStateIsAtLeast(SB_READY); + + if(users.size() == 1) + { + p2p.sendInk(*this, image); +// return; + } + + std::string body("base64:"+image); + bool one_packet = false; + + if(body.size() <= 1202) // if we need more than 1 packet, then use multipacket + one_packet = true ; + + if(one_packet) + { + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: image/gif\r\n\r\n"; + msg_ << body; + + size_t msg_length = msg_.str().size(); + buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str(); + write(buf_); + return; + } + else + { + std::istringstream body_stream(body); + std::string messageid = new_branch(); + std::vector chunks; + // spliting the message + while(!body_stream.eof()) + { + char *part = new char[1203]; + memset(part,0,1203); + body_stream.read((char*)part, 1202); + std::string part1(part); + chunks.push_back(part1); + delete [] part; + } + + // sending the first one + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: image/gif\r\n"; + msg_ << "Message-ID: " << messageid << "\r\n"; + msg_ << "Chunks: " << chunks.size() << "\r\n\r\n"; + msg_ << chunks.front(); + + size_t msg_length = msg_.str().size(); + buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str(); + write(buf_); + + std::vector::iterator i = chunks.begin(); + + for(int num=0; i!=chunks.end(); i++, num++) + { + if(i == chunks.begin()) + continue; + + std::ostringstream buf2_, msg2_; + msg2_ << "Message-ID: " << messageid << "\r\n"; + msg2_ << "Chunk: " << num << "\r\n\r\n"; + msg2_ << (*i); + + size_t msg_length2 = msg2_.str().size(); + buf2_ << "MSG " << this->trID++ << " N " << (int) msg_length2 << "\r\n" << msg2_.str(); + write(buf2_); + } + } + } + + void SwitchboardServerConnection::sendTypingNotification() + { + this->assertConnectionStateIsAtLeast(SB_READY); + std::ostringstream buf_, msg_; + msg_ << "MIME-Version: 1.0\r\n"; + msg_ << "Content-Type: text/x-msmsgscontrol\r\n"; + msg_ << "TypingUser: " << this->auth.username << "\r\n"; + msg_ << "\r\n"; + msg_ << "\r\n"; + size_t msg_length = msg_.str().size(); + + buf_ << "MSG " << this->trID++ << " U " << (int) msg_length << "\r\n" << msg_.str(); + + write(buf_); + } + + void SwitchboardServerConnection::inviteUser(Passport userName) + { + this->assertConnectionStateIsAtLeast(SB_WAITING_FOR_USERS); + std::ostringstream buf_; + buf_ << "CAL " << this->trID++ << " " << userName << "\r\n"; + write(buf_); + } + + void SwitchboardServerConnection::connect(const std::string & hostname, unsigned int port) + { + this->assertConnectionStateIs(SB_DISCONNECTED); + + if ((this->sock = this->myNotificationServer()->externalCallbacks.connectToServer(hostname, port, &this->connected)) == NULL) + { + this->myNotificationServer()->externalCallbacks.showError(this, "Could not connect to switchboard server"); + return; + } + + this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 0, 1, false); + this->setConnectionState(SB_CONNECTING); + + if (this->connected) + this->socketConnectionCompleted(); + + std::ostringstream buf_; + if (this->auth.sessionID.empty()) + { + buf_ << "USR " << this->trID << " " << this->auth.username << " " << this->auth.cookie << "\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + this->addCallback(&SwitchboardServerConnection::callback_InviteUsers, this->trID, NULL); + } + else + { + buf_ << "ANS " << this->trID << " " << this->auth.username << " " << this->auth.cookie << " " << this->auth.sessionID << "\r\n"; + if (this->write(buf_) != buf_.str().size()) + return; + this->myNotificationServer()->externalCallbacks.gotNewConnection(this); + this->addCallback(&SwitchboardServerConnection::callback_AnsweredCall, this->trID, NULL); + } + + this->trID++; + } + + void SwitchboardServerConnection::disconnect() + { + if (this->connectionState() == SB_DISCONNECTED) + return; + + notificationServer.removeSwitchboardConnection(this); + this->myNotificationServer()->externalCallbacks.closingConnection(this); + + std::list list2 = _fileTransferConnectionsP2P; + std::list::iterator j = list2.begin(); + for (; j != list2.end(); j++) + { + removeFileTransferConnectionP2P(*j); + } + + this->callbacks.clear(); + Connection::disconnect(); + this->setConnectionState(SB_DISCONNECTED); + } + + void SwitchboardServerConnection::socketConnectionCompleted() + { + this->assertConnectionStateIs(SB_CONNECTING); + Connection::socketConnectionCompleted(); + this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock); + this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 1, 0, false); + this->setConnectionState(SB_WAITING_FOR_USERS); + } + + void SwitchboardServerConnection::handleIncomingData() + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + while (this->isWholeLineAvailable()) + { + std::vector args = this->getLine(); + if (args[0] == "MSG" || args[0] == "NOT") + { + int dataLength = decimalFromString(args[3]); + if (this->readBuffer.find("\r\n") + 2 + dataLength > this->readBuffer.size()) + return; + } + this->readBuffer = this->readBuffer.substr(this->readBuffer.find("\r\n") + 2); + + int trid = 0; + + if (args.size() > 1) + { + try + { + trid = decimalFromString(args[1]); + } + catch (...) + { + } + } + + if (!this->callbacks.empty() && trid > 0) + { + if (this->callbacks.find(trid) != this->callbacks.end()) + { + (this->*(this->callbacks[trid].first))(args, trid, this->callbacks[trid].second); + continue; + } + } + if (!this->callbacks2.empty() && trid > 0) + { + if (this->callbacks2.find(trid) != this->callbacks2.end()) + { + (this->*(this->callbacks2[trid].first))(args, trid, this->callbacks2[trid].second); + continue; + } + } + + if (isdigit(args[0][0])) + this->showError(decimalFromString(args[0])); + else + this->dispatchCommand(args); + } + } + + void SwitchboardServerConnection::callback_messageACK(std::vector & args, int trid, void *) + { + this->removeCallback(trid); + this->myNotificationServer()->externalCallbacks.gotMessageSentACK(this, trid); + } + + void SwitchboardServerConnection::callback_InviteUsers(std::vector & args, int trid, void *) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + this->removeCallback(trid); + + if (args.size() < 3) + { + this->showError(decimalFromString(args[0])); + this->disconnect(); + return; + } + + if(args[2] != "OK") + { + this->showError(decimalFromString(args[0])); + this->disconnect(); + return; + } + + this->myNotificationServer()->externalCallbacks.gotSwitchboard(this, this->auth.tag); + this->myNotificationServer()->externalCallbacks.gotNewConnection(this); + } + + void SwitchboardServerConnection::callback_continueTransfer(std::vector & args, int trid, unsigned int sessionID) + { + this->removeP2PCallback(trid); + p2p.handle_MSGACKReceived(*this,sessionID, args[1]); + } + + void SwitchboardServerConnection::fileTransferResponse(unsigned int sessionID, std::string filename, bool response) + { + p2p.handle_fileTransferResponse(*this,sessionID, filename, response); + } + + void SwitchboardServerConnection::callback_AnsweredCall(std::vector & args, int trid, void * data) + { + this->assertConnectionStateIs(SB_WAITING_FOR_USERS); + if (args.size() >= 3 && args[0] == "ANS" && args[2] == "OK") + return; + + if (isdigit(args[0][0])) + { + this->removeCallback(trid); + this->showError(decimalFromString(args[0])); + this->disconnect(); + return; + } + + if (args.size() >= 6 && args[0] == "IRO") + { + if (args[4] == this->auth.username) + return; + + this->users.push_back(args[4]); + this->myNotificationServer()->externalCallbacks.buddyJoinedConversation(this, args[4], decodeURL(args[5]), 1); + if (args[2] == args[3]) + { + this->removeCallback(trid); + this->setConnectionState(SB_READY); + } + } + } + + void SwitchboardServerConnection::requestEmoticon(unsigned int id, std::string filename, std::string msnobject, std::string alias) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + p2p.requestEmoticon(*this,id, filename, msnobject, alias); + } + + void SwitchboardServerConnection::requestWink(unsigned int id, std::string filename, std::string msnobject) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + p2p.requestWink(*this,id, filename, msnobject); + } + + void SwitchboardServerConnection::requestVoiceClip(unsigned int id, std::string filename, std::string msnobject) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + p2p.requestVoiceClip(*this,id, filename, msnobject); + } + + void SwitchboardServerConnection::requestDisplayPicture(unsigned int id, std::string filename, std::string msnobject) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + p2p.requestDisplayPicture(*this,id, filename, msnobject); + } + + void SwitchboardServerConnection::cancelFileTransfer(unsigned int id) + { + this->assertConnectionStateIsAtLeast(SB_CONNECTED); + p2p.cancelTransfer(*this,id); + } +} diff --git a/libs/libmsn/switchboardserver.h b/libs/libmsn/switchboardserver.h new file mode 100644 index 0000000..a6dd500 --- /dev/null +++ b/libs/libmsn/switchboardserver.h @@ -0,0 +1,253 @@ +#ifndef __msn_switchboardserver_h__ +#define __msn_switchboardserver_h__ + +/* + * switchboardserver.h + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "message.h" +#include "authdata.h" +#include "connection.h" +#include "passport.h" +#include "p2p.h" +#include +#include + +#include "libmsn_export.h" + +namespace MSN +{ + class NotificationServerConnection; + class FileTransferConnectionP2P; + + /** Represents a connection to a MSN switchboard. + */ + class LIBMSN_EXPORT SwitchboardServerConnection : public Connection + { +private: + typedef void (SwitchboardServerConnection::*SwitchboardServerCallback)(std::vector & args, int trid, void *); + typedef void (SwitchboardServerConnection::*SwitchboardServerCallback2)(std::vector & args, int trid, unsigned int sessionID); + + typedef struct + { + int chunks; + int receivedChunks; + std::string mime; + std::string body; + } MultiPacketSession; + +public: + class AuthData : public ::MSN::AuthData + { +public: + std::string sessionID; + bool direct_connection; + std::string cookie; + const void *tag; + + AuthData(Passport & username_, const std::string & sessionID_, + const std::string & cookie_, const void *tag_=NULL) : + ::MSN::AuthData(username_), sessionID(sessionID_), cookie(cookie_), tag(tag_) {}; + + AuthData(Passport & username_, const void *tag_=NULL) : + ::MSN::AuthData(username_), sessionID(""), cookie(""), tag(tag_) {}; + }; + + SwitchboardServerConnection::AuthData auth; + + /** A list of the users in this switchboard session. + */ + std::list users; + + P2P p2p; + + SwitchboardServerConnection(AuthData & auth_, NotificationServerConnection &); + virtual ~SwitchboardServerConnection(); + virtual void dispatchCommand(std::vector & args); + + /** Return a connection that is associated with @a fd. + * + * If @a fd is equal to @p sock, @c this is returned. Otherwise + * connectionWithSocket is sent to each FileTransferConnection + * until a match is found. + * + * @return The matching connection, if found. Otherwise, @c NULL. + */ + Connection *connectionWithSocket(void *sock); + + std::map MultiPacketSessions; + + /** Add a FileTransferConnection to the list of associated connections. + */ + void addFileTransferConnectionP2P(FileTransferConnectionP2P *); + + /** Remove a FileTransferConnection from the list of associated connections. + */ + void removeFileTransferConnectionP2P(FileTransferConnectionP2P *); + + /** Send a typing notification to the switchboard server. + */ + void sendTypingNotification(); + + /** Send a text action + */ + void sendAction(std::string action); + + /** Send a Voice Clip + */ + void sendVoiceClip(std::string msnobject); + + /** Send a Wink + */ + void sendWink(std::string msnobject); + + /** Send Ink + */ + void sendInk(std::string image); + + /** Send an emoticon + */ + void sendEmoticon(std::string alias, std::string file); + + /** Send a nudge + */ + void sendNudge(); + + /** Send a keep alive message + */ + void sendKeepAlive(); + + /** Send a file + */ + void sendFile(MSN::fileTransferInvite ft); + + /** Invite @a userName into this conversation. + */ + void inviteUser(Passport userName); + + /** Response to a file transfer invitation + */ + void fileTransferResponse(unsigned int sessionID, std::string filename, bool response); + + /** Cancel a file transfer in progress + */ + void cancelFileTransfer(unsigned int sessionID); + + virtual void connect(const std::string & hostname, unsigned int port); + virtual void disconnect(); + + /** Send formatted message, returns the trID + */ + virtual int sendMessage(const Message *msg); + + /** Send plain text message, returns the trID + */ + virtual int sendMessage(const std::string & s); + + /** Add @a cb as a callback that will be called when a response is received + * a transaction ID of @a trid. + */ + virtual void addCallback(SwitchboardServerCallback, int trid, void *data); + + // callback of msg acks + virtual void addP2PCallback(SwitchboardServerCallback2, int trid, unsigned int sessionID); + + /** Remove callbacks for transaction ID @a trid. + */ + virtual void removeCallback(int trid); + + virtual void removeP2PCallback(int trid); + + virtual void socketConnectionCompleted(); + + enum SwitchboardServerState + { + SB_DISCONNECTED, + SB_CONNECTING, + SB_CONNECTED, + SB_WAITING_FOR_USERS, + SB_READY + }; + + SwitchboardServerState connectionState() const { return this->_connectionState; }; + virtual NotificationServerConnection *myNotificationServer() { return ¬ificationServer; }; + void callback_continueTransfer(std::vector & args, int trid, unsigned int sessionID); + + /** Request an emoticon + */ + void requestEmoticon(unsigned int id, std::string filename, std::string msnobject, std::string alias); + + /** Request a voice clip + */ + void requestVoiceClip(unsigned int id, std::string filename, std::string msnobject); + + /** Request a wink + */ + void requestWink(unsigned int id, std::string filename, std::string msnobject); + + /** Request a display picture + */ + void requestDisplayPicture(unsigned int id, std::string filename, std::string msnobject); +protected: + virtual void handleIncomingData(); + SwitchboardServerState _connectionState; + + void setConnectionState(SwitchboardServerState s) { this->_connectionState = s; }; + void assertConnectionStateIs(SwitchboardServerState s) { assert(this->_connectionState == s); }; + void assertConnectionStateIsNot(SwitchboardServerState s) { assert(this->_connectionState != s); }; + void assertConnectionStateIsAtLeast(SwitchboardServerState s) { assert(this->_connectionState >= s); }; +private: + NotificationServerConnection & notificationServer; + std::list _fileTransferConnectionsP2P; + + std::map > callbacks; + std::map > callbacks2; + + static std::map &)> commandHandlers; + static std::map &, std::string, std::string)> messageHandlers; + void registerCommandHandlers(); + void registerMessageHandlers(); + void handle_BYE(std::vector & args); + void handle_JOI(std::vector & args); + void handle_NAK(std::vector & args); + void handle_MSG(std::vector & args); + + void callback_InviteUsers(std::vector & args, int trid, void * data); + void callback_AnsweredCall(std::vector & args, int trid, void * data); + void callback_messageACK(std::vector & args, int trid, void * data); + + void handleInvite(Passport from, const std::string & friendly, const std::string & mime, const std::string & body); + void handleNewInvite(Passport & from, const std::string & friendly, const std::string & mime, const std::string & body); + void message_plain(std::vector & args, std::string mime, std::string body); + void message_invitation(std::vector & args, std::string mime, std::string body); + void message_typing_user(std::vector & args, std::string mime, std::string body); + void message_p2p(std::vector & args, std::string mime, std::string body); + void message_datacast(std::vector & args, std::string mime, std::string body); + void message_emoticon(std::vector & args, std::string mime, std::string body); +public: + void message_ink(std::vector & args, std::string mime, std::string body); + + friend class Connection; + }; +} +#endif diff --git a/libs/libmsn/util.cpp b/libs/libmsn/util.cpp new file mode 100644 index 0000000..7b13f5a --- /dev/null +++ b/libs/libmsn/util.cpp @@ -0,0 +1,624 @@ +/* + * util.cpp + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include "util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "md5.h" +#include "libsiren/siren7.h" + +#ifdef _WIN32 +#define random rand +#endif + +namespace MSN +{ + std::pair splitServerAddress(const std::string & address, int default_port) + { + size_t pos; + std::string host = address; + int port = default_port; + + if ((pos = address.find(":")) != std::string::npos) + { + std::string port_s = address.substr(pos + 1); + host = address.substr(0, pos); + port = decimalFromString(port_s); + } + + if (host == "" || port < 0) + throw std::runtime_error("Invalid zero-length address or negative port number!"); + + return std::make_pair(host, port); + } + + std::string decodeURL(const std::string & s) + { + std::string out; + std::string::const_iterator i; + + for (i = s.begin(); i != s.end(); i++) + { + if (*i == '%') + { + char entity[3] = {0, 0, 0}; + if (++i == s.end()) + break; + + entity[0] = *i; + + bool doBreak = false; + if (++i != s.end()) + entity[1] = *i; + else + doBreak = true; + + int c = strtol(entity, NULL, 16); + out += c; + + if (doBreak) + break; + } + else + out += *i; + } + return out; + } + + std::string encodeURL(const std::string & s) + { + std::string out; + std::string::const_iterator i; + + for (i = s.begin(); i != s.end(); i++) + { + if(!(isalpha(*i) || isdigit(*i))) + { + unsigned char high_nibble = ((unsigned char) *i) >> 4; + unsigned char low_nibble = ((unsigned char) *i) & 0x0F; + out += '%'; + out += (high_nibble < 0x0A ? '0' + high_nibble : 'A' + high_nibble - 0x0A); + out += (low_nibble < 0x0A ? '0' + low_nibble : 'A' + low_nibble - 0x0A); + + continue; + } + out += *i; + } + + return out; + } + + std::vector splitString(const std::string & s, const std::string & sep, bool suppressBlanks) + { + std::vector array; + size_t position, last_position; + + last_position = position = 0; + while (position + sep.size() <= s.size()) + { + if (s[position] == sep[0] && s.substr(position, sep.size()) == sep) + { + if (!suppressBlanks || position - last_position > 0) + array.push_back(s.substr(last_position, position - last_position)); + last_position = position = position + sep.size(); + } + else + position++; + } + if (!suppressBlanks || last_position - s.size()) + array.push_back(s.substr(last_position)); + + return array; + } + + int nocase_cmp(const std::string & s1, const std::string& s2) + { + std::string::const_iterator it1, it2; + + for (it1 = s1.begin(), it2 = s2.begin(); + it1 != s1.end() && it2 != s2.end(); + ++it1, ++it2) + { + if (std::toupper(*it1) != std::toupper(*it2)) + return std::toupper(*it1) - std::toupper(*it2); + } + size_t size1 = s1.size(), size2 = s2.size(); + return (int) (size1 - size2); + } + + std::string toStr(int var) { + std::ostringstream tmp; tmp << var; return tmp.str(); + } + + std::string unsignedToStr(unsigned int var) { + std::ostringstream tmp; tmp << var; return tmp.str(); + } + + unsigned int decimalFromString(const std::string & s) throw (std::logic_error) + { + unsigned int result = strtol(s.c_str(), NULL, 10); + errno = 0; + if (result == 0 && errno != 0) + throw std::logic_error(strerror(errno)); + return result; + } + + std::string hmac_sha(std::string key, std::string message) + { + unsigned int buf_len=0; + unsigned char buf[50]; + memset(&buf,0,50); + HMAC(EVP_sha1(), key.c_str(), key.length(), (const unsigned char*)message.c_str(), message.length(), buf, &buf_len); + std::string a((char *)buf,buf_len); + return a; + } + + std::string derive_key(std::string key, std::string magic) + { + std::string hash1(hmac_sha(key, magic)); + std::string hash2(hmac_sha(key, hash1+magic)); + std::string hash3(hmac_sha(key, hash1)); + std::string hash4(hmac_sha(key, hash3+magic)); + std::string final(hash2+hash4.substr(0,4)); + return final; + } + + std::string b64_encode(const char *input, int t) + { + BIO *mbio, *b64bio, *bio; + char *outbuf; + int inlen, outlen; + char *output; + + mbio = BIO_new(BIO_s_mem()); + b64bio = BIO_new(BIO_f_base64()); + BIO_set_flags(b64bio, BIO_FLAGS_BASE64_NO_NL); + bio = BIO_push(b64bio, mbio); + + inlen = t ; + if (BIO_write(bio, input, inlen) != inlen) { + return ""; + } + BIO_flush(bio); + + outlen = BIO_get_mem_data(bio, &outbuf); + + output = (char*) malloc(outlen+1); + memcpy(output, outbuf, outlen); + output[outlen] = '\0'; + std::string output1(output); + BIO_free_all(bio); + free(output); + + return output1; + } + + std::string b64_decode(const char *input) + { + BIO *mbio, *b64bio, *bio; + int inlen, outlen; + char *output; + + mbio = BIO_new_mem_buf((void *)input, -1); + b64bio = BIO_new(BIO_f_base64()); + BIO_set_flags(b64bio, BIO_FLAGS_BASE64_NO_NL); + bio = BIO_push(b64bio, mbio); + + inlen = strlen(input); + outlen = inlen*2; + + output = (char*) malloc(outlen+1); + + if ((outlen = BIO_read(bio, output, outlen)) <= 0) { + return ""; + } + output[outlen] = '\0'; + std::string output1(output, outlen); + free(output); + BIO_free_all(bio); + + return output1; + } + + std::string mdi_encrypt(std::string key, std::string nonce) + { + tagMSGRUSRKEY MSGUSRKEY; + std::string key1,key2,key3; + + key1 = b64_decode(key.c_str()); + key2 = derive_key(key1, "WS-SecureConversationSESSION KEY HASH"); + key3 = derive_key(key1, "WS-SecureConversationSESSION KEY ENCRYPTION"); + + std::string hash = hmac_sha(key2, nonce); + + unsigned char workvec[8]; + RAND_bytes(workvec, 8); + des_key_schedule ks1,ks2,ks3; + + const char *one=key3.c_str(); + const char *two=key3.c_str()+8; + const char *three=key3.c_str()+16; + + des_set_key((C_Block *)one,ks1); + des_set_key((C_Block *)two,ks2); + des_set_key((C_Block *)three,ks3); + + unsigned char output[72]; + memset(&output,0,72); + + memcpy(&MSGUSRKEY.aIVBytes, &workvec, sizeof(workvec)); + memcpy(&MSGUSRKEY.aHashBytes, hash.c_str() , hash.length()); + + // ugly, but I think it is working properly + std::ostringstream buf_; + buf_ << nonce << "\x08\x08\x08\x08\x08\x08\x08\x08"; + DES_ede3_cbc_encrypt((const unsigned char*)buf_.str().c_str(),output,buf_.str().size(),&ks1,&ks2,&ks3,(C_Block *)workvec,DES_ENCRYPT); + + MSGUSRKEY.uStructHeaderSize=28; + MSGUSRKEY.uCryptMode=1; + MSGUSRKEY.uCipherType=0x6603; + MSGUSRKEY.uHashType=0x8004; + MSGUSRKEY.uIVLen=8; + MSGUSRKEY.uHashLen=hash.length(); + MSGUSRKEY.uCipherLen=72; + + // set + memcpy(&MSGUSRKEY.aCipherBytes,output, 72); + unsigned char a[129]; // last is \0 to b64_encode + memset(&a,0,129); + memcpy(&a, &MSGUSRKEY, sizeof(tagMSGRUSRKEY)); + + return b64_encode((const char*)a,128); + } + + void DoMSNP11Challenge(const char *szChallenge, char *szOutput) + { + int i; + md5_state_t state; + md5_byte_t digest[16]; + md5_init(&state); + md5_append(&state, (const md5_byte_t *)szChallenge, strlen(szChallenge)); + md5_append(&state, (const md5_byte_t *)szClientCode, strlen(szClientCode)); + md5_finish(&state, digest); + + unsigned char pMD5Hash[16]; + memcpy(pMD5Hash,digest,16); + int *pMD5Parts=(int *)digest; + for (i=0; i<4; i++) { + pMD5Parts[i]&=0x7FFFFFFF; + } + int nchlLen=strlen(szChallenge)+strlen(szClientID); + if (nchlLen%8!=0) + nchlLen+=8-(nchlLen%8); + char *chlString=new char[nchlLen]; + memset(chlString,'0',nchlLen); + memcpy(chlString,szChallenge,strlen(szChallenge)); + memcpy(chlString+strlen(szChallenge),szClientID,strlen(szClientID)); + int *pchlStringParts=(int *)chlString; + + long long nHigh=0; + long long nLow=0; + + for (i=0; i<(nchlLen/4)-1; i+=2) { + long long temp=pchlStringParts[i]; + temp=(pMD5Parts[0] * (((0x0E79A9C1 * (long long)pchlStringParts[i]) % 0x7FFFFFFF)+nHigh) + pMD5Parts[1])%0x7FFFFFFF; + nHigh=(pMD5Parts[2] * (((long long)pchlStringParts[i+1]+temp) % 0x7FFFFFFF) + pMD5Parts[3]) % 0x7FFFFFFF; + nLow=nLow + nHigh + temp; + } + nHigh=(nHigh+pMD5Parts[1]) % 0x7FFFFFFF; + nLow=(nLow+pMD5Parts[3]) % 0x7FFFFFFF; + delete[] chlString; + + unsigned int *pNewHash=(unsigned int *)pMD5Hash; + + pNewHash[0]^=nHigh; + pNewHash[1]^=nLow; + pNewHash[2]^=nHigh; + pNewHash[3]^=nLow; + + char szHexChars[]="0123456789abcdef"; + for (i=0; i<16; i++) { + szOutput[i*2]=szHexChars[(pMD5Hash[i]>>4)&0xF]; + szOutput[(i*2)+1]=szHexChars[pMD5Hash[i]&0xF]; + } + } + // 4-byte number + unsigned int little2big_endian(unsigned int i) + { + return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff); + } + + int FileSize(const char* sFileName) + { + std::ifstream f; + f.open(sFileName, std::ios_base::binary | std::ios_base::in); + if (!f.good() || f.eof() || !f.is_open()) { return 0; } + f.seekg(0, std::ios_base::beg); + std::ifstream::pos_type begin_pos = f.tellg(); + f.seekg(0, std::ios_base::end); + return static_cast(f.tellg() - begin_pos); + } + + std::string new_branch() + { + struct timeb t; + ftime(&t); + char branch[100]; + srand(t.millitm); + unsigned int a=random(); + srand(a); + unsigned short b=random(); + srand(b); + unsigned short c=random(); + srand(c); + unsigned short d=random(); + srand(d); + double e=random(); + sprintf(branch,"{%.8X-%.4X-%.4X-%.4X-%.12X}",a,b,c,d,(unsigned int)e); + std::string newbranch(branch); + return newbranch; + } + + // Code from http://search.cpan.org/src/DANKOGAI/Jcode-2.06/Unicode/uni.c + U32 _ucs2_utf8(U8 *dst, U8 *src, U32 nchar) + { + U32 ucs2; + U32 result = 0; + for (nchar /= 2; nchar > 0; nchar--, src += 2) { + ucs2 = src[0]*256 + src[1]; + if (ucs2 < 0x80){ /* 1 byte */ + *dst++ = ucs2; + result += 1; + }else if (ucs2 < 0x800){ /* 2 bytes */ + *dst++ = (0xC0 | (ucs2 >> 6)); + *dst++ = (0x80 | (ucs2 & 0x3F)); + result += 2; + }else{ /* 3 bytes */ + *dst++ = (0xE0 | (ucs2 >> 12)); + *dst++ = (0x80 | ((ucs2 >> 6) & 0x3F)); + *dst++ = (0x80 | (ucs2 & 0x3F)); + result += 3; + } + } + *dst = '\0'; + return result; + } + + U32 _utf8_ucs2(U8 *dst, U8 *src) + { + U32 ucs2; + U8 c1, c2, c3; + U32 result = 0; + + for(; *src != '\0'; src++, result++){ + if (*src < 0x80) { /* 1 byte */ + ucs2 = *src; + }else if (*src < 0xE0){ /* 2 bytes */ + if (src[1]){ + c1 = *src++; c2 = *src; + ucs2 = ((c1 & 0x1F) << 6) | (c2 & 0x3F); + }else{ + ucs2 = FB_UNI; + } + }else{ /* 3 bytes */ + if (src[1] && src[2]){ + c1 = *src++; c2 = *src++; c3 = *src; + ucs2 = ((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6)| (c3 & 0x3F); + }else{ + ucs2 = FB_UNI; + if (src[1]) + src++; + } + } + *dst++ = (ucs2 & 0xff00) >> 8; /* 1st byte */ + *dst++ = (ucs2 & 0xff); /* 2nd byte */; + } + return result * 2; + } + + // convert from siren codec to a regular wav file + void + libmsn_Siren7_DecodeVoiceClip(std::string input_file) + { + FILE * input; + FILE * output; + riff_data riff_header; + wav_data current_chunk; + fmt_chunk_ex fmt_info; + unsigned char *out_data = NULL; + unsigned char *out_ptr = NULL; + unsigned char in_buffer[40]; + unsigned int file_offset; + unsigned int chunk_offset; + + std::string new_voice(input_file.c_str()); + std::string old_voice = new_voice + "-old"; + rename(new_voice.c_str(), old_voice.c_str()); + + SirenDecoder decoder = Siren7_NewDecoder (16000); + + input = fopen (old_voice.c_str(), "rb"); + output = fopen (new_voice.c_str(), "wb"); + + file_offset = 0; + fread (&riff_header, sizeof (riff_data), 1, input); + file_offset += sizeof (riff_data); + + riff_header.chunk_id = GUINT32_FROM_LE (riff_header.chunk_id); + riff_header.chunk_size = GUINT32_FROM_LE (riff_header.chunk_size); + riff_header.type_id = GUINT32_FROM_LE (riff_header.type_id); + + if (riff_header.chunk_id == RIFF_ID && riff_header.type_id == WAVE_ID) + { + while (file_offset < riff_header.chunk_size) + { + fread (¤t_chunk, sizeof (wav_data), 1, input); + file_offset += sizeof (wav_data); + current_chunk.chunk_id = GUINT32_FROM_LE (current_chunk.chunk_id); + current_chunk.chunk_size = GUINT32_FROM_LE (current_chunk.chunk_size); + + chunk_offset = 0; + if (current_chunk.chunk_id == FMT_ID) + { + fread (&fmt_info, sizeof (fmt_chunk), 1, input); + /* Should convert from LE the fmt_info structure, but it's not necessary... */ + if (current_chunk.chunk_size > sizeof (fmt_chunk)) + { + fread (&(fmt_info.extra_size), sizeof (short), 1, input); + fmt_info.extra_size = GUINT32_FROM_LE (fmt_info.extra_size); + fmt_info.extra_content = (unsigned char *) malloc (fmt_info.extra_size); + fread (fmt_info.extra_content, fmt_info.extra_size, 1, input); + } + else + { + fmt_info.extra_size = 0; + fmt_info.extra_content = NULL; + } + } + else if (current_chunk.chunk_id == DATA_ID) + { + out_data = (unsigned char *) malloc (current_chunk.chunk_size * 16); + out_ptr = out_data; + while (chunk_offset + 40 <= current_chunk.chunk_size) + { + fread (in_buffer, 1, 40, input); + Siren7_DecodeFrame (decoder, in_buffer, out_ptr); + out_ptr += 640; + chunk_offset += 40; + } + fread (in_buffer, 1, current_chunk.chunk_size - chunk_offset, input); + } + else + { + fseek (input, current_chunk.chunk_size, SEEK_CUR); + } + + file_offset += current_chunk.chunk_size; + } + } + + /* The WAV heder should be converted TO LE, but should be done inside the library and it's not important for now ... */ + fwrite (&(decoder->WavHeader), sizeof (decoder->WavHeader), 1, output); + fwrite (out_data, 1, GUINT32_FROM_LE (decoder->WavHeader.DataSize), output); + fclose (output); + + Siren7_CloseDecoder (decoder); + + free (out_data); + free (fmt_info.extra_content); + + // remove the siren encoded file + unlink(old_voice.c_str()); + } + + // convert to siren codec from a regular wav file + void + libmsn_Siren7_EncodeVoiceClip(std::string input_file) + { + FILE * input; + FILE * output; + riff_data riff_header; + wav_data current_chunk; + fmt_chunk_ex fmt_info; + unsigned char *out_data = NULL; + unsigned char *out_ptr = NULL; + unsigned char InBuffer[640]; + unsigned int fileOffset; + unsigned int chunkOffset; + + SirenEncoder encoder = Siren7_NewEncoder(16000); + + std::string new_voice(input_file.c_str()); + std::string old_voice = new_voice + "-old"; + rename(new_voice.c_str(), old_voice.c_str()); + + input = fopen (old_voice.c_str(), "rb"); + output = fopen (new_voice.c_str(), "wb"); + + fileOffset = 0; + fread(&riff_header, sizeof(riff_data), 1, input); + fileOffset += sizeof(riff_data); + + riff_header.chunk_id = GUINT32_FROM_LE(riff_header.chunk_id); + riff_header.chunk_size = GUINT32_FROM_LE(riff_header.chunk_size); + riff_header.type_id = GUINT32_FROM_LE(riff_header.type_id); + + if (riff_header.chunk_id == RIFF_ID && riff_header.type_id == WAVE_ID) { + while (fileOffset < riff_header.chunk_size) { + fread(¤t_chunk, sizeof(wav_data), 1, input); + fileOffset += sizeof(wav_data); + current_chunk.chunk_id = GUINT32_FROM_LE(current_chunk.chunk_id); + current_chunk.chunk_size = GUINT32_FROM_LE(current_chunk.chunk_size); + + chunkOffset = 0; + if (current_chunk.chunk_id == FMT__ID) { + fread(&fmt_info, sizeof(fmt_chunk), 1, input); + /* Should convert from LE the fmt_info structure, but it's not necessary... */ + if (current_chunk.chunk_size > sizeof(fmt_chunk)) { + fread(&(fmt_info.extra_size), sizeof(short), 1, input); + fmt_info.extra_size= GUINT32_FROM_LE(fmt_info.extra_size); + fmt_info.extra_content = (unsigned char *) malloc (fmt_info.extra_size); + fread(fmt_info.extra_content, fmt_info.extra_size, 1, input); + } else { + fmt_info.extra_size = 0; + fmt_info.extra_content = NULL; + } + } else if (current_chunk.chunk_id == DATA_ID) { + out_data = (unsigned char *) malloc(current_chunk.chunk_size / 16); + out_ptr = out_data; + while (chunkOffset + 640 <= current_chunk.chunk_size) { + fread(InBuffer, 1, 640, input); + Siren7_EncodeFrame(encoder, InBuffer, out_ptr); + out_ptr += 40; + chunkOffset += 640; + } + fread(InBuffer, 1, current_chunk.chunk_size - chunkOffset, input); + } else { + fseek(input, current_chunk.chunk_size, SEEK_CUR); + } + fileOffset += current_chunk.chunk_size; + } + } + + /* The WAV heder should be converted TO LE, but should be done inside the library and it's not important for now ... */ + fwrite(&(encoder->WavHeader), sizeof(encoder->WavHeader), 1, output); + fwrite(out_data, 1, GUINT32_FROM_LE(encoder->WavHeader.DataSize), output); + fclose(output); + + Siren7_CloseEncoder(encoder); + + free(out_data); + if (fmt_info.extra_content != NULL) + free(fmt_info.extra_content); + + // remove the siren encoded file + unlink(old_voice.c_str()); + } +} diff --git a/libs/libmsn/util.h b/libs/libmsn/util.h new file mode 100644 index 0000000..b8725b2 --- /dev/null +++ b/libs/libmsn/util.h @@ -0,0 +1,279 @@ +#ifndef __msn_util_h__ +#define __msn_util_h__ + +/* + * util.h + * libmsn + * + * Created by Mark Rowe on Mon Mar 22 2004. + * Refactored by Tiago Salem Herrmann on 08/2007. + * Copyright (c) 2004 Mark Rowe. All rights reserved. + * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning( disable : 4290 ) +#endif + +// this is for CHL command +#define szClientID "PROD0114ES4Z%Q5W" +#define szClientCode "PK}_A_0N_K%O?A9S" + +#ifndef U8 +#define U8 unsigned char +#endif +#ifndef U16 +#define U16 unsigned short +#endif +#ifndef U32 +#define U32 unsigned int +#endif +#define FB_UNI 0xFFFd + +// for libsiren +#define RIFF_ID 0x46464952 +#define WAVE_ID 0x45564157 +#define FMT_ID 0x20746d66 +#define DATA_ID 0x61746164 +#define FACT_ID 0x74636166 + +typedef struct +{ + unsigned int chunk_id; + unsigned int chunk_size; +} wav_data; + +typedef struct +{ + unsigned int chunk_id; + unsigned int chunk_size; + unsigned int type_id; +} riff_data; + +typedef struct +{ + unsigned short format; + unsigned short channels; + unsigned int sample_rate; + unsigned int byte_rate; + unsigned short block_align; + unsigned short bits_per_sample; +} fmt_chunk; + +typedef struct +{ + fmt_chunk fmt; + unsigned short extra_size; + unsigned char *extra_content; +} fmt_chunk_ex; + +#define IDX(val, i) ((unsigned int) ((unsigned char *) &val)[i]) + +#define GUINT16_FROM_LE(val) ((unsigned short) (IDX (val, 0) + (unsigned short) IDX (val, 1) * 256)) +#define GUINT32_FROM_LE(val) ((unsigned int) (IDX (val, 0) + IDX (val, 1) * 256 + \ + IDX (val, 2) * 65536 + IDX (val, 3) * 16777216)) + +namespace MSN +{ + /** URL-encode a string + * + * @param s The string to encode. + * @return A string with all non-alphanumeric characters replaced by their + * URL-encoded equivalent. + */ + std::string encodeURL(const std::string & s); + + /** URL-decode a string + * + * @param s The URL-encoded string to decode. + * @return A string with all URL-encoded sequences replaced by their + * @c ASCII equivalent. + */ + std::string decodeURL(const std::string & s); + + /** Split a string containing a hostname and port number into its respective parts. + * + * @param address A string in the form "hostname:port". + * @param default_port A port number to return in the event that ":port" is omitted from @a address. + * @return A pair containing the hostname and port number. + */ + std::pair splitServerAddress(const std::string & address, int default_port=1863); + + /** Compare two strings in a case insensitive fashion + */ + int nocase_cmp(const std::string & s1, const std::string & s2); + + /** Split @a string at each occurence of @a separator. + */ + std::vector splitString(const std::string & string, const std::string & separator, bool suppressBlanks=true); + + std::string toStr(int var); + std::string unsignedToStr(unsigned int var); + /** Convert a string, @a s, that contains decimal digits into an unsigned int. + */ + unsigned int decimalFromString(const std::string & s) throw (std::logic_error); + + U32 _ucs2_utf8(U8 *dst, U8 *src, U32 nchar); + U32 _utf8_ucs2(U8 *dst, U8 *src); + + /** represents a contact pesonal message */ + struct personalInfo + { + std::string PSM; /**< personal status message */ + std::string mediaApp; /**< iTunes, Winamp or keep it empty */ + std::string mediaType; /**< 'Music', 'Games' or 'Office' */ + bool mediaIsEnabled; /**< enable/disable the Current Media setting */ + std::string mediaFormat; /**< for example, "{0} - {1}" */ + std::vector mediaLines; /**< index 0 will be {0}, etc.. */ + + personalInfo() { + mediaIsEnabled = false; + } + }; + + struct hotmailInfo + { + std::string rru; + std::string url; + std::string id; + std::string sl; + std::string kv; + std::string sid; + std::string MSPAuth; + std::string creds; + }; + + /** Represents the lists present on server side */ + typedef enum + { + LST_AB = 1, /**< Address book */ + LST_AL = 2, /**< Allow */ + LST_BL = 4, /**< Block */ + LST_RL = 8, /**< Reverse */ + LST_PL = 16 /**< Pending */ + }ContactList; + + struct tagMSGRUSRKEY + { + unsigned int uStructHeaderSize; // 28. Does not count data + unsigned int uCryptMode; // CRYPT_MODE_CBC (1) + unsigned int uCipherType; // TripleDES (0x6603) + unsigned int uHashType; // SHA1 (0x8004) + unsigned int uIVLen; // 8 + unsigned int uHashLen; // 20 + unsigned int uCipherLen; // 72 + // Data + unsigned char aIVBytes[8]; + unsigned char aHashBytes[20]; + unsigned char aCipherBytes[72]; + }; + + /** represents an offline message */ + typedef struct + { + std::string from; /**< sender passport */ + std::string fromFN; /**< sender nickname */ + std::string id; /**< ID of this offline message */ + } eachOIM; + + std::string new_branch(); + std::string generate_soap_auth(std::string user, std::string pass, std::string ticket); + std::string mdi_encrypt(std::string key, std::string nonce); + std::string b64_decode(const char *input); + std::string b64_encode(const char *input, int size); + + unsigned int little2big_endian(unsigned int i); + int FileSize(const char* sFileName); + void DoMSNP11Challenge(const char *szChallenge, char *szOutput); + + // stolen from kopete + /** List of possible capabilities for a contact */ + typedef enum + { + WindowsMobile = 0x1, + InkGifSupport = 0x4, + InkIsfSupport = 0x8, + SupportWebcam = 0x10, + SupportMultiPacketMessaging = 0x20, + MSNMobileDevice = 0x40, + MSNDirectDevice = 0x80, + WebMessenger = 0x100, + OtherSideWebMessenger = 0x200, + InternalMicrosoftClient = 0x800, //Internal Microsoft client and/or Microsoft Office Live client. + MSNSpace = 0x1000, + WinXPMediaCenter = 0x2000, // This means you are using Windows XP Media Center Edition. + SupportDirectIM = 0x4000, + SupportWinks = 0x8000, + MSNSearch = 0x10000, + VoiceClips = 0x40000, + SecureChannel = 0x80000, + SIPInvitations = 0x100000, + SharingFolders = 0x400000, + MSNC1 = 0x10000000, + MSNC2 = 0x20000000, + MSNC3 = 0x30000000, + MSNC4 = 0x40000000, + MSNC5 = 0x50000000, + MSNC6 = 0x60000000, + MSNC7 = 0x70000000 + } MSNClientInformationFields; + + /** Defines the file transfer type */ + enum fileTransferType + { + FILE_TRANSFER_WITH_PREVIEW = 0x0, /**< With preview */ + FILE_TRANSFER_WITHOUT_PREVIEW = 0x1, /**< Without preview */ + FILE_TRANSFER_BACKGROUND_SHARING = 0x4, /**< Transfer of a sharing background */ + // it is not a simple jpg file, there is a cab file inside it + FILE_TRANSFER_BACKGROUND_SHARING_CUSTOM = 0xC /**< Custom and not supported by libmsn yet */ + }; + + /** Type of the error when a file transfer fails */ + enum fileTransferError + { + FILE_TRANSFER_ERROR_USER_CANCELED, /**< The other user canceled */ + FILE_TRANSFER_ERROR_UNKNOWN /**< Unknown error */ + }; + + /** Represents a file transfer request */ + typedef struct + { + int type; /**< 0 = no preview, 1 = has preview, 4 = background sharing */ + unsigned int sessionId; /**< Id of this session */ + std::string userPassport; /**< passport of the origin or the destination */ + std::string filename; /**< name the file to receive, or the path of the file to send */ + std::string friendlyname; /**< suggested name <- required when sending a file */ + std::string preview; /**< base64 encoded 96x96 png file, if applicable */ + unsigned long long filesize; /**< size of the file to send or receive */ + } fileTransferInvite; + + void libmsn_Siren7_DecodeVoiceClip(std::string input_file); + void libmsn_Siren7_EncodeVoiceClip(std::string input_file); +} +#endif diff --git a/libs/libmsn/xmlParser.cpp b/libs/libmsn/xmlParser.cpp new file mode 100644 index 0000000..3f4a582 --- /dev/null +++ b/libs/libmsn/xmlParser.cpp @@ -0,0 +1,2675 @@ +/** + **************************************************************************** + *

XML.c - implementation file for basic XML parser written in ANSI C++ + * for portability. It works by using recursion and a node tree for breaking + * down the elements of an XML document.

+ * + * @version V2.29 + * @author Frank Vanden Berghen + * + * NOTE: + * + * If you add "#define STRICT_PARSING", on the first line of this file + * the parser will see the following XML-stream: + * some textother text + * as an error. Otherwise, this tring will be equivalent to: + * some textother text + * + * NOTE: + * + * If you add "#define APPROXIMATE_PARSING" on the first line of this file + * the parser will see the following XML-stream: + * + * + * + * as equivalent to the following XML-stream: + * + * + * + * This can be useful for badly-formed XML-streams but prevent the use + * of the following XML-stream (problem is: tags at contiguous levels + * have the same names): + * + * + * + * + * + * + * NOTE: + * + * If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file + * the "openFileHelper" function will always display error messages inside the + * console instead of inside a message-box-window. Message-box-windows are + * available on windows 9x/NT/2000/XP/Vista only. + * + * BSD license: + * Copyright (c) 2002, Frank Vanden Berghen + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Frank Vanden Berghen nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#include "xmlParser.h" +#ifdef _XMLWINDOWS +//#ifdef _DEBUG +//#define _CRTDBG_MAP_ALLOC +//#include +//#endif +#define WIN32_LEAN_AND_MEAN +#include // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files + // to have "MessageBoxA" to display error messages for openFilHelper +#endif + +#include +#include +#include +#include +#include + +XMLCSTR XMLNode::getVersion() { return _T("v2.29"); } +void freeXMLString(XMLSTR t){free(t);} + +static XMLNode::XMLCharEncoding characterEncoding=XMLNode::encoding_UTF8; +static char guessWideCharChars=1, dropWhiteSpace=1; + +inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; } + +// You can modify the initialization of the variable "XMLClearTags" below +// to change the clearTags that are currently recognized by the library. +// The number on the second columns is the length of the string inside the +// first column. The "") }, + { _T("") }, + { _T("
")    ,5,  _T("
") }, + { _T("")}, + { _T("") }, + { NULL ,0, NULL } +}; + +// You can modify the initialization of the variable "XMLEntities" below +// to change the character entities that are currently recognized by the library. +// The number on the second columns is the length of the string inside the +// first column. Additionally, the syntaxes " " and " " are recognized. +typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity; +static XMLCharacterEntity XMLEntities[] = +{ + { _T("&" ), 5, _T('&' )}, + { _T("<" ), 4, _T('<' )}, + { _T(">" ), 4, _T('>' )}, + { _T("""), 6, _T('\"')}, + { _T("'"), 6, _T('\'')}, + { NULL , 0, '\0' } +}; + +// When rendering the XMLNode to a string (using the "createXMLString" function), +// you can ask for a beautiful formatting. This formatting is using the +// following indentation character: +#define INDENTCHAR _T('\t') + +// The following function parses the XML errors into a user friendly string. +// You can edit this to change the output language of the library to something else. +XMLCSTR XMLNode::getError(XMLError xerror) +{ + switch (xerror) + { + case eXMLErrorNone: return _T("No error"); + case eXMLErrorMissingEndTag: return _T("Warning: Unmatched end tag"); + case eXMLErrorEmpty: return _T("Error: No XML data"); + case eXMLErrorFirstNotStartTag: return _T("Error: First token not start tag"); + case eXMLErrorMissingTagName: return _T("Error: Missing start tag name"); + case eXMLErrorMissingEndTagName: return _T("Error: Missing end tag name"); + case eXMLErrorNoMatchingQuote: return _T("Error: Unmatched quote"); + case eXMLErrorUnmatchedEndTag: return _T("Error: Unmatched end tag"); + case eXMLErrorUnmatchedEndClearTag: return _T("Error: Unmatched clear tag end"); + case eXMLErrorUnexpectedToken: return _T("Error: Unexpected token found"); + case eXMLErrorInvalidTag: return _T("Error: Invalid tag found"); + case eXMLErrorNoElements: return _T("Error: No elements found"); + case eXMLErrorFileNotFound: return _T("Error: File not found"); + case eXMLErrorFirstTagNotFound: return _T("Error: First Tag not found"); + case eXMLErrorUnknownCharacterEntity:return _T("Error: Unknown character entity"); + case eXMLErrorCharConversionError: return _T("Error: unable to convert between WideChar and MultiByte chars"); + case eXMLErrorCannotOpenWriteFile: return _T("Error: unable to open file for writing"); + case eXMLErrorCannotWriteFile: return _T("Error: cannot write into file"); + + case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _T("Warning: Base64-string length is not a multiple of 4"); + case eXMLErrorBase64DecodeTruncatedData: return _T("Warning: Base64-string is truncated"); + case eXMLErrorBase64DecodeIllegalCharacter: return _T("Error: Base64-string contains an illegal character"); + case eXMLErrorBase64DecodeBufferTooSmall: return _T("Error: Base64 decode output buffer is too small"); + }; + return _T("Unknown"); +} + +///////////////////////////////////////////////////////////////////////// +// Here start the abstraction layer to be OS-independent // +///////////////////////////////////////////////////////////////////////// + +// Here is an abstraction layer to access some common string manipulation functions. +// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0, +// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++. +// If you plan to "port" the library to a new system/compiler, all you have to do is +// to edit the following lines. +#ifdef XML_NO_WIDE_CHAR +char myIsTextWideChar(const void *b, int len) { return FALSE; } +#else + #if defined (UNDER_CE) || !defined(WIN32) + char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode + { +#ifdef sun + // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer. + if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE; +#endif + const wchar_t *s=(const wchar_t*)b; + + // buffer too small: + if (len<(int)sizeof(wchar_t)) return FALSE; + + // odd length test + if (len&1) return FALSE; + + /* only checks the first 256 characters */ + len=mmin(256,len/sizeof(wchar_t)); + + // Check for the special byte order: + if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE; + if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE + + // checks for ASCII characters in the UNICODE stream + int i,stats=0; + for (i=0; ilen/2) return TRUE; + + // Check for UNICODE NULL chars + for (i=0; i + int _tcsnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);} + int _tcsncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);} + int _tcsicmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); } + #else + // for gcc + int _tcsnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);} + int _tcsncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);} + int _tcsicmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); } + #endif + XMLSTR _tcsstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); } + XMLSTR _tcsncpy(XMLSTR c1, XMLCSTR c2, int n) { + if (n<=0) { + return NULL; + } + XMLSTR result=(XMLSTR)wcsncpy(c1,c2,n); + result[n-1]=L'\0'; + return result; + } + FILE *_tfopen(XMLCSTR filename,XMLCSTR mode) + { + char *filenameAscii=myWideCharToMultiByte(filename); + FILE *f; + if (mode[0]==_T('r')) f=fopen(filenameAscii,"rb"); + else f=fopen(filenameAscii,"wb"); + free(filenameAscii); + return f; + } + #else + FILE *_tfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); } + int _tcslen(XMLCSTR c) { return strlen(c); } + int _tcsnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);} + int _tcsncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);} + int _tcsicmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); } + XMLSTR _tcsstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); } + XMLSTR _tcsncpy(XMLSTR c1, XMLCSTR c2, int n) { + if (n<=0) { + return NULL; + } + XMLSTR result=(XMLSTR)strncpy(c1,c2,n); + result[n-1]='\0'; + return result; + } + #endif + int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);} +#endif + +///////////////////////////////////////////////////////////////////////// +// the "openFileHelper" function // +///////////////////////////////////////////////////////////////////////// + +// Since each application has its own way to report and deal with errors, you should modify & rewrite +// the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs. +XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag) +{ + // guess the value of the global parameter "characterEncoding" + // (the guess is based on the first 200 bytes of the file). + FILE *f=_tfopen(filename,_T("rb")); + if (f) + { + char bb[205]; + int l=(int)fread(bb,1,200,f); + setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace); + fclose(f); + } + + // parse the file + XMLResults pResults; + XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults); + + // display error message (if any) + if (pResults.error != eXMLErrorNone) + { + // create message + char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_T(""); + if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; } + sprintf(message, +#ifdef _XMLWIDECHAR + "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s" +#else + "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s" +#endif + ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3); + + // display message +#if defined(WIN32) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_) + MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST); +#else + printf("%s",message); +#endif + exit(255); + } + return xnode; +} + +///////////////////////////////////////////////////////////////////////// +// Here start the core implementation of the XMLParser library // +///////////////////////////////////////////////////////////////////////// + +// You should normally not change anything below this point. + +#ifndef _XMLWIDECHAR +// If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte. +// If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes). +// If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes). +// This table is used as lookup-table to know the length of a character (in byte) based on the +// content of the first byte of the character. +// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ). +static const char XML_utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70End of ASCII range + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 + 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte + 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; +static const char XML_asciiByteTable[256] = +{ + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +}; +static const char XML_sjisByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 +}; +static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8" +#endif + + +XMLNode XMLNode::emptyXMLNode; +XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL}; +XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL}; + +// Enumeration used to decipher what type a token is +typedef enum XMLTokenTypeTag +{ + eTokenText = 0, + eTokenQuotedText, + eTokenTagStart, /* "<" */ + eTokenTagEnd, /* "" */ + eTokenEquals, /* "=" */ + eTokenDeclaration, /* "" */ + eTokenClear, + eTokenError +} XMLTokenType; + +// Main structure used for parsing XML +typedef struct XML +{ + XMLCSTR lpXML; + XMLCSTR lpszText; + int nIndex,nIndexMissigEndTag; + enum XMLError error; + XMLCSTR lpEndTag; + int cbEndTag; + XMLCSTR lpNewElement; + int cbNewElement; + int nFirst; +} XML; + +typedef struct +{ + ALLXMLClearTag *pClr; + XMLCSTR pStr; +} NextToken; + +// Enumeration used when parsing attributes +typedef enum Attrib +{ + eAttribName = 0, + eAttribEquals, + eAttribValue +} Attrib; + +// Enumeration used when parsing elements to dictate whether we are currently +// inside a tag +typedef enum Status +{ + eInsideTag = 0, + eOutsideTag +} Status; + +XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const +{ + if (!d) return eXMLErrorNone; + FILE *f=_tfopen(filename,_T("wb")); + if (!f) return eXMLErrorCannotOpenWriteFile; +#ifdef _XMLWIDECHAR + unsigned char h[2]={ 0xFF, 0xFE }; + if (!fwrite(h,2,1,f)) return eXMLErrorCannotWriteFile; + if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) + { + if (!fwrite(_T("\n"),sizeof(wchar_t)*40,1,f)) + return eXMLErrorCannotWriteFile; + } +#else + if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) + { + if (characterEncoding==encoding_UTF8) + { + // header so that windows recognize the file as UTF-8: + unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile; + encoding="utf-8"; + } else if (characterEncoding==encoding_ShiftJIS) encoding="SHIFT-JIS"; + + if (!encoding) encoding="ISO-8859-1"; + if (fprintf(f,"\n",encoding)<0) return eXMLErrorCannotWriteFile; + } else + { + if (characterEncoding==encoding_UTF8) + { + unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile; + } + } +#endif + int i; + XMLSTR t=createXMLString(nFormat,&i); + if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) return eXMLErrorCannotWriteFile; + if (fclose(f)!=0) return eXMLErrorCannotWriteFile; + free(t); + return eXMLErrorNone; +} + +// Duplicate a given string. +XMLSTR stringDup(XMLCSTR lpszData, int cbData) +{ + if (lpszData==NULL) return NULL; + + XMLSTR lpszNew; + if (cbData==0) cbData=(int)_tcslen(lpszData); + lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR)); + if (lpszNew) + { + memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR)); + lpszNew[cbData] = (XMLCHAR)NULL; + } + return lpszNew; +} + +XMLSTR toXMLStringUnSafe(XMLSTR dest,XMLCSTR source,int length) +{ + XMLSTR dd=dest; + XMLCHAR ch; + XMLCharacterEntity *entity; + while ((ch=*source) && length > 0) + { + entity=XMLEntities; + do + { + if (ch==entity->c) + { + _tcsncpy(dest,entity->s,length); + dest+=entity->l; + length-=entity->l; + source++; + goto out_of_loop1; + } + entity++; + } while(entity->s); + if (length > 0) + { +#ifdef _XMLWIDECHAR + *(dest++)=*(source++); + length--; +#else + switch(XML_ByteTable[(unsigned char)ch]) + { + case 4: *(dest++)=*(source++); length--; + case 3: *(dest++)=*(source++); length--; + case 2: *(dest++)=*(source++); length--; + case 1: *(dest++)=*(source++); length--; + } + } +#endif +out_of_loop1: + ; + } + *dest=0; + return dd; +} + +// private (used while rendering): +int lengthXMLString(XMLCSTR source) +{ + int r=0; + XMLCharacterEntity *entity; + XMLCHAR ch; + while ((ch=*source)) + { + entity=XMLEntities; + do + { + if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; } + entity++; + } while(entity->s); +#ifdef _XMLWIDECHAR + r++; source++; +#else + ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch; +#endif +out_of_loop1: + ; + } + return r; +} + +ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); } +void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; } +XMLSTR ToXMLStringTool::toXML(XMLCSTR source) +{ + int l=lengthXMLString(source)+1; + if (l>buflen) { buflen=l; buf=(XMLSTR)realloc(buf,l*sizeof(XMLCHAR)); } + return toXMLStringUnSafe(buf,source,buflen); +} + +// private: +XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML) +{ + // This function is the opposite of the function "toXMLString". It decodes the escape + // sequences &, ", ', <, > and replace them by the characters + // &,",',<,>. This function is used internally by the XML Parser. All the calls to + // the XML library will always gives you back "decoded" strings. + // + // in: string (s) and length (lo) of string + // out: new allocated string converted from xml + if (!s) return NULL; + + int ll=0,j; + XMLSTR d; + XMLCSTR ss=s; + XMLCharacterEntity *entity; + while ((lo>0)&&(*s)) + { + if (*s==_T('&')) + { + if ((lo>2)&&(s[1]==_T('#'))) + { + s+=2; lo-=2; + if ((*s==_T('X'))||(*s==_T('x'))) { s++; lo--; } + while ((*s)&&(*s!=_T(';'))&&((lo--)>0)) s++; + if (*s!=_T(';')) + { + pXML->error=eXMLErrorUnknownCharacterEntity; + return NULL; + } + s++; lo--; + } else + { + entity=XMLEntities; + do + { + if ((lo>=entity->l)&&(_tcsnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; } + entity++; + } while(entity->s); + if (!entity->s) + { + pXML->error=eXMLErrorUnknownCharacterEntity; + return NULL; + } + } + } else + { +#ifdef _XMLWIDECHAR + s++; lo--; +#else + j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1; +#endif + } + ll++; + } + + d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR)); + s=d; + while (ll-->0) + { + if (*ss==_T('&')) + { + if (ss[1]==_T('#')) + { + ss+=2; j=0; + if ((*ss==_T('X'))||(*ss==_T('x'))) + { + ss++; + while (*ss!=_T(';')) + { + if ((*ss>=_T('0'))&&(*ss<=_T('9'))) j=(j<<4)+*ss-_T('0'); + else if ((*ss>=_T('A'))&&(*ss<=_T('F'))) j=(j<<4)+*ss-_T('A')+10; + else if ((*ss>=_T('a'))&&(*ss<=_T('f'))) j=(j<<4)+*ss-_T('a')+10; + else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} + ss++; + } + } else + { + while (*ss!=_T(';')) + { + if ((*ss>=_T('0'))&&(*ss<=_T('9'))) j=(j*10)+*ss-_T('0'); + else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} + ss++; + } + } + (*d++)=(XMLCHAR)j; ss++; + } else + { + entity=XMLEntities; + do + { + if (_tcsnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; } + entity++; + } while(entity->s); + } + } else + { +#ifdef _XMLWIDECHAR + *(d++)=*(ss++); +#else + switch(XML_ByteTable[(unsigned char)*ss]) + { + case 4: *(d++)=*(ss++); ll--; + case 3: *(d++)=*(ss++); ll--; + case 2: *(d++)=*(ss++); ll--; + case 1: *(d++)=*(ss++); + } +#endif + } + } + *d=0; + return (XMLSTR)s; +} + +#define XML_isSPACECHAR(ch) ((ch==_T('\n'))||(ch==_T(' '))||(ch== _T('\t'))||(ch==_T('\r'))) + +// private: +char myTagCompare(XMLCSTR cclose, XMLCSTR copen) +// !!!! WARNING strange convention&: +// return 0 if equals +// return 1 if different +{ + if (!cclose) return 1; + int l=(int)_tcslen(cclose); + if (_tcsnicmp(cclose, copen, l)!=0) return 1; + const XMLCHAR c=copen[l]; + if (XML_isSPACECHAR(c)|| + (c==_T('/' ))|| + (c==_T('<' ))|| + (c==_T('>' ))|| + (c==_T('=' ))) return 0; + return 1; +} + +// Obtain the next character from the string. +static inline XMLCHAR getNextChar(XML *pXML) +{ + XMLCHAR ch = pXML->lpXML[pXML->nIndex]; +#ifdef _XMLWIDECHAR + if (ch!=0) pXML->nIndex++; +#else + pXML->nIndex+=XML_ByteTable[(unsigned char)ch]; +#endif + return ch; +} + +// Find the next token in a string. +// pcbToken contains the number of characters that have been read. +static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType) +{ + NextToken result; + XMLCHAR ch; + XMLCHAR chTemp; + int indexStart,nFoundMatch,nIsText=FALSE; + result.pClr=NULL; // prevent warning + + // Find next non-white space character + do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch); + + if (ch) + { + // Cache the current string pointer + result.pStr = &pXML->lpXML[indexStart]; + + // First check whether the token is in the clear tag list (meaning it + // does not need formatting). + ALLXMLClearTag *ctag=XMLClearTags; + do + { + if (_tcsncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)==0) + { + result.pClr=ctag; + pXML->nIndex+=ctag->openTagLen-1; + *pType=eTokenClear; + return result; + } + ctag++; + } while(ctag->lpszOpen); + + // If we didn't find a clear tag then check for standard tokens + switch(ch) + { + // Check for quotes + case _T('\''): + case _T('\"'): + // Type of token + *pType = eTokenQuotedText; + chTemp = ch; + + // Set the size + nFoundMatch = FALSE; + + // Search through the string to find a matching quote + while((ch = getNextChar(pXML))) + { + if (ch==chTemp) { nFoundMatch = TRUE; break; } + if (ch==_T('<')) break; + } + + // If we failed to find a matching quote + if (nFoundMatch == FALSE) + { + pXML->nIndex=indexStart+1; + nIsText=TRUE; + break; + } + +// 4.02.2002 +// if (FindNonWhiteSpace(pXML)) pXML->nIndex--; + + break; + + // Equals (used with attribute values) + case _T('='): + *pType = eTokenEquals; + break; + + // Close tag + case _T('>'): + *pType = eTokenCloseTag; + break; + + // Check for tag start and tag end + case _T('<'): + + // Peek at the next character to see if we have an end tag 'lpXML[pXML->nIndex]; + + // If we have a tag end... + if (chTemp == _T('/')) + { + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenTagEnd; + } + + // If we have an XML declaration tag + else if (chTemp == _T('?')) + { + + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenDeclaration; + } + + // Otherwise we must have a start tag + else + { + *pType = eTokenTagStart; + } + break; + + // Check to see if we have a short hand type end tag ('/>'). + case _T('/'): + + // Peek at the next character to see if we have a short end tag '/>' + chTemp = pXML->lpXML[pXML->nIndex]; + + // If we have a short hand end tag... + if (chTemp == _T('>')) + { + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenShortHandClose; + break; + } + + // If we haven't found a short hand closing tag then drop into the + // text process + + // Other characters + default: + nIsText = TRUE; + } + + // If this is a TEXT node + if (nIsText) + { + // Indicate we are dealing with text + *pType = eTokenText; + while((ch = getNextChar(pXML))) + { + if XML_isSPACECHAR(ch) + { + indexStart++; break; + + } else if (ch==_T('/')) + { + // If we find a slash then this maybe text or a short hand end tag + // Peek at the next character to see it we have short hand end tag + ch=pXML->lpXML[pXML->nIndex]; + // If we found a short hand end tag then we need to exit the loop + if (ch==_T('>')) { pXML->nIndex--; break; } + + } else if ((ch==_T('<'))||(ch==_T('>'))||(ch==_T('='))) + { + pXML->nIndex--; break; + } + } + } + *pcbToken = pXML->nIndex-indexStart; + } else + { + // If we failed to obtain a valid character + *pcbToken = 0; + *pType = eTokenError; + result.pStr=NULL; + } + + return result; +} + +XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName) +{ + if (!d) { free(lpszName); return NULL; } + if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName); + d->lpszName=lpszName; + return lpszName; +} + +// private: +XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; } +XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration) +{ + d=(XMLNodeData*)malloc(sizeof(XMLNodeData)); + d->ref_count=1; + + d->lpszName=NULL; + d->nChild= 0; + d->nText = 0; + d->nClear = 0; + d->nAttribute = 0; + + d->isDeclaration = isDeclaration; + + d->pParent = pParent; + d->pChild= NULL; + d->pText= NULL; + d->pClear= NULL; + d->pAttribute= NULL; + d->pOrder= NULL; + + updateName_WOSD(lpszName); +} + +XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); } +XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); } + +#define MEMORYINCREASE 50 + +static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem) +{ + if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); } + if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem); +// if (!p) +// { +// printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220); +// } + return p; +} + +// private: +int XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xtype) +{ + if (index<0) return -1; + int i=0,j=(int)((index<<2)+xtype),*o=d->pOrder; while (o[i]!=j) i++; return i; +} + +// private: +// update "order" information when deleting a content of a XMLNode +int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index) +{ + int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t); + memmove(o+i, o+i+1, (n-i)*sizeof(int)); + for (;ipOrder=(int)realloc(d->pOrder,n*sizeof(int)); + // but we skip reallocation because it's too time consuming. + // Anyway, at the end, it will be free'd completely at once. + return i; +} + +void *XMLNode::addToOrder(int memoryIncrease,int *_pos, int nc, void *p, int size, XMLElementType xtype) +{ + // in: *_pos is the position inside d->pOrder ("-1" means "EndOf") + // out: *_pos is the index inside p + p=myRealloc(p,(nc+1),memoryIncrease,size); + int n=d->nChild+d->nText+d->nClear; + d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int)); + int pos=*_pos,*o=d->pOrder; + + if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; } + + int i=pos; + memmove(o+i+1, o+i, (n-i)*sizeof(int)); + + while ((pos>2; + memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size); + + return p; +} + +// Add a child node to the given element. +XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName, char isDeclaration, int pos) +{ + if (!lpszName) return emptyXMLNode; + d->pChild=(XMLNode*)addToOrder(memoryIncrease,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild); + d->pChild[pos].d=NULL; + d->pChild[pos]=XMLNode(d,lpszName,isDeclaration); + d->nChild++; + return d->pChild[pos]; +} + +// Add an attribute to an element. +XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease,XMLSTR lpszName, XMLSTR lpszValuev) +{ + if (!lpszName) return &emptyXMLAttribute; + if (!d) { if (lpszName) free(lpszName); if (lpszValuev) free(lpszValuev); return &emptyXMLAttribute; } + int nc=d->nAttribute; + d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute)); + XMLAttribute *pAttr=d->pAttribute+nc; + pAttr->lpszName = lpszName; + pAttr->lpszValue = lpszValuev; + d->nAttribute++; + return pAttr; +} + +// Add text to the element. +XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos) +{ + if (!lpszValue) return NULL; + if (!d) { if (lpszValue) free(lpszValue); return NULL; } + d->pText=(XMLCSTR*)addToOrder(memoryIncrease,&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText); + d->pText[pos]=lpszValue; + d->nText++; + return lpszValue; +} + +// Add clear (unformatted) text to the element. +XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos) +{ + if (!lpszValue) return &emptyXMLClear; + if (!d) { if (lpszValue) free(lpszValue); return &emptyXMLClear; } + d->pClear=(XMLClear *)addToOrder(memoryIncrease,&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear); + XMLClear *pNewClear=d->pClear+pos; + pNewClear->lpszValue = lpszValue; + if (!lpszOpen) lpszOpen=XMLClearTags->lpszOpen; + if (!lpszClose) lpszClose=XMLClearTags->lpszClose; + pNewClear->lpszOpenTag = lpszOpen; + pNewClear->lpszCloseTag = lpszClose; + d->nClear++; + return pNewClear; +} + +// private: +// Parse a clear (unformatted) type node. +char XMLNode::parseClearTag(void *px, void *_pClear) +{ + XML *pXML=(XML *)px; + ALLXMLClearTag *pClear=(ALLXMLClearTag*)_pClear; + int cbTemp=0; + XMLCSTR lpszTemp=NULL; + XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex]; + static XMLCSTR docTypeEnd=_T("]>"); + + // Find the closing tag + // Seems the lpszOpen==XMLClearTags[1].lpszOpen) + { + XMLCSTR pCh=lpXML; + while (*pCh) + { + if (*pCh==_T('<')) { pClear->lpszClose=docTypeEnd; lpszTemp=_tcsstr(lpXML,docTypeEnd); break; } + else if (*pCh==_T('>')) { lpszTemp=pCh; break; } +#ifdef _XMLWIDECHAR + pCh++; +#else + pCh+=XML_ByteTable[(unsigned char)(*pCh)]; +#endif + } + } else lpszTemp=_tcsstr(lpXML, pClear->lpszClose); + + if (lpszTemp) + { + // Cache the size and increment the index + cbTemp = (int)(lpszTemp - lpXML); + + pXML->nIndex += cbTemp+(int)_tcslen(pClear->lpszClose); + + // Add the clear node to the current element + addClear_priv(MEMORYINCREASE,stringDup(lpXML,cbTemp), pClear->lpszOpen, pClear->lpszClose,-1); + return 0; + } + + // If we failed to find the end tag + pXML->error = eXMLErrorUnmatchedEndClearTag; + return 1; +} + +void XMLNode::exactMemory(XMLNodeData *d) +{ + if (d->pOrder) d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nText+d->nClear)*sizeof(int)); + if (d->pChild) d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode)); + if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute)); + if (d->pText) d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR)); + if (d->pClear) d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear)); +} + +char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr) +{ + XML *pXML=(XML *)pa; + XMLCSTR lpszText=pXML->lpszText; + if (!lpszText) return 0; + if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++; + int cbText = (int)(tokenPStr - lpszText); + if (!cbText) { pXML->lpszText=NULL; return 0; } + if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; } + if (!cbText) { pXML->lpszText=NULL; return 0; } + XMLSTR lpt=fromXMLString(lpszText,cbText,pXML); + if (!lpt) return 1; + addText_priv(MEMORYINCREASE,lpt,-1); + pXML->lpszText=NULL; + return 0; +} +// private: +// Recursively parse an XML element. +int XMLNode::ParseXMLElement(void *pa) +{ + XML *pXML=(XML *)pa; + int cbToken; + enum XMLTokenTypeTag type; + NextToken token; + XMLCSTR lpszTemp=NULL; + int cbTemp=0; + char nDeclaration; + XMLNode pNew; + enum Status status; // inside or outside a tag + enum Attrib attrib = eAttribName; + + assert(pXML); + + // If this is the first call to the function + if (pXML->nFirst) + { + // Assume we are outside of a tag definition + pXML->nFirst = FALSE; + status = eOutsideTag; + } else + { + // If this is not the first call then we should only be called when inside a tag. + status = eInsideTag; + } + + // Iterate through the tokens in the document + for(;;) + { + // Obtain the next token + token = GetNextToken(pXML, &cbToken, &type); + + if (type != eTokenError) + { + // Check the current status + switch(status) + { + + // If we are outside of a tag definition + case eOutsideTag: + + // Check what type of token we obtained + switch(type) + { + // If we have found text or quoted text + case eTokenText: + case eTokenCloseTag: /* '>' */ + case eTokenShortHandClose: /* '/>' */ + case eTokenQuotedText: + case eTokenEquals: + break; + + // If we found a start tag '<' and declarations 'error = eXMLErrorMissingTagName; + return FALSE; + } + + // If we found a new element which is the same as this + // element then we need to pass this back to the caller.. + +#ifdef APPROXIMATE_PARSING + if (d->lpszName && + myTagCompare(d->lpszName, token.pStr) == 0) + { + // Indicate to the caller that it needs to create a + // new element. + pXML->lpNewElement = token.pStr; + pXML->cbNewElement = cbToken; + return TRUE; + } else +#endif + { + // If the name of the new element differs from the name of + // the current element we need to add the new element to + // the current one and recurse + pNew = addChild_priv(MEMORYINCREASE,stringDup(token.pStr,cbToken), nDeclaration,-1); + + while (!pNew.isEmpty()) + { + // Callself to process the new node. If we return + // FALSE this means we dont have any more + // processing to do... + + if (!pNew.ParseXMLElement(pXML)) return FALSE; + else + { + // If the call to recurse this function + // evented in a end tag specified in XML then + // we need to unwind the calls to this + // function until we find the appropriate node + // (the element name and end tag name must + // match) + if (pXML->cbEndTag) + { + // If we are back at the root node then we + // have an unmatched end tag + if (!d->lpszName) + { + pXML->error=eXMLErrorUnmatchedEndTag; + return FALSE; + } + + // If the end tag matches the name of this + // element then we only need to unwind + // once more... + + if (myTagCompare(d->lpszName, pXML->lpEndTag)==0) + { + pXML->cbEndTag = 0; + } + + return TRUE; + } else + if (pXML->cbNewElement) + { + // If the call indicated a new element is to + // be created on THIS element. + + // If the name of this element matches the + // name of the element we need to create + // then we need to return to the caller + // and let it process the element. + + if (myTagCompare(d->lpszName, pXML->lpNewElement)==0) + { + return TRUE; + } + + // Add the new element and recurse + pNew = addChild_priv(MEMORYINCREASE,stringDup(pXML->lpNewElement,pXML->cbNewElement),0,-1); + pXML->cbNewElement = 0; + } + else + { + // If we didn't have a new element to create + pNew = emptyXMLNode; + + } + } + } + } + break; + + // If we found an end tag + case eTokenTagEnd: + + // If we have node text then add this to the element + if (maybeAddTxT(pXML,token.pStr)) return FALSE; + + // Find the name of the end tag + token = GetNextToken(pXML, &cbTemp, &type); + + // The end tag should be text + if (type != eTokenText) + { + pXML->error = eXMLErrorMissingEndTagName; + return FALSE; + } + lpszTemp = token.pStr; + + // After the end tag we should find a closing tag + token = GetNextToken(pXML, &cbToken, &type); + if (type != eTokenCloseTag) + { + pXML->error = eXMLErrorMissingEndTagName; + return FALSE; + } + pXML->lpszText=pXML->lpXML+pXML->nIndex; + + // We need to return to the previous caller. If the name + // of the tag cannot be found we need to keep returning to + // caller until we find a match + if (myTagCompare(d->lpszName, lpszTemp) != 0) +#ifdef STRICT_PARSING + { + pXML->error=eXMLErrorUnmatchedEndTag; + pXML->nIndexMissigEndTag=pXML->nIndex; + return FALSE; + } +#else + { + pXML->error=eXMLErrorMissingEndTag; + pXML->nIndexMissigEndTag=pXML->nIndex; + pXML->lpEndTag = lpszTemp; + pXML->cbEndTag = cbTemp; + } +#endif + + // Return to the caller + exactMemory(d); + return TRUE; + + // If we found a clear (unformatted) token + case eTokenClear: + // If we have node text then add this to the element + if (maybeAddTxT(pXML,token.pStr)) return FALSE; + if (parseClearTag(pXML, token.pClr)) return FALSE; + pXML->lpszText=pXML->lpXML+pXML->nIndex; + break; + + default: + break; + } + break; + + // If we are inside a tag definition we need to search for attributes + case eInsideTag: + + // Check what part of the attribute (name, equals, value) we + // are looking for. + switch(attrib) + { + // If we are looking for a new attribute + case eAttribName: + + // Check what the current token type is + switch(type) + { + // If the current type is text... + // Eg. 'attribute' + case eTokenText: + // Cache the token then indicate that we are next to + // look for the equals + lpszTemp = token.pStr; + cbTemp = cbToken; + attrib = eAttribEquals; + break; + + // If we found a closing tag... + // Eg. '>' + case eTokenCloseTag: + // We are now outside the tag + status = eOutsideTag; + pXML->lpszText=pXML->lpXML+pXML->nIndex; + break; + + // If we found a short hand '/>' closing tag then we can + // return to the caller + case eTokenShortHandClose: + exactMemory(d); + pXML->lpszText=pXML->lpXML+pXML->nIndex; + return TRUE; + + // Errors... + case eTokenQuotedText: /* '"SomeText"' */ + case eTokenTagStart: /* '<' */ + case eTokenTagEnd: /* 'error = eXMLErrorUnexpectedToken; + return FALSE; + default: break; + } + break; + + // If we are looking for an equals + case eAttribEquals: + // Check what the current token type is + switch(type) + { + // If the current type is text... + // Eg. 'Attribute AnotherAttribute' + case eTokenText: + // Add the unvalued attribute to the list + addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL); + // Cache the token then indicate. We are next to + // look for the equals attribute + lpszTemp = token.pStr; + cbTemp = cbToken; + break; + + // If we found a closing tag 'Attribute >' or a short hand + // closing tag 'Attribute />' + case eTokenShortHandClose: + case eTokenCloseTag: + // If we are a declaration element 'lpszText=pXML->lpXML+pXML->nIndex; + + if (d->isDeclaration && + (lpszTemp[cbTemp-1]) == _T('?')) + { + cbTemp--; + } + + if (cbTemp) + { + // Add the unvalued attribute to the list + addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL); + } + + // If this is the end of the tag then return to the caller + if (type == eTokenShortHandClose) + { + exactMemory(d); + return TRUE; + } + + // We are now outside the tag + status = eOutsideTag; + break; + + // If we found the equals token... + // Eg. 'Attribute =' + case eTokenEquals: + // Indicate that we next need to search for the value + // for the attribute + attrib = eAttribValue; + break; + + // Errors... + case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/ + case eTokenTagStart: /* 'Attribute <' */ + case eTokenTagEnd: /* 'Attribute error = eXMLErrorUnexpectedToken; + return FALSE; + default: break; + } + break; + + // If we are looking for an attribute value + case eAttribValue: + // Check what the current token type is + switch(type) + { + // If the current type is text or quoted text... + // Eg. 'Attribute = "Value"' or 'Attribute = Value' or + // 'Attribute = 'Value''. + case eTokenText: + case eTokenQuotedText: + // If we are a declaration element 'isDeclaration && + (token.pStr[cbToken-1]) == _T('?')) + { + cbToken--; + } + + if (cbTemp) + { + // Add the valued attribute to the list + if (type==eTokenQuotedText) { token.pStr++; cbToken-=2; } + XMLSTR attrVal=(XMLSTR)token.pStr; + if (attrVal) + { + attrVal=fromXMLString(attrVal,cbToken,pXML); + if (!attrVal) return FALSE; + } + addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp),attrVal); + } + + // Indicate we are searching for a new attribute + attrib = eAttribName; + break; + + // Errors... + case eTokenTagStart: /* 'Attr = <' */ + case eTokenTagEnd: /* 'Attr = ' */ + case eTokenShortHandClose: /* "Attr = />" */ + case eTokenEquals: /* 'Attr = =' */ + case eTokenDeclaration: /* 'Attr = error = eXMLErrorUnexpectedToken; + return FALSE; + break; + default: break; + } + } + } + } + // If we failed to obtain the next token + else + { + if ((!d->isDeclaration)&&(d->pParent)) + { +#ifdef STRICT_PARSING + pXML->error=eXMLErrorUnmatchedEndTag; +#else + pXML->error=eXMLErrorMissingEndTag; +#endif + pXML->nIndexMissigEndTag=pXML->nIndex; + } + return FALSE; + } + } +} + +// Count the number of lines and columns in an XML string. +static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults) +{ + XMLCHAR ch; + assert(lpXML); + assert(pResults); + + struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE }; + + pResults->nLine = 1; + pResults->nColumn = 1; + while (xml.nIndexnColumn++; + else + { + pResults->nLine++; + pResults->nColumn=1; + } + } +} + +// Parse XML and return the root element. +XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults) +{ + if (!lpszXML) + { + if (pResults) + { + pResults->error=eXMLErrorNoElements; + pResults->nLine=0; + pResults->nColumn=0; + } + return emptyXMLNode; + } + + XMLNode xnode(NULL,NULL,FALSE); + struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE }; + + // Create header element + xnode.ParseXMLElement(&xml); + enum XMLError error = xml.error; + if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node + + // If no error occurred + if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag)) + { + XMLCSTR name=xnode.getName(); + if (tag&&_tcslen(tag)&&((!name)||(_tcsicmp(xnode.getName(),tag)))) + { + XMLNode nodeTmp; + int i=0; + while (i=xnode.nChildNode()) + { + if (pResults) + { + pResults->error=eXMLErrorFirstTagNotFound; + pResults->nLine=0; + pResults->nColumn=0; + } + return emptyXMLNode; + } + xnode=nodeTmp; + } + } else + { + // Cleanup: this will destroy all the nodes + xnode = emptyXMLNode; + } + + + // If we have been given somewhere to place results + if (pResults) + { + pResults->error = error; + + // If we have an error + if (error!=eXMLErrorNone) + { + if (error==eXMLErrorMissingEndTag) xml.nIndex=xml.nIndexMissigEndTag; + // Find which line and column it starts on. + CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults); + } + } + return xnode; +} + +XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults) +{ + if (pResults) { pResults->nLine=0; pResults->nColumn=0; } + FILE *f=_tfopen(filename,_T("rb")); + if (f==NULL) { if (pResults) pResults->error=eXMLErrorFileNotFound; return emptyXMLNode; } + fseek(f,0,SEEK_END); + int l=ftell(f),headerSz=0; + if (!l) { if (pResults) pResults->error=eXMLErrorEmpty; return emptyXMLNode; } + fseek(f,0,SEEK_SET); + unsigned char *buf=(unsigned char*)malloc(l+4); + fread(buf,l,1,f); + fclose(f); + buf[l]=0;buf[l+1]=0;buf[l+2]=0;buf[l+3]=0; +#ifdef _XMLWIDECHAR + if (guessWideCharChars) + { + if (!myIsTextWideChar(buf,l)) + { + if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3; + XMLSTR b2=myMultiByteToWideChar((const char*)(buf+headerSz)); + free(buf); buf=(unsigned char*)b2; headerSz=0; + } else + { + if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; + if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; + } + } +#else + if (guessWideCharChars) + { + if (myIsTextWideChar(buf,l)) + { + l/=sizeof(wchar_t); + if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; + if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; + char *b2=myWideCharToMultiByte((const wchar_t*)(buf+headerSz)); + free(buf); buf=(unsigned char*)b2; headerSz=0; + } else + { + if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3; + } + } +#endif + + if (!buf) { if (pResults) pResults->error=eXMLErrorCharConversionError; return emptyXMLNode; } + XMLNode x=parseString((XMLSTR)(buf+headerSz),tag,pResults); + free(buf); + return x; +} + +static inline void charmemset(XMLSTR dest,XMLCHAR c,int l) { while (l--) *(dest++)=c; } +// private: +// Creates an user friendly XML string from a given element with +// appropriate white space and carriage returns. +// +// This recurses through all subnodes then adds contents of the nodes to the +// string. +int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int length, int nFormat) +{ + int nResult = 0; + int cb; + int cbElement; + int nChildFormat=-1; + int nElementI=pEntry->nChild+pEntry->nText+pEntry->nClear; + int i,j; + + assert(pEntry); + +#define LENSTR(lpsz) (lpsz ? _tcslen(lpsz) : 0) + + // If the element has no name then assume this is the head node. + cbElement = (int)LENSTR(pEntry->lpszName); + + if (cbElement) + { + // "isDeclaration) lpszMarker[nResult++]=_T('?'); + _tcsncpy(&lpszMarker[nResult], pEntry->lpszName, length-nResult); + nResult+=cbElement; + lpszMarker[nResult++]=_T(' '); + + } else + { + nResult+=cbElement+2+cb; + if (pEntry->isDeclaration) nResult++; + } + + // Enumerate attributes and add them to the string + XMLAttribute *pAttr=pEntry->pAttribute; + for (i=0; inAttribute; i++) + { + // "Attrib + cb = (int)LENSTR(pAttr->lpszName); + if (cb) + { + if (lpszMarker) _tcsncpy(&lpszMarker[nResult], pAttr->lpszName, length-nResult); + nResult += cb; + // "Attrib=Value " + if (pAttr->lpszValue) + { + cb=(int)lengthXMLString(pAttr->lpszValue); + if (lpszMarker) + { + lpszMarker[nResult]=_T('='); + lpszMarker[nResult+1]=_T('"'); + if (cb) toXMLStringUnSafe(&lpszMarker[nResult+2],pAttr->lpszValue, length-(nResult+2)); + lpszMarker[nResult+cb+2]=_T('"'); + } + nResult+=cb+3; + } + if (lpszMarker) lpszMarker[nResult] = _T(' '); + nResult++; + } + pAttr++; + } + + if (pEntry->isDeclaration) + { + if (lpszMarker) + { + lpszMarker[nResult-1]=_T('?'); + lpszMarker[nResult]=_T('>'); + } + nResult++; + if (nFormat!=-1) + { + if (lpszMarker) lpszMarker[nResult]=_T('\n'); + nResult++; + } + } else + // If there are child nodes we need to terminate the start tag + if (nElementI) + { + if (lpszMarker) lpszMarker[nResult-1]=_T('>'); + if (nFormat!=-1) + { + if (lpszMarker) lpszMarker[nResult]=_T('\n'); + nResult++; + } + } else nResult--; + } + + // Calculate the child format for when we recurse. This is used to + // determine the number of spaces used for prefixes. + if (nFormat!=-1) + { + if (cbElement&&(!pEntry->isDeclaration)) nChildFormat=nFormat+1; + else nChildFormat=nFormat; + } + + // Enumerate through remaining children + for (i=0; ipOrder[i]; + switch((XMLElementType)(j&3)) + { + // Text nodes + case eNodeText: + { + // "Text" + XMLCSTR pChild=pEntry->pText[j>>2]; + cb = (int)lengthXMLString(pChild); + if (cb) + { + if (nFormat!=-1) + { + if (lpszMarker) + { + charmemset(&lpszMarker[nResult],INDENTCHAR,sizeof(XMLCHAR)*(nFormat + 1)); + toXMLStringUnSafe(&lpszMarker[nResult+nFormat+1],pChild, length - (nResult + nFormat + 1)); + lpszMarker[nResult+nFormat+1+cb]=_T('\n'); + } + nResult+=cb+nFormat+2; + } else + { + if (lpszMarker) toXMLStringUnSafe(&lpszMarker[nResult], pChild, length - nResult); + nResult += cb; + } + } + break; + } + + // Clear type nodes + case eNodeClear: + { + XMLClear *pChild=pEntry->pClear+(j>>2); + // "OpenTag" + cb = (int)LENSTR(pChild->lpszOpenTag); + if (cb) + { + if (nFormat!=-1) + { + if (lpszMarker) + { + charmemset(&lpszMarker[nResult], INDENTCHAR, sizeof(XMLCHAR)*(nFormat + 1)); + _tcsncpy(&lpszMarker[nResult+nFormat+1], pChild->lpszOpenTag, length - (nResult + nFormat + 1)); + } + nResult+=cb+nFormat+1; + } + else + { + if (lpszMarker)_tcsncpy(&lpszMarker[nResult], pChild->lpszOpenTag, length - nResult); + nResult += cb; + } + } + + // "OpenTag Value" + cb = (int)LENSTR(pChild->lpszValue); + if (cb) + { + if (lpszMarker) _tcsncpy(&lpszMarker[nResult], pChild->lpszValue, length - nResult); + nResult += cb; + } + + // "OpenTag Value CloseTag" + cb = (int)LENSTR(pChild->lpszCloseTag); + if (cb) + { + if (lpszMarker) _tcsncpy(&lpszMarker[nResult], pChild->lpszCloseTag, length - nResult); + nResult += cb; + } + + if (nFormat!=-1) + { + if (lpszMarker) lpszMarker[nResult] = _T('\n'); + nResult++; + } + break; + } + + // Element nodes + case eNodeChild: + { + // Recursively add child nodes + nResult += CreateXMLStringR(pEntry->pChild[j>>2].d, lpszMarker ? lpszMarker + nResult : 0, lpszMarker ? length - nResult : 0, nChildFormat); + break; + } + default: break; + } + } + + if ((cbElement)&&(!pEntry->isDeclaration)) + { + // If we have child entries we need to use long XML notation for + // closing the element - "blah blah blah" + if (nElementI) + { + // "\0" + if (lpszMarker) + { + if (nFormat != -1) + { + if (nFormat) + { + charmemset(&lpszMarker[nResult], INDENTCHAR,sizeof(XMLCHAR)*nFormat); + nResult+=nFormat; + } + } + + _tcsncpy(&lpszMarker[nResult], _T("lpszName, length - nResult); + nResult += cbElement; + + if (nFormat == -1) + { + _tcsncpy(&lpszMarker[nResult], _T(">"), length - nResult); + nResult++; + } else + { + _tcsncpy(&lpszMarker[nResult], _T(">\n"), length - nResult); + nResult+=2; + } + } else + { + if (nFormat != -1) nResult+=cbElement+4+nFormat; + else nResult+=cbElement+3; + } + } else + { + // If there are no children we can use shorthand XML notation - + // "" + // "/>\0" + if (lpszMarker) + { + if (nFormat == -1) + { + _tcsncpy(&lpszMarker[nResult], _T("/>"), length - nResult); + nResult += 2; + } + else + { + _tcsncpy(&lpszMarker[nResult], _T("/>\n"), length - nResult); + nResult += 3; + } + } + else + { + nResult += nFormat == -1 ? 2 : 3; + } + } + } + + return nResult; +} + +#undef LENSTR + +// Create an XML string +// @param int nFormat - 0 if no formatting is required +// otherwise nonzero for formatted text +// with carriage returns and indentation. +// @param int *pnSize - [out] pointer to the size of the +// returned string not including the +// NULL terminator. +// @return XMLSTR - Allocated XML string, you must free +// this with free(). +XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const +{ + if (!d) { if (pnSize) *pnSize=0; return NULL; } + + XMLSTR lpszResult = NULL; + int cbStr; + + // Recursively Calculate the size of the XML string + if (!dropWhiteSpace) nFormat=0; + nFormat = nFormat ? 0 : -1; + cbStr = CreateXMLStringR(d, 0, 0, nFormat); + assert(cbStr); + // Alllocate memory for the XML string + the NULL terminator and + // create the recursively XML string. + lpszResult=(XMLSTR)malloc((cbStr+1)*sizeof(XMLCHAR)); + CreateXMLStringR(d, lpszResult, cbStr+1, nFormat); + if (pnSize) *pnSize = cbStr; + return lpszResult; +} + +XMLNode::~XMLNode() { deleteNodeContent(); } + +int XMLNode::detachFromParent(XMLNodeData *d) +{ + XMLNode *pa=d->pParent->pChild; + int i=0; + while (((void*)(pa[i].d))!=((void*)d)) i++; + d->pParent->nChild--; + if (d->pParent->nChild) memmove(pa+i,pa+i+1,(d->pParent->nChild-i)*sizeof(XMLNode)); + else { free(pa); d->pParent->pChild=NULL; } + return removeOrderElement(d->pParent,eNodeChild,i); +} + +void XMLNode::deleteNodeContent(char force) +{ + if (!d) return; + (d->ref_count) --; + if ((d->ref_count==0)||force) + { + int i; + if (d->pParent) detachFromParent(d); + for(i=0; inChild; i++) { d->pChild[i].d->pParent=NULL; d->pChild[i].deleteNodeContent(force); } + free(d->pChild); + for(i=0; inText; i++) free((void*)d->pText[i]); + free(d->pText); + for(i=0; inClear; i++) free((void*)d->pClear[i].lpszValue); + free(d->pClear); + for(i=0; inAttribute; i++) + { + free((void*)d->pAttribute[i].lpszName); + if (d->pAttribute[i].lpszValue) free((void*)d->pAttribute[i].lpszValue); + } + free(d->pAttribute); + free(d->pOrder); + free((void*)d->lpszName); + free(d); + d=NULL; + } +} + +XMLNode XMLNode::addChild(XMLNode childNode, int pos) +{ + XMLNodeData *dc=childNode.d; + if ((!dc)||(!d)) return childNode; + if (dc->pParent) { if ((detachFromParent(dc)<=pos)&&(dc->pParent==d)) pos--; } else dc->ref_count++; + dc->pParent=d; +// int nc=d->nChild; +// d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode)); + d->pChild=(XMLNode*)addToOrder(0,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild); + d->pChild[pos].d=dc; + d->nChild++; + return childNode; +} + +void XMLNode::deleteAttribute(int i) +{ + if ((!d)||(i<0)||(i>=d->nAttribute)) return; + d->nAttribute--; + XMLAttribute *p=d->pAttribute+i; + free((void*)p->lpszName); + if (p->lpszValue) free((void*)p->lpszValue); + if (d->nAttribute) memmove(p,p+1,(d->nAttribute-i)*sizeof(XMLAttribute)); else { free(p); d->pAttribute=NULL; } +} + +void XMLNode::deleteAttribute(XMLAttribute *a){ if (a) deleteAttribute(a->lpszName); } +void XMLNode::deleteAttribute(XMLCSTR lpszName) +{ + int j=0; + getAttribute(lpszName,&j); + if (j) deleteAttribute(j-1); +} + +XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,int i) +{ + if (!d) { if (lpszNewValue) free(lpszNewValue); if (lpszNewName) free(lpszNewName); return NULL; } + if (i>=d->nAttribute) + { + if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue); + return NULL; + } + XMLAttribute *p=d->pAttribute+i; + if (p->lpszValue&&p->lpszValue!=lpszNewValue) free((void*)p->lpszValue); + p->lpszValue=lpszNewValue; + if (lpszNewName&&p->lpszName!=lpszNewName) { free((void*)p->lpszName); p->lpszName=lpszNewName; }; + return p; +} + +XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute) +{ + if (oldAttribute) return updateAttribute_WOSD((XMLSTR)newAttribute->lpszValue,(XMLSTR)newAttribute->lpszName,oldAttribute->lpszName); + return addAttribute_WOSD((XMLSTR)newAttribute->lpszName,(XMLSTR)newAttribute->lpszValue); +} + +XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName) +{ + int j=0; + getAttribute(lpszOldName,&j); + if (j) return updateAttribute_WOSD(lpszNewValue,lpszNewName,j-1); + else + { + if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue); + else return addAttribute_WOSD(stringDup(lpszOldName),lpszNewValue); + } +} + +int XMLNode::indexText(XMLCSTR lpszValue) const +{ + if (!d) return -1; + int i,l=d->nText; + if (!lpszValue) { if (l) return 0; return -1; } + XMLCSTR *p=d->pText; + for (i=0; i=d->nText)) return; + d->nText--; + XMLCSTR *p=d->pText+i; + free((void*)*p); + if (d->nText) memmove(p,p+1,(d->nText-i)*sizeof(XMLCSTR)); else { free(p); d->pText=NULL; } + removeOrderElement(d,eNodeText,i); +} + +void XMLNode::deleteText(XMLCSTR lpszValue) { deleteText(indexText(lpszValue)); } + +XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, int i) +{ + if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; } + if (i>=d->nText) return addText_WOSD(lpszNewValue); + XMLCSTR *p=d->pText+i; + if (*p!=lpszNewValue) { free((void*)*p); *p=lpszNewValue; } + return lpszNewValue; +} + +XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue) +{ + if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; } + int i=indexText(lpszOldValue); + if (i>=0) return updateText_WOSD(lpszNewValue,i); + return addText_WOSD(lpszNewValue); +} + +void XMLNode::deleteClear(int i) +{ + if ((!d)||(i<0)||(i>=d->nClear)) return; + d->nClear--; + XMLClear *p=d->pClear+i; + free((void*)p->lpszValue); + if (d->nClear) memmove(p,p+1,(d->nClear-i)*sizeof(XMLClear)); else { free(p); d->pClear=NULL; } + removeOrderElement(d,eNodeClear,i); +} + +int XMLNode::indexClear(XMLCSTR lpszValue) const +{ + if (!d) return -1; + int i,l=d->nClear; + if (!lpszValue) { if (l) return 0; return -1; } + XMLClear *p=d->pClear; + for (i=0; ilpszValue); } + +XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, int i) +{ + if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; } + if (i>=d->nClear) return addClear_WOSD(lpszNewContent); + XMLClear *p=d->pClear+i; + if (lpszNewContent!=p->lpszValue) { free((void*)p->lpszValue); p->lpszValue=lpszNewContent; } + return p; +} + +XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, XMLCSTR lpszOldValue) +{ + if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; } + int i=indexClear(lpszOldValue); + if (i>=0) return updateClear_WOSD(lpszNewContent,i); + return addClear_WOSD(lpszNewContent); +} + +XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP,XMLClear *oldP) +{ + if (oldP) return updateClear_WOSD((XMLSTR)newP->lpszValue,(XMLSTR)oldP->lpszValue); + return NULL; +} + +XMLNode& XMLNode::operator=( const XMLNode& A ) +{ + // shallow copy + if (this != &A) + { + deleteNodeContent(); + d=A.d; + if (d) (d->ref_count) ++ ; + } + return *this; +} + +XMLNode::XMLNode(const XMLNode &A) +{ + // shallow copy + d=A.d; + if (d) (d->ref_count)++ ; +} + +int XMLNode::nChildNode(XMLCSTR name) const +{ + if (!d) return 0; + int i,j=0,n=d->nChild; + XMLNode *pc=d->pChild; + for (i=0; id->lpszName, name)==0) j++; + pc++; + } + return j; +} + +XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const +{ + if (!d) return emptyXMLNode; + int i=0,n=d->nChild; + if (j) i=*j; + XMLNode *pc=d->pChild+i; + for (; id->lpszName, name)==0) + { + if (j) *j=i+1; + return *pc; + } + pc++; + } + return emptyXMLNode; +} + +XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const +{ + if (!d) return emptyXMLNode; + int i=0; + while (j-->0) getChildNode(name,&i); + return getChildNode(name,&i); +} + +int XMLNode::positionOfText (int i) const { if (i>=d->nText ) i=d->nText-1; return findPosition(d,i,eNodeText ); } +int XMLNode::positionOfClear (int i) const { if (i>=d->nClear) i=d->nClear-1; return findPosition(d,i,eNodeClear); } +int XMLNode::positionOfChildNode(int i) const { if (i>=d->nChild) i=d->nChild-1; return findPosition(d,i,eNodeChild); } +int XMLNode::positionOfText (XMLCSTR lpszValue) const { return positionOfText (indexText (lpszValue)); } +int XMLNode::positionOfClear(XMLCSTR lpszValue) const { return positionOfClear(indexClear(lpszValue)); } +int XMLNode::positionOfClear(XMLClear *a) const { if (a) return positionOfClear(a->lpszValue); return positionOfClear(); } +int XMLNode::positionOfChildNode(XMLNode x) const +{ + if ((!d)||(!x.d)) return -1; + XMLNodeData *dd=x.d; + XMLNode *pc=d->pChild; + int i=d->nChild; + while (i--) if (pc[i].d==dd) return findPosition(d,i,eNodeChild); + return -1; +} +int XMLNode::positionOfChildNode(XMLCSTR name, int count) const +{ + if (!name) return positionOfChildNode(count); + int j=0; + do { getChildNode(name,&j); if (j<0) return -1; } while (count--); + return findPosition(d,j-1,eNodeChild); +} + +XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name,XMLCSTR attributeName,XMLCSTR attributeValue, int *k) const +{ + int i=0,j; + if (k) i=*k; + XMLNode x; + XMLCSTR t; + do + { + x=getChildNode(name,&i); + if (!x.isEmpty()) + { + if (attributeValue) + { + j=0; + do + { + t=x.getAttribute(attributeName,&j); + if (t&&(_tcsicmp(attributeValue,t)==0)) { if (k) *k=i+1; return x; } + } while (t); + } else + { + if (x.isAttributeSet(attributeName)) { if (k) *k=i+1; return x; } + } + } + } while (!x.isEmpty()); + return emptyXMLNode; +} + +// Find an attribute on an node. +XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const +{ + if (!d) return NULL; + int i=0,n=d->nAttribute; + if (j) i=*j; + XMLAttribute *pAttr=d->pAttribute+i; + for (; ilpszName, lpszAttrib)==0) + { + if (j) *j=i+1; + return pAttr->lpszValue; + } + pAttr++; + } + return NULL; +} + +char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const +{ + if (!d) return FALSE; + int i,n=d->nAttribute; + XMLAttribute *pAttr=d->pAttribute; + for (i=0; ilpszName, lpszAttrib)==0) + { + return TRUE; + } + pAttr++; + } + return FALSE; +} + +XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const +{ + if (!d) return NULL; + int i=0; + while (j-->0) getAttribute(name,&i); + return getAttribute(name,&i); +} + +XMLNodeContents XMLNode::enumContents(int i) const +{ + XMLNodeContents c; + if (!d) { c.type=eNodeNULL; return c; } + if (inAttribute) + { + c.type=eNodeAttribute; + c.attrib=d->pAttribute[i]; + return c; + } + i-=d->nAttribute; + c.type=(XMLElementType)(d->pOrder[i]&3); + i=(d->pOrder[i])>>2; + switch (c.type) + { + case eNodeChild: c.child = d->pChild[i]; break; + case eNodeText: c.text = d->pText[i]; break; + case eNodeClear: c.clear = d->pClear[i]; break; + default: break; + } + return c; +} + +XMLCSTR XMLNode::getName() const { if (!d) return NULL; return d->lpszName; } +int XMLNode::nText() const { if (!d) return 0; return d->nText; } +int XMLNode::nChildNode() const { if (!d) return 0; return d->nChild; } +int XMLNode::nAttribute() const { if (!d) return 0; return d->nAttribute; } +int XMLNode::nClear() const { if (!d) return 0; return d->nClear; } +int XMLNode::nElement() const { if (!d) return 0; return d->nAttribute+d->nChild+d->nText+d->nClear; } +XMLClear XMLNode::getClear (int i) const { if ((!d)||(i>=d->nClear )) return emptyXMLClear; return d->pClear[i]; } +XMLAttribute XMLNode::getAttribute (int i) const { if ((!d)||(i>=d->nAttribute)) return emptyXMLAttribute; return d->pAttribute[i]; } +XMLCSTR XMLNode::getAttributeName (int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszName; } +XMLCSTR XMLNode::getAttributeValue(int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszValue; } +XMLCSTR XMLNode::getText (int i) const { if ((!d)||(i>=d->nText )) return NULL; return d->pText[i]; } +XMLNode XMLNode::getChildNode (int i) const { if ((!d)||(i>=d->nChild )) return emptyXMLNode; return d->pChild[i]; } +XMLNode XMLNode::getParentNode ( ) const { if ((!d)||(!d->pParent )) return emptyXMLNode; return XMLNode(d->pParent); } +char XMLNode::isDeclaration ( ) const { if (!d) return 0; return d->isDeclaration; } +char XMLNode::isEmpty ( ) const { return (d==NULL); } +XMLNode XMLNode::emptyNode ( ) { return XMLNode::emptyXMLNode; } + +XMLNode XMLNode::addChild(XMLCSTR lpszName, char isDeclaration, int pos) + { return addChild_priv(0,stringDup(lpszName),isDeclaration,pos); } +XMLNode XMLNode::addChild_WOSD(XMLSTR lpszName, char isDeclaration, int pos) + { return addChild_priv(0,lpszName,isDeclaration,pos); } +XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue) + { return addAttribute_priv(0,stringDup(lpszName),stringDup(lpszValue)); } +XMLAttribute *XMLNode::addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValuev) + { return addAttribute_priv(0,lpszName,lpszValuev); } +XMLCSTR XMLNode::addText(XMLCSTR lpszValue, int pos) + { return addText_priv(0,stringDup(lpszValue),pos); } +XMLCSTR XMLNode::addText_WOSD(XMLSTR lpszValue, int pos) + { return addText_priv(0,lpszValue,pos); } +XMLClear *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos) + { return addClear_priv(0,stringDup(lpszValue),lpszOpen,lpszClose,pos); } +XMLClear *XMLNode::addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos) + { return addClear_priv(0,lpszValue,lpszOpen,lpszClose,pos); } +XMLCSTR XMLNode::updateName(XMLCSTR lpszName) + { return updateName_WOSD(stringDup(lpszName)); } +XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute) + { return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),stringDup(newAttribute->lpszName),oldAttribute->lpszName); } +XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i) + { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),i); } +XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName) + { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),lpszOldName); } +XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, int i) + { return updateText_WOSD(stringDup(lpszNewValue),i); } +XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) + { return updateText_WOSD(stringDup(lpszNewValue),lpszOldValue); } +XMLClear *XMLNode::updateClear(XMLCSTR lpszNewContent, int i) + { return updateClear_WOSD(stringDup(lpszNewContent),i); } +XMLClear *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) + { return updateClear_WOSD(stringDup(lpszNewValue),lpszOldValue); } +XMLClear *XMLNode::updateClear(XMLClear *newP,XMLClear *oldP) + { return updateClear_WOSD(stringDup(newP->lpszValue),oldP->lpszValue); } + +char XMLNode::setGlobalOptions(XMLCharEncoding _characterEncoding, char _guessWideCharChars, char _dropWhiteSpace) +{ + guessWideCharChars=_guessWideCharChars; dropWhiteSpace=_dropWhiteSpace; +#ifdef _XMLWIDECHAR + if (_characterEncoding) characterEncoding=_characterEncoding; +#else + switch(_characterEncoding) + { + case encoding_UTF8: characterEncoding=_characterEncoding; XML_ByteTable=XML_utf8ByteTable; break; + case encoding_ascii: characterEncoding=_characterEncoding; XML_ByteTable=XML_asciiByteTable; break; + case encoding_ShiftJIS: characterEncoding=_characterEncoding; XML_ByteTable=XML_sjisByteTable; break; + default: return 1; + } +#endif + return 0; +} + +XMLNode::XMLCharEncoding XMLNode::guessCharEncoding(void *buf,int l, char useXMLEncodingAttribute) +{ +#ifdef _XMLWIDECHAR + return (XMLCharEncoding)0; +#else + if (l<25) return (XMLCharEncoding)0; + if (guessWideCharChars&&(myIsTextWideChar(buf,l))) return (XMLCharEncoding)0; + unsigned char *b=(unsigned char*)buf; + if ((b[0]==0xef)&&(b[1]==0xbb)&&(b[2]==0xbf)) return encoding_UTF8; + + // Match utf-8 model ? + XMLCharEncoding bestGuess=encoding_UTF8; + int i=0; + while (i>2 ]; + *(curr++)=base64EncodeTable[(inbuf[0]<<4)&0x3F]; + *(curr++)=base64Fillchar; + *(curr++)=base64Fillchar; + } else if (eLen==2) + { + j=(inbuf[0]<<8)|inbuf[1]; + *(curr++)=base64EncodeTable[ j>>10 ]; + *(curr++)=base64EncodeTable[(j>> 4)&0x3f]; + *(curr++)=base64EncodeTable[(j<< 2)&0x3f]; + *(curr++)=base64Fillchar; + } + *(curr++)=0; + return (XMLSTR)buf; +} + +unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data,XMLError *xe) +{ + if (xe) *xe=eXMLErrorNone; + int size=0; + unsigned char c; + //skip any extra characters (e.g. newlines or spaces) + while (*data) + { +#ifdef _XMLWIDECHAR + if (*data>255) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } +#endif + c=base64DecodeTable[(unsigned char)(*data)]; + if (c<97) size++; + else if (c==98) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } + data++; + } + if (xe&&(size%4!=0)) *xe=eXMLErrorBase64DataSizeIsNotMultipleOf4; + if (size==0) return 0; + do { data--; size--; } while(*data==base64Fillchar); size++; + return (unsigned int)((size*3)/4); +} + +unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf, int len, XMLError *xe) +{ + if (xe) *xe=eXMLErrorNone; + int i=0,p=0; + unsigned char d,c; + for(;;) + { + +#ifdef _XMLWIDECHAR +#define BASE64DECODE_READ_NEXT_CHAR(c) \ + do { \ + if (data[i]>255){ c=98; break; } \ + c=base64DecodeTable[(unsigned char)data[i++]]; \ + }while (c==97); \ + if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } +#else +#define BASE64DECODE_READ_NEXT_CHAR(c) \ + do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97); \ + if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } +#endif + + BASE64DECODE_READ_NEXT_CHAR(c) + if (c==99) { return 2; } + if (c==96) + { + if (p==(int)len) return 2; + if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; + return 1; + } + + BASE64DECODE_READ_NEXT_CHAR(d) + if ((d==99)||(d==96)) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + if (p==(int)len) { if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; return 0; } + buf[p++]=(unsigned char)((c<<2)|((d>>4)&0x3)); + + BASE64DECODE_READ_NEXT_CHAR(c) + if (c==99) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + if (p==(int)len) + { + if (c==96) return 2; + if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; + return 0; + } + if (c==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + buf[p++]=(unsigned char)(((d<<4)&0xf0)|((c>>2)&0xf)); + + BASE64DECODE_READ_NEXT_CHAR(d) + if (d==99 ) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + if (p==(int)len) + { + if (d==96) return 2; + if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; + return 0; + } + if (d==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + buf[p++]=(unsigned char)(((c<<6)&0xc0)|d); + } +} +#undef BASE64DECODE_READ_NEXT_CHAR + +void XMLParserBase64Tool::alloc(int newsize) +{ + if ((!buf)&&(newsize)) { buf=malloc(newsize); buflen=newsize; return; } + if (newsize>buflen) { buf=realloc(buf,newsize); buflen=newsize; } +} + +unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe) +{ + if (xe) *xe=eXMLErrorNone; + unsigned int len=decodeSize(data,xe); + if (outlen) *outlen=len; + if (!len) return NULL; + alloc(len+1); + if(!decode(data,(unsigned char*)buf,len,xe)){ return NULL; } + return (unsigned char*)buf; +} + diff --git a/libs/libmsn/xmlParser.h b/libs/libmsn/xmlParser.h new file mode 100755 index 0000000..dc3fcb4 --- /dev/null +++ b/libs/libmsn/xmlParser.h @@ -0,0 +1,553 @@ +/** + **************************************************************************** + *

XML.c - implementation file for basic XML parser written in ANSI C++ + * for portability. It works by using recursion and a node tree for breaking + * down the elements of an XML document.

+ * + * @version V2.29 + * @author Frank Vanden Berghen + * + * BSD license: + * Copyright (c) 2002, Frank Vanden Berghen + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Frank Vanden Berghen nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + */ +#ifndef __INCLUDE_XML_NODE__ +#define __INCLUDE_XML_NODE__ + +#include + +#ifdef _UNICODE + // If you comment the next "define" line then the library will never "switch to" _UNICODE (wchar_t*) mode (16/32 bits per characters). + // This is useful when you get error messages like: + // 'XMLNode::openFileHelper' : cannot convert parameter 2 from 'const char [5]' to 'const wchar_t *' + // The _XMLWIDECHAR preprocessor variable force the XMLParser library into either utf16/32-mode (the proprocessor variable + // must be defined) or utf8-mode(the pre-processor variable must be undefined). +#define _XMLWIDECHAR +#endif + +#if defined(WIN32) || defined(UNDER_CE) + // comment the next line if you are under windows and the compiler is not Microsoft Visual Studio (6.0 or .NET) +#define _XMLWINDOWS +#endif + +#ifdef XMLDLLENTRY +#undef XMLDLLENTRY +#endif +#ifdef _USE_XMLPARSER_DLL +#ifdef _DLL_EXPORTS_ +#define XMLDLLENTRY __declspec(dllexport) +#else +#define XMLDLLENTRY __declspec(dllimport) +#endif +#else +#define XMLDLLENTRY +#endif + + // uncomment the next line if you want no support for wchar_t* (no need for the or libraries anymore to compile) + //#define XML_NO_WIDE_CHAR + +#ifdef XML_NO_WIDE_CHAR +#undef _XMLWINDOWS +#undef _XMLWIDECHAR +#endif + +#ifdef _XMLWINDOWS +#include +#else +#define XMLDLLENTRY +#ifndef XML_NO_WIDE_CHAR +#include // to have 'wcsrtombs' for ANSI version + // to have 'mbsrtowcs' for WIDECHAR version +#endif +#endif + + // Some common types for char set portable code +#ifdef _XMLWIDECHAR + #ifndef _T + #define _T(c) L ## c + #endif + #define XMLCSTR const wchar_t * + #define XMLSTR wchar_t * + #define XMLCHAR wchar_t +#else + #ifndef _T + #define _T(c) c + #endif + #define XMLCSTR const char * + #define XMLSTR char * + #define XMLCHAR char +#endif +#ifndef FALSE + #define FALSE 0 +#endif /* FALSE */ +#ifndef TRUE + #define TRUE 1 +#endif /* TRUE */ + + + // Enumeration for XML parse errors. + typedef enum XMLError + { + eXMLErrorNone = 0, + eXMLErrorMissingEndTag, + eXMLErrorEmpty, + eXMLErrorFirstNotStartTag, + eXMLErrorMissingTagName, + eXMLErrorMissingEndTagName, + eXMLErrorNoMatchingQuote, + eXMLErrorUnmatchedEndTag, + eXMLErrorUnmatchedEndClearTag, + eXMLErrorUnexpectedToken, + eXMLErrorInvalidTag, + eXMLErrorNoElements, + eXMLErrorFileNotFound, + eXMLErrorFirstTagNotFound, + eXMLErrorUnknownCharacterEntity, + eXMLErrorCharConversionError, + eXMLErrorCannotOpenWriteFile, + eXMLErrorCannotWriteFile, + + eXMLErrorBase64DataSizeIsNotMultipleOf4, + eXMLErrorBase64DecodeIllegalCharacter, + eXMLErrorBase64DecodeTruncatedData, + eXMLErrorBase64DecodeBufferTooSmall + } XMLError; + + + // Enumeration used to manage type of data. Use in conjunction with structure XMLNodeContents + typedef enum XMLElementType + { + eNodeChild=0, + eNodeAttribute=1, + eNodeText=2, + eNodeClear=3, + eNodeNULL=4 + } XMLElementType; + + // Structure used to obtain error details if the parse fails. + typedef struct XMLResults + { + enum XMLError error; + int nLine,nColumn; + } XMLResults; + + // Structure for XML clear (unformatted) node (usually comments) + typedef struct XMLClear { + XMLCSTR lpszValue; XMLCSTR lpszOpenTag; XMLCSTR lpszCloseTag; + } XMLClear; + + // Structure for XML attribute. + typedef struct XMLAttribute { + XMLCSTR lpszName; XMLCSTR lpszValue; + } XMLAttribute; + + struct XMLNodeContents; + + typedef struct XMLDLLENTRY XMLNode + { + private: + + struct XMLNodeDataTag; + + // protected constructors: use one of these four methods to get your first instance of XMLNode: + // - parseString + // - parseFile + // - openFileHelper + // - createXMLTopNode + XMLNode(struct XMLNodeDataTag *pParent, XMLSTR lpszName, char isDeclaration); + XMLNode(struct XMLNodeDataTag *p); + + public: + + // You can create your first instance of XMLNode with these 4 functions: + // (see complete explanation of parameters below) + + static XMLNode createXMLTopNode(XMLCSTR lpszName, char isDeclaration=FALSE); + static XMLNode parseString (XMLCSTR lpXMLString, XMLCSTR tag=NULL, XMLResults *pResults=NULL); + static XMLNode parseFile (XMLCSTR filename, XMLCSTR tag=NULL, XMLResults *pResults=NULL); + static XMLNode openFileHelper(XMLCSTR filename, XMLCSTR tag=NULL ); + + // The tag parameter should be the name of the first tag inside the XML file. + // If the tag parameter is omitted, the 3 functions return a node that represents + // the head of the xml document including the declaration term (). + + // The "openFileHelper" reports to the screen all the warnings & errors that occurred during + // parsing of the XML file. Since each application has its own way to report and deal with errors, + // you should rather use the "parseFile" function to parse XML files and program yourself thereafter + // an "error reporting" tailored for your needs (instead of using the very crude "error reporting" + // mechanism included inside the "openFileHelper" function). + + // If the XML document is corrupted: + // * The "openFileHelper" method will: + // - display an error message on the console (or inside a messageBox for windows). + // - stop execution (exit). + // I suggest that you write your own "openFileHelper" method tailored to your needs. + // * The 2 other methods will initialize the "pResults" variable with some information that + // can be used to trace the error. + // * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as + // explained inside the note at the beginning of the "xmlParser.cpp" file. + // You can have a user-friendly explanation of the parsing error with this function: + static XMLCSTR getError(XMLError error); + static XMLCSTR getVersion(); + + XMLCSTR getName() const; // name of the node + XMLCSTR getText(int i=0) const; // return ith text field + int nText() const; // nbr of text field + XMLNode getParentNode() const; // return the parent node + XMLNode getChildNode(int i=0) const; // return ith child node + XMLNode getChildNode(XMLCSTR name, int i) const; // return ith child node with specific name + // (return an empty node if failing) + XMLNode getChildNode(XMLCSTR name, int *i=NULL) const; // return next child node with specific name + // (return an empty node if failing) + XMLNode getChildNodeWithAttribute(XMLCSTR tagName, // return child node with specific name/attribute + XMLCSTR attributeName, // (return an empty node if failing) + XMLCSTR attributeValue=NULL, // + int *i=NULL) const; // + int nChildNode(XMLCSTR name) const; // return the number of child node with specific name + int nChildNode() const; // nbr of child node + XMLAttribute getAttribute(int i=0) const; // return ith attribute + XMLCSTR getAttributeName(int i=0) const; // return ith attribute name + XMLCSTR getAttributeValue(int i=0) const; // return ith attribute value + char isAttributeSet(XMLCSTR name) const; // test if an attribute with a specific name is given + XMLCSTR getAttribute(XMLCSTR name, int i) const; // return ith attribute content with specific name + // (return a NULL if failing) + XMLCSTR getAttribute(XMLCSTR name, int *i=NULL) const; // return next attribute content with specific name + // (return a NULL if failing) + int nAttribute() const; // nbr of attribute + XMLClear getClear(int i=0) const; // return ith clear field (comments) + int nClear() const; // nbr of clear field + XMLSTR createXMLString(int nFormat=1, int *pnSize=NULL) const; // create XML string starting from current XMLNode + // if nFormat==0, no formatting is required + // otherwise this returns an user friendly XML string from a + // given element with appropriate white spaces and carriage returns. + // if pnSize is given it returns the size in character of the string. + XMLError writeToFile(XMLCSTR filename, const char *encoding=NULL, char nFormat=1) const; + // Save the content of an xmlNode inside a file. + // The nFormat parameter has the same meaning as in the + // createXMLString function. If the global parameter + // "characterEncoding==encoding_UTF8", then the "encoding" parameter is + // ignored and always set to "utf-8". If the global parameter + // "characterEncoding==encoding_ShiftJIS", then the "encoding" parameter + // is ignored and always set to "SHIFT-JIS". If "_XMLWIDECHAR=1", then + // the "encoding" parameter is ignored and always set to "utf-16". + // If no "encoding" parameter is given the "ISO-8859-1" encoding is used. + XMLNodeContents enumContents(int i) const; // enumerate all the different contents (attribute,child,text, + // clear) of the current XMLNode. The order is reflecting + // the order of the original file/string. + // NOTE: 0 <= i < nElement(); + int nElement() const; // nbr of different contents for current node + char isEmpty() const; // is this node Empty? + char isDeclaration() const; // is this node a declaration + static XMLNode emptyNode(); // return XMLNode::emptyXMLNode; + +// to allow shallow/fast copy: + ~XMLNode(); + XMLNode(const XMLNode &A); + XMLNode& operator=( const XMLNode& A ); + + XMLNode(): d(NULL){}; + static XMLNode emptyXMLNode; + static XMLClear emptyXMLClear; + static XMLAttribute emptyXMLAttribute; + + // The following functions allows you to create from scratch (or update) a XMLNode structure + // Start by creating your top node with the "createXMLTopNode" function and then add new nodes with the "addChild" function. + // The parameter 'pos' gives the position where the childNode, the text or the XMLClearTag will be inserted. + // The default value (pos=-1) inserts at the end. The value (pos=0) insert at the beginning (Insertion at the beginning is slower than at the end). + // REMARK: 0 <= pos < nChild()+nText()+nClear() + XMLNode addChild(XMLCSTR lpszName, char isDeclaration=FALSE, int pos=-1); + XMLAttribute *addAttribute(XMLCSTR lpszName, XMLCSTR lpszValuev); + XMLCSTR addText(XMLCSTR lpszValue, int pos=-1); + XMLClear *addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, int pos=-1); + // default values: lpszOpen ="" + XMLNode addChild(XMLNode nodeToAdd, int pos=-1); // If the "nodeToAdd" has some parents, it will be detached + // from it's parents before being attached to the current XMLNode + // Some update functions: + XMLCSTR updateName(XMLCSTR lpszName); // change node's name + XMLAttribute *updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); // if the attribute to update is missing, a new one will be added + XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName=NULL,int i=0); // if the attribute to update is missing, a new one will be added + XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName); // set lpszNewName=NULL if you don't want to change the name of the attribute + // if the attribute to update is missing, a new one will be added + XMLCSTR updateText(XMLCSTR lpszNewValue, int i=0); // if the text to update is missing, a new one will be added + XMLCSTR updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); // if the text to update is missing, a new one will be added + XMLClear *updateClear(XMLCSTR lpszNewContent, int i=0); // if the clearTag to update is missing, a new one will be added + XMLClear *updateClear(XMLClear *newP,XMLClear *oldP); // if the clearTag to update is missing, a new one will be added + XMLClear *updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); // if the clearTag to update is missing, a new one will be added + + // Some deletion functions: + void deleteNodeContent(char force=0); // delete the content of this XMLNode and the subtree. + // if force=0, while (references to this node still exist), no memory free occurs + // if force=1, always delete the content of this XMLNode and the subtree and free associated memory + void deleteAttribute(XMLCSTR lpszName); + void deleteAttribute(int i=0); + void deleteAttribute(XMLAttribute *anAttribute); + void deleteText(int i=0); + void deleteText(XMLCSTR lpszValue); + void deleteClear(int i=0); + void deleteClear(XMLClear *p); + void deleteClear(XMLCSTR lpszValue); + + // The strings given as parameters for the following add and update methods (all these methods have + // a name with the postfix "_WOSD" that means "WithOut String Duplication" ) will be free'd by the + // XMLNode class. For example, it means that this is incorrect: + // xNode.addText_WOSD("foo"); + // xNode.updateAttribute_WOSD("#newcolor" ,NULL,"color"); + // In opposition, this is correct: + // xNode.addText("foo"); + // xNode.addText_WOSD(stringDup("foo")); + // xNode.updateAttribute("#newcolor" ,NULL,"color"); + // xNode.updateAttribute_WOSD(stringDup("#newcolor"),NULL,"color"); + // Typically, you will never do: + // char *b=(char*)malloc(...); + // xNode.addText(b); + // free(b); + // ... but rather: + // char *b=(char*)malloc(...); + // xNode.addText_WOSD(b); + // ('free(b)' is performed by the XMLNode class) + + static XMLNode createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration=FALSE); + XMLNode addChild_WOSD(XMLSTR lpszName, char isDeclaration=FALSE, int pos=-1); + XMLAttribute *addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValue); + XMLCSTR addText_WOSD(XMLSTR lpszValue, int pos=-1); + XMLClear *addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, int pos=-1); + + XMLCSTR updateName_WOSD(XMLSTR lpszName); + XMLAttribute *updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); + XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName=NULL,int i=0); + XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName); + XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, int i=0); + XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); + XMLClear *updateClear_WOSD(XMLSTR lpszNewContent, int i=0); + XMLClear *updateClear_WOSD(XMLClear *newP,XMLClear *oldP); + XMLClear *updateClear_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); + + // These are some useful functions when you want to insert a childNode, a text or a XMLClearTag in the + // middle (at a specified position) of a XMLNode tree already constructed. The value returned by these + // methods is to be used as last parameter (parameter 'pos') of addChild, addText or addClear. + int positionOfText(int i=0) const; + int positionOfText(XMLCSTR lpszValue) const; + int positionOfClear(int i=0) const; + int positionOfClear(XMLCSTR lpszValue) const; + int positionOfClear(XMLClear *a) const; + int positionOfChildNode(int i=0) const; + int positionOfChildNode(XMLNode x) const; + int positionOfChildNode(XMLCSTR name, int i=0) const; // return the position of the ith childNode with the specified name + // if (name==NULL) return the position of the ith childNode + + // The setGlobalOptions function allows you to change tree global parameters that affect string&file + // parsing. First of all, you most-probably will never have to change these 3 global parameters. + // The return value of the setGlobalOptions function is "0" when there are no errors. If you try to + // set an unrecognized encoding then the return value will be "1" to signal an error. + // + // About the "guessWideCharChars" parameter: + // If "guessWideCharChars=1" and if this library is compiled in WideChar mode, then the + // "parseFile" and "openFileHelper" functions will test if the file contains ASCII + // characters. If this is the case, then the file will be loaded and converted in memory to + // WideChar before being parsed. If "guessWideCharChars=0", no conversion will + // be performed. + // + // If "guessWideCharChars=1" and if this library is compiled in ASCII/UTF8/char* mode, then the + // "parseFile" and "openFileHelper" functions will test if the file contains WideChar + // characters. If this is the case, then the file will be loaded and converted in memory to + // ASCII/UTF8/char* before being parsed. If "guessWideCharChars=0", no conversion will + // be performed + // + // Sometime, it's useful to set "guessWideCharChars=0" to disable any conversion + // because the test to detect the file-type (ASCII/UTF8/char* or WideChar) may fail (rarely). + // + // About the "characterEncoding" parameter: + // This parameter is only meaningful when compiling in char* mode (multibyte character mode). + // In wchar_t* (wide char mode), this parameter is ignored. This parameter should be one of the + // three currently recognized encodings: XMLNode::encoding_UTF8, XMLNode::encoding_ascii, + // XMLNode::encoding_ShiftJIS. + // + // About the "dropWhiteSpace" parameter: + // In most situations, text fields containing only white spaces (and carriage returns) + // are useless. Even more, these "empty" text fields are annoying because they increase the + // complexity of the user's code for parsing. So, 99% of the time, it's better to drop + // the "empty" text fields. However The XML specification indicates that no white spaces + // should be lost when parsing the file. So to be perfectly XML-compliant, you should set + // dropWhiteSpace=0. A note of caution: if you set "dropWhiteSpace=0", the parser will be + // slower and your code will be more complex. + + // Enumeration for XML character encoding. + typedef enum XMLCharEncoding { encoding_UTF8=1, encoding_ascii=2, encoding_ShiftJIS=3 } XMLCharEncoding; + + static char setGlobalOptions(XMLCharEncoding characterEncoding=XMLNode::encoding_UTF8, char guessWideCharChars=1, char dropWhiteSpace=1); + + // The next function try to guess the character encoding. You most-probably will never + // have to use this function. It then returns the appropriate value of the global parameter + // "characterEncoding" described above. The guess is based on the content of a buffer of length + // "bufLen" bytes that contains the first bytes (minimum 25 bytes; 200 bytes is a good value) of the + // file to be parsed. The "openFileHelper" function is using this function to automatically compute + // the value of the "characterEncoding" global parameter. There are several heuristics used to do the + // guess. One of the heuristic is based on the "encoding" attribute. The original XML specifications + // forbids to use this attribute to do the guess but you can still use it if you set + // "useXMLEncodingAttribute" to 1 (this is the default behavior and the behavior of most parsers). + // If an inconsistency in the encoding is detected, then the return value is "0". + + static XMLCharEncoding guessCharEncoding(void *buffer, int bufLen, char useXMLEncodingAttribute=1); + + private: + +// these are functions and structures used internally by the XMLNode class (don't bother about them): + + typedef struct XMLNodeDataTag // to allow shallow copy and "intelligent/smart" pointers (automatic delete): + { + XMLCSTR lpszName; // Element name (=NULL if root) + int nChild, // Number of child nodes + nText, // Number of text fields + nClear, // Number of Clear fields (comments) + nAttribute; // Number of attributes + char isDeclaration; // Whether node is an XML declaration - '' + struct XMLNodeDataTag *pParent; // Pointer to parent element (=NULL if root) + XMLNode *pChild; // Array of child nodes + XMLCSTR *pText; // Array of text fields + XMLClear *pClear; // Array of clear fields + XMLAttribute *pAttribute; // Array of attributes + int *pOrder; // order of the child_nodes,text_fields,clear_fields + int ref_count; // for garbage collection (smart pointers) + } XMLNodeData; + XMLNodeData *d; + + char parseClearTag(void *px, void *pa); + char maybeAddTxT(void *pa, XMLCSTR tokenPStr); + int ParseXMLElement(void *pXML); + void *addToOrder(int memInc, int *_pos, int nc, void *p, int size, XMLElementType xtype); + int indexText(XMLCSTR lpszValue) const; + int indexClear(XMLCSTR lpszValue) const; + XMLNode addChild_priv(int,XMLSTR,char,int); + XMLAttribute *addAttribute_priv(int,XMLSTR,XMLSTR); + XMLCSTR addText_priv(int,XMLSTR,int); + XMLClear *addClear_priv(int,XMLSTR,XMLCSTR,XMLCSTR,int); + static inline int findPosition(XMLNodeData *d, int index, XMLElementType xtype); + static int CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int length, int nFormat); + static int removeOrderElement(XMLNodeData *d, XMLElementType t, int index); + static void exactMemory(XMLNodeData *d); + static int detachFromParent(XMLNodeData *d); +} XMLNode; + +// This structure is given by the function "enumContents". +typedef struct XMLNodeContents +{ + // This dictates what's the content of the XMLNodeContent + enum XMLElementType type; + // should be an union to access the appropriate data. + // compiler does not allow union of object with constructor... too bad. + XMLNode child; + XMLAttribute attrib; + XMLCSTR text; + XMLClear clear; + +} XMLNodeContents; + +XMLDLLENTRY void freeXMLString(XMLSTR t); // {free(t);} + +// Duplicate (copy in a new allocated buffer) the source string. This is +// a very handy function when used with all the "XMLNode::*_WOSD" functions. +// (If (cbData!=0) then cbData is the number of chars to duplicate) +XMLDLLENTRY XMLSTR stringDup(XMLCSTR source, int cbData=0); + +// The following class is processing strings so that all the characters +// &,",',<,> are replaced by their XML equivalent: &, ", ', <, >. +// This class is useful when creating from scratch an XML file using the +// "printf", "fprintf", "cout",... functions. If you are creating from scratch an +// XML file using the provided XMLNode class you must not use the "ToXMLStringTool" +// class (the "XMLNode" class does the processing job for you during rendering). +// Using the "ToXMLStringTool class" and the "fprintf function" is THE most efficient +// way to produce VERY large XML documents VERY fast. +typedef struct XMLDLLENTRY ToXMLStringTool +{ +public: + ToXMLStringTool(): buf(NULL),buflen(0){} + ~ToXMLStringTool(); + void freeBuffer(); + + XMLSTR toXML(XMLCSTR source); + + // The next function is deprecated because there is a possibility of + // "destination-buffer-overflow". It converts the string + // "source" to the string "dest". + static XMLSTR toXMLUnSafe(XMLSTR dest,XMLCSTR source); + +private: + XMLSTR buf; + int buflen; +}ToXMLStringTool; + +// Below is a class that allows you to include any binary data (images, sounds,...) +// into an XML document using "Base64 encoding". This class is completely +// separated from the rest of the xmlParser library and can be removed without any problem. +// To include some binary data into an XML file, you must convert the binary data into +// standard text (using "encode"). To retrieve the original binary data from the +// b64-encoded text included inside the XML file use "decode". Alternatively, these +// functions can also be used to "encrypt/decrypt" some critical data contained inside +// the XML (it's not a strong encryption at all, but sometimes it can be useful). + +typedef struct XMLDLLENTRY XMLParserBase64Tool +{ +public: + XMLParserBase64Tool(): buf(NULL),buflen(0){} + ~XMLParserBase64Tool(); + void freeBuffer(); + + // returns the length of the base64 string that encodes a data buffer of size inBufLen bytes. + // If "formatted" parameter is true, some space will be reserved for a carriage-return every 72 chars. + static int encodeLength(int inBufLen, char formatted=0); + + // The "base64Encode" function returns a string containing the base64 encoding of "inByteLen" bytes + // from "inByteBuf". If "formatted" parameter is true, then there will be a carriage-return every 72 chars. + // The string will be free'd when the XMLParserBase64Tool object is deleted. + // All returned strings are sharing the same memory space. + XMLSTR encode(unsigned char *inByteBuf, unsigned int inByteLen, char formatted=0); + + // returns the number of bytes which will be decoded from "inString". + static unsigned int decodeSize(XMLCSTR inString, XMLError *xe=NULL); + + // returns a pointer to a buffer containing the binary data decoded from "inString" + // If "inString" is malformed NULL will be returned + // The output buffer will be free'd when the XMLParserBase64Tool object is deleted. + // All output buffer are sharing the same memory space. + unsigned char* decode(XMLCSTR inString, int *outByteLen=NULL, XMLError *xe=NULL); + + // The next function is deprecated. + // decodes data from "inString" to "outByteBuf". You need to provide the size (in byte) of "outByteBuf" + // in "inMaxByteOutBuflen". If "outByteBuf" is not large enough or if data is malformed, then "FALSE" + // will be returned; otherwise "TRUE". + static unsigned char decode(XMLCSTR inString, unsigned char *outByteBuf, int inMaxByteOutBuflen, XMLError *xe=NULL); + +private: + void *buf; + int buflen; + void alloc(int newsize); +}XMLParserBase64Tool; + +#undef XMLDLLENTRY + +#endif