Merging caya and caya-gpl-protocols
This commit is contained in:
commit
830f1216f5
10
Contributors
10
Contributors
|
@ -10,14 +10,17 @@ We will add you!
|
||||||
|
|
||||||
* Andrea Anzani <andrea.anzani@gmail.com>
|
* Andrea Anzani <andrea.anzani@gmail.com>
|
||||||
* Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
* Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
||||||
|
* Dario Casalinuovo <b.vitruvio@gmail.com>
|
||||||
|
|
||||||
== Code ==
|
== Code ==
|
||||||
|
|
||||||
Contributors in alphabetical order by last name:
|
Contributors in alphabetical order by last name:
|
||||||
|
|
||||||
* Alexander Botero-Lowry
|
* Alexander Botero-Lowry
|
||||||
|
|
||||||
* Oliver Ruiz Dorantes <oliver.ruiz.dorantes@gmail.com>
|
* Oliver Ruiz Dorantes <oliver.ruiz.dorantes@gmail.com>
|
||||||
* Rene Gollent <anevilyak@gmail.com>
|
* Rene Gollent <anevilyak@gmail.com>
|
||||||
|
* Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
|
||||||
|
|
||||||
== Icons ==
|
== Icons ==
|
||||||
|
|
||||||
|
@ -26,3 +29,10 @@ We will add you!
|
||||||
* Caya
|
* Caya
|
||||||
* AIM protocol
|
* AIM protocol
|
||||||
* ICQ protocol
|
* ICQ protocol
|
||||||
|
* GoogleTalk protocol
|
||||||
|
* MSN protocol
|
||||||
|
|
||||||
|
Mark Erben <markerben@googlemail.com>
|
||||||
|
|
||||||
|
* Facebook protocol
|
||||||
|
|
||||||
|
|
12
Jamrules
12
Jamrules
|
@ -45,11 +45,23 @@ LOCATE on $(HCACHEFILE) $(JCACHEFILE) = $(GENERATED_DIR) ;
|
||||||
# Perform configuration checks
|
# Perform configuration checks
|
||||||
include [ FDirName $(JAM_DIR) CheckRules ] ;
|
include [ FDirName $(JAM_DIR) CheckRules ] ;
|
||||||
CheckGccPlatform ;
|
CheckGccPlatform ;
|
||||||
|
|
||||||
#CheckCurl ;
|
#CheckCurl ;
|
||||||
#if ! $(HAVE_CURL) {
|
#if ! $(HAVE_CURL) {
|
||||||
# Echo "** Caya needs Curl" ;
|
# Echo "** Caya needs Curl" ;
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
CheckCaya ;
|
||||||
|
if ! $(HAVE_CAYA) {
|
||||||
|
Echo "** Caya library is needed!" ;
|
||||||
|
Exit 1 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckOpenSSL ;
|
||||||
|
if ! $(HAVE_OPENSSL) {
|
||||||
|
Echo "** MSN, Jabber, GoogleTalk and Facebook protocols are disabled for lack of OpenSSL" ;
|
||||||
|
}
|
||||||
|
|
||||||
# Include jam scripts
|
# Include jam scripts
|
||||||
include [ FDirName $(JAM_DIR) HelperRules ] ;
|
include [ FDirName $(JAM_DIR) HelperRules ] ;
|
||||||
include [ FDirName $(JAM_DIR) ConfigRules ] ;
|
include [ FDirName $(JAM_DIR) ConfigRules ] ;
|
||||||
|
|
344
License
344
License
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
Copyright (c) 2009-2010, Andrea Anzani
|
Copyright (c) 2009-2010, Andrea Anzani
|
||||||
|
Copyright (c) 2009-2012, Dario Casalinuovo
|
||||||
Copyright (c) 2009-2010, Pier Luigi Fiorini
|
Copyright (c) 2009-2010, Pier Luigi Fiorini
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
@ -24,3 +26,345 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
/*************************************************************/
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 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 Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
|
|
|
@ -75,3 +75,59 @@ rule CheckCurl
|
||||||
HAVE_CURL = $(haveLibs) ;
|
HAVE_CURL = $(haveLibs) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rule CheckLibYahoo2
|
||||||
|
{
|
||||||
|
# CheckLibYahoo2
|
||||||
|
# Check for LibYahoo2 and defined HAVE_LIBYAHOO2 according, it also defines
|
||||||
|
# LIBYAHOO2_INCLUDE_DIR and LIBYAHOO2_LIBRARY_DIR with location of respectively
|
||||||
|
# include and library files.
|
||||||
|
|
||||||
|
HAVE_LIBYAHOO2 = ;
|
||||||
|
LIBYAHOO2_INCLUDE_DIR = ;
|
||||||
|
LIBYAHOO2_LIBRARY_DIR = ;
|
||||||
|
|
||||||
|
local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/libyahoo2 : yahoo2.h ] ;
|
||||||
|
if $(haveHeaders) {
|
||||||
|
LIBYAHOO2_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/libyahoo2 ;
|
||||||
|
|
||||||
|
local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libyahoo2.so ] ;
|
||||||
|
if $(haveLibs) {
|
||||||
|
LIBYAHOO2_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ;
|
||||||
|
|
||||||
|
Echo Yahoo Headers: $(LIBYAHOO2_INCLUDE_DIR) ;
|
||||||
|
Echo Yahoo Libs: $(LIBYAHOO2_LIBRARY_DIR) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAVE_LIBYAHOO2 = $(haveLibs) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule CheckCaya
|
||||||
|
{
|
||||||
|
# CheckCaya
|
||||||
|
# Check for Caya and defined HAVE_CAYA according, it also defines
|
||||||
|
# CAYA_INCLUDE_DIR and CAYA_LIBRARY_DIR with location of respectively
|
||||||
|
# include and library files.
|
||||||
|
|
||||||
|
HAVE_CAYA = ;
|
||||||
|
CAYA_INCLUDE_DIR = ;
|
||||||
|
CAYA_LIBRARY_DIR = ;
|
||||||
|
|
||||||
|
local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/caya : CayaProtocol.h ] ;
|
||||||
|
if $(haveHeaders) {
|
||||||
|
CAYA_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/caya ;
|
||||||
|
|
||||||
|
# local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libinfopopper.so ] ;
|
||||||
|
# if $(haveLibs) {
|
||||||
|
# CAYA_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ;
|
||||||
|
|
||||||
|
Echo Caya Headers: $(CAYA_INCLUDE_DIR) ;
|
||||||
|
# Echo Caya Libs: $(CAYA_LIBRARY_DIR) ;
|
||||||
|
# }
|
||||||
|
|
||||||
|
# HAVE_CAYA = $(haveLibs) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAVE_CAYA = $(haveHeaders) ;
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,3 +6,7 @@ SubInclude TOP libs librunview ;
|
||||||
SubInclude TOP libs libsupport ;
|
SubInclude TOP libs libsupport ;
|
||||||
SubInclude TOP libs libinterface ;
|
SubInclude TOP libs libinterface ;
|
||||||
SubInclude TOP libs libimcomm ;
|
SubInclude TOP libs libimcomm ;
|
||||||
|
SubInclude TOP libs libgloox ;
|
||||||
|
SubInclude TOP libs libmsn ;
|
||||||
|
SubInclude TOP libs libyahoo2 ;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
SubDir TOP libs libgloox ;
|
||||||
|
|
||||||
|
SubDirSysHdrs [ FDirName $(TOP) ] ;
|
||||||
|
SubDirSysHdrs [ FDirName $(TOP) libs ] ;
|
||||||
|
SubDirSysHdrs [ FDirName $(OPENSSL_INCLUDE_DIR) ] ;
|
||||||
|
|
||||||
|
SEARCH_SOURCE += [ FDirName $(TOP) libs ] ;
|
||||||
|
|
||||||
|
StaticLibrary libgloox.a :
|
||||||
|
adhoc.cpp
|
||||||
|
amp.cpp
|
||||||
|
annotations.cpp
|
||||||
|
attention.cpp
|
||||||
|
base64.cpp
|
||||||
|
bookmarkstorage.cpp
|
||||||
|
capabilities.cpp
|
||||||
|
chatstate.cpp
|
||||||
|
chatstatefilter.cpp
|
||||||
|
client.cpp
|
||||||
|
clientbase.cpp
|
||||||
|
component.cpp
|
||||||
|
compressiondefault.cpp
|
||||||
|
compressionzlib.cpp
|
||||||
|
connectionbosh.cpp
|
||||||
|
connectionhttpproxy.cpp
|
||||||
|
connectionsocks5proxy.cpp
|
||||||
|
connectiontcpbase.cpp
|
||||||
|
connectiontcpclient.cpp
|
||||||
|
connectiontcpserver.cpp
|
||||||
|
connectiontls.cpp
|
||||||
|
connectiontlsserver.cpp
|
||||||
|
dataform.cpp
|
||||||
|
dataformfield.cpp
|
||||||
|
dataformfieldcontainer.cpp
|
||||||
|
dataformitem.cpp
|
||||||
|
dataformreported.cpp
|
||||||
|
delayeddelivery.cpp
|
||||||
|
disco.cpp
|
||||||
|
dns.cpp
|
||||||
|
error.cpp
|
||||||
|
eventdispatcher.cpp
|
||||||
|
featureneg.cpp
|
||||||
|
flexoff.cpp
|
||||||
|
gloox.cpp
|
||||||
|
gpgencrypted.cpp
|
||||||
|
gpgsigned.cpp
|
||||||
|
inbandbytestream.cpp
|
||||||
|
instantmucroom.cpp
|
||||||
|
iq.cpp
|
||||||
|
jid.cpp
|
||||||
|
lastactivity.cpp
|
||||||
|
logsink.cpp
|
||||||
|
md5.cpp
|
||||||
|
message.cpp
|
||||||
|
messageevent.cpp
|
||||||
|
messageeventfilter.cpp
|
||||||
|
messagefilter.cpp
|
||||||
|
messagesession.cpp
|
||||||
|
mucmessagesession.cpp
|
||||||
|
mucroom.cpp
|
||||||
|
mutex.cpp
|
||||||
|
nickname.cpp
|
||||||
|
nonsaslauth.cpp
|
||||||
|
oob.cpp
|
||||||
|
parser.cpp
|
||||||
|
prep.cpp
|
||||||
|
presence.cpp
|
||||||
|
privacyitem.cpp
|
||||||
|
privacymanager.cpp
|
||||||
|
privatexml.cpp
|
||||||
|
pubsubevent.cpp
|
||||||
|
pubsubitem.cpp
|
||||||
|
pubsubmanager.cpp
|
||||||
|
receipt.cpp
|
||||||
|
registration.cpp
|
||||||
|
rosteritem.cpp
|
||||||
|
rostermanager.cpp
|
||||||
|
search.cpp
|
||||||
|
sha.cpp
|
||||||
|
shim.cpp
|
||||||
|
simanager.cpp
|
||||||
|
siprofileft.cpp
|
||||||
|
socks5bytestream.cpp
|
||||||
|
socks5bytestreammanager.cpp
|
||||||
|
socks5bytestreamserver.cpp
|
||||||
|
softwareversion.cpp
|
||||||
|
stanza.cpp
|
||||||
|
stanzaextensionfactory.cpp
|
||||||
|
subscription.cpp
|
||||||
|
tag.cpp
|
||||||
|
tlsdefault.cpp
|
||||||
|
tlsgnutlsbase.cpp
|
||||||
|
tlsgnutlsclient.cpp
|
||||||
|
tlsgnutlsclientanon.cpp
|
||||||
|
tlsgnutlsserveranon.cpp
|
||||||
|
tlsopensslbase.cpp
|
||||||
|
tlsopensslclient.cpp
|
||||||
|
tlsopensslserver.cpp
|
||||||
|
tlsschannel.cpp
|
||||||
|
uniquemucroom.cpp
|
||||||
|
util.cpp
|
||||||
|
vcard.cpp
|
||||||
|
vcardmanager.cpp
|
||||||
|
vcardupdate.cpp
|
||||||
|
xhtmlim.cpp
|
||||||
|
;
|
|
@ -0,0 +1,472 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "adhoc.h"
|
||||||
|
#include "adhochandler.h"
|
||||||
|
#include "adhoccommandprovider.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* cmdActionStringValues[] =
|
||||||
|
{
|
||||||
|
"execute", "cancel", "prev", "next", "complete"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const std::string actionString( Adhoc::Command::Action action )
|
||||||
|
{
|
||||||
|
return util::lookup2( action, cmdActionStringValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* cmdStatusStringValues[] =
|
||||||
|
{
|
||||||
|
"executing", "completed", "canceled"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const std::string statusString( Adhoc::Command::Status status )
|
||||||
|
{
|
||||||
|
return util::lookup( status, cmdStatusStringValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* cmdNoteStringValues[] =
|
||||||
|
{
|
||||||
|
"info", "warn", "error"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const std::string noteString( Adhoc::Command::Note::Severity sev )
|
||||||
|
{
|
||||||
|
return util::lookup( sev, cmdNoteStringValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Adhoc::Command::Note ----
|
||||||
|
Adhoc::Command::Note::Note( const Tag* tag )
|
||||||
|
: m_severity( InvalidSeverity )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "note" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_severity = (Severity)util::deflookup( tag->findAttribute( "type" ), cmdNoteStringValues, Info );
|
||||||
|
m_note = tag->cdata();
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Adhoc::Command::Note::tag() const
|
||||||
|
{
|
||||||
|
if( m_note.empty() || m_severity == InvalidSeverity )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* n = new Tag( "note", m_note );
|
||||||
|
n->addAttribute( TYPE, noteString( m_severity ) );
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
// ---- ~Adhoc::Command::Note ----
|
||||||
|
|
||||||
|
// ---- Adhoc::Command ----
|
||||||
|
Adhoc::Command::Command( const std::string& node, Adhoc::Command::Action action,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_form( form ), m_action( action ),
|
||||||
|
m_status( InvalidStatus ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
|
||||||
|
m_form( form ), m_action( InvalidAction ), m_status( status ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const std::string& node, const std::string& sessionid,
|
||||||
|
Adhoc::Command::Action action,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
|
||||||
|
m_form( form ), m_action( action ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
Action executeAction, int allowedActions,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
|
||||||
|
m_form( form ), m_action( executeAction ), m_status( status ), m_actions( allowedActions )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_form( 0 ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "command" || tag->xmlns() != XMLNS_ADHOC_COMMANDS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
m_sessionid = tag->findAttribute( "sessionid" );
|
||||||
|
m_status = (Status)util::lookup( tag->findAttribute( "status" ), cmdStatusStringValues );
|
||||||
|
|
||||||
|
Tag* a = tag->findChild( "actions" );
|
||||||
|
if( a )
|
||||||
|
{
|
||||||
|
// Multi-stage response
|
||||||
|
m_action = (Action)util::deflookup2( a->findAttribute( "action" ), cmdActionStringValues, Complete );
|
||||||
|
if( a->hasChild( "prev" ) )
|
||||||
|
m_actions |= Previous;
|
||||||
|
if( a->hasChild( "next" ) )
|
||||||
|
m_actions |= Next;
|
||||||
|
if( a->hasChild( "complete" ) )
|
||||||
|
m_actions |= Complete;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_action = (Action)util::deflookup2( tag->findAttribute( "action" ), cmdActionStringValues, Execute );
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConstTagList& l = tag->findTagList( "/command/note" );
|
||||||
|
ConstTagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
m_notes.push_back( new Note( (*it) ) );
|
||||||
|
|
||||||
|
Tag* x = tag->findChild( "x", "xmlns", XMLNS_X_DATA );
|
||||||
|
if( x )
|
||||||
|
m_form = new DataForm( x );
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::~Command()
|
||||||
|
{
|
||||||
|
util::clearList( m_notes );
|
||||||
|
delete m_form;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Adhoc::Command::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/command[@xmlns='" + XMLNS_ADHOC_COMMANDS + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Adhoc::Command::tag() const
|
||||||
|
{
|
||||||
|
if( m_node.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* c = new Tag( "command" );
|
||||||
|
c->setXmlns( XMLNS_ADHOC_COMMANDS );
|
||||||
|
c->addAttribute( "node", m_node );
|
||||||
|
if( m_actions != 0 )
|
||||||
|
{
|
||||||
|
// Multi-stage command response
|
||||||
|
|
||||||
|
if( m_status != InvalidStatus )
|
||||||
|
c->addAttribute( "status", statusString( m_status ) );
|
||||||
|
else
|
||||||
|
c->addAttribute( "status", statusString( Executing ) );
|
||||||
|
|
||||||
|
Tag* actions = new Tag( c, "actions" );
|
||||||
|
|
||||||
|
if( m_action != InvalidAction )
|
||||||
|
c->addAttribute( "execute", actionString( m_action ) );
|
||||||
|
else
|
||||||
|
c->addAttribute( "execute", actionString( Complete ) );
|
||||||
|
|
||||||
|
if( ( m_actions & Previous ) == Previous )
|
||||||
|
new Tag( actions, "prev" );
|
||||||
|
if( ( m_actions & Next ) == Next )
|
||||||
|
new Tag( actions, "next" );
|
||||||
|
if( ( m_actions & Complete ) == Complete )
|
||||||
|
new Tag( actions, "complete" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Single-stage command request/response or Multi-stage command request
|
||||||
|
|
||||||
|
if( m_action != InvalidAction )
|
||||||
|
c->addAttribute( "action", actionString( m_action ) );
|
||||||
|
if( m_status != InvalidStatus )
|
||||||
|
c->addAttribute( "status", statusString( m_status ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !m_sessionid.empty() )
|
||||||
|
c->addAttribute( "sessionid", m_sessionid );
|
||||||
|
|
||||||
|
if( m_form && *m_form )
|
||||||
|
c->addChild( m_form->tag() );
|
||||||
|
|
||||||
|
NoteList::const_iterator it = m_notes.begin();
|
||||||
|
for( ; it != m_notes.end(); ++it )
|
||||||
|
c->addChild( (*it)->tag() );
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
// ---- ~Adhoc::Command ----
|
||||||
|
|
||||||
|
// ---- Adhoc ----
|
||||||
|
Adhoc::Adhoc( ClientBase* parent )
|
||||||
|
: m_parent( parent )
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->addFeature( XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->registerNodeHandler( this, XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->registerNodeHandler( this, EmptyString );
|
||||||
|
m_parent->registerIqHandler( this, ExtAdhocCommand );
|
||||||
|
m_parent->registerStanzaExtension( new Adhoc::Command() );
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::~Adhoc()
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->removeFeature( XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->removeNodeHandler( this, XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->removeNodeHandler( this, EmptyString );
|
||||||
|
m_parent->removeIqHandler( this, ExtAdhocCommand );
|
||||||
|
m_parent->removeIDHandler( this );
|
||||||
|
m_parent->removeStanzaExtension( ExtAdhocCommand );
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList Adhoc::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ )
|
||||||
|
{
|
||||||
|
StringList features;
|
||||||
|
features.push_back( XMLNS_ADHOC_COMMANDS );
|
||||||
|
return features;
|
||||||
|
// return StringList( 1, XMLNS_ADHOC_COMMANDS );
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& /*to*/, const std::string& node )
|
||||||
|
{
|
||||||
|
Disco::ItemList l;
|
||||||
|
if( node.empty() )
|
||||||
|
{
|
||||||
|
l.push_back( new Disco::Item( m_parent->jid(), XMLNS_ADHOC_COMMANDS, "Ad-Hoc Commands" ) );
|
||||||
|
}
|
||||||
|
else if( node == XMLNS_ADHOC_COMMANDS )
|
||||||
|
{
|
||||||
|
StringMap::const_iterator it = m_items.begin();
|
||||||
|
for( ; it != m_items.end(); ++it )
|
||||||
|
{
|
||||||
|
AdhocCommandProviderMap::const_iterator itp = m_adhocCommandProviders.find( (*it).first );
|
||||||
|
if( itp != m_adhocCommandProviders.end()
|
||||||
|
&& (*itp).second
|
||||||
|
&& (*itp).second->handleAdhocAccessRequest( from, (*it).first ) )
|
||||||
|
{
|
||||||
|
l.push_back( new Disco::Item( m_parent->jid(), (*it).first, (*it).second ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::IdentityList Adhoc::handleDiscoNodeIdentities( const JID& /*from*/, const std::string& node )
|
||||||
|
{
|
||||||
|
Disco::IdentityList l;
|
||||||
|
StringMap::const_iterator it = m_items.find( node );
|
||||||
|
l.push_back( new Disco::Identity( "automation",
|
||||||
|
node == XMLNS_ADHOC_COMMANDS ? "command-list" : "command-node",
|
||||||
|
it == m_items.end() ? "Ad-Hoc Commands" : (*it).second ) );
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adhoc::handleIq( const IQ& iq )
|
||||||
|
{
|
||||||
|
if( iq.subtype() != IQ::Set )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand );
|
||||||
|
if( !ac || ac->node().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AdhocCommandProviderMap::const_iterator it = m_adhocCommandProviders.find( ac->node() );
|
||||||
|
if( it != m_adhocCommandProviders.end() )
|
||||||
|
{
|
||||||
|
const std::string& sess = ac->sessionID().empty() ? m_parent->getID() : ac->sessionID();
|
||||||
|
m_activeSessions[sess] = iq.id();
|
||||||
|
(*it).second->handleAdhocCommand( iq.from(), *ac, sess );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleIqID( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
if( context != ExecuteAdhocCommand )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.find( iq.id() );
|
||||||
|
if( it == m_adhocTrackMap.end() || (*it).second.context != context
|
||||||
|
|| (*it).second.remote != iq.from() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Error:
|
||||||
|
(*it).second.ah->handleAdhocError( iq.from(), iq.error() );
|
||||||
|
break;
|
||||||
|
case IQ::Result:
|
||||||
|
{
|
||||||
|
const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand );
|
||||||
|
if( ac )
|
||||||
|
(*it).second.ah->handleAdhocExecutionResult( iq.from(), *ac );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command,
|
||||||
|
const std::string& name )
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->registerNodeHandler( this, command );
|
||||||
|
m_adhocCommandProviders[command] = acp;
|
||||||
|
m_items[command] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleDiscoInfo( const JID& from, const Disco::Info& info, int context )
|
||||||
|
{
|
||||||
|
if( context != CheckAdhocSupport )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
|
||||||
|
for( ; it != m_adhocTrackMap.end() && (*it).second.context != context
|
||||||
|
&& (*it).second.remote != from; ++it )
|
||||||
|
;
|
||||||
|
if( it == m_adhocTrackMap.end() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
(*it).second.ah->handleAdhocSupport( from, info.hasFeature( XMLNS_ADHOC_COMMANDS ) );
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleDiscoItems( const JID& from, const Disco::Items& items, int context )
|
||||||
|
{
|
||||||
|
if( context != FetchAdhocCommands )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
|
||||||
|
for( ; it != m_adhocTrackMap.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it).second.context == context && (*it).second.remote == from )
|
||||||
|
{
|
||||||
|
StringMap commands;
|
||||||
|
const Disco::ItemList& l = items.items();
|
||||||
|
Disco::ItemList::const_iterator it2 = l.begin();
|
||||||
|
for( ; it2 != l.end(); ++it2 )
|
||||||
|
{
|
||||||
|
commands[(*it2)->node()] = (*it2)->name();
|
||||||
|
}
|
||||||
|
(*it).second.ah->handleAdhocCommands( from, commands );
|
||||||
|
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleDiscoError( const JID& from, const Error* error, int context )
|
||||||
|
{
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
|
||||||
|
for( ; it != m_adhocTrackMap.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it).second.context == context && (*it).second.remote == from )
|
||||||
|
{
|
||||||
|
(*it).second.ah->handleAdhocError( from, error );
|
||||||
|
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::checkSupport( const JID& remote, AdhocHandler* ah )
|
||||||
|
{
|
||||||
|
if( !remote || !ah || !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TrackStruct track;
|
||||||
|
track.remote = remote;
|
||||||
|
track.context = CheckAdhocSupport;
|
||||||
|
track.ah = ah;
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
m_adhocTrackMap[id] = track;
|
||||||
|
m_parent->disco()->getDiscoInfo( remote, EmptyString, this, CheckAdhocSupport, id );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::getCommands( const JID& remote, AdhocHandler* ah )
|
||||||
|
{
|
||||||
|
if( !remote || !ah || !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TrackStruct track;
|
||||||
|
track.remote = remote;
|
||||||
|
track.context = FetchAdhocCommands;
|
||||||
|
track.ah = ah;
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
m_adhocTrackMap[id] = track;
|
||||||
|
m_parent->disco()->getDiscoItems( remote, XMLNS_ADHOC_COMMANDS, this, FetchAdhocCommands, id );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah )
|
||||||
|
{
|
||||||
|
if( !remote || !command || !m_parent || !ah )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
IQ iq( IQ::Set, remote, id );
|
||||||
|
iq.addExtension( command );
|
||||||
|
|
||||||
|
TrackStruct track;
|
||||||
|
track.remote = remote;
|
||||||
|
track.context = ExecuteAdhocCommand;
|
||||||
|
track.session = command->sessionID();
|
||||||
|
track.ah = ah;
|
||||||
|
m_adhocTrackMap[id] = track;
|
||||||
|
|
||||||
|
m_parent->send( iq, this, ExecuteAdhocCommand );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::respond( const JID& remote, const Adhoc::Command* command, const Error* error )
|
||||||
|
{
|
||||||
|
if( !remote || !command || !m_parent )
|
||||||
|
return;
|
||||||
|
|
||||||
|
StringMap::iterator it = m_activeSessions.find( command->sessionID() );
|
||||||
|
if( it == m_activeSessions.end() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
IQ re( error ? IQ::Error : IQ::Result, remote, (*it).second );
|
||||||
|
re.addExtension( command );
|
||||||
|
if( error )
|
||||||
|
re.addExtension( error );
|
||||||
|
m_parent->send( re );
|
||||||
|
m_activeSessions.erase( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::removeAdhocCommandProvider( const std::string& command )
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->removeNodeHandler( this, command );
|
||||||
|
m_adhocCommandProviders.erase( command );
|
||||||
|
m_items.erase( command );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,487 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADHOC_H__
|
||||||
|
#define ADHOC_H__
|
||||||
|
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "disconodehandler.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "iqhandler.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClientBase;
|
||||||
|
class Stanza;
|
||||||
|
class AdhocHandler;
|
||||||
|
class AdhocCommandProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class implements a provider for XEP-0050 (Ad-hoc Commands).
|
||||||
|
*
|
||||||
|
* The current, not complete, implementation is probably best suited for fire-and-forget
|
||||||
|
* type of commands. Any additional feature, like multiple stages, etc., would have to be
|
||||||
|
* added separately.
|
||||||
|
*
|
||||||
|
* To offer commands to remote entities, use this class as follows:<br>
|
||||||
|
* Create a class that will handle command execution requests and derive it from
|
||||||
|
* AdhocCommandProvider. Instantiate an Adhoc object and register your
|
||||||
|
* AdhocCommandProvider-derived object with the Adhoc object using
|
||||||
|
* registerAdhocCommandProvider(). The additional parameters to that method are the internal
|
||||||
|
* name of the command as used in the code, and the public name of the command as it
|
||||||
|
* will be shown to an end user:
|
||||||
|
* @code
|
||||||
|
* MyClass::someFunc()
|
||||||
|
* {
|
||||||
|
* Adhoc* m_adhoc = new Adhoc( m_client );
|
||||||
|
*
|
||||||
|
* // this might be a bot monitoring a weather station, for example
|
||||||
|
* m_adhoc->registerAdhocCommandProvider( this, "getTemp", "Retrieve current temperature" );
|
||||||
|
* m_adhoc->registerAdhocCommandProvider( this, "getPressure", "Retrieve current air pressure" );
|
||||||
|
* [...]
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* In this example, MyClass is AdhocCommandProvider-derived so it is obviously the command handler, too.
|
||||||
|
*
|
||||||
|
* And that's about it you can do with the Adhoc class. Of course you can have a AdhocCommandProvider
|
||||||
|
* handle more than one command, just register it with the Adhoc object for every desired command,
|
||||||
|
* like shown above.
|
||||||
|
*
|
||||||
|
* What the Adhoc object does when you install a new command is tell the supplied Disco object
|
||||||
|
* to advertise these commands to clients using the 'Service Discovery' protocol to learn about
|
||||||
|
* this implementation's features. These clients can then call and execute the command. Of course you
|
||||||
|
* are free to implement access restrictions to not let anyone mess with your bot, for example.
|
||||||
|
* However, the commands offered using Service Discovery are publically visible in any case.
|
||||||
|
*
|
||||||
|
* To execute commands offered by a remote entity:<br>
|
||||||
|
* ...TBC...
|
||||||
|
*
|
||||||
|
* XEP version: 1.2
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API Adhoc : public DiscoNodeHandler, public DiscoHandler, public IqHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of an Adhoc Command element (from Adhoc Commands, XEP-0050)
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Command : public StanzaExtension
|
||||||
|
{
|
||||||
|
friend class Adhoc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the action to undertake with the given command.
|
||||||
|
*/
|
||||||
|
enum Action
|
||||||
|
{
|
||||||
|
Execute = 1, /**< The command should be executed or continue to be executed.
|
||||||
|
* This is the default value. */
|
||||||
|
Cancel = 2, /**< The command should be canceled. */
|
||||||
|
Previous = 4, /**< The command should be digress to the previous stage of
|
||||||
|
* execution. */
|
||||||
|
Next = 8, /**< The command should progress to the next stage of
|
||||||
|
* execution. */
|
||||||
|
Complete = 16, /**< The command should be completed (if possible). */
|
||||||
|
InvalidAction = 32 /**< The action is unknown or invalid. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the current status of a command.
|
||||||
|
*/
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
Executing, /**< The command is being executed. */
|
||||||
|
Completed, /**< The command has completed. The command session has ended. */
|
||||||
|
Canceled, /**< The command has been canceled. The command session has ended. */
|
||||||
|
InvalidStatus /**< The status is unknown or invalid. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstraction of a command note.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Note
|
||||||
|
{
|
||||||
|
|
||||||
|
friend class Command;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Specifies the severity of a note.
|
||||||
|
*/
|
||||||
|
enum Severity
|
||||||
|
{
|
||||||
|
Info, /**< The note is informational only. This is not really an
|
||||||
|
* exceptional condition. */
|
||||||
|
Warning, /**< The note indicates a warning. Possibly due to illogical
|
||||||
|
* (yet valid) data. */
|
||||||
|
Error, /**< The note indicates an error. The text should indicate the
|
||||||
|
* reason for the error. */
|
||||||
|
InvalidSeverity /**< The note type is unknown or invalid. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience constructor.
|
||||||
|
* @param sev The note's severity.
|
||||||
|
* @param note The note's content.
|
||||||
|
*/
|
||||||
|
Note( Severity sev, const std::string& note )
|
||||||
|
: m_severity( sev ), m_note( note ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Note() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the note's severity.
|
||||||
|
* @return The note's severity.
|
||||||
|
*/
|
||||||
|
Severity severity() const { return m_severity; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the note's content.
|
||||||
|
* @return The note's content.
|
||||||
|
*/
|
||||||
|
const std::string& content() const { return m_note; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Tag representation of the Note.
|
||||||
|
* @return A Tag representation.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ADHOC_COMMANDS_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Constructs a new Note from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Note( const Tag* tag );
|
||||||
|
|
||||||
|
Severity m_severity; /**< The note's severity. */
|
||||||
|
std::string m_note; /**< The note's content. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of command notes.
|
||||||
|
*/
|
||||||
|
typedef std::list<const Note*> NoteList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to continue execution of a multi stage command
|
||||||
|
* (for which the session ID must be known).
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param sessionid The session ID of an already running adhoc command session.
|
||||||
|
* @param action The action to perform.
|
||||||
|
* @param form An optional DataForm to include in the request. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, const std::string& sessionid, Action action,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to reply to an execute request.
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param sessionid The (possibly newly created) session ID of the adhoc command session.
|
||||||
|
* @param status The execution status.
|
||||||
|
* @param form An optional DataForm to include in the reply. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to reply to a multi stage command that is not yet completed
|
||||||
|
* (for which the session ID must be known).
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param sessionid The (possibly newly created) session ID of the adhoc command session.
|
||||||
|
* @param status The execution status.
|
||||||
|
* @param executeAction The action to execute.
|
||||||
|
* @param allowedActions Allowed reply actions.
|
||||||
|
* @param form An optional DataForm to include in the reply. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
Action executeAction, int allowedActions = Complete,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to execute the initial step of a command
|
||||||
|
* (single or multi stage).
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param action The action to perform.
|
||||||
|
* @param form An optional DataForm to include in the request. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, Action action,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object from the given Tag.
|
||||||
|
* @param tag A <command> tag in the adhoc commands' namespace.
|
||||||
|
*/
|
||||||
|
Command( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Command();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node identifier (the command).
|
||||||
|
* @return The node identifier.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command's session ID, if any.
|
||||||
|
* @return The command's session ID.
|
||||||
|
*/
|
||||||
|
const std::string& sessionID() const { return m_sessionid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the execution status for a command. Only valid for execution
|
||||||
|
* results.
|
||||||
|
* @return The execution status for a command.
|
||||||
|
*/
|
||||||
|
Status status() const { return m_status; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command's action.
|
||||||
|
* @return The command's action.
|
||||||
|
*/
|
||||||
|
Action action() const { return m_action; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ORed actions that are allowed to be executed on the
|
||||||
|
* current stage.
|
||||||
|
* @return An int containing the ORed actions.
|
||||||
|
*/
|
||||||
|
int actions() const { return m_actions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of notes associated with the command.
|
||||||
|
* @return The list of notes.
|
||||||
|
*/
|
||||||
|
const NoteList& notes() const { return m_notes; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to add a note to the command.
|
||||||
|
* @param note A pointer to a Note object. The Command will own
|
||||||
|
* the Note.
|
||||||
|
*/
|
||||||
|
void addNote( const Note* note ) { m_notes.push_back( note ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command's embedded DataForm.
|
||||||
|
* @return The command's embedded DataForm. May be 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Command( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
Command* c = new Command();
|
||||||
|
|
||||||
|
NoteList::const_iterator it = m_notes.begin();
|
||||||
|
for( ; it != m_notes.end(); ++it )
|
||||||
|
c->m_notes.push_back( new Note( *(*it) ) );
|
||||||
|
|
||||||
|
c->m_node = m_node;
|
||||||
|
c->m_sessionid = m_sessionid;
|
||||||
|
c->m_form = m_form ? static_cast<DataForm*>( m_form->clone() ) : 0;
|
||||||
|
c->m_action = m_action;
|
||||||
|
c->m_status = m_status;
|
||||||
|
c->m_actions = m_actions;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ADHOC_COMMANDS_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
NoteList m_notes;
|
||||||
|
|
||||||
|
std::string m_node;
|
||||||
|
std::string m_sessionid;
|
||||||
|
DataForm* m_form;
|
||||||
|
Action m_action;
|
||||||
|
Status m_status;
|
||||||
|
int m_actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* Creates a new Adhoc client that registers as IqHandler with a ClientBase.
|
||||||
|
* @param parent The ClientBase used for XMPP communication.
|
||||||
|
*/
|
||||||
|
Adhoc( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Adhoc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function queries the given remote entity for Adhoc Commands support.
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param ah The object handling the result of this request.
|
||||||
|
*/
|
||||||
|
void checkSupport( const JID& remote, AdhocHandler* ah );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of commands from the remote entity. You should check whether the remote
|
||||||
|
* entity actually supports Adhoc Commands by means of checkSupport().
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param ah The object handling the result of this request.
|
||||||
|
*/
|
||||||
|
void getCommands( const JID& remote, AdhocHandler* ah );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes or continues the given command on the given remote entity.
|
||||||
|
* To construct the @c command object, it is recommended to use either
|
||||||
|
* Command( const std::string&, Action ) to begin execution of a command, or
|
||||||
|
* Command( const std::string&, const std::string&, Action ) to continue execution
|
||||||
|
* of a command.
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param command The command to execute.
|
||||||
|
* @param ah The object handling the result of this request.
|
||||||
|
*/
|
||||||
|
void execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to respond to an execution request submitted by means
|
||||||
|
* of AdhocCommandProvider::handleAdhocCommand().
|
||||||
|
* It is recommended to use
|
||||||
|
* Command( const std::string&, const std::string&, Status, DataForm* )
|
||||||
|
* to construct the @c command object.
|
||||||
|
* Optionally, an Error object can be included. In that case the IQ sent is of type @c error.
|
||||||
|
* @param remote The requester's JID.
|
||||||
|
* @param command The response. The Adhoc object will own and delete the
|
||||||
|
* command object pointed to.
|
||||||
|
* @param error An optional Error obejct to include.
|
||||||
|
*/
|
||||||
|
void respond( const JID& remote, const Adhoc::Command* command, const Error* error = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using this function, you can register a AdhocCommandProvider -derived object as
|
||||||
|
* handler for a specific Ad-hoc Command as defined in XEP-0050.
|
||||||
|
* @param acp The obejct to register as handler for the specified command.
|
||||||
|
* @param command The node name of the command. Will be announced in disco#items.
|
||||||
|
* @param name The natural-language name of the command. Will be announced in disco#items.
|
||||||
|
*/
|
||||||
|
void registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command,
|
||||||
|
const std::string& name );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to unregister an adhoc command previously registered using
|
||||||
|
* registerAdhocCommandProvider().
|
||||||
|
* @param command The command to unregister.
|
||||||
|
*/
|
||||||
|
void removeAdhocCommandProvider( const std::string& command );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from,
|
||||||
|
const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler
|
||||||
|
virtual bool handleIq( const IQ& iq );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler
|
||||||
|
virtual void handleIqID( const IQ& iq, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoError( const JID& from, const Error* error, int context );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ADHOC_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
typedef std::map<const std::string, AdhocCommandProvider*> AdhocCommandProviderMap;
|
||||||
|
AdhocCommandProviderMap m_adhocCommandProviders;
|
||||||
|
|
||||||
|
enum AdhocContext
|
||||||
|
{
|
||||||
|
CheckAdhocSupport,
|
||||||
|
FetchAdhocCommands,
|
||||||
|
ExecuteAdhocCommand
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrackStruct
|
||||||
|
{
|
||||||
|
JID remote;
|
||||||
|
AdhocContext context;
|
||||||
|
std::string session;
|
||||||
|
AdhocHandler* ah;
|
||||||
|
};
|
||||||
|
typedef std::map<std::string, TrackStruct> AdhocTrackMap;
|
||||||
|
AdhocTrackMap m_adhocTrackMap;
|
||||||
|
|
||||||
|
ClientBase* m_parent;
|
||||||
|
|
||||||
|
StringMap m_items;
|
||||||
|
StringMap m_activeSessions;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADHOC_H__
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADHOCCOMMANDPROVIDER_H__
|
||||||
|
#define ADHOCCOMMANDPROVIDER_H__
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
#include "jid.h"
|
||||||
|
#include "adhoc.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface for an Ad-hoc Command Provider according to XEP-0050.
|
||||||
|
*
|
||||||
|
* Derived classes can be registered as Command Providers with the Adhoc object.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API AdhocCommandProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AdhocCommandProvider() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when an Ad-hoc Command needs to be handled.
|
||||||
|
* The callee is responsible for the whole command execution, i.e. session
|
||||||
|
* handling etc.
|
||||||
|
* @param from The sender of the command request.
|
||||||
|
* @param command The name of the command to be executed.
|
||||||
|
* @param sessionID The session ID. Either newly generated or taken from the command.
|
||||||
|
* When responding, its value must be passed to Adhoc::Command's constructor.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocCommand( const JID& from, const Adhoc::Command& command,
|
||||||
|
const std::string& sessionID ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gets called for each registered command when a remote
|
||||||
|
* entity requests the list of available commands.
|
||||||
|
* @param from The requesting entity.
|
||||||
|
* @param command The command's name.
|
||||||
|
* @return @b True if the remote entity is allowed to see the command, @b false if not.
|
||||||
|
* @note The return value of this function does not influence
|
||||||
|
* the execution of a command. That is, you have to
|
||||||
|
* implement additional access control at the execution
|
||||||
|
* stage.
|
||||||
|
* @note This function should not block.
|
||||||
|
*/
|
||||||
|
virtual bool handleAdhocAccessRequest( const JID& from, const std::string& command )
|
||||||
|
{
|
||||||
|
(void)from;
|
||||||
|
(void)command;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADHOCCOMMANDPROVIDER_H__
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADHOCHANDLER_H__
|
||||||
|
#define ADHOCHANDLER_H__
|
||||||
|
|
||||||
|
#include "adhoc.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface for an Ad-hoc Command users according to XEP-0050.
|
||||||
|
*
|
||||||
|
* Derived classes can be registered with the Adhoc object to receive notifications
|
||||||
|
* about Adhoc Commands remote entities support.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API AdhocHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AdhocHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a call to Adhoc::checkSupport().
|
||||||
|
* @param remote The queried remote entity's JID.
|
||||||
|
* @param support Whether the remote entity supports Adhoc Commands.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocSupport( const JID& remote, bool support ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a call to Adhoc::getCommands()
|
||||||
|
* and delivers a list of supported commands.
|
||||||
|
* @param remote The queried remote entity's JID.
|
||||||
|
* @param commands A map of supported commands and their human-readable name.
|
||||||
|
* The map may be empty.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocCommands( const JID& remote, const StringMap& commands ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a call to Adhoc::getCommands() or
|
||||||
|
* Adhoc::checkSupport() or Adhoc::execute() in case the respective request returned
|
||||||
|
* an error.
|
||||||
|
* @param remote The queried remote entity's JID.
|
||||||
|
* @param error The error condition. May be 0.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocError( const JID& remote, const Error* error ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a remote command execution.
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param command The command being executed.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocExecutionResult( const JID& remote, const Adhoc::Command& command ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADHOCHANDLER_H__
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "amp.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* conditionValues[] =
|
||||||
|
{
|
||||||
|
"deliver", "expire-at", "match-resource"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* actionValues[] =
|
||||||
|
{
|
||||||
|
"alert", "error", "drop", "notify"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* deliverValues[] =
|
||||||
|
{
|
||||||
|
"direct", "forward", "gateway", "none", "stored"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* matchResourceValues[] =
|
||||||
|
{
|
||||||
|
"any", "exact", "other"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* statusValues[] =
|
||||||
|
{
|
||||||
|
"alert", "notify"
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- AMP::Rule ----
|
||||||
|
AMP::Rule::Rule( DeliverType deliver, ActionType action )
|
||||||
|
: m_condition( ConditionDeliver ), m_deliver( deliver ), m_action( action )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::Rule( const std::string& date, ActionType action )
|
||||||
|
: m_condition( ConditionExpireAt ), m_expireat( new std::string( date ) ), m_action( action )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::Rule( MatchResourceType match, ActionType action )
|
||||||
|
: m_condition( ConditionMatchResource ), m_matchresource( match ), m_action( action )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::Rule( const std::string& condition, const std::string& action,
|
||||||
|
const std::string& value )
|
||||||
|
{
|
||||||
|
m_condition = (ConditionType)util::lookup( condition, conditionValues );
|
||||||
|
m_action = (ActionType)util::lookup( action, actionValues );
|
||||||
|
switch( m_condition )
|
||||||
|
{
|
||||||
|
case ConditionDeliver:
|
||||||
|
m_deliver = (DeliverType)util::lookup( value, deliverValues );
|
||||||
|
break;
|
||||||
|
case ConditionExpireAt:
|
||||||
|
m_expireat = new std::string( value );
|
||||||
|
break;
|
||||||
|
case ConditionMatchResource:
|
||||||
|
m_matchresource = (MatchResourceType)util::lookup( value, matchResourceValues );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case ConditionInvalid: // shouldn't happen
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::~Rule()
|
||||||
|
{
|
||||||
|
if( m_condition == ConditionExpireAt && m_expireat )
|
||||||
|
delete m_expireat;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* AMP::Rule::tag() const
|
||||||
|
{
|
||||||
|
if( m_condition == ConditionInvalid || m_action == ActionInvalid
|
||||||
|
|| ( m_condition == ConditionDeliver && m_deliver == DeliverInvalid )
|
||||||
|
|| ( m_condition == ConditionMatchResource && m_matchresource == MatchResourceInvalid )
|
||||||
|
|| ( m_condition == ConditionExpireAt && !m_expireat ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* rule = new Tag( "rule" );
|
||||||
|
rule->addAttribute( "condition", util::lookup( m_condition, conditionValues ) );
|
||||||
|
rule->addAttribute( "action", util::lookup( m_action, actionValues ) );
|
||||||
|
|
||||||
|
switch( m_condition )
|
||||||
|
{
|
||||||
|
case ConditionDeliver:
|
||||||
|
rule->addAttribute( "value", util::lookup( m_deliver, deliverValues ) );
|
||||||
|
break;
|
||||||
|
case ConditionExpireAt:
|
||||||
|
rule->addAttribute( "value", *m_expireat );
|
||||||
|
break;
|
||||||
|
case ConditionMatchResource:
|
||||||
|
rule->addAttribute( "value", util::lookup( m_matchresource, matchResourceValues ) );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
// ---- AMP::Rule ----
|
||||||
|
|
||||||
|
// ---- AMP ----
|
||||||
|
AMP::AMP( bool perhop )
|
||||||
|
: StanzaExtension( ExtAMP ), m_perhop( perhop ), m_status( StatusInvalid )
|
||||||
|
{
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::AMP( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtAMP ), m_perhop( false )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "amp" || tag->xmlns() != XMLNS_AMP )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ConstTagList& rules = tag->findTagList( "/amp/rule" );
|
||||||
|
ConstTagList::const_iterator it = rules.begin();
|
||||||
|
for( ; it != rules.end(); ++it )
|
||||||
|
{
|
||||||
|
m_rules.push_back( new Rule( (*it)->findAttribute( "condition" ),
|
||||||
|
(*it)->findAttribute( "action" ),
|
||||||
|
(*it)->findAttribute( "value" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_from = tag->findAttribute( "from" );
|
||||||
|
m_to = tag->findAttribute( "to" );
|
||||||
|
m_status = (Status)util::lookup( tag->findAttribute( "status" ), statusValues );
|
||||||
|
if( tag->hasAttribute( "per-hop", "true" ) || tag->hasAttribute( "per-hop", "1" ) )
|
||||||
|
m_perhop = true;
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::~AMP()
|
||||||
|
{
|
||||||
|
util::clearList( m_rules );
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMP::addRule( const Rule* rule )
|
||||||
|
{
|
||||||
|
if( rule )
|
||||||
|
m_rules.push_back( rule );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& AMP::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/amp[@xmlns='" + XMLNS_AMP + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* AMP::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid || !m_rules.size() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* amp = new Tag( "amp" );
|
||||||
|
amp->setXmlns( XMLNS_AMP );
|
||||||
|
if( m_from )
|
||||||
|
amp->addAttribute( "from", m_from.full() );
|
||||||
|
if( m_to )
|
||||||
|
amp->addAttribute( "to", m_to.full() );
|
||||||
|
if( m_status != StatusInvalid )
|
||||||
|
amp->addAttribute( "status", util::lookup( m_status, statusValues ) );
|
||||||
|
if( m_perhop )
|
||||||
|
amp->addAttribute( "per-hop", "true" );
|
||||||
|
RuleList::const_iterator it = m_rules.begin();
|
||||||
|
for( ; it != m_rules.end(); ++it )
|
||||||
|
amp->addChild( (*it)->tag() );
|
||||||
|
|
||||||
|
return amp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AMP_H__
|
||||||
|
#define AMP_H__
|
||||||
|
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
#include "jid.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0079 (Advanced Message Processing)
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* XEP Version: 1.2
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @author Vincent Thomasset
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API AMP : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Possible types for a rule's condition.
|
||||||
|
*/
|
||||||
|
enum ConditionType
|
||||||
|
{
|
||||||
|
ConditionDeliver, /**< Ensures (non-)delivery of the message */
|
||||||
|
ConditionExpireAt, /**< Ensures delivery only before a certain time (UTC) */
|
||||||
|
ConditionMatchResource, /**< Ensures delivery only to a specific resource type */
|
||||||
|
ConditionInvalid /**< Invalid condition */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible actions to take when the corresponding condition is met.
|
||||||
|
*/
|
||||||
|
enum ActionType
|
||||||
|
{
|
||||||
|
|
||||||
|
ActionAlert, /**< Sends back a message stanza with an 'alert' status */
|
||||||
|
ActionError, /**< Sends back a message stanza with an error type */
|
||||||
|
ActionDrop, /**< Silently ignore the message */
|
||||||
|
ActionNotify, /**< Sends back a message stanza with a 'notify' status */
|
||||||
|
ActionInvalid /**< Invalid action */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible delivery rules.
|
||||||
|
*/
|
||||||
|
enum DeliverType
|
||||||
|
{
|
||||||
|
DeliverDirect, /**< The message would be immediately delivered to the intended
|
||||||
|
* recipient or routed to the next hop. */
|
||||||
|
DeliverForward, /**< The message would be forwarded to another XMPP address or
|
||||||
|
* account. */
|
||||||
|
DeliverGateway, /**< The message would be sent through a gateway to an address
|
||||||
|
* or account on a non-XMPP system. */
|
||||||
|
DeliverNone, /**< The message would not be delivered at all (e.g., because
|
||||||
|
* the intended recipient is offline and message storage is
|
||||||
|
* not enabled). */
|
||||||
|
DeliverStored, /**< The message would be stored offline for later delivery
|
||||||
|
* to the intended recipient. */
|
||||||
|
DeliverInvalid /**< Invalid deliver value */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible resource matching rules.
|
||||||
|
*/
|
||||||
|
enum MatchResourceType
|
||||||
|
{
|
||||||
|
MatchResourceAny, /**< Destination resource matches any value, effectively
|
||||||
|
* ignoring the intended resource. */
|
||||||
|
MatchResourceExact, /**< Destination resource exactly matches the intended
|
||||||
|
* resource. */
|
||||||
|
MatchResourceOther, /**< Destination resource matches any value except for
|
||||||
|
* the intended resource. */
|
||||||
|
MatchResourceInvalid /**< Invalid match-resource value */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available Stati.
|
||||||
|
*/
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
StatusAlert, /**< The message is a reply to a @c Alert rule. */
|
||||||
|
StatusNotify, /**< The message is a reply to a @c Notify rule. */
|
||||||
|
StatusInvalid /**< Invalid status. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes an AMP rule.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Rule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object with a condition of 'deliver'.
|
||||||
|
* @param deliver The delivery type.
|
||||||
|
* @param action The rule's action.
|
||||||
|
*/
|
||||||
|
Rule( DeliverType deliver, ActionType action );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object with a condition of 'expire-at'.
|
||||||
|
* @param date The expiry date/time in the format defined in XEP-0082.
|
||||||
|
* @param action The rule's action.
|
||||||
|
*/
|
||||||
|
Rule( const std::string& date, ActionType action );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object with a condition of 'match-resource'.
|
||||||
|
* @param match The match type.
|
||||||
|
* @param action The rule's action.
|
||||||
|
*/
|
||||||
|
Rule( MatchResourceType match, ActionType action );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object from the given strings.
|
||||||
|
* @param condition The rule's condition.
|
||||||
|
* @param action The rule's action.
|
||||||
|
* @param value The rule's value.
|
||||||
|
*/
|
||||||
|
Rule( const std::string& condition, const std::string& action,
|
||||||
|
const std::string& value );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Rule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Tag representation from the current rule.
|
||||||
|
* @return A Tag representation of the rule.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConditionType m_condition;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
DeliverType m_deliver;
|
||||||
|
MatchResourceType m_matchresource;
|
||||||
|
std::string* m_expireat;
|
||||||
|
};
|
||||||
|
ActionType m_action;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of AMP rules.
|
||||||
|
*/
|
||||||
|
typedef std::list<const Rule*> RuleList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object.
|
||||||
|
* @param perhop Indicates whether the ruleset should be applied to all hops,
|
||||||
|
* or at the edge servers only. Default: @c false (edge servers only)
|
||||||
|
*/
|
||||||
|
AMP( bool perhop = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The AMP Tag to parse.
|
||||||
|
*/
|
||||||
|
AMP( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given rule to the list of rules.
|
||||||
|
* @param rule The rule to add.
|
||||||
|
*/
|
||||||
|
void addRule( const Rule* rule );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current list of rules for inspection.
|
||||||
|
* @return The current list of rules.
|
||||||
|
*/
|
||||||
|
const RuleList& rules() const { return m_rules; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AMP();
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new AMP( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
AMP* a = new AMP();
|
||||||
|
a->m_perhop = m_perhop;
|
||||||
|
RuleList::const_iterator it = m_rules.begin();
|
||||||
|
for( ; it != m_rules.end(); ++it )
|
||||||
|
a->m_rules.push_back( new Rule( *(*it) ) );
|
||||||
|
a->m_status = m_status;
|
||||||
|
a->m_from = m_from;
|
||||||
|
a->m_to = m_to;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_perhop;
|
||||||
|
RuleList m_rules;
|
||||||
|
Status m_status;
|
||||||
|
JID m_from;
|
||||||
|
JID m_to;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AMP_H__
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "annotations.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Annotations::Annotations( ClientBase* parent )
|
||||||
|
: PrivateXML( parent ),
|
||||||
|
m_annotationsHandler( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotations::~Annotations()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::storeAnnotations( const AnnotationsList& aList )
|
||||||
|
{
|
||||||
|
Tag* s = new Tag( "storage", XMLNS, XMLNS_ANNOTATIONS );
|
||||||
|
|
||||||
|
AnnotationsList::const_iterator it = aList.begin();
|
||||||
|
for( ; it != aList.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* n = new Tag( s, "note", (*it).note );
|
||||||
|
n->addAttribute( "jid", (*it).jid );
|
||||||
|
n->addAttribute( "cdate", (*it).cdate );
|
||||||
|
n->addAttribute( "mdate", (*it).mdate );
|
||||||
|
}
|
||||||
|
|
||||||
|
storeXML( s, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::requestAnnotations()
|
||||||
|
{
|
||||||
|
requestXML( "storage", XMLNS_ANNOTATIONS, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::handlePrivateXML( const Tag* xml )
|
||||||
|
{
|
||||||
|
if( !xml )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AnnotationsList aList;
|
||||||
|
const TagList& l = xml->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "note" )
|
||||||
|
{
|
||||||
|
const std::string& jid = (*it)->findAttribute( "jid" );
|
||||||
|
const std::string& note = (*it)->cdata();
|
||||||
|
|
||||||
|
if( !jid.empty() && !note.empty() )
|
||||||
|
{
|
||||||
|
const std::string& cdate = (*it)->findAttribute( "cdate" );
|
||||||
|
const std::string& mdate = (*it)->findAttribute( "mdate" );
|
||||||
|
AnnotationsListItem item;
|
||||||
|
item.jid = jid;
|
||||||
|
item.cdate = cdate;
|
||||||
|
item.mdate = mdate;
|
||||||
|
item.note = note;
|
||||||
|
aList.push_back( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_annotationsHandler )
|
||||||
|
m_annotationsHandler->handleAnnotations( aList );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ANNOTATIONS_H__
|
||||||
|
#define ANNOTATIONS_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include "annotationshandler.h"
|
||||||
|
#include "privatexml.h"
|
||||||
|
#include "privatexmlhandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0145 (Annotations).
|
||||||
|
*
|
||||||
|
* You can use this class to store arbitrary notes about a roster item on the server
|
||||||
|
* (and to retrieve them later on).
|
||||||
|
* To retrieve all stored annotations for the current user's roster you have to create
|
||||||
|
* a class which inherits from AnnotationsHandler. This handler receives retrieved notes.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* class MyClass : public AnnotationsHandler
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* // ...
|
||||||
|
* void myFuncRetrieve();
|
||||||
|
* void myFuncStore();
|
||||||
|
* void handleAnnotations( const AnnotationsList &aList );
|
||||||
|
*
|
||||||
|
* private:
|
||||||
|
* Annotations* m_notes;
|
||||||
|
* AnnotationsList m_list;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* void MyClass::myFuncRetrieve()
|
||||||
|
* {
|
||||||
|
* [...]
|
||||||
|
* m_notes = new Annotations( m_client );
|
||||||
|
* m_notes->requestAnnotations();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void MyClass::handleAnnotations( const AnnotationsList &aList )
|
||||||
|
* {
|
||||||
|
* m_list = aList;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* To store an additional note you have to fetch the currently stored notes first,
|
||||||
|
* add your new note to the list of notes, and transfer them all together back to the
|
||||||
|
* server. This protocol does not support storage of 'deltas', that is, when saving
|
||||||
|
* notes all previously saved notes are overwritten.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void MyClass::myFuncStore()
|
||||||
|
* {
|
||||||
|
* annotationsListItem item;
|
||||||
|
* item.jid = "me@example.com";
|
||||||
|
* item.cdate = "2006-02-04T15:23:21Z";
|
||||||
|
* item.note = "some guy at example.com";
|
||||||
|
* m_list.push_back( item );
|
||||||
|
*
|
||||||
|
* item.jid = "abc@def.com";
|
||||||
|
* item.cdate = "2006-01-24T15:23:21Z";
|
||||||
|
* item.mdate = "2006-02-04T05:11:46Z";
|
||||||
|
* item.note = "some other guy";
|
||||||
|
* m_list.push_back( item );
|
||||||
|
*
|
||||||
|
* m_notes->storeAnnotations( m_list );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API Annotations : public PrivateXML, public PrivateXMLHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new Annotations object.
|
||||||
|
* @param parent The ClientBase to use for communication.
|
||||||
|
*/
|
||||||
|
Annotations( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Annotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to store notes (annotations to contacts in a roster) on the server.
|
||||||
|
* Make sure you store the whole set of annotations, not a 'delta'.
|
||||||
|
* @param aList A list of notes to store.
|
||||||
|
*/
|
||||||
|
void storeAnnotations( const AnnotationsList& aList );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to initiate retrieval of annotations. Use registerAnnotationsHandler()
|
||||||
|
* to register an object which will receive the lists of notes.
|
||||||
|
*/
|
||||||
|
void requestAnnotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a AnnotationsHandler.
|
||||||
|
* @param ah The AnnotationsHandler which shall receive retrieved notes.
|
||||||
|
*/
|
||||||
|
void registerAnnotationsHandler( AnnotationsHandler* ah )
|
||||||
|
{ m_annotationsHandler = ah; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to un-register the AnnotationsHandler.
|
||||||
|
*/
|
||||||
|
void removeAnnotationsHandler()
|
||||||
|
{ m_annotationsHandler = 0; }
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXML( const Tag* xml );
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult );
|
||||||
|
|
||||||
|
private:
|
||||||
|
AnnotationsHandler* m_annotationsHandler;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ANNOTATIONS_H__
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ANNOTATIONSHANDLER_H__
|
||||||
|
#define ANNOTATIONSHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes a single note item.
|
||||||
|
*/
|
||||||
|
struct AnnotationsListItem
|
||||||
|
{
|
||||||
|
std::string jid; /**< The JID of the roster item this note is about */
|
||||||
|
std::string cdate; /**< Creation date of this note. */
|
||||||
|
std::string mdate; /**< Date of last modification of this note. */
|
||||||
|
std::string note; /**< The note. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of note items.
|
||||||
|
*/
|
||||||
|
typedef std::list<AnnotationsListItem> AnnotationsList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface which can be reimplemented to receive notes with help of
|
||||||
|
* the Annotations object.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API AnnotationsHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AnnotationsHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when notes arrive from the server.
|
||||||
|
* @param aList A list of notes.
|
||||||
|
*/
|
||||||
|
virtual void handleAnnotations( const AnnotationsList &aList ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ANNOTATIONSHANDLER_H__
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "attention.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Attention::Attention()
|
||||||
|
: StanzaExtension( ExtAttention )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Attention::~Attention()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Attention::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/attention[@xmlns='" + XMLNS_ATTENTION + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Attention::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "attention" );
|
||||||
|
t->setXmlns( XMLNS_ATTENTION );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ATTENTION_H__
|
||||||
|
#define ATTENTION_H__
|
||||||
|
|
||||||
|
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0224 as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Attention : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
*/
|
||||||
|
Attention();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Attention();
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* /*tag*/ ) const
|
||||||
|
{
|
||||||
|
return new Attention();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Attention();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif// ATTENTION_H__
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace Base64
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string alphabet64( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" );
|
||||||
|
static const char pad = '=';
|
||||||
|
static const char np = (char)std::string::npos;
|
||||||
|
static char table64vals[] =
|
||||||
|
{
|
||||||
|
62, np, np, np, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, np, np, np, np, np,
|
||||||
|
np, np, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
18, 19, 20, 21, 22, 23, 24, 25, np, np, np, np, np, np, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||||
|
};
|
||||||
|
|
||||||
|
inline char table64( unsigned char c )
|
||||||
|
{
|
||||||
|
return ( c < 43 || c > 122 ) ? np : table64vals[c-43];
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string encode64( const std::string& input )
|
||||||
|
{
|
||||||
|
std::string encoded;
|
||||||
|
char c;
|
||||||
|
const std::string::size_type length = input.length();
|
||||||
|
|
||||||
|
encoded.reserve( length * 2 );
|
||||||
|
|
||||||
|
for( std::string::size_type i = 0; i < length; ++i )
|
||||||
|
{
|
||||||
|
c = static_cast<char>( ( input[i] >> 2 ) & 0x3f );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
|
||||||
|
c = static_cast<char>( ( input[i] << 4 ) & 0x3f );
|
||||||
|
if( ++i < length )
|
||||||
|
c = static_cast<char>( c | static_cast<char>( ( input[i] >> 4 ) & 0x0f ) );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
|
||||||
|
if( i < length )
|
||||||
|
{
|
||||||
|
c = static_cast<char>( ( input[i] << 2 ) & 0x3c );
|
||||||
|
if( ++i < length )
|
||||||
|
c = static_cast<char>( c | static_cast<char>( ( input[i] >> 6 ) & 0x03 ) );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
encoded += pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( i < length )
|
||||||
|
{
|
||||||
|
c = static_cast<char>( input[i] & 0x3f );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encoded += pad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string decode64( const std::string& input )
|
||||||
|
{
|
||||||
|
char c, d;
|
||||||
|
const std::string::size_type length = input.length();
|
||||||
|
std::string decoded;
|
||||||
|
|
||||||
|
decoded.reserve( length );
|
||||||
|
|
||||||
|
for( std::string::size_type i = 0; i < length; ++i )
|
||||||
|
{
|
||||||
|
c = table64(input[i]);
|
||||||
|
++i;
|
||||||
|
d = table64(input[i]);
|
||||||
|
c = static_cast<char>( ( c << 2 ) | ( ( d >> 4 ) & 0x3 ) );
|
||||||
|
decoded += c;
|
||||||
|
if( ++i < length )
|
||||||
|
{
|
||||||
|
c = input[i];
|
||||||
|
if( pad == c )
|
||||||
|
break;
|
||||||
|
|
||||||
|
c = table64(input[i]);
|
||||||
|
d = static_cast<char>( ( ( d << 4 ) & 0xf0 ) | ( ( c >> 2 ) & 0xf ) );
|
||||||
|
decoded += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ++i < length )
|
||||||
|
{
|
||||||
|
d = input[i];
|
||||||
|
if( pad == d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
d = table64(input[i]);
|
||||||
|
c = static_cast<char>( ( ( c << 6 ) & 0xc0 ) | d );
|
||||||
|
decoded += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BASE64_H__
|
||||||
|
#define BASE64_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An implementation of the Base64 data encoding (RFC 3548)
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
namespace Base64
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64-encodes the input according to RFC 3548.
|
||||||
|
* @param input The data to encode.
|
||||||
|
* @return The encoded string.
|
||||||
|
*/
|
||||||
|
GLOOX_API const std::string encode64( const std::string& input );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64-decodes the input according to RFC 3548.
|
||||||
|
* @param input The encoded data.
|
||||||
|
* @return The decoded data.
|
||||||
|
*/
|
||||||
|
GLOOX_API const std::string decode64( const std::string& input );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BASE64_H__
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOOKMARKHANDLER_H__
|
||||||
|
#define BOOKMARKHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes a single bookmarked URL item.
|
||||||
|
*/
|
||||||
|
struct BookmarkListItem
|
||||||
|
{
|
||||||
|
std::string name; /**< A human readable name of the bookmark. */
|
||||||
|
std::string url; /**< The URL of the bookmark. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes a single bookmarked conference item.
|
||||||
|
*/
|
||||||
|
struct ConferenceListItem
|
||||||
|
{
|
||||||
|
std::string name; /**< A human readable name of the conference room. */
|
||||||
|
std::string jid; /**< The address of the room. */
|
||||||
|
std::string nick; /**< The nick name to use in this room. */
|
||||||
|
std::string password; /**< The password to use for a protected room. */
|
||||||
|
bool autojoin; /**< The conference shall be joined automatically on login. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of URL items.
|
||||||
|
*/
|
||||||
|
typedef std::list<BookmarkListItem> BookmarkList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of conference items.
|
||||||
|
*/
|
||||||
|
typedef std::list<ConferenceListItem> ConferenceList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface which can be reimplemented to receive bookmarks with help of a
|
||||||
|
* BookmarkStorage object.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API BookmarkHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BookmarkHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when bookmarks arrive from the server.
|
||||||
|
* @param bList A list of URL bookmarks.
|
||||||
|
* @param cList A list of conference bookmarks.
|
||||||
|
*/
|
||||||
|
virtual void handleBookmarks( const BookmarkList &bList, const ConferenceList &cList ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOOKMARKHANDLER_H__
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "bookmarkstorage.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
BookmarkStorage::BookmarkStorage( ClientBase* parent )
|
||||||
|
: PrivateXML( parent ),
|
||||||
|
m_bookmarkHandler( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BookmarkStorage::~BookmarkStorage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::storeBookmarks( const BookmarkList& bList, const ConferenceList& cList )
|
||||||
|
{
|
||||||
|
Tag* s = new Tag( "storage" );
|
||||||
|
s->addAttribute( XMLNS, XMLNS_BOOKMARKS );
|
||||||
|
|
||||||
|
BookmarkList::const_iterator itb = bList.begin();
|
||||||
|
for( ; itb != bList.end(); ++itb )
|
||||||
|
{
|
||||||
|
Tag* i = new Tag( s, "url", "name", (*itb).name );
|
||||||
|
i->addAttribute( "url", (*itb).url );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConferenceList::const_iterator itc = cList.begin();
|
||||||
|
for( ; itc != cList.end(); ++itc )
|
||||||
|
{
|
||||||
|
Tag* i = new Tag( s, "conference", "name", (*itc).name );
|
||||||
|
i->addAttribute( "jid", (*itc).jid );
|
||||||
|
i->addAttribute( "autojoin", (*itc).autojoin ? "true" : "false" );
|
||||||
|
|
||||||
|
new Tag( i, "nick", (*itc).nick );
|
||||||
|
new Tag( i, "password", (*itc).password );
|
||||||
|
}
|
||||||
|
|
||||||
|
storeXML( s, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::requestBookmarks()
|
||||||
|
{
|
||||||
|
requestXML( "storage", XMLNS_BOOKMARKS, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::handlePrivateXML( const Tag* xml )
|
||||||
|
{
|
||||||
|
if( !xml )
|
||||||
|
return;
|
||||||
|
|
||||||
|
BookmarkList bList;
|
||||||
|
ConferenceList cList;
|
||||||
|
const TagList& l = xml->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "url" )
|
||||||
|
{
|
||||||
|
const std::string& url = (*it)->findAttribute( "url" );
|
||||||
|
const std::string& name = (*it)->findAttribute( "name" );
|
||||||
|
|
||||||
|
if( !url.empty() && !name.empty() )
|
||||||
|
{
|
||||||
|
BookmarkListItem item;
|
||||||
|
item.url = url;
|
||||||
|
item.name = name;
|
||||||
|
bList.push_back( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( (*it)->name() == "conference" )
|
||||||
|
{
|
||||||
|
const std::string& jid = (*it)->findAttribute( "jid" );
|
||||||
|
const std::string& name = (*it)->findAttribute( "name" );
|
||||||
|
|
||||||
|
if( !jid.empty() && !name.empty() )
|
||||||
|
{
|
||||||
|
const std::string& join = (*it)->findAttribute( "autojoin" );
|
||||||
|
ConferenceListItem item;
|
||||||
|
item.jid = jid;
|
||||||
|
item.name = name;
|
||||||
|
const Tag* nick = (*it)->findChild( "nick" );
|
||||||
|
if( nick )
|
||||||
|
item.nick = nick->cdata();
|
||||||
|
const Tag* pwd = (*it)->findChild( "password" );
|
||||||
|
if( pwd )
|
||||||
|
item.password = pwd->cdata();
|
||||||
|
item.autojoin = ( join == "true" || join == "1" );
|
||||||
|
cList.push_back( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_bookmarkHandler )
|
||||||
|
m_bookmarkHandler->handleBookmarks( bList, cList );
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOOKMARKSTORAGE_H__
|
||||||
|
#define BOOKMARKSTORAGE_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include "bookmarkhandler.h"
|
||||||
|
#include "privatexml.h"
|
||||||
|
#include "privatexmlhandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0048 (Bookmark Storage).
|
||||||
|
*
|
||||||
|
* You can use this class to store bookmarks to multi-user chat rooms or ordinary URLs
|
||||||
|
* on the server (and to retrieve them later on).
|
||||||
|
* To retrieve all stored bookmarks for the current user you have to create a class which
|
||||||
|
* inherits from BookmarkHandler. This handler receives retrieved bookmarks.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* class MyClass : public BookmarkHandler
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* // ...
|
||||||
|
* void myFuncRetrieve();
|
||||||
|
* void myFuncStore();
|
||||||
|
* void handleBookmarks( const BookmarkList &bList, const ConferenceList &cList );
|
||||||
|
*
|
||||||
|
* private:
|
||||||
|
* BookmarkStorage* m_bs;
|
||||||
|
* BookmarkList m_bList;
|
||||||
|
* ConferenceList m_cList;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* void MyClass::myFuncRetrieve()
|
||||||
|
* {
|
||||||
|
* m_bs = new BookmarkStorage( m_client );
|
||||||
|
* m_bs->requestBookmarks();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void MyClass::handleBookmarks( const BookmarkList &bList, const ConferenceList &cList )
|
||||||
|
* {
|
||||||
|
* m_bList = bList;
|
||||||
|
* m_cList = cList;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* To store additional bookmarks you have to fetch the currently stored ones first,
|
||||||
|
* add your new bookmark to the list, and transfer them all together back to the
|
||||||
|
* server. This protocol does not support storage of 'deltas', that is, when saving
|
||||||
|
* bookmarks all previously saved bookmarks are overwritten.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void MyClass::myFuncStore()
|
||||||
|
* {
|
||||||
|
* BookmarkListItem bi;
|
||||||
|
* bi.url = "http://www.jabber.org";
|
||||||
|
* bi.name = "my favourite IM protocol";
|
||||||
|
* m_bList.push_back( bi );
|
||||||
|
*
|
||||||
|
* conferenceListItem ci
|
||||||
|
* ci.name = "jabber/xmpp development room";
|
||||||
|
* ci.jid = "jdev@conference.jabber.org";
|
||||||
|
* ci.nick = "myNick";
|
||||||
|
* ci.password = EmptyString;
|
||||||
|
* ci.autojoin = true;
|
||||||
|
* m_cList.push_back( ci );
|
||||||
|
*
|
||||||
|
* m_bs->storeBookmarks( m_bList, m_cList );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API BookmarkStorage : public PrivateXML, public PrivateXMLHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new BookmarkStorage object.
|
||||||
|
* @param parent The ClientBase to use for communication.
|
||||||
|
*/
|
||||||
|
BookmarkStorage( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BookmarkStorage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to store a number of URL and conference bookmarks on the server.
|
||||||
|
* Make sure you store the whole set of bookmarks, not a 'delta'.
|
||||||
|
* @param bList A list of URLs to store.
|
||||||
|
* @param cList A list of conferences to store.
|
||||||
|
*/
|
||||||
|
void storeBookmarks( const BookmarkList& bList, const ConferenceList& cList );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to initiate retrieval of bookmarks. Use registerBookmarkHandler()
|
||||||
|
* to register an object which will receive the lists of bookmarks.
|
||||||
|
*/
|
||||||
|
void requestBookmarks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a BookmarkHandler.
|
||||||
|
* @param bmh The BookmarkHandler which shall receive retrieved bookmarks.
|
||||||
|
*/
|
||||||
|
void registerBookmarkHandler( BookmarkHandler* bmh )
|
||||||
|
{ m_bookmarkHandler = bmh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to un-register the BookmarkHandler.
|
||||||
|
*/
|
||||||
|
void removeBookmarkHandler()
|
||||||
|
{ m_bookmarkHandler = 0; }
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXML( const Tag* xml );
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult );
|
||||||
|
|
||||||
|
private:
|
||||||
|
BookmarkHandler* m_bookmarkHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOOKMARKSTORAGE_H__
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BYTESTREAM_H__
|
||||||
|
#define BYTESTREAM_H__
|
||||||
|
|
||||||
|
#include "jid.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class BytestreamDataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a single bytestream.
|
||||||
|
*
|
||||||
|
* Used as a base class for InBand Bytestreams as well as SOCKS5 Bytestreams.
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Bytestream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Available stream types.
|
||||||
|
*/
|
||||||
|
enum StreamType
|
||||||
|
{
|
||||||
|
S5B, /**< SOCKS5 Bytestream */
|
||||||
|
IBB /**< In-Band Bytestream */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Bytestream.
|
||||||
|
* @param type The stream type.
|
||||||
|
* @param logInstance A Logsink to use for logging. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param initiator The initiator of the stream (usually the sender).
|
||||||
|
* @param target The target of the stream (usually the receiver).
|
||||||
|
* @param sid The stream's ID.
|
||||||
|
*/
|
||||||
|
Bytestream( StreamType type, LogSink& logInstance, const JID& initiator, const JID& target,
|
||||||
|
const std::string& sid )
|
||||||
|
: m_handler( 0 ), m_logInstance( logInstance ), m_initiator( initiator ), m_target( target ),
|
||||||
|
m_type( type ), m_sid( sid ), m_open( false )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Bytestream() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the bytestream is open, that is, accepted by both parties and ready
|
||||||
|
* to send/receive data.
|
||||||
|
* @return Whether or not the bytestream is open.
|
||||||
|
*/
|
||||||
|
bool isOpen() const { return m_open; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function starts the connection process.
|
||||||
|
* @return @b True if a connection to a remote entity could be established, @b false
|
||||||
|
* otherwise.
|
||||||
|
* @note If @b false is returned you should pass this Bytestream object
|
||||||
|
* to SIProfileFT::dispose() for deletion.
|
||||||
|
* @note Make sure you have a BytestreamDataHandler registered (using
|
||||||
|
* registerBytestreamDataHandler()) before calling this function.
|
||||||
|
*/
|
||||||
|
virtual bool connect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the bytestream.
|
||||||
|
*/
|
||||||
|
virtual void close() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to send a chunk of data over an open bytestream.
|
||||||
|
* If the stream is not open or has been closed again
|
||||||
|
* (by the remote entity or locally), nothing is sent and @b false is returned.
|
||||||
|
* This function does any base64 encoding for you, if necessary.
|
||||||
|
* @param data The block of data to send.
|
||||||
|
* @return @b True if the data has been sent (no guarantee of receipt), @b false
|
||||||
|
* in case of an error.
|
||||||
|
*/
|
||||||
|
virtual bool send( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function repeatedly to receive data. You should even do this
|
||||||
|
* if you use the bytestream to merely @b send data. May be a NOOP, depending on the actual
|
||||||
|
* stream type.
|
||||||
|
* @param timeout The timeout to use for select in microseconds. Default of -1 means blocking.
|
||||||
|
* @return The state of the connection.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError recv( int timeout = -1 ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you retrieve the stream's ID.
|
||||||
|
* @return The stream's ID.
|
||||||
|
*/
|
||||||
|
const std::string& sid() const { return m_sid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stream's type.
|
||||||
|
* @return The stream's type.
|
||||||
|
*/
|
||||||
|
StreamType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the target entity's JID. If this bytestream is remote-initiated, this is
|
||||||
|
* the local JID. If it is local-initiated, this is the remote entity's JID.
|
||||||
|
* @return The target's JID.
|
||||||
|
*/
|
||||||
|
const JID& target() const { return m_target; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the initiating entity's JID. If this bytestream is remote-initiated, this is
|
||||||
|
* the remote entity's JID. If it is local-initiated, this is the local JID.
|
||||||
|
* @return The initiator's JID.
|
||||||
|
*/
|
||||||
|
const JID& initiator() const { return m_initiator; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register an object that will receive any notifications from
|
||||||
|
* the Bytestream instance. Only one BytestreamDataHandler can be registered
|
||||||
|
* at any one time.
|
||||||
|
* @param bdh The BytestreamDataHandler-derived object to receive notifications.
|
||||||
|
*/
|
||||||
|
void registerBytestreamDataHandler( BytestreamDataHandler* bdh )
|
||||||
|
{ m_handler = bdh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the registered BytestreamDataHandler.
|
||||||
|
*/
|
||||||
|
void removeBytestreamDataHandler()
|
||||||
|
{ m_handler = 0; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for incoming data and open/close events. */
|
||||||
|
BytestreamDataHandler* m_handler;
|
||||||
|
|
||||||
|
/** A LogSink instance to use for logging. */
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
/** The initiator's JID. */
|
||||||
|
const JID m_initiator;
|
||||||
|
|
||||||
|
/** The target's JID. */
|
||||||
|
const JID m_target;
|
||||||
|
|
||||||
|
/** The stream type. */
|
||||||
|
StreamType m_type;
|
||||||
|
|
||||||
|
/** The stream ID. */
|
||||||
|
std::string m_sid;
|
||||||
|
|
||||||
|
/** Indicates whether or not the stream is open. */
|
||||||
|
bool m_open;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Bytestream& operator=( const Bytestream& );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BYTESTREAM_H__
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BYTESTREAMDATAHANDLER_H__
|
||||||
|
#define BYTESTREAMDATAHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Bytestream;
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that allows implementors to receive data
|
||||||
|
* sent over a SOCKS5 Bytestream as defined in XEP-0066, or an In-Band Bytestream
|
||||||
|
* as defined in XEP-0047. You'll also need it for sending of data.
|
||||||
|
*
|
||||||
|
* An BytestreamDataHandler is registered with a Bytestream.
|
||||||
|
*
|
||||||
|
* See SIProfileFT for more information regarding file transfer.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API BytestreamDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BytestreamDataHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function to receive data which is sent over the bytestream.
|
||||||
|
* The data received here is (probably) only a single chunk of the complete data (depending
|
||||||
|
* on the amount of data you want to send).
|
||||||
|
* @param bs The bytestream.
|
||||||
|
* @param data The actual stream payload.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamData( Bytestream* bs, const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies about an error occuring while using a bytestream.
|
||||||
|
* When this handler is called the stream has already been closed.
|
||||||
|
* @param bs The bytestream.
|
||||||
|
* @param iq The error stanza.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamError( Bytestream* bs, const IQ& iq ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the handler that the given bytestream has been acknowledged
|
||||||
|
* and is ready to send/receive data.
|
||||||
|
* @param bs The opened bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamOpen( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the handler that the given bytestream has been closed.
|
||||||
|
* @param bs The closed bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamClose( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BYTESTREAMDATAHANDLER_H__
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BYTESTREAMHANDLER_H__
|
||||||
|
#define BYTESTREAMHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "jid.h"
|
||||||
|
#include "bytestream.h"
|
||||||
|
#include "iq.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that allows to receive new incoming Bytestream requests
|
||||||
|
* from remote entities.
|
||||||
|
*
|
||||||
|
* You should not need to use this interface directly.
|
||||||
|
*
|
||||||
|
* See SIProfileFT on how to implement file transfer in general.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API BytestreamHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BytestreamHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the implementor of a new incoming bytestream request.
|
||||||
|
* You have to call either
|
||||||
|
* BytestreamManager::acceptBytestream() or
|
||||||
|
* BytestreamManager::rejectBytestream(), to accept or reject the bytestream
|
||||||
|
* request, respectively.
|
||||||
|
* @param sid The bytestream's id, to be passed to BytestreamManager::acceptBytestream()
|
||||||
|
* and BytestreamManager::rejectBytestream(), respectively.
|
||||||
|
* @param from The remote initiator of the bytestream request.
|
||||||
|
*/
|
||||||
|
virtual void handleIncomingBytestreamRequest( const std::string& sid, const JID& from ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the implementor of a new incoming bytestream. The bytestream is not yet ready to
|
||||||
|
* send data.
|
||||||
|
* To initialize the bytestream and to prepare it for data transfer, register a
|
||||||
|
* BytestreamDataHandler with it and call its connect() method.
|
||||||
|
* To not block your application while the data transfer lasts, you most
|
||||||
|
* likely want to put the bytestream into its own thread or process (before calling connect() on it).
|
||||||
|
* It is safe to do so without additional synchronization.
|
||||||
|
* When you are finished using the bytestream, use SIProfileFT::dispose() to get rid of it.
|
||||||
|
* @param bs The bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleIncomingBytestream( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the implementor of successful establishing of an outgoing bytestream request.
|
||||||
|
* The stream has been accepted by the remote entity and is ready to send data.
|
||||||
|
* The BytestreamHandler does @b not become the owner of the Bytestream object.
|
||||||
|
* Use SIProfileFT::dispose() to get rid of the bytestream object after it has been closed.
|
||||||
|
* @param bs The new bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleOutgoingBytestream( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the handler of errors occuring when a bytestream was requested.
|
||||||
|
* For example, if the remote entity does not implement SOCKS5 bytestreams.
|
||||||
|
* @param iq The error stanza.
|
||||||
|
* @param sid The request's SID.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamError( const IQ& iq, const std::string& sid ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BYTESTREAMHANDLER_H__
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "capabilities.h"
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "sha.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Capabilities::Capabilities( Disco* disco )
|
||||||
|
: StanzaExtension( ExtCaps ), m_disco( disco ), m_node( GLOOX_CAPS_NODE ),
|
||||||
|
m_hash( "sha-1" ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( m_disco )
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Capabilities::Capabilities( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtCaps ), m_disco( 0 ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "c" || !tag->hasAttribute( XMLNS, XMLNS_CAPS )
|
||||||
|
|| !tag->hasAttribute( "node" ) || !tag->hasAttribute( "ver" ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
m_ver = tag->findAttribute( "ver" );
|
||||||
|
m_hash = tag->findAttribute( "hash" );
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Capabilities::~Capabilities()
|
||||||
|
{
|
||||||
|
if( m_disco )
|
||||||
|
m_disco->removeNodeHandlers( const_cast<Capabilities*>( this ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string Capabilities::ver() const
|
||||||
|
{
|
||||||
|
if( !m_disco )
|
||||||
|
return m_ver;
|
||||||
|
|
||||||
|
SHA sha;
|
||||||
|
sha.feed( generate( m_disco->identities(), m_disco->features( true ), m_disco->form() ) );
|
||||||
|
const std::string& hash = Base64::encode64( sha.binary() );
|
||||||
|
m_disco->removeNodeHandlers( const_cast<Capabilities*>( this ) );
|
||||||
|
m_disco->registerNodeHandler( const_cast<Capabilities*>( this ), m_node + '#' + hash );
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Capabilities::generate( const Disco::IdentityList& il, const StringList& features, const DataForm* form )
|
||||||
|
{
|
||||||
|
StringList sl;
|
||||||
|
Disco::IdentityList::const_iterator it = il.begin();
|
||||||
|
for( ; it != il.end(); ++it )
|
||||||
|
{
|
||||||
|
std::string id = (*it)->category();
|
||||||
|
id += '/';
|
||||||
|
id += (*it)->type();
|
||||||
|
id += '/';
|
||||||
|
// FIXME add xml:lang caps here. see XEP-0115 Section 5
|
||||||
|
id += '/';
|
||||||
|
id += (*it)->name();
|
||||||
|
sl.push_back( id );
|
||||||
|
}
|
||||||
|
sl.sort();
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
StringList::const_iterator it2 = sl.begin();
|
||||||
|
for( ; it2 != sl.end(); ++it2 )
|
||||||
|
{
|
||||||
|
s += (*it2);
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList f = features;
|
||||||
|
f.sort();
|
||||||
|
it2 = f.begin();
|
||||||
|
for( ; it2 != f.end(); ++it2 )
|
||||||
|
{
|
||||||
|
s += (*it2);
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
|
||||||
|
if( form )
|
||||||
|
{
|
||||||
|
DataForm::FieldList::const_iterator it3 = form->fields().begin();
|
||||||
|
typedef std::map<std::string, StringList> MapSSL;
|
||||||
|
|
||||||
|
MapSSL m;
|
||||||
|
for( ; it3 != form->fields().end(); ++it3 )
|
||||||
|
{
|
||||||
|
if( (*it3)->name() == "FORM_TYPE" )
|
||||||
|
{
|
||||||
|
s += (*it3)->value();
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m.insert( std::make_pair( (*it3)->name(), (*it3)->values() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
MapSSL::iterator it4 = m.begin();
|
||||||
|
for( ; it4 != m.end(); ++it4 )
|
||||||
|
{
|
||||||
|
s += it4->first;
|
||||||
|
s += '<';
|
||||||
|
it2 = it4->second.begin();
|
||||||
|
for( ; it2 != it4->second.end(); ++it2 )
|
||||||
|
{
|
||||||
|
s += (*it2);
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Capabilities::generate( const Disco::Info* info )
|
||||||
|
{
|
||||||
|
return info ? generate( info->identities(), info->features(), info->form() ) : EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Capabilities::generate( const Disco* disco )
|
||||||
|
{
|
||||||
|
return disco ? generate( disco->identities(), disco->features(), disco->form() ) : EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Capabilities::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/presence/c[@xmlns='" + XMLNS_CAPS + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Capabilities::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid || m_node.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( "c" );
|
||||||
|
t->setXmlns( XMLNS_CAPS );
|
||||||
|
t->addAttribute( "hash", m_hash );
|
||||||
|
t->addAttribute( "node", m_node );
|
||||||
|
t->addAttribute( "ver", ver() );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList Capabilities::handleDiscoNodeFeatures( const JID&, const std::string& )
|
||||||
|
{
|
||||||
|
return m_disco->features();
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::IdentityList Capabilities::handleDiscoNodeIdentities( const JID&, const std::string& )
|
||||||
|
{
|
||||||
|
const Disco::IdentityList& il = m_disco->identities();
|
||||||
|
Disco::IdentityList ret;
|
||||||
|
Disco::IdentityList::const_iterator it = il.begin();
|
||||||
|
for( ; it != il.end(); ++it )
|
||||||
|
{
|
||||||
|
ret.push_back( new Disco::Identity( *(*it) ) );
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::ItemList Capabilities::handleDiscoNodeItems( const JID&, const JID&, const std::string& )
|
||||||
|
{
|
||||||
|
return Disco::ItemList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CAPABILITIES_H__
|
||||||
|
#define CAPABILITIES_H__
|
||||||
|
|
||||||
|
#include "disconodehandler.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Disco;
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0115 (Entity Capabilities).
|
||||||
|
*
|
||||||
|
* XEP Version: 1.5-15
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Capabilities : public StanzaExtension, public DiscoNodeHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object and fills it according to the parameters.
|
||||||
|
* @param disco The current Client's Disco object.
|
||||||
|
*/
|
||||||
|
Capabilities( Disco* disco );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Capabilities( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Capabilities();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client's identifying node.
|
||||||
|
* @return The node.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client's identifying node.
|
||||||
|
* @param node The node.
|
||||||
|
*/
|
||||||
|
void setNode( const std::string& node ) { m_node = node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client's identifying ver string.
|
||||||
|
* @return The ver string.
|
||||||
|
*/
|
||||||
|
const std::string ver() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Capabilities( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Capabilities( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from,
|
||||||
|
const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to,
|
||||||
|
const std::string& node = EmptyString );
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Returns the hash function used for creating the caps info.
|
||||||
|
* @return The current hash function's name.
|
||||||
|
*/
|
||||||
|
const std::string& hash() const { return m_hash; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the hash function to use.
|
||||||
|
* @param hash The hash function.
|
||||||
|
* @todo Convert to using an enum and make public.
|
||||||
|
*/
|
||||||
|
void setHash( const std::string& hash ) { m_hash = hash; }
|
||||||
|
|
||||||
|
static std::string generate( const Disco::IdentityList& identities,
|
||||||
|
const StringList& features, const DataForm* form = 0 );
|
||||||
|
static std::string generate( const Disco::Info* info );
|
||||||
|
static std::string generate( const Disco* disco );
|
||||||
|
|
||||||
|
Disco* m_disco;
|
||||||
|
std::string m_node;
|
||||||
|
std::string m_hash;
|
||||||
|
std::string m_ver;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CAPABILITIES_H__
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chatstate.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/* chat state type values */
|
||||||
|
static const char* stateValues [] = {
|
||||||
|
"active",
|
||||||
|
"composing",
|
||||||
|
"paused",
|
||||||
|
"inactive",
|
||||||
|
"gone"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline ChatStateType chatStateType( const std::string& type )
|
||||||
|
{
|
||||||
|
return (ChatStateType)util::lookup2( type, stateValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatState::ChatState( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtChatState )
|
||||||
|
{
|
||||||
|
if( tag )
|
||||||
|
m_state = chatStateType( tag->name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& ChatState::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter =
|
||||||
|
"/message/active[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/composing[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/paused[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/inactive[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/gone[@xmlns='" + XMLNS_CHAT_STATES + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* ChatState::tag() const
|
||||||
|
{
|
||||||
|
if( m_state == ChatStateInvalid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return new Tag( util::lookup2( m_state, stateValues ), XMLNS, XMLNS_CHAT_STATES );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATSTATE_H__
|
||||||
|
#define CHATSTATE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An implementation of Chat State Notifications (XEP-0085) as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Vincent Thomasset
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API ChatState : public StanzaExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag A Tag to parse.
|
||||||
|
*/
|
||||||
|
ChatState( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object of the given type.
|
||||||
|
* @param state The chat state.
|
||||||
|
*/
|
||||||
|
ChatState( ChatStateType state )
|
||||||
|
: StanzaExtension( ExtChatState ), m_state( state )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ChatState() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object's state.
|
||||||
|
* @return The object's state.
|
||||||
|
*/
|
||||||
|
ChatStateType state() const { return m_state; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new ChatState( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new ChatState( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatStateType m_state;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHATSTATE_H__
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "chatstatefilter.h"
|
||||||
|
#include "chatstatehandler.h"
|
||||||
|
#include "messageeventhandler.h"
|
||||||
|
#include "messagesession.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "chatstate.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ChatStateFilter::ChatStateFilter( MessageSession* parent )
|
||||||
|
: MessageFilter( parent ), m_chatStateHandler( 0 ), m_lastSent( ChatStateGone ),
|
||||||
|
m_enableChatStates( true )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatStateFilter::~ChatStateFilter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatStateFilter::filter( Message& msg )
|
||||||
|
{
|
||||||
|
if( m_enableChatStates && m_chatStateHandler )
|
||||||
|
{
|
||||||
|
const ChatState* state = msg.findExtension<ChatState>( ExtChatState );
|
||||||
|
|
||||||
|
m_enableChatStates = state && state->state() != ChatStateInvalid;
|
||||||
|
if( m_enableChatStates && msg.body().empty() )
|
||||||
|
m_chatStateHandler->handleChatState( msg.from(), state->state() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatStateFilter::setChatState( ChatStateType state )
|
||||||
|
{
|
||||||
|
if( !m_enableChatStates || state == m_lastSent || state == ChatStateInvalid )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Message m( Message::Chat, m_parent->target() );
|
||||||
|
m.addExtension( new ChatState( state ) );
|
||||||
|
|
||||||
|
m_lastSent = state;
|
||||||
|
|
||||||
|
send( m );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatStateFilter::decorate( Message& msg )
|
||||||
|
{
|
||||||
|
if( m_enableChatStates )
|
||||||
|
msg.addExtension( new ChatState( ChatStateActive ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CHATSTATEFILTER_H__
|
||||||
|
#define CHATSTATEFILTER_H__
|
||||||
|
|
||||||
|
#include "messagefilter.h"
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
class ChatStateHandler;
|
||||||
|
class MessageSession;
|
||||||
|
class Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class adds Chat State Notifications (XEP-0085) support to a MessageSession.
|
||||||
|
*
|
||||||
|
* This implementation of Chat States is fully transparent to the user of the class.
|
||||||
|
* If the remote entity does not request chat states, ChatStateFilter will not send
|
||||||
|
* any, even if the user requests it. (This is required by the protocol specification.)
|
||||||
|
* You MUST annouce this capability by use of Disco (associated namespace is XMLNS_CHAT_STATES).
|
||||||
|
* (This is also required by the protocol specification.)
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
class GLOOX_API ChatStateFilter : public MessageFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Contstructs a new Chat State filter for a MessageSession.
|
||||||
|
* @param parent The MessageSession to decorate.
|
||||||
|
*/
|
||||||
|
ChatStateFilter( MessageSession* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ChatStateFilter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set a chat state as defined in XEP-0085.
|
||||||
|
* @note The Spec states that Chat States shall not be sent to an entity
|
||||||
|
* which did not request them. Reasonable effort is taken in this function to
|
||||||
|
* avoid spurious state sending. You should be safe to call this even if Message
|
||||||
|
* Events were not requested by the remote entity. However,
|
||||||
|
* calling setChatState( CHAT_STATE_COMPOSING ) for every keystroke still is
|
||||||
|
* discouraged. ;)
|
||||||
|
* @param state The state to set.
|
||||||
|
*/
|
||||||
|
void setChatState( ChatStateType state );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ChatStateHandler registered here will receive Chat States according
|
||||||
|
* to XEP-0085.
|
||||||
|
* @param csh The ChatStateHandler to register.
|
||||||
|
*/
|
||||||
|
void registerChatStateHandler( ChatStateHandler* csh )
|
||||||
|
{ m_chatStateHandler = csh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function clears the internal pointer to the ChatStateHandler.
|
||||||
|
* Chat States will not be delivered anymore after calling this function until another
|
||||||
|
* ChatStateHandler is registered.
|
||||||
|
*/
|
||||||
|
void removeChatStateHandler()
|
||||||
|
{ m_chatStateHandler = 0; }
|
||||||
|
|
||||||
|
// reimplemented from MessageFilter
|
||||||
|
virtual void decorate( Message& msg );
|
||||||
|
|
||||||
|
// reimplemented from MessageFilter
|
||||||
|
virtual void filter( Message& msg );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for incoming chat state changes. */
|
||||||
|
ChatStateHandler* m_chatStateHandler;
|
||||||
|
|
||||||
|
/** Holds the state sent last. */
|
||||||
|
ChatStateType m_lastSent;
|
||||||
|
|
||||||
|
/** Indicates whether or not chat states are currently enabled. */
|
||||||
|
bool m_enableChatStates;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHATSTATEFILTER_H__
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CHATSTATEHANDLER_H__
|
||||||
|
#define CHATSTATEHANDLER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class JID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that enables an object to be notified about
|
||||||
|
* a remote entity's Chat States (XEP-0085).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
class GLOOX_API ChatStateHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ChatStateHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the ChatStateHandler that a different chat state has been set by the remote
|
||||||
|
* contact.
|
||||||
|
* @param from The originator of the Event.
|
||||||
|
* @param state The chat state set by the remote entity.
|
||||||
|
*/
|
||||||
|
virtual void handleChatState( const JID& from, ChatStateType state ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHATSTATEHANDLER_H__
|
|
@ -0,0 +1,603 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
#include "capabilities.h"
|
||||||
|
#include "rostermanager.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "nonsaslauth.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "stanzaextensionfactory.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "tlsbase.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#if !defined( _WIN32 ) && !defined( _WIN32_WCE )
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
// ---- Client::ResourceBind ----
|
||||||
|
Client::ResourceBind::ResourceBind( const std::string& resource, bool bind )
|
||||||
|
: StanzaExtension( ExtResourceBind ), m_jid( JID() ), m_bind( bind )
|
||||||
|
{
|
||||||
|
prep::resourceprep( resource, m_resource );
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::ResourceBind::ResourceBind( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtResourceBind ), m_resource( EmptyString ), m_bind( true )
|
||||||
|
{
|
||||||
|
if( !tag )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( tag->name() == "unbind" )
|
||||||
|
m_bind = false;
|
||||||
|
else if( tag->name() == "bind" )
|
||||||
|
m_bind = true;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( tag->hasChild( "jid" ) )
|
||||||
|
m_jid.setJID( tag->findChild( "jid" )->cdata() );
|
||||||
|
else if( tag->hasChild( "resource" ) )
|
||||||
|
m_resource = tag->findChild( "resource" )->cdata();
|
||||||
|
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::ResourceBind::~ResourceBind()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Client::ResourceBind::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/bind[@xmlns='" + XMLNS_STREAM_BIND + "']"
|
||||||
|
"|/iq/unbind[@xmlns='" + XMLNS_STREAM_BIND + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Client::ResourceBind::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( m_bind ? "bind" : "unbind" );
|
||||||
|
t->setXmlns( XMLNS_STREAM_BIND );
|
||||||
|
|
||||||
|
if( m_bind && m_resource.empty() && m_jid )
|
||||||
|
new Tag( t, "jid", m_jid.full() );
|
||||||
|
else
|
||||||
|
new Tag( t, "resource", m_resource );
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~Client::ResourceBind ----
|
||||||
|
|
||||||
|
// ---- Client::SessionCreation ----
|
||||||
|
Tag* Client::SessionCreation::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "session" );
|
||||||
|
t->setXmlns( XMLNS_STREAM_SESSION );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- Client::SessionCreation ----
|
||||||
|
|
||||||
|
// ---- Client ----
|
||||||
|
Client::Client( const std::string& server )
|
||||||
|
: ClientBase( XMLNS_CLIENT, server ),
|
||||||
|
m_rosterManager( 0 ), m_auth( 0 ),
|
||||||
|
m_presence( Presence::Available, JID() ), m_resourceBound( false ),
|
||||||
|
m_forceNonSasl( false ), m_manageRoster( true ),
|
||||||
|
m_streamFeatures( 0 )
|
||||||
|
{
|
||||||
|
m_jid.setServer( server );
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::Client( const JID& jid, const std::string& password, int port )
|
||||||
|
: ClientBase( XMLNS_CLIENT, password, EmptyString, port ),
|
||||||
|
m_rosterManager( 0 ), m_auth( 0 ),
|
||||||
|
m_presence( Presence::Available, JID() ), m_resourceBound( false ),
|
||||||
|
m_forceNonSasl( false ), m_manageRoster( true ),
|
||||||
|
m_streamFeatures( 0 )
|
||||||
|
{
|
||||||
|
m_jid = jid;
|
||||||
|
m_server = m_jid.serverRaw();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::~Client()
|
||||||
|
{
|
||||||
|
delete m_rosterManager;
|
||||||
|
delete m_auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::init()
|
||||||
|
{
|
||||||
|
m_rosterManager = new RosterManager( this );
|
||||||
|
m_disco->setIdentity( "client", "bot" );
|
||||||
|
registerStanzaExtension( new ResourceBind( 0 ) );
|
||||||
|
registerStanzaExtension( new Capabilities() );
|
||||||
|
m_presenceExtensions.push_back( new Capabilities( m_disco ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setUsername( const std::string &username )
|
||||||
|
{
|
||||||
|
m_jid.setUsername( username );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::handleNormalNode( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() == "features" && tag->xmlns() == XMLNS_STREAM )
|
||||||
|
{
|
||||||
|
m_streamFeatures = getStreamFeatures( tag );
|
||||||
|
|
||||||
|
if( m_tls == TLSRequired && !m_encryptionActive
|
||||||
|
&& ( !m_encryption || !( m_streamFeatures & StreamFeatureStartTls ) ) )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "Client is configured to require"
|
||||||
|
" TLS but either the server didn't offer TLS or"
|
||||||
|
" TLS support is not compiled in." );
|
||||||
|
disconnect( ConnTlsNotAvailable );
|
||||||
|
}
|
||||||
|
else if( m_tls > TLSDisabled && m_encryption && !m_encryptionActive
|
||||||
|
&& ( m_streamFeatures & StreamFeatureStartTls ) )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventEncryption );
|
||||||
|
startTls();
|
||||||
|
}
|
||||||
|
else if( m_compress && m_compression && !m_compressionActive
|
||||||
|
&& ( m_streamFeatures & StreamFeatureCompressZlib ) )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventCompression );
|
||||||
|
logInstance().warn( LogAreaClassClient, "The server offers compression, but negotiating Compression at this stage is not recommended. See XEP-0170 for details. We'll continue anyway." );
|
||||||
|
negotiateCompression( StreamFeatureCompressZlib );
|
||||||
|
}
|
||||||
|
else if( m_sasl )
|
||||||
|
{
|
||||||
|
if( m_authed )
|
||||||
|
{
|
||||||
|
if( m_streamFeatures & StreamFeatureBind )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventResourceBinding );
|
||||||
|
bindResource( resource() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( !username().empty() && !password().empty() )
|
||||||
|
{
|
||||||
|
if( !login() )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "The server doesn't support"
|
||||||
|
" any auth mechanisms we know about" );
|
||||||
|
disconnect( ConnNoSupportedAuth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( !m_clientCerts.empty() && !m_clientKey.empty()
|
||||||
|
&& m_streamFeatures & SaslMechExternal && m_availableSaslMechs & SaslMechExternal )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechExternal );
|
||||||
|
}
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
else if( m_streamFeatures & SaslMechGssapi && m_availableSaslMechs & SaslMechGssapi )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechGssapi );
|
||||||
|
}
|
||||||
|
else if( m_streamFeatures & SaslMechNTLM && m_availableSaslMechs & SaslMechNTLM )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechNTLM );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if( m_streamFeatures & SaslMechAnonymous
|
||||||
|
&& m_availableSaslMechs & SaslMechAnonymous )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechAnonymous );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
connected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( m_compress && m_compression && !m_compressionActive
|
||||||
|
&& ( m_streamFeatures & StreamFeatureCompressZlib ) )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventCompression );
|
||||||
|
negotiateCompression( StreamFeatureCompressZlib );
|
||||||
|
}
|
||||||
|
// else if( ( m_streamFeatures & StreamFeatureCompressDclz )
|
||||||
|
// && m_connection->initCompression( StreamFeatureCompressDclz ) )
|
||||||
|
// {
|
||||||
|
// negotiateCompression( StreamFeatureCompressDclz );
|
||||||
|
// }
|
||||||
|
else if( m_streamFeatures & StreamFeatureIqAuth )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
nonSaslLogin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "fallback: the server doesn't "
|
||||||
|
"support any auth mechanisms we know about" );
|
||||||
|
disconnect( ConnNoSupportedAuth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string& name = tag->name(),
|
||||||
|
xmlns = tag->findAttribute( XMLNS );
|
||||||
|
if( name == "proceed" && xmlns == XMLNS_STREAM_TLS )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "starting TLS handshake..." );
|
||||||
|
|
||||||
|
if( m_encryption )
|
||||||
|
{
|
||||||
|
m_encryptionActive = true;
|
||||||
|
m_encryption->handshake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( name == "failure" )
|
||||||
|
{
|
||||||
|
if( xmlns == XMLNS_STREAM_TLS )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "TLS handshake failed (server-side)!" );
|
||||||
|
disconnect( ConnTlsFailed );
|
||||||
|
}
|
||||||
|
else if( xmlns == XMLNS_COMPRESSION )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "Stream compression init failed!" );
|
||||||
|
disconnect( ConnCompressionFailed );
|
||||||
|
}
|
||||||
|
else if( xmlns == XMLNS_STREAM_SASL )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "SASL authentication failed!" );
|
||||||
|
processSASLError( tag );
|
||||||
|
disconnect( ConnAuthenticationFailed );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( name == "compressed" && xmlns == XMLNS_COMPRESSION )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "Stream compression initialized" );
|
||||||
|
m_compressionActive = true;
|
||||||
|
header();
|
||||||
|
}
|
||||||
|
else if( name == "challenge" && xmlns == XMLNS_STREAM_SASL )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "Processing SASL challenge" );
|
||||||
|
processSASLChallenge( tag->cdata() );
|
||||||
|
}
|
||||||
|
else if( name == "success" && xmlns == XMLNS_STREAM_SASL )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "SASL authentication successful" );
|
||||||
|
processSASLSuccess();
|
||||||
|
setAuthed( true );
|
||||||
|
header();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Client::getStreamFeatures( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "features" || tag->xmlns() != XMLNS_STREAM )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int features = 0;
|
||||||
|
|
||||||
|
if( tag->hasChild( "starttls", XMLNS, XMLNS_STREAM_TLS ) )
|
||||||
|
features |= StreamFeatureStartTls;
|
||||||
|
|
||||||
|
if( tag->hasChild( "mechanisms", XMLNS, XMLNS_STREAM_SASL ) )
|
||||||
|
features |= getSaslMechs( tag->findChild( "mechanisms" ) );
|
||||||
|
|
||||||
|
if( tag->hasChild( "bind", XMLNS, XMLNS_STREAM_BIND ) )
|
||||||
|
features |= StreamFeatureBind;
|
||||||
|
|
||||||
|
if( tag->hasChild( "unbind", XMLNS, XMLNS_STREAM_BIND ) )
|
||||||
|
features |= StreamFeatureUnbind;
|
||||||
|
|
||||||
|
if( tag->hasChild( "session", XMLNS, XMLNS_STREAM_SESSION ) )
|
||||||
|
features |= StreamFeatureSession;
|
||||||
|
|
||||||
|
if( tag->hasChild( "auth", XMLNS, XMLNS_STREAM_IQAUTH ) )
|
||||||
|
features |= StreamFeatureIqAuth;
|
||||||
|
|
||||||
|
if( tag->hasChild( "register", XMLNS, XMLNS_STREAM_IQREGISTER ) )
|
||||||
|
features |= StreamFeatureIqRegister;
|
||||||
|
|
||||||
|
if( tag->hasChild( "compression", XMLNS, XMLNS_STREAM_COMPRESS ) )
|
||||||
|
features |= getCompressionMethods( tag->findChild( "compression" ) );
|
||||||
|
|
||||||
|
if( features == 0 )
|
||||||
|
features = StreamFeatureIqAuth;
|
||||||
|
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Client::getSaslMechs( Tag* tag )
|
||||||
|
{
|
||||||
|
int mechs = SaslMechNone;
|
||||||
|
|
||||||
|
const std::string mech = "mechanism";
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "DIGEST-MD5" ) )
|
||||||
|
mechs |= SaslMechDigestMd5;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "PLAIN" ) )
|
||||||
|
mechs |= SaslMechPlain;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "ANONYMOUS" ) )
|
||||||
|
mechs |= SaslMechAnonymous;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "EXTERNAL" ) )
|
||||||
|
mechs |= SaslMechExternal;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "GSSAPI" ) )
|
||||||
|
mechs |= SaslMechGssapi;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "NTLM" ) )
|
||||||
|
mechs |= SaslMechNTLM;
|
||||||
|
|
||||||
|
return mechs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Client::getCompressionMethods( Tag* tag )
|
||||||
|
{
|
||||||
|
int meths = 0;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( "method", "zlib" ) )
|
||||||
|
meths |= StreamFeatureCompressZlib;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( "method", "lzw" ) )
|
||||||
|
meths |= StreamFeatureCompressDclz;
|
||||||
|
|
||||||
|
return meths;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::login()
|
||||||
|
{
|
||||||
|
bool retval = true;
|
||||||
|
|
||||||
|
if( m_streamFeatures & SaslMechDigestMd5 && m_availableSaslMechs & SaslMechDigestMd5
|
||||||
|
&& !m_forceNonSasl )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechDigestMd5 );
|
||||||
|
}
|
||||||
|
else if( m_streamFeatures & SaslMechPlain && m_availableSaslMechs & SaslMechPlain
|
||||||
|
&& !m_forceNonSasl )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechPlain );
|
||||||
|
}
|
||||||
|
else if( m_streamFeatures & StreamFeatureIqAuth || m_forceNonSasl )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
nonSaslLogin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
retval = false;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::handleIqIDForward( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
switch( context )
|
||||||
|
{
|
||||||
|
case CtxResourceUnbind:
|
||||||
|
// we don't store known resources anyway
|
||||||
|
break;
|
||||||
|
case CtxResourceBind:
|
||||||
|
processResourceBind( iq );
|
||||||
|
break;
|
||||||
|
case CtxSessionEstablishment:
|
||||||
|
processCreateSession( iq );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::bindOperation( const std::string& resource, bool bind )
|
||||||
|
{
|
||||||
|
if( !( m_streamFeatures & StreamFeatureUnbind ) && m_resourceBound )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IQ iq( IQ::Set, JID(), getID() );
|
||||||
|
iq.addExtension( new ResourceBind( resource, bind ) );
|
||||||
|
|
||||||
|
send( iq, this, bind ? CtxResourceBind : CtxResourceUnbind );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::selectResource( const std::string& resource )
|
||||||
|
{
|
||||||
|
if( !( m_streamFeatures & StreamFeatureUnbind ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_selectedResource = resource;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::processResourceBind( const IQ& iq )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
{
|
||||||
|
const ResourceBind* rb = iq.findExtension<ResourceBind>( ExtResourceBind );
|
||||||
|
if( !rb || !rb->jid() )
|
||||||
|
{
|
||||||
|
notifyOnResourceBindError( 0 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_jid = rb->jid();
|
||||||
|
m_resourceBound = true;
|
||||||
|
m_selectedResource = m_jid.resource();
|
||||||
|
notifyOnResourceBind( m_jid.resource() );
|
||||||
|
|
||||||
|
if( m_streamFeatures & StreamFeatureSession )
|
||||||
|
createSession();
|
||||||
|
else
|
||||||
|
connected();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IQ::Error:
|
||||||
|
{
|
||||||
|
notifyOnResourceBindError( iq.error() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::createSession()
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventSessionCreation );
|
||||||
|
IQ iq( IQ::Set, JID(), getID() );
|
||||||
|
iq.addExtension( new SessionCreation() );
|
||||||
|
send( iq, this, CtxSessionEstablishment );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::processCreateSession( const IQ& iq )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
connected();
|
||||||
|
break;
|
||||||
|
case IQ::Error:
|
||||||
|
notifyOnSessionCreateError( iq.error() );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::negotiateCompression( StreamFeature method )
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "compress", XMLNS, XMLNS_COMPRESSION );
|
||||||
|
|
||||||
|
if( method == StreamFeatureCompressZlib )
|
||||||
|
new Tag( t, "method", "zlib" );
|
||||||
|
|
||||||
|
if( method == StreamFeatureCompressDclz )
|
||||||
|
new Tag( t, "method", "lzw" );
|
||||||
|
|
||||||
|
send( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setPresence( Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status )
|
||||||
|
{
|
||||||
|
m_presence.setPresence( pres );
|
||||||
|
m_presence.setPriority( priority );
|
||||||
|
m_presence.addStatus( status );
|
||||||
|
sendPresence( m_presence );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setPresence( const JID& to, Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status )
|
||||||
|
{
|
||||||
|
Presence p( pres, to, status, priority );
|
||||||
|
sendPresence( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::sendPresence( Presence& pres )
|
||||||
|
{
|
||||||
|
if( state() < StateConnected )
|
||||||
|
return;
|
||||||
|
|
||||||
|
send( pres );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disableRoster()
|
||||||
|
{
|
||||||
|
m_manageRoster = false;
|
||||||
|
delete m_rosterManager;
|
||||||
|
m_rosterManager = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::nonSaslLogin()
|
||||||
|
{
|
||||||
|
if( !m_auth )
|
||||||
|
m_auth = new NonSaslAuth( this );
|
||||||
|
m_auth->doAuth( m_sid );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::connected()
|
||||||
|
{
|
||||||
|
if( m_authed )
|
||||||
|
{
|
||||||
|
if( m_manageRoster )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventRoster );
|
||||||
|
m_rosterManager->fill();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rosterFilled();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
notifyOnConnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::rosterFilled()
|
||||||
|
{
|
||||||
|
sendPresence( m_presence );
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
notifyOnConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disconnect()
|
||||||
|
{
|
||||||
|
disconnect( ConnUserDisconnected );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disconnect( ConnectionError reason )
|
||||||
|
{
|
||||||
|
m_resourceBound = false;
|
||||||
|
m_authed = false;
|
||||||
|
m_streamFeatures = 0;
|
||||||
|
ClientBase::disconnect( reason );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::cleanup()
|
||||||
|
{
|
||||||
|
m_authed = false;
|
||||||
|
m_resourceBound = false;
|
||||||
|
m_streamFeatures = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,441 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CLIENT_H__
|
||||||
|
#define CLIENT_H__
|
||||||
|
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "presence.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Capabilities;
|
||||||
|
class RosterManager;
|
||||||
|
class NonSaslAuth;
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class implements a basic Jabber Client.
|
||||||
|
*
|
||||||
|
* It supports @ref sasl_auth as well as TLS (Encryption), which can be
|
||||||
|
* switched on/off separately. They are used automatically if the server supports them.
|
||||||
|
*
|
||||||
|
* To use, create a new Client instance and feed it connection credentials, either in the Constructor or
|
||||||
|
* afterwards using the setters. You should then register packet handlers implementing the corresponding
|
||||||
|
* Interfaces (ConnectionListener, PresenceHandler, MessageHandler, IqHandler, SubscriptionHandler),
|
||||||
|
* and call @ref connect() to establish the connection to the server.<br>
|
||||||
|
*
|
||||||
|
* @note While the MessageHandler interface is still available (and will be in future versions)
|
||||||
|
* it is now recommended to use the new @link gloox::MessageSession MessageSession @endlink for any
|
||||||
|
* serious messaging.
|
||||||
|
*
|
||||||
|
* Simple usage example:
|
||||||
|
* @code
|
||||||
|
* using namespace gloox;
|
||||||
|
*
|
||||||
|
* void TestProg::doIt()
|
||||||
|
* {
|
||||||
|
* Client* j = new Client( "user@server/resource", "password" );
|
||||||
|
* j->registerPresenceHandler( this );
|
||||||
|
* j->disco()->setVersion( "TestProg", "1.0" );
|
||||||
|
* j->disco()->setIdentity( "client", "bot" );
|
||||||
|
* j->connect();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* virtual void TestProg::presenceHandler( Presence* presence )
|
||||||
|
* {
|
||||||
|
* // handle incoming presence packets here
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* However, you can skip the presence handling stuff if you make use of the RosterManager.
|
||||||
|
*
|
||||||
|
* By default, the library handles a few (incoming) IQ namespaces on the application's behalf. These
|
||||||
|
* include:
|
||||||
|
* @li jabber:iq:roster: by default the server-side roster is fetched and handled. Use
|
||||||
|
* @ref rosterManager() and @ref RosterManager to interact with the Roster.
|
||||||
|
* @li XEP-0092 (Software Version): If no version is specified, a name of "based on gloox" with
|
||||||
|
* gloox's current version is announced.
|
||||||
|
* @li XEP-0030 (Service Discovery): All supported/available services are announced. No items are
|
||||||
|
* returned.
|
||||||
|
* @note As of gloox 0.9, by default a priority of 0 is sent along with the initial presence.
|
||||||
|
* @note As of gloox 0.9, initial presence is automatically sent. Presence: available, Priority: 0.
|
||||||
|
* To disable sending of initial Presence use setPresence() with a value of Unavailable
|
||||||
|
* prior to connecting.
|
||||||
|
*
|
||||||
|
* @section sasl_auth SASL Authentication
|
||||||
|
*
|
||||||
|
* Besides the simple, IQ-based authentication (XEP-0078), gloox supports several SASL (Simple
|
||||||
|
* Authentication and Security Layer, RFC 2222) authentication mechanisms.
|
||||||
|
* @li DIGEST-MD5: This mechanism is preferred over all other mechanisms if username and password are
|
||||||
|
* provided to the Client instance. It is secure even without TLS encryption.
|
||||||
|
* @li PLAIN: This mechanism is used if DIGEST-MD5 is not available. It is @b not secure without
|
||||||
|
* encryption.
|
||||||
|
* @li ANONYMOUS This mechanism is used if neither username nor password are set. The server generates
|
||||||
|
* random, temporary username and resource and may restrict available services.
|
||||||
|
* @li EXTERNAL This mechanism is currently only available if client certificate and private key
|
||||||
|
* are provided. The server tries to figure out who the client is by external means -- for instance,
|
||||||
|
* using the provided certificate or even the IP address. (The restriction to certificate/key
|
||||||
|
* availability is likely to be lifted in the future.)
|
||||||
|
*
|
||||||
|
* Of course, all these mechanisms are not tried unless the server offers them.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API Client : public ClientBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
friend class NonSaslAuth;
|
||||||
|
friend class Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Client which can be used for account registration only.
|
||||||
|
* SASL and TLS are on by default. The port will be determined by looking up SRV records.
|
||||||
|
* Alternatively, you can set the port explicitly by calling @ref setPort().
|
||||||
|
* @param server The server to connect to.
|
||||||
|
*/
|
||||||
|
Client( const std::string& server );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Client.
|
||||||
|
* SASL and TLS are on by default. This should be the default constructor for most use cases.
|
||||||
|
* The server address will be taken from the JID. The actual host will be resolved using SRV
|
||||||
|
* records. The domain part of the JID is used as a fallback in case no SRV record is found, or
|
||||||
|
* you can set the server address separately by calling @ref setServer().
|
||||||
|
* @param jid A full Jabber ID used for connecting to the server.
|
||||||
|
* @param password The password used for authentication.
|
||||||
|
* @param port The port to connect to. The default of -1 means to look up the port via DNS SRV.
|
||||||
|
*/
|
||||||
|
Client( const JID& jid, const std::string& password, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Client();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to bind an additional resource or to @b re-try to bind a
|
||||||
|
* resource in case previous binding failed and you were notified by means of
|
||||||
|
* ConnectionListener::onResourceBindError(). Use hasResourceBind() to find out if the
|
||||||
|
* server supports binding of multiple resources. bindResource() is a NOOP if it doesn't.
|
||||||
|
* @note ConnectionListener::onResourceBound() and ConnectionListener::onResourceBindError()
|
||||||
|
* will be called in case of success and failure, respectively.
|
||||||
|
* @param resource The resource identifier to bind. May be empty. In that case
|
||||||
|
* the server will assign a unique resource identifier.
|
||||||
|
* @return Returns @b true if binding of multiple resources is supported, @b false
|
||||||
|
* otherwise. A return value of @b true does not indicate that the resource was
|
||||||
|
* successfully bound.
|
||||||
|
* @note It is not necessary to call this function to bind the initial, main, resource.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
bool bindResource( const std::string& resource )
|
||||||
|
{ return bindOperation( resource, true ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to select a resource identifier that has been bound
|
||||||
|
* previously by means of bindResource(). It is not necessary to call this function
|
||||||
|
* if only one resource is bound. Use hasResourceBind() to find out if the
|
||||||
|
* server supports binding of multiple resources. selectResource() is a NOOP if it doesn't.
|
||||||
|
* @param resource A resource string that has been bound previously.
|
||||||
|
* @note If the resource string has not been bound previously, future sending of
|
||||||
|
* stanzas will fail.
|
||||||
|
*/
|
||||||
|
bool selectResource( const std::string& resource );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to find out whether the server supports binding of multiple
|
||||||
|
* resources.
|
||||||
|
* @return @b True if binding of multiple resources is supported by the server,
|
||||||
|
* @b false otherwise.
|
||||||
|
*/
|
||||||
|
bool hasResourceBind() const { return ((m_streamFeatures & StreamFeatureUnbind) == StreamFeatureUnbind); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to unbind a resource identifier that has been bound
|
||||||
|
* previously by means of bindResource(). Use hasResourceBind() to find out if the
|
||||||
|
* server supports binding of multiple resources. unbindResource() is a NOOP if it doesn't.
|
||||||
|
* @param resource A resource string that has been bound previously.
|
||||||
|
* @note Servers are encouraged to terminate the connection should the only bound
|
||||||
|
* resource be unbound.
|
||||||
|
*/
|
||||||
|
bool unbindResource( const std::string& resource )
|
||||||
|
{ return bindOperation( resource, false ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current prepped main resource.
|
||||||
|
* @return The resource used to connect.
|
||||||
|
*/
|
||||||
|
const std::string& resource() const { return m_jid.resource(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current priority.
|
||||||
|
* @return The priority of the current resource.
|
||||||
|
*/
|
||||||
|
int priority() const { return m_presence.priority(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the username to use to connect to the XMPP server.
|
||||||
|
* @param username The username to authenticate with.
|
||||||
|
*/
|
||||||
|
void setUsername( const std::string &username );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the main resource to use to connect to the XMPP server.
|
||||||
|
* @param resource The resource to use to log into the server.
|
||||||
|
*/
|
||||||
|
void setResource( const std::string &resource ) { m_jid.setResource( resource ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends directed presence to the given JID. This is a NOOP if there's no active connection.
|
||||||
|
* To broadcast presence use setPresence( Presence::PresenceType, int, const std::string& ).
|
||||||
|
* @param to The JID to send directed Presence to.
|
||||||
|
* @param pres The presence to send.
|
||||||
|
* @param priority The priority to include. Legal values: -128 <= priority <= 127
|
||||||
|
* @param status The optional status message to include.
|
||||||
|
* @note This function does not include any presence extensions (as added by
|
||||||
|
* means of addPresenceExtension()) to the stanza.
|
||||||
|
*/
|
||||||
|
void setPresence( const JID& to, Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the entity's presence, that is, to broadcast presence to all
|
||||||
|
* subscribed entities. To send directed presence, use
|
||||||
|
* setPresence( const JID&, Presence::PresenceType, int, const std::string& ).
|
||||||
|
* If used prior to establishing a connection, the set values will be sent with
|
||||||
|
* the initial presence stanza.
|
||||||
|
* If used while a connection already is established, a presence stanza will be
|
||||||
|
* sent out immediately.
|
||||||
|
* @param pres The Presence value to set.
|
||||||
|
* @param priority An optional priority value. Legal values: -128 <= priority <= 127
|
||||||
|
* @param status An optional message describing the presence state.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
void setPresence( Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to broadcast the entity's presence to all
|
||||||
|
* subscribed entities. This is a NOOP if there's no active connection.
|
||||||
|
* To send directed presence, use
|
||||||
|
* setPresence( const JID&, Presence::PresenceType, int, const std::string& ).
|
||||||
|
* If used while a connection already is established a repective presence stanza will be
|
||||||
|
* sent out immediately. Use presence() to modify the Presence object.
|
||||||
|
* @note When login is finished, initial presence will be sent automatically.
|
||||||
|
* So you do not need to call this function after login.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
void setPresence() { sendPresence( m_presence ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current presence.
|
||||||
|
* @return The current presence.
|
||||||
|
*/
|
||||||
|
Presence& presence() { return m_presence; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a temporary hack to enforce Non-SASL login. You should not need to use it.
|
||||||
|
* @param force Whether to force non-SASL auth. Default @b true.
|
||||||
|
* @deprecated Please update the server to properly support SASL instead.
|
||||||
|
*/
|
||||||
|
GLOOX_DEPRECATED void setForceNonSasl( bool force = true ) { m_forceNonSasl = force; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the automatic roster management.
|
||||||
|
* You have to keep track of incoming presence yourself if
|
||||||
|
* you want to have a roster.
|
||||||
|
*/
|
||||||
|
void disableRoster();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gives access to the @c RosterManager object.
|
||||||
|
* @return A pointer to the RosterManager.
|
||||||
|
*/
|
||||||
|
RosterManager* rosterManager() { return m_rosterManager; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from the server.
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a login attempt (currently SASL External not supported).
|
||||||
|
* This is useful after registering a new account. Simply use setUsername() and setPassword(),
|
||||||
|
* and call login().
|
||||||
|
* @return @b True if a login attempt could be started, @b false otherwise. A return
|
||||||
|
* value of @b true does not indicate that login was successful.
|
||||||
|
*/
|
||||||
|
bool login();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Initiates non-SASL login.
|
||||||
|
*/
|
||||||
|
void nonSaslLogin();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a resource binding StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class ResourceBind : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object with the given resource string.
|
||||||
|
* @param resource The resource to set.
|
||||||
|
* @param bind Indicates whether this is an bind or unbind request.
|
||||||
|
* Defaults to @b true (bind).
|
||||||
|
*/
|
||||||
|
ResourceBind( const std::string& resource, bool bind = true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
ResourceBind( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~ResourceBind();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the requested resource.
|
||||||
|
* @return The requested resource.
|
||||||
|
*/
|
||||||
|
const std::string& resource() const { return m_resource; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the assigned JID.
|
||||||
|
* @return The assigned JID.
|
||||||
|
*/
|
||||||
|
const JID& jid() const { return m_jid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to find out whether the extension contains a
|
||||||
|
* bind or unbind request.
|
||||||
|
* @return @b True if the extension contains an unbind request, @b false otherwise.
|
||||||
|
*/
|
||||||
|
bool unbind() const { return !m_bind; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new ResourceBind( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new ResourceBind( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_resource;
|
||||||
|
JID m_jid;
|
||||||
|
bool m_bind;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a session creating StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class SessionCreation : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object.
|
||||||
|
*/
|
||||||
|
SessionCreation() : StanzaExtension( ExtSessionCreation ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~SessionCreation() {}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const { return EmptyString; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{ (void)tag; return 0; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void handleStartNode() {}
|
||||||
|
virtual bool handleNormalNode( Tag* tag );
|
||||||
|
virtual void disconnect( ConnectionError reason );
|
||||||
|
virtual void handleIqIDForward( const IQ& iq, int context );
|
||||||
|
|
||||||
|
int getStreamFeatures( Tag* tag );
|
||||||
|
int getSaslMechs( Tag* tag );
|
||||||
|
int getCompressionMethods( Tag* tag );
|
||||||
|
void processResourceBind( const IQ& iq );
|
||||||
|
void processCreateSession( const IQ& iq );
|
||||||
|
void sendPresence( Presence& pres );
|
||||||
|
void createSession();
|
||||||
|
void negotiateCompression( StreamFeature method );
|
||||||
|
void connected();
|
||||||
|
virtual void rosterFilled();
|
||||||
|
virtual void cleanup();
|
||||||
|
bool bindOperation( const std::string& resource, bool bind );
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
enum TrackContext
|
||||||
|
{
|
||||||
|
CtxResourceBind = 1000, // must be higher than the last element in ClientBase's TrackContext
|
||||||
|
CtxResourceUnbind,
|
||||||
|
CtxSessionEstablishment
|
||||||
|
};
|
||||||
|
|
||||||
|
RosterManager* m_rosterManager;
|
||||||
|
NonSaslAuth* m_auth;
|
||||||
|
|
||||||
|
Presence m_presence;
|
||||||
|
|
||||||
|
bool m_resourceBound;
|
||||||
|
bool m_forceNonSasl;
|
||||||
|
bool m_manageRoster;
|
||||||
|
|
||||||
|
int m_streamFeatures;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CLIENT_H__
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "component.h"
|
||||||
|
|
||||||
|
#include "disco.h"
|
||||||
|
#include "stanza.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "sha.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Component::Component( const std::string& ns, const std::string& server,
|
||||||
|
const std::string& component, const std::string& password, int port )
|
||||||
|
: ClientBase( ns, password, server, port )
|
||||||
|
{
|
||||||
|
m_jid.setServer( component );
|
||||||
|
m_disco->setIdentity( "component", "generic" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::handleStartNode()
|
||||||
|
{
|
||||||
|
if( m_sid.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
|
||||||
|
SHA sha;
|
||||||
|
sha.feed( m_sid + m_password );
|
||||||
|
sha.finalize();
|
||||||
|
|
||||||
|
Tag* h = new Tag( "handshake", sha.hex() );
|
||||||
|
send( h );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Component::handleNormalNode( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "handshake" )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_authed = true;
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
notifyOnConnect();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H__
|
||||||
|
#define COMPONENT_H__
|
||||||
|
|
||||||
|
#include "clientbase.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a basic jabber Component.
|
||||||
|
*
|
||||||
|
* It's using XEP-0114 (Jabber Component Protocol) to authenticate with a server.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API Component : public ClientBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new Component.
|
||||||
|
* @param ns The namespace that qualifies the stream. Either @b jabber:component:accept or
|
||||||
|
* @b jabber:component:connect. See XEP-0114 for details.
|
||||||
|
* @param server The server to connect to.
|
||||||
|
* @param component The component's hostname. FQDN.
|
||||||
|
* @param password The component's password.
|
||||||
|
* @param port The port to connect to. The default of 5347 is the default port of the router
|
||||||
|
* in jabberd2.
|
||||||
|
*/
|
||||||
|
Component( const std::string& ns, const std::string& server,
|
||||||
|
const std::string& component, const std::string& password, int port = 5347 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Component() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from the server.
|
||||||
|
*/
|
||||||
|
void disconnect() { ClientBase::disconnect( ConnUserDisconnected ); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual void handleStartNode();
|
||||||
|
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual bool handleNormalNode( Tag* tag );
|
||||||
|
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual bool checkStreamVersion( const std::string& /*version*/ ) { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual void rosterFilled() {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPONENT_H__
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONBASE_H__
|
||||||
|
#define COMPRESSIONBASE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "compressiondatahandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstract base class for stream compression implementations.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Contructor.
|
||||||
|
* @param cdh A CompressionDataHandler-derived object that will be notified
|
||||||
|
* about finished de/compression.
|
||||||
|
*/
|
||||||
|
CompressionBase( CompressionDataHandler* cdh ) : m_handler( cdh ), m_valid( false ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionBase() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function initializes the compression module.
|
||||||
|
* it is mandatory to be called.
|
||||||
|
* @return @b True if the module was initialized successfully, false otherwise.
|
||||||
|
*/
|
||||||
|
virtual bool init() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compresses the given chunk of data.
|
||||||
|
* @param data The original (uncompressed) data.
|
||||||
|
*/
|
||||||
|
virtual void compress( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompresses the given chunk of data.
|
||||||
|
* @param data The compressed data.
|
||||||
|
*/
|
||||||
|
virtual void decompress( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs internal cleanup.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void cleanup() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for compressed/uncompressed data. */
|
||||||
|
CompressionDataHandler* m_handler;
|
||||||
|
|
||||||
|
/** Whether the compression module can be used. */
|
||||||
|
bool m_valid;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPRESSIONBASE_H__
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONDATAHANDLER_H__
|
||||||
|
#define COMPRESSIONDATAHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstract base class used to receive de/compressed data from a
|
||||||
|
* CompressionBase-derived object.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionDataHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when compression is finished.
|
||||||
|
* @param data The compressed data.
|
||||||
|
*/
|
||||||
|
virtual void handleCompressedData( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when decompression is finished.
|
||||||
|
* @param data The decompressed data.
|
||||||
|
*/
|
||||||
|
virtual void handleDecompressedData( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPRESSIONDATAHANDLER_H__
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compressiondefault.h"
|
||||||
|
|
||||||
|
#include "compressiondatahandler.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if defined( HAVE_ZLIB )
|
||||||
|
# define HAVE_COMPRESSION
|
||||||
|
# include "compressionzlib.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #if defined( HAVE_LZW )
|
||||||
|
// # define HAVE_COMPRESSION
|
||||||
|
// # include "compressionlzw.h"
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
CompressionDefault::CompressionDefault( CompressionDataHandler* cdh, Method method )
|
||||||
|
: CompressionBase( cdh ), m_impl( 0 )
|
||||||
|
{
|
||||||
|
switch( method )
|
||||||
|
{
|
||||||
|
case MethodZlib:
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
m_impl = new CompressionZlib( cdh );
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case MethodLZW:
|
||||||
|
#ifdef HAVE_LZW
|
||||||
|
m_impl = new CompressionLZW( cdh );
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionDefault::~CompressionDefault()
|
||||||
|
{
|
||||||
|
delete m_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompressionDefault::init()
|
||||||
|
{
|
||||||
|
return m_impl ? m_impl->init() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CompressionDefault::types()
|
||||||
|
{
|
||||||
|
int types = 0;
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
types |= MethodZlib;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LZW
|
||||||
|
types |= MethodLZW;
|
||||||
|
#endif
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionDefault::compress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_impl )
|
||||||
|
m_impl->compress( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionDefault::decompress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_impl )
|
||||||
|
m_impl->decompress( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionDefault::cleanup()
|
||||||
|
{
|
||||||
|
if( m_impl )
|
||||||
|
m_impl->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONDEFAULT_H__
|
||||||
|
#define COMPRESSIONDEFAULT_H__
|
||||||
|
|
||||||
|
#include "compressionbase.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class CompressionDataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstraction of the various Compression implementations.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionDefault : public CompressionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported ctypes.
|
||||||
|
*/
|
||||||
|
enum Method
|
||||||
|
{
|
||||||
|
MethodZlib = 1, /**< Zlib compression. */
|
||||||
|
MethodLZW = 2 /**< LZW compression. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new compression wrapper.
|
||||||
|
* @param cdh The CompressionDataHandler to handle de/compressed data.
|
||||||
|
* @param method The desired compression method.
|
||||||
|
*/
|
||||||
|
CompressionDefault( CompressionDataHandler* cdh, Method method = MethodZlib );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionDefault();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an int holding the available compression types, ORed.
|
||||||
|
* @return An int holding the available compression types, ORed.
|
||||||
|
*/
|
||||||
|
static int types();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual bool init();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void compress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void decompress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CompressionBase* m_impl;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPRESSIONDEFAULT_H__
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "compressionzlib.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
CompressionZlib::CompressionZlib( CompressionDataHandler* cdh )
|
||||||
|
: CompressionBase( cdh )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompressionZlib::init()
|
||||||
|
{
|
||||||
|
int ret = Z_OK;
|
||||||
|
m_zinflate.zalloc = Z_NULL;
|
||||||
|
m_zinflate.zfree = Z_NULL;
|
||||||
|
m_zinflate.opaque = Z_NULL;
|
||||||
|
m_zinflate.avail_in = 0;
|
||||||
|
m_zinflate.next_in = Z_NULL;
|
||||||
|
ret = inflateInit( &m_zinflate );
|
||||||
|
if( ret != Z_OK )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_zdeflate.zalloc = Z_NULL;
|
||||||
|
m_zdeflate.zfree = Z_NULL;
|
||||||
|
m_zdeflate.opaque = Z_NULL;
|
||||||
|
m_zinflate.avail_in = 0;
|
||||||
|
m_zinflate.next_in = Z_NULL;
|
||||||
|
ret = deflateInit( &m_zdeflate, Z_BEST_COMPRESSION/*Z_DEFAULT_COMPRESSION*/ );
|
||||||
|
if( ret != Z_OK )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_valid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionZlib::~CompressionZlib()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionZlib::compress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
init();
|
||||||
|
|
||||||
|
if( !m_valid || !m_handler || data.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
long unsigned int CHUNK = data.length() + ( data.length() / 100 ) + 13;
|
||||||
|
Bytef* out = new Bytef[CHUNK];
|
||||||
|
char* in = const_cast<char*>( data.c_str() );
|
||||||
|
|
||||||
|
m_compressMutex.lock();
|
||||||
|
|
||||||
|
m_zdeflate.avail_in = static_cast<uInt>( data.length() );
|
||||||
|
m_zdeflate.next_in = (Bytef*)in;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
std::string result;
|
||||||
|
do {
|
||||||
|
m_zdeflate.avail_out = static_cast<uInt>( CHUNK );
|
||||||
|
m_zdeflate.next_out = (Bytef*)out;
|
||||||
|
|
||||||
|
ret = deflate( &m_zdeflate, Z_SYNC_FLUSH );
|
||||||
|
result.append( (char*)out, CHUNK - m_zdeflate.avail_out );
|
||||||
|
} while( m_zdeflate.avail_out == 0 );
|
||||||
|
|
||||||
|
m_compressMutex.unlock();
|
||||||
|
|
||||||
|
delete[] out;
|
||||||
|
|
||||||
|
m_handler->handleCompressedData( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionZlib::decompress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
init();
|
||||||
|
|
||||||
|
if( !m_valid || !m_handler || data.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int CHUNK = 50;
|
||||||
|
char* out = new char[CHUNK];
|
||||||
|
char* in = const_cast<char*>( data.c_str() );
|
||||||
|
|
||||||
|
m_zinflate.avail_in = static_cast<uInt>( data.length() );
|
||||||
|
m_zinflate.next_in = (Bytef*)in;
|
||||||
|
|
||||||
|
int ret = Z_OK;
|
||||||
|
std::string result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
m_zinflate.avail_out = CHUNK;
|
||||||
|
m_zinflate.next_out = (Bytef*)out;
|
||||||
|
|
||||||
|
ret = inflate( &m_zinflate, Z_SYNC_FLUSH );
|
||||||
|
result.append( out, CHUNK - m_zinflate.avail_out );
|
||||||
|
} while( m_zinflate.avail_out == 0 );
|
||||||
|
|
||||||
|
delete[] out;
|
||||||
|
|
||||||
|
m_handler->handleDecompressedData( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionZlib::cleanup()
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
return;
|
||||||
|
|
||||||
|
inflateEnd( &m_zinflate );
|
||||||
|
deflateEnd( &m_zdeflate );
|
||||||
|
|
||||||
|
m_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_ZLIB
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONZLIB_H__
|
||||||
|
#define COMPRESSIONZLIB_H__
|
||||||
|
|
||||||
|
#include "compressionbase.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* An implementation of CompressionBase using zlib.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionZlib : public CompressionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Contructor.
|
||||||
|
* @param cdh The CompressionDataHandler to receive de/compressed data.
|
||||||
|
*/
|
||||||
|
CompressionZlib( CompressionDataHandler* cdh );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionZlib();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual bool init();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void compress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void decompress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
z_stream m_zinflate;
|
||||||
|
z_stream m_zdeflate;
|
||||||
|
|
||||||
|
util::Mutex m_compressMutex;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_ZLIB
|
||||||
|
|
||||||
|
#endif // COMPRESSIONZLIB_H__
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_H__
|
||||||
|
#define CONFIG_H__
|
||||||
|
|
||||||
|
#if ( defined _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include "../config.h.win"
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include "../config.h.win"
|
||||||
|
#elif defined( __SYMBIAN32__ )
|
||||||
|
# include "../config.h.symbian"
|
||||||
|
#else
|
||||||
|
# include "config.h.unix" // run ./configure to create config.h.unix
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CONFIG_H__
|
|
@ -0,0 +1,123 @@
|
||||||
|
/* config.h.unix. Generated from config.h.unix.in by configure. */
|
||||||
|
/* config.h.unix.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <arpa/nameser.h> header file. */
|
||||||
|
#define HAVE_ARPA_NAMESER_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#define HAVE_DLFCN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `dn_skipname' function. */
|
||||||
|
/* #undef HAVE_DN_SKIPNAME */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <errno.h> header file. */
|
||||||
|
#define HAVE_ERRNO_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||||
|
/* #undef HAVE_GETADDRINFO */
|
||||||
|
|
||||||
|
/* Define to 1 if you want TLS support (GnuTLS). Undefine HAVE_OPENSSL. */
|
||||||
|
/* #undef HAVE_GNUTLS */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `bind' library (-lbind). */
|
||||||
|
/* #undef HAVE_LIBBIND */
|
||||||
|
|
||||||
|
/* Define to 1 if you want IDN support. */
|
||||||
|
/* #undef HAVE_LIBIDN */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `network' library (-lnetwork). */
|
||||||
|
#define HAVE_LIBNETWORK 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `resolv' library (-lresolv). */
|
||||||
|
/* #undef HAVE_LIBRESOLV */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||||
|
/* #undef HAVE_LIBSOCKET */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you want TLS support (OpenSSL). Undefine HAVE_GNUTLS. */
|
||||||
|
#define HAVE_OPENSSL 1
|
||||||
|
|
||||||
|
/* Define if you have POSIX threads libraries and header files. */
|
||||||
|
#define HAVE_PTHREAD 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `res_query' function. */
|
||||||
|
/* #undef HAVE_RES_QUERY */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `res_querydomain' function. */
|
||||||
|
/* #undef HAVE_RES_QUERYDOMAIN */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `setsockopt' function. */
|
||||||
|
#define HAVE_SETSOCKOPT 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#define HAVE_STRINGS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you want Stream Compression support. */
|
||||||
|
#define HAVE_ZLIB 1
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
#define LT_OBJDIR ".libs/"
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#define PACKAGE "gloox"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "js@camaya.net"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "gloox"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "gloox 1.0"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "gloox"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#define PACKAGE_URL ""
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "1.0"
|
||||||
|
|
||||||
|
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||||
|
your system. */
|
||||||
|
/* #undef PTHREAD_CREATE_JOINABLE */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
|
||||||
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
|
/* #undef const */
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
/* #undef inline */
|
||||||
|
#endif
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONBASE_H__
|
||||||
|
#define CONNECTIONBASE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectiondatahandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstract base class for a connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param cdh An object derived from @ref ConnectionDataHandler that will receive
|
||||||
|
* received data.
|
||||||
|
*/
|
||||||
|
ConnectionBase( ConnectionDataHandler* cdh )
|
||||||
|
: m_handler( cdh ), m_state( StateDisconnected ), m_port( -1 )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionBase() { cleanup(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to initiate the connection.
|
||||||
|
* @return Returns the connection state.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError connect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this periodically to receive data from the socket.
|
||||||
|
* @param timeout The timeout to use for select in microseconds. Default of -1 means blocking.
|
||||||
|
* @return The state of the connection.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError recv( int timeout = -1 ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to send a string of data over the wire. The function returns only after
|
||||||
|
* all data has been sent.
|
||||||
|
* @param data The data to send.
|
||||||
|
* @return @b True if the data has been sent (no guarantee of receipt), @b false
|
||||||
|
* in case of an error.
|
||||||
|
*/
|
||||||
|
virtual bool send( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to put the connection into 'receive mode', i.e. this function returns only
|
||||||
|
* when the connection is terminated.
|
||||||
|
* @return Returns a value indicating the disconnection reason.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError receive() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects an established connection. NOOP if no active connection exists.
|
||||||
|
*/
|
||||||
|
virtual void disconnect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called after a disconnect to clean up internal state. It is also called by
|
||||||
|
* ConnectionBase's destructor.
|
||||||
|
*/
|
||||||
|
virtual void cleanup() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current connection state.
|
||||||
|
* @return The state of the connection.
|
||||||
|
*/
|
||||||
|
ConnectionState state() const { return m_state; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a new ConnectionDataHandler. There can be only one
|
||||||
|
* ConnectionDataHandler at any one time.
|
||||||
|
* @param cdh The new ConnectionDataHandler.
|
||||||
|
*/
|
||||||
|
void registerConnectionDataHandler( ConnectionDataHandler* cdh ) { m_handler = cdh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the server to connect to.
|
||||||
|
* @param server The server to connect to. Either IP or fully qualified domain name.
|
||||||
|
* @param port The port to connect to.
|
||||||
|
*/
|
||||||
|
void setServer( const std::string &server, int port = -1 ) { m_server = server; m_port = port; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently set server/IP.
|
||||||
|
* @return The server host/IP.
|
||||||
|
*/
|
||||||
|
const std::string& server() const { return m_server; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently set port.
|
||||||
|
* @return The server port.
|
||||||
|
*/
|
||||||
|
int port() const { return m_port; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local port.
|
||||||
|
* @return The local port.
|
||||||
|
*/
|
||||||
|
virtual int localPort() const { return -1; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locally bound IP address.
|
||||||
|
* @return The locally bound IP address.
|
||||||
|
*/
|
||||||
|
virtual const std::string localInterface() const { return EmptyString; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current connection statistics.
|
||||||
|
* @param totalIn The total number of bytes received.
|
||||||
|
* @param totalOut The total number of bytes sent.
|
||||||
|
*/
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns a new instance of the current ConnectionBase-derived object.
|
||||||
|
* The idea is to be able to 'clone' ConnectionBase-derived objects without knowing of
|
||||||
|
* what type they are exactly.
|
||||||
|
* @return A new Connection* instance.
|
||||||
|
*/
|
||||||
|
virtual ConnectionBase* newInstance() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for incoming data and connect/disconnect events. */
|
||||||
|
ConnectionDataHandler* m_handler;
|
||||||
|
|
||||||
|
/** Holds the current connection state. */
|
||||||
|
ConnectionState m_state;
|
||||||
|
|
||||||
|
/** Holds the server's name/address. */
|
||||||
|
std::string m_server;
|
||||||
|
|
||||||
|
/** Holds the port to connect to. */
|
||||||
|
int m_port;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONBASE_H__
|
|
@ -0,0 +1,641 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectionbosh.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionBOSH::ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance,
|
||||||
|
const std::string& boshHost, const std::string& xmppServer,
|
||||||
|
int xmppPort )
|
||||||
|
: ConnectionBase( 0 ),
|
||||||
|
m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
|
||||||
|
m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
|
||||||
|
m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ),
|
||||||
|
m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
|
||||||
|
m_connMode( ModePipelining )
|
||||||
|
{
|
||||||
|
initInstance( connection, xmppServer, xmppPort );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBOSH::ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance, const std::string& boshHost,
|
||||||
|
const std::string& xmppServer, int xmppPort )
|
||||||
|
: ConnectionBase( cdh ),
|
||||||
|
m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
|
||||||
|
m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
|
||||||
|
m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ),
|
||||||
|
m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
|
||||||
|
m_connMode( ModePipelining )
|
||||||
|
{
|
||||||
|
initInstance( connection, xmppServer, xmppPort );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::initInstance( ConnectionBase* connection, const std::string& xmppServer,
|
||||||
|
const int xmppPort )
|
||||||
|
{
|
||||||
|
// FIXME: check return value
|
||||||
|
prep::idna( xmppServer, m_server );
|
||||||
|
m_port = xmppPort;
|
||||||
|
if( m_port != -1 )
|
||||||
|
{
|
||||||
|
m_boshedHost = m_boshHost + ":" + util::int2string( m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop this connection into our pool of available connections
|
||||||
|
if( connection )
|
||||||
|
{
|
||||||
|
connection->registerConnectionDataHandler( this );
|
||||||
|
m_connectionPool.push_back( connection );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBOSH::~ConnectionBOSH()
|
||||||
|
{
|
||||||
|
util::clearList( m_activeConnections );
|
||||||
|
util::clearList( m_connectionPool );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionBOSH::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* pBaseConn = 0;
|
||||||
|
|
||||||
|
if( !m_connectionPool.empty() )
|
||||||
|
{
|
||||||
|
pBaseConn = m_connectionPool.front()->newInstance();
|
||||||
|
}
|
||||||
|
else if( !m_activeConnections.empty() )
|
||||||
|
{
|
||||||
|
pBaseConn = m_activeConnections.front()->newInstance();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConnectionBOSH( m_handler, pBaseConn, m_logInstance,
|
||||||
|
m_boshHost, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionBOSH::connect()
|
||||||
|
{
|
||||||
|
if( m_state >= StateConnecting )
|
||||||
|
return ConnNoError;
|
||||||
|
|
||||||
|
if( !m_handler )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh initiating connection to server: " +
|
||||||
|
( ( m_connMode == ModePipelining ) ? std::string( "Pipelining" )
|
||||||
|
: ( ( m_connMode == ModeLegacyHTTP ) ? std::string( "LegacyHTTP" )
|
||||||
|
: std::string( "PersistentHTTP" ) ) ) );
|
||||||
|
getConnection();
|
||||||
|
return ConnNoError; // FIXME?
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::disconnect()
|
||||||
|
{
|
||||||
|
if( ( m_connMode == ModePipelining && m_activeConnections.empty() )
|
||||||
|
|| ( m_connectionPool.empty() && m_activeConnections.empty() ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_state != StateDisconnected )
|
||||||
|
{
|
||||||
|
++m_rid;
|
||||||
|
|
||||||
|
std::string requestBody = "<body rid='" + util::int2string( m_rid ) + "' ";
|
||||||
|
requestBody += "sid='" + m_sid + "' ";
|
||||||
|
requestBody += "type='terminal' ";
|
||||||
|
requestBody += "xml:lang='en' ";
|
||||||
|
requestBody += "xmlns='" + XMLNS_HTTPBIND + "'";
|
||||||
|
if( m_sendBuffer.empty() ) // Make sure that any data in the send buffer gets sent
|
||||||
|
requestBody += "/>";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBody += ">" + m_sendBuffer + "</body>";
|
||||||
|
m_sendBuffer = EmptyString;
|
||||||
|
}
|
||||||
|
sendRequest( requestBody );
|
||||||
|
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "bosh disconnection request sent" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_logInstance.err( LogAreaClassConnectionBOSH,
|
||||||
|
"disconnecting from server in a non-graceful fashion" );
|
||||||
|
}
|
||||||
|
|
||||||
|
util::ForEach( m_activeConnections, &ConnectionBase::disconnect );
|
||||||
|
util::ForEach( m_connectionPool, &ConnectionBase::disconnect );
|
||||||
|
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnUserDisconnected );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionBOSH::recv( int timeout )
|
||||||
|
{
|
||||||
|
if( m_state == StateDisconnected )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
if( !m_connectionPool.empty() )
|
||||||
|
m_connectionPool.front()->recv( 0 );
|
||||||
|
if( !m_activeConnections.empty() )
|
||||||
|
m_activeConnections.front()->recv( timeout );
|
||||||
|
|
||||||
|
// If there are no open requests then the spec allows us to send an empty request...
|
||||||
|
// (Some CMs do not obey this, it seems)
|
||||||
|
if( ( m_openRequests == 0 || m_sendBuffer.size() > 0 ) && m_state == StateConnected )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"Sending empty request (or there is data in the send buffer)" );
|
||||||
|
sendXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnNoError; // FIXME?
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionBOSH::send( const std::string& data )
|
||||||
|
{
|
||||||
|
|
||||||
|
if( m_state == StateDisconnected )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( data.substr( 0, 2 ) == "<?" )
|
||||||
|
{
|
||||||
|
// if( m_initialStreamSent )
|
||||||
|
{
|
||||||
|
m_streamRestart = true;
|
||||||
|
sendXML();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// m_initialStreamSent = true;
|
||||||
|
// m_logInstance.dbg( LogAreaClassConnectionBOSH, "initial <stream:stream> dropped" );
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
else if( data == "</stream:stream>" )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_sendBuffer += data;
|
||||||
|
sendXML();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends XML. Wraps data in a <body/> tag, and then passes to sendRequest(). */
|
||||||
|
bool ConnectionBOSH::sendXML()
|
||||||
|
{
|
||||||
|
if( m_state != StateConnected )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Data sent before connection established (will be buffered)" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_sendBuffer.empty() )
|
||||||
|
{
|
||||||
|
time_t now = time( 0 );
|
||||||
|
unsigned int delta = (int)(now - m_lastRequestTime);
|
||||||
|
if( delta < m_minTimePerRequest && m_openRequests > 0 )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Too little time between requests: " + util::int2string( delta ) + " seconds" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Send buffer is empty, sending empty request" );
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_rid;
|
||||||
|
|
||||||
|
std::string requestBody = "<body rid='" + util::int2string( m_rid ) + "' ";
|
||||||
|
requestBody += "sid='" + m_sid + "' ";
|
||||||
|
requestBody += "xmlns='" + XMLNS_HTTPBIND + "'";
|
||||||
|
|
||||||
|
if( m_streamRestart )
|
||||||
|
{
|
||||||
|
requestBody += " xmpp:restart='true' to='" + m_server + "' xml:lang='en' xmlns:xmpp='"
|
||||||
|
+ XMLNS_XMPP_BOSH + "' />";
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Restarting stream" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBody += ">" + m_sendBuffer + "</body>";
|
||||||
|
}
|
||||||
|
// Send a request. Force if we are not sending an empty request, or if there are no connections open
|
||||||
|
if( sendRequest( requestBody ) )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Successfully sent m_sendBuffer" );
|
||||||
|
m_sendBuffer = EmptyString;
|
||||||
|
m_streamRestart = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
--m_rid; // I think... (may need to rethink when acks are implemented)
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Unable to send. Connection not complete, or too many open requests,"
|
||||||
|
" so added to buffer.\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chooses the appropriate connection, or opens a new one if necessary. Wraps xml in HTTP and sends. */
|
||||||
|
bool ConnectionBOSH::sendRequest( const std::string& xml )
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = getConnection();
|
||||||
|
if( !conn )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string request = "POST " + m_path;
|
||||||
|
if( m_connMode == ModeLegacyHTTP )
|
||||||
|
{
|
||||||
|
request += " HTTP/1.0\r\n";
|
||||||
|
request += "Connection: close\r\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
request += " HTTP/1.1\r\n";
|
||||||
|
|
||||||
|
request += "Host: " + m_boshedHost + "\r\n";
|
||||||
|
request += "Content-Type: text/xml; charset=utf-8\r\n";
|
||||||
|
request += "Content-Length: " + util::int2string( xml.length() ) + "\r\n";
|
||||||
|
request += "User-Agent: gloox/" + GLOOX_VERSION + "\r\n\r\n";
|
||||||
|
request += xml;
|
||||||
|
|
||||||
|
|
||||||
|
if( conn->send( request ) )
|
||||||
|
{
|
||||||
|
m_lastRequestTime = time( 0 );
|
||||||
|
++m_openRequests;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// else // FIXME What to do in this case?
|
||||||
|
// printf( "Error while trying to send on socket (state: %d)\n", conn->state() );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ci_equal( char ch1, char ch2 )
|
||||||
|
{
|
||||||
|
return std::toupper( (unsigned char)ch1 ) == std::toupper( (unsigned char)ch2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::size_type ci_find( const std::string& str1, const std::string& str2 )
|
||||||
|
{
|
||||||
|
std::string::const_iterator pos = std::search( str1.begin(), str1.end(),
|
||||||
|
str2.begin(), str2.end(), ci_equal );
|
||||||
|
if( pos == str1.end() )
|
||||||
|
return std::string::npos;
|
||||||
|
else
|
||||||
|
return std::distance( str1.begin(), pos );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ConnectionBOSH::getHTTPField( const std::string& field )
|
||||||
|
{
|
||||||
|
std::string::size_type fp = ci_find( m_bufferHeader, "\r\n" + field + ": " );
|
||||||
|
|
||||||
|
if( fp == std::string::npos )
|
||||||
|
return EmptyString;
|
||||||
|
|
||||||
|
fp += field.length() + 4;
|
||||||
|
|
||||||
|
const std::string::size_type fp2 = m_bufferHeader.find( "\r\n", fp );
|
||||||
|
if( fp2 == std::string::npos )
|
||||||
|
return EmptyString;
|
||||||
|
|
||||||
|
return m_bufferHeader.substr( fp, fp2 - fp );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionBOSH::receive()
|
||||||
|
{
|
||||||
|
ConnectionError err = ConnNoError;
|
||||||
|
while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError )
|
||||||
|
;
|
||||||
|
return err == ConnNoError ? ConnNotConnected : err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::cleanup()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
|
||||||
|
util::ForEach( m_activeConnections, &ConnectionBase::cleanup );
|
||||||
|
util::ForEach( m_connectionPool, &ConnectionBase::cleanup );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::getStatistics( long int& totalIn, long int& totalOut )
|
||||||
|
{
|
||||||
|
util::ForEach( m_activeConnections, &ConnectionBase::getStatistics, totalIn, totalOut );
|
||||||
|
util::ForEach( m_connectionPool, &ConnectionBase::getStatistics, totalIn, totalOut );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleReceivedData( const ConnectionBase* /*connection*/,
|
||||||
|
const std::string& data )
|
||||||
|
{
|
||||||
|
m_buffer += data;
|
||||||
|
std::string::size_type headerLength = 0;
|
||||||
|
while( ( headerLength = m_buffer.find( "\r\n\r\n" ) ) != std::string::npos )
|
||||||
|
{
|
||||||
|
m_bufferHeader = m_buffer.substr( 0, headerLength+2 );
|
||||||
|
|
||||||
|
const std::string& statusCode = m_bufferHeader.substr( 9, 3 );
|
||||||
|
if( statusCode != "200" )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Received error via legacy HTTP status code: " + statusCode
|
||||||
|
+ ". Disconnecting." );
|
||||||
|
m_state = StateDisconnected; // As per XEP, consider connection broken
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bufferContentLength = atol( getHTTPField( "Content-Length" ).c_str() );
|
||||||
|
if( !m_bufferContentLength )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_connMode != ModeLegacyHTTP && ( getHTTPField( "Connection" ) == "close"
|
||||||
|
|| m_bufferHeader.substr( 0, 8 ) == "HTTP/1.0" ) )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"Server indicated lack of support for HTTP/1.1 - falling back to HTTP/1.0" );
|
||||||
|
m_connMode = ModeLegacyHTTP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_buffer.length() >= ( headerLength + 4 + m_bufferContentLength ) )
|
||||||
|
{
|
||||||
|
putConnection();
|
||||||
|
--m_openRequests;
|
||||||
|
std::string xml = m_buffer.substr( headerLength + 4, m_bufferContentLength );
|
||||||
|
m_parser.feed( xml );
|
||||||
|
m_buffer.erase( 0, headerLength + 4 + m_bufferContentLength );
|
||||||
|
m_bufferContentLength = 0;
|
||||||
|
m_bufferHeader = EmptyString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH, "buffer length mismatch" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_state == StateConnecting )
|
||||||
|
{
|
||||||
|
m_rid = rand() % 100000 + 1728679472;
|
||||||
|
|
||||||
|
Tag requestBody( "body" );
|
||||||
|
requestBody.setXmlns( XMLNS_HTTPBIND );
|
||||||
|
requestBody.setXmlns( XMLNS_XMPP_BOSH, "xmpp" );
|
||||||
|
|
||||||
|
requestBody.addAttribute( "content", "text/xml; charset=utf-8" );
|
||||||
|
requestBody.addAttribute( "hold", (long)m_hold );
|
||||||
|
requestBody.addAttribute( "rid", (long)m_rid );
|
||||||
|
requestBody.addAttribute( "ver", "1.6" );
|
||||||
|
requestBody.addAttribute( "wait", (long)m_wait );
|
||||||
|
requestBody.addAttribute( "ack", 0 );
|
||||||
|
requestBody.addAttribute( "secure", "false" );
|
||||||
|
requestBody.addAttribute( "route", "xmpp:" + m_server + ":5222" );
|
||||||
|
requestBody.addAttribute( "xml:lang", "en" );
|
||||||
|
requestBody.addAttribute( "xmpp:version", "1.0" );
|
||||||
|
requestBody.addAttribute( "to", m_server );
|
||||||
|
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending bosh connection request" );
|
||||||
|
sendRequest( requestBody.xml() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleDisconnect( const ConnectionBase* /*connection*/,
|
||||||
|
ConnectionError reason )
|
||||||
|
{
|
||||||
|
if( m_handler && m_state == StateConnecting )
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( m_connMode ) // FIXME avoid that if we're disconnecting on purpose
|
||||||
|
{
|
||||||
|
case ModePipelining:
|
||||||
|
m_connMode = ModeLegacyHTTP; // Server seems not to support pipelining
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"connection closed - falling back to HTTP/1.0 connection method" );
|
||||||
|
break;
|
||||||
|
case ModeLegacyHTTP:
|
||||||
|
case ModePersistentHTTP:
|
||||||
|
// FIXME do we need to do anything here?
|
||||||
|
// printf( "A TCP connection %p was disconnected (reason: %d).\n", connection, reason );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleTag( Tag* tag )
|
||||||
|
{
|
||||||
|
if( !m_handler || tag->name() != "body" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_streamRestart )
|
||||||
|
{
|
||||||
|
m_streamRestart = false;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending spoofed <stream:stream>" );
|
||||||
|
m_handler->handleReceivedData( this, "<?xml version='1.0' ?>"
|
||||||
|
"<stream:stream xmlns:stream='http://etherx.jabber.org/streams'"
|
||||||
|
" xmlns='" + XMLNS_CLIENT + "' version='" + XMPP_STREAM_VERSION_MAJOR
|
||||||
|
+ "." + XMPP_STREAM_VERSION_MINOR + "' from='" + m_server + "' id ='"
|
||||||
|
+ m_sid + "' xml:lang='en'>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "sid" ) )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_sid = tag->findAttribute( "sid" );
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "requests" ) )
|
||||||
|
{
|
||||||
|
const int serverRequests = atoi( tag->findAttribute( "requests" ).c_str() );
|
||||||
|
if( serverRequests < m_maxOpenRequests )
|
||||||
|
{
|
||||||
|
m_maxOpenRequests = serverRequests;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'requests' now set to " + tag->findAttribute( "requests" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( tag->hasAttribute( "hold" ) )
|
||||||
|
{
|
||||||
|
const int maxHold = atoi( tag->findAttribute( "hold" ).c_str() );
|
||||||
|
if( maxHold < m_hold )
|
||||||
|
{
|
||||||
|
m_hold = maxHold;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'hold' now set to " + tag->findAttribute( "hold" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( tag->hasAttribute( "wait" ) )
|
||||||
|
{
|
||||||
|
const int maxWait = atoi( tag->findAttribute( "wait" ).c_str() );
|
||||||
|
if( maxWait < m_wait )
|
||||||
|
{
|
||||||
|
m_wait = maxWait;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'wait' now set to " + tag->findAttribute( "wait" )
|
||||||
|
+ " seconds" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( tag->hasAttribute( "polling" ) )
|
||||||
|
{
|
||||||
|
const int minTime = atoi( tag->findAttribute( "polling" ).c_str() );
|
||||||
|
m_minTimePerRequest = minTime;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'polling' now set to " + tag->findAttribute( "polling" )
|
||||||
|
+ " seconds" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_state < StateConnected )
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
|
||||||
|
m_handler->handleReceivedData( this, "<?xml version='1.0' ?>" // FIXME move to send() so that
|
||||||
|
// it is more clearly a response
|
||||||
|
// to the initial stream opener?
|
||||||
|
"<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
|
||||||
|
"xmlns='" + XMLNS_CLIENT
|
||||||
|
+ "' version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR
|
||||||
|
+ "' from='" + m_server + "' id ='" + m_sid + "' xml:lang='en'>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tag->findAttribute( "type" ) == "terminate" )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh connection closed by server: " + tag->findAttribute( "condition" ) );
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_handler->handleDisconnect( this, ConnStreamClosed );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagList& stanzas = tag->children();
|
||||||
|
TagList::const_iterator it = stanzas.begin();
|
||||||
|
for( ; it != stanzas.end(); ++it )
|
||||||
|
m_handler->handleReceivedData( this, (*it)->xml() );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionBOSH::getConnection()
|
||||||
|
{
|
||||||
|
if( m_openRequests > 0 && m_openRequests >= m_maxOpenRequests )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Too many requests already open. Cannot send." );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* conn = 0;
|
||||||
|
switch( m_connMode )
|
||||||
|
{
|
||||||
|
case ModePipelining:
|
||||||
|
if( !m_activeConnections.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Using default connection for Pipelining." );
|
||||||
|
return m_activeConnections.front();
|
||||||
|
}
|
||||||
|
else if( !m_connectionPool.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Pipelining selected, but no connection open. Opening one." );
|
||||||
|
return activateConnection();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"No available connections to pipeline on." );
|
||||||
|
break;
|
||||||
|
case ModeLegacyHTTP:
|
||||||
|
case ModePersistentHTTP:
|
||||||
|
{
|
||||||
|
if( !m_connectionPool.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "LegacyHTTP/PersistentHTTP selected, "
|
||||||
|
"using connection from pool." );
|
||||||
|
return activateConnection();
|
||||||
|
}
|
||||||
|
else if( !m_activeConnections.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "No connections in pool, creating a new one." );
|
||||||
|
conn = m_activeConnections.front()->newInstance();
|
||||||
|
conn->registerConnectionDataHandler( this );
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
conn->connect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"No available connections to send on." );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionBOSH::activateConnection()
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_connectionPool.front();
|
||||||
|
m_connectionPool.pop_front();
|
||||||
|
if( conn->state() == StateConnected )
|
||||||
|
{
|
||||||
|
m_activeConnections.push_back( conn );
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Connecting pooled connection." );
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
conn->connect();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::putConnection()
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_activeConnections.front();
|
||||||
|
|
||||||
|
switch( m_connMode )
|
||||||
|
{
|
||||||
|
case ModeLegacyHTTP:
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Disconnecting LegacyHTTP connection" );
|
||||||
|
conn->disconnect();
|
||||||
|
conn->cleanup(); // This is necessary
|
||||||
|
m_activeConnections.pop_front();
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
break;
|
||||||
|
case ModePersistentHTTP:
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Deactivating PersistentHTTP connection" );
|
||||||
|
m_activeConnections.pop_front();
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
break;
|
||||||
|
case ModePipelining:
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Keeping Pipelining connection" );
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONBOSH_H__
|
||||||
|
#define CONNECTIONBOSH_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "taghandler.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a BOSH (HTTP binding) connection.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Client *c = new Client( ... );
|
||||||
|
* c->setConnectionImpl( new ConnectionBOSH( c,
|
||||||
|
* new ConnectionTCPClient( c->logInstance(), httpServer, httpPort ),
|
||||||
|
* c->logInstance(), boshHost, xmpphost, xmppPort ) );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Make sure to pass the BOSH connection manager's host/port to the transport connection
|
||||||
|
* (ConnectionTCPClient in this case), and the XMPP server's host and port to the BOSH connection.
|
||||||
|
* You must also pass to BOSH the address of the BOSH server you are dealing with, this is used
|
||||||
|
* in the HTTP Host header.
|
||||||
|
*
|
||||||
|
* In the case of using ConnectionBOSH through a HTTP proxy, supply httpServer and httpPort as
|
||||||
|
* those of the proxy. In all cases, boshHost should be set to the hostname (not IP address) of
|
||||||
|
* the server running the BOSH connection manager.
|
||||||
|
*
|
||||||
|
* The reason why ConnectionBOSH doesn't manage its own ConnectionTCPClient is that it allows it
|
||||||
|
* to be used with other transports (like chained SOCKS5/HTTP proxies, or ConnectionTLS
|
||||||
|
* for HTTPS).
|
||||||
|
*
|
||||||
|
* @note To avoid problems, you should disable TLS in gloox by calling
|
||||||
|
* ClientBase::setTls( TLSDisabled ).
|
||||||
|
*
|
||||||
|
* Sample configurations for different servers can be found in the bosh_example.cpp file included
|
||||||
|
* with gloox in the @b src/examples/ directory.
|
||||||
|
*
|
||||||
|
* @author Matthew Wild <mwild1@gmail.com>
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionBOSH : public ConnectionBase, ConnectionDataHandler, TagHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionBOSH object.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the BOSH connection manager's (or a HTTP proxy's) host and port, @b not to the XMPP host.
|
||||||
|
* ConnectionBOSH will own the transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param boshHost The hostname of the BOSH connection manager
|
||||||
|
* @param xmppServer A server to connect to. This is the XMPP server's address, @b not the
|
||||||
|
* connection manager's.
|
||||||
|
* @param xmppPort The port to connect to. This is the XMPP server's port, @b not the connection
|
||||||
|
* manager's.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionSOCKS5Proxy.
|
||||||
|
*/
|
||||||
|
ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance, const std::string& boshHost,
|
||||||
|
const std::string& xmppServer, int xmppPort = 5222 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionBOSH object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the connection manager's (or proxy's) host and port, @b not to the XMPP host. ConnectionBOSH
|
||||||
|
* will own the transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param boshHost The hostname of the BOSH connection manager (not any intermediate proxy)
|
||||||
|
* @param xmppServer A server to connect to. This is the XMPP server's address, @b not the connection
|
||||||
|
* manager's.
|
||||||
|
* @param xmppPort The port to connect to. This is the XMPP server's port, @b not the connection
|
||||||
|
* manager's.
|
||||||
|
*/
|
||||||
|
ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance, const std::string& boshHost,
|
||||||
|
const std::string& xmppServer, int xmppPort = 5222 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionBOSH();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The supported connection modes. Usually auto-detected.
|
||||||
|
*/
|
||||||
|
enum ConnMode
|
||||||
|
{
|
||||||
|
ModeLegacyHTTP, /**< HTTP 1.0 connections, closed after receiving a response */
|
||||||
|
ModePersistentHTTP, /**< HTTP 1.1 connections, re-used after receiving a response */
|
||||||
|
ModePipelining /**< HTTP Pipelining (implies HTTP 1.1) a single connection is used */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the XMPP server to proxy to.
|
||||||
|
* @param xmppHost The XMPP server hostname (IP address).
|
||||||
|
* @param xmppPort The XMPP server port.
|
||||||
|
*/
|
||||||
|
void setServer( const std::string& xmppHost, unsigned short xmppPort = 5222 )
|
||||||
|
{ m_server = xmppHost; m_port = xmppPort; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the path on the connection manager to request
|
||||||
|
* @param path The path, the default is "/http-bind/", which is the default for
|
||||||
|
* many connection managers.
|
||||||
|
*/
|
||||||
|
void setPath( const std::string& path ) { m_path = path; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the connection mode
|
||||||
|
* @param mode The connection mode, @sa ConnMode
|
||||||
|
* @note In the case that a mode is selected that the connection manager
|
||||||
|
* or proxy does not support, gloox will fall back to using HTTP/1.0 connections,
|
||||||
|
* which should work with any server.
|
||||||
|
*/
|
||||||
|
void setMode( ConnMode mode ) { m_connMode = mode; }
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int& totalIn, long int& totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
// reimplemented from TagHandler
|
||||||
|
virtual void handleTag( Tag* tag );
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionBOSH& operator=( const ConnectionBOSH& );
|
||||||
|
void initInstance( ConnectionBase* connection, const std::string& xmppServer, const int xmppPort );
|
||||||
|
bool sendRequest( const std::string& xml );
|
||||||
|
bool sendXML();
|
||||||
|
const std::string getHTTPField( const std::string& field );
|
||||||
|
ConnectionBase* getConnection();
|
||||||
|
ConnectionBase* activateConnection();
|
||||||
|
void putConnection();
|
||||||
|
|
||||||
|
//ConnectionBase *m_connection;
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
Parser m_parser; // Used for parsing XML section of responses
|
||||||
|
std::string m_boshHost; // The hostname of the BOSH connection manager
|
||||||
|
std::string m_boshedHost; // The hostname of the BOSH connection manager + : + port
|
||||||
|
std::string m_path; // The path part of the URL that we need to request
|
||||||
|
|
||||||
|
// BOSH parameters
|
||||||
|
unsigned long m_rid;
|
||||||
|
std::string m_sid;
|
||||||
|
|
||||||
|
bool m_initialStreamSent;
|
||||||
|
int m_openRequests;
|
||||||
|
int m_maxOpenRequests;
|
||||||
|
int m_wait;
|
||||||
|
int m_hold;
|
||||||
|
|
||||||
|
bool m_streamRestart; // Set to true if we are waiting for an acknowledgement of a stream restart
|
||||||
|
|
||||||
|
time_t m_lastRequestTime;
|
||||||
|
unsigned long m_minTimePerRequest;
|
||||||
|
|
||||||
|
std::string m_buffer; // Buffer of received data
|
||||||
|
std::string m_bufferHeader; // HTTP header of data currently in buffer // FIXME doens't need to be member
|
||||||
|
std::string::size_type m_bufferContentLength; // Length of the data in the current response
|
||||||
|
|
||||||
|
std::string m_sendBuffer; // Data waiting to be sent
|
||||||
|
|
||||||
|
typedef std::list<ConnectionBase*> ConnectionList;
|
||||||
|
ConnectionList m_activeConnections;
|
||||||
|
ConnectionList m_connectionPool;
|
||||||
|
ConnMode m_connMode;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONBOSH_H__
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONDATAHANDLER_H__
|
||||||
|
#define CONNECTIONDATAHANDLER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ConnectionBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstract base class to receive events from a ConnectionBase-derived object.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionDataHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called for received from the underlying transport.
|
||||||
|
* @param connection The connection that received the data.
|
||||||
|
* @param data The data received.
|
||||||
|
*/
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when e.g. the raw TCP connection was established.
|
||||||
|
* @param connection The connection.
|
||||||
|
*/
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This connection is called when e.g. the raw TCP connection was closed.
|
||||||
|
* @param connection The connection.
|
||||||
|
* @param reason The reason for the disconnect.
|
||||||
|
*/
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONDATAHANDLER_H__
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONHANDLER_H__
|
||||||
|
#define CONNECTIONHANDLER_H__
|
||||||
|
|
||||||
|
#include "connectionbase.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstract base class to receive incoming connection attempts. Do not
|
||||||
|
* confuse this with ConnectionListener, which is used with XMPP streams and has a
|
||||||
|
* completely different meaning.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called to receive an incoming connection.
|
||||||
|
* @param server The server that the connection was made to.
|
||||||
|
* @param connection The incoming connection.
|
||||||
|
*/
|
||||||
|
virtual void handleIncomingConnection( ConnectionBase* server, ConnectionBase* connection ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONHANDLER_H__
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectionhttpproxy.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionHTTPProxy::ConnectionHTTPProxy( ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( 0 ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance ), m_http11( false )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionHTTPProxy::ConnectionHTTPProxy( ConnectionDataHandler* cdh,
|
||||||
|
ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( cdh ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionHTTPProxy::~ConnectionHTTPProxy()
|
||||||
|
{
|
||||||
|
delete m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionHTTPProxy::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
|
||||||
|
return new ConnectionHTTPProxy( m_handler, conn, m_logInstance, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::setConnectionImpl( ConnectionBase* connection )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
delete m_connection;
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionHTTPProxy::connect()
|
||||||
|
{
|
||||||
|
if( m_connection && m_handler )
|
||||||
|
{
|
||||||
|
m_state = StateConnecting;
|
||||||
|
return m_connection->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::disconnect()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionHTTPProxy::recv( int timeout )
|
||||||
|
{
|
||||||
|
return m_connection ? m_connection->recv( timeout ) : ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionHTTPProxy::receive()
|
||||||
|
{
|
||||||
|
return m_connection ? m_connection->receive() : ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionHTTPProxy::send( const std::string& data )
|
||||||
|
{
|
||||||
|
return m_connection && m_connection->send( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::cleanup()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::getStatistics( long int& totalIn, long int& totalOut )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->getStatistics( totalIn, totalOut );
|
||||||
|
else
|
||||||
|
totalIn = totalOut = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::handleReceivedData( const ConnectionBase* /*connection*/,
|
||||||
|
const std::string& data )
|
||||||
|
{
|
||||||
|
if( !m_handler )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_state == StateConnecting )
|
||||||
|
{
|
||||||
|
m_proxyHandshakeBuffer += data;
|
||||||
|
if( ( !m_proxyHandshakeBuffer.compare( 0, 12, "HTTP/1.0 200" )
|
||||||
|
|| !m_proxyHandshakeBuffer.compare( 0, 12, "HTTP/1.1 200" ) )
|
||||||
|
&& !m_proxyHandshakeBuffer.compare( m_proxyHandshakeBuffer.length() - 4, 4, "\r\n\r\n" ) )
|
||||||
|
{
|
||||||
|
m_proxyHandshakeBuffer = EmptyString;
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionHTTPProxy,
|
||||||
|
"http proxy connection established" );
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
}
|
||||||
|
else if( !m_proxyHandshakeBuffer.compare( 9, 3, "407" ) )
|
||||||
|
{
|
||||||
|
m_handler->handleDisconnect( this, ConnProxyAuthRequired );
|
||||||
|
m_connection->disconnect();
|
||||||
|
}
|
||||||
|
else if( !m_proxyHandshakeBuffer.compare( 9, 3, "403" )
|
||||||
|
|| !m_proxyHandshakeBuffer.compare( 9, 3, "404" ) )
|
||||||
|
{
|
||||||
|
m_handler->handleDisconnect( this, ConnProxyAuthFailed );
|
||||||
|
m_connection->disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( m_state == StateConnected )
|
||||||
|
m_handler->handleReceivedData( this, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
{
|
||||||
|
std::string server = m_server;
|
||||||
|
int port = m_port;
|
||||||
|
if( port == -1 )
|
||||||
|
{
|
||||||
|
const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
|
||||||
|
if( !servers.empty() )
|
||||||
|
{
|
||||||
|
const std::pair< std::string, int >& host = *servers.begin();
|
||||||
|
server = host.first;
|
||||||
|
port = host.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string message = "Requesting http proxy connection to " + server + ":"
|
||||||
|
+ util::int2string( port );
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, message );
|
||||||
|
|
||||||
|
std::string os = "CONNECT " + server + ":" + util::int2string( port ) + " HTTP/1."
|
||||||
|
+ util::int2string( m_http11 ? 1 : 0 ) + "\r\n"
|
||||||
|
"Host: " + server + "\r\n"
|
||||||
|
"Content-Length: 0\r\n"
|
||||||
|
"Proxy-Connection: Keep-Alive\r\n"
|
||||||
|
"Pragma: no-cache\r\n"
|
||||||
|
"User-Agent: gloox/" + GLOOX_VERSION + "\r\n";
|
||||||
|
|
||||||
|
if( !m_proxyUser.empty() && !m_proxyPwd.empty() )
|
||||||
|
{
|
||||||
|
os += "Proxy-Authorization: Basic " + Base64::encode64( m_proxyUser + ":" + m_proxyPwd )
|
||||||
|
+ "\r\n";
|
||||||
|
}
|
||||||
|
os += "\r\n";
|
||||||
|
|
||||||
|
if( !m_connection->send( os ) )
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::handleDisconnect( const ConnectionBase* /*connection*/,
|
||||||
|
ConnectionError reason )
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, "HTTP Proxy connection closed" );
|
||||||
|
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONHTTPPROXY_H__
|
||||||
|
#define CONNECTIONHTTPPROXY_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple HTTP Proxying connection.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Client* c = new Client( ... );
|
||||||
|
* ConnectionTCPClient* conn0 = new ConnectionTCPClient( c->logInstance(),
|
||||||
|
* proxyHost, proxyPort );
|
||||||
|
* ConnectionHTTPProxy* conn1 = new ConnectionHTTPProxy( c, conn0, c->logInstance(),
|
||||||
|
* xmppHost, xmppPort );
|
||||||
|
* c->setConnectionImpl( conn1 );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Make sure to pass the proxy host/port to the transport connection (ConnectionTCPClient in this case),
|
||||||
|
* and the XMPP host/port to the proxy connection.
|
||||||
|
*
|
||||||
|
* ConnectionHTTPProxy uses the CONNECT method to pass through the proxy. If your proxy does not
|
||||||
|
* allow this kind of connections, or if it kills connections after some time, you may want to use
|
||||||
|
* ConnectionBOSH instead or in addition.
|
||||||
|
*
|
||||||
|
* The reason why ConnectionHTTPProxy doesn't manage its own ConnectionTCPClient is that it allows it
|
||||||
|
* to be used with other transports (like IPv6 or chained SOCKS5/HTTP proxies).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionHTTPProxy : public ConnectionBase, public ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionHTTPProxy object.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the XMPP host. ConnectionHTTPProxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The port to connect to. This is the XMPP server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionSOCKS5Proxy.
|
||||||
|
*/
|
||||||
|
ConnectionHTTPProxy( ConnectionBase* connection, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionHTTPProxy object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the XMPP host. ConnectionHTTPProxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The port to connect to. This is the XMPP server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
ConnectionHTTPProxy( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionHTTPProxy();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the XMPP server to proxy to.
|
||||||
|
* @param host The XMPP server hostname (IP address).
|
||||||
|
* @param port The XMPP server port. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
void setServer( const std::string& host, int port = -1 )
|
||||||
|
{ m_server = host; m_port = port; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets proxy authorization credentials.
|
||||||
|
* @param user The user name to use for proxy authorization.
|
||||||
|
* @param password The password to use for proxy authorization.
|
||||||
|
*/
|
||||||
|
void setProxyAuth( const std::string& user, const std::string& password )
|
||||||
|
{ m_proxyUser = user; m_proxyPwd = password; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying transport connection. A possibly existing connection will be deleted.
|
||||||
|
* @param connection The ConnectionBase to replace the current connection, if any.
|
||||||
|
*/
|
||||||
|
void setConnectionImpl( ConnectionBase* connection );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches usage of HTTP/1.1 on or off.
|
||||||
|
* @param http11 Set this to @b true to connect through a HTTP/1.1-only proxy, or @b false
|
||||||
|
* to use HTTP/1.0. Defaults to HTTP/1.0 which should work with 99.9% of proxies.
|
||||||
|
*/
|
||||||
|
void setHTTP11( bool http11 ) { m_http11 = http11; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionHTTPProxy &operator=( const ConnectionHTTPProxy& );
|
||||||
|
|
||||||
|
ConnectionBase* m_connection;
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
std::string m_proxyUser;
|
||||||
|
std::string m_proxyPwd;
|
||||||
|
std::string m_proxyHandshakeBuffer;
|
||||||
|
|
||||||
|
bool m_http11;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONHTTPPROXY_H__
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONLISTENER_H__
|
||||||
|
#define CONNECTIONLISTENER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Derived classes can be registered as ConnectionListeners with the Client.
|
||||||
|
*
|
||||||
|
* This interface is mandatory to implement if a connection is to be made TLS-encrypted.
|
||||||
|
* In onTLSConnect(), the server's certificate information needs to be checked, and @b true
|
||||||
|
* returned if the certificate is to be accepted.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionListener() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function notifies about successful connections. It will be called either after all
|
||||||
|
* authentication is finished if username/password were supplied, or after a connection has
|
||||||
|
* been established if no credentials were supplied. Depending on the setting of AutoPresence,
|
||||||
|
* a presence stanza is sent or not.
|
||||||
|
*/
|
||||||
|
virtual void onConnect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function notifies about disconnection and its reason.
|
||||||
|
* If @b e indicates a stream error, you can use @ref ClientBase::streamError() to find out
|
||||||
|
* what exactly went wrong, and @ref ClientBase::streamErrorText() to retrieve any explaining text
|
||||||
|
* sent along with the error.
|
||||||
|
* If @b e indicates an authentication error, you can use @ref ClientBase::authError()
|
||||||
|
* to get a finer grained reason.
|
||||||
|
* @param e The reason for the disconnection.
|
||||||
|
*/
|
||||||
|
virtual void onDisconnect( ConnectionError e ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will be called when a resource has been bound to the stream. It
|
||||||
|
* will be called for any bound resource, including the main one.
|
||||||
|
* @note The bound resource may be different from the one requested. The server
|
||||||
|
* has the authority to change/overwrite the requested resource.
|
||||||
|
* @param resource The resource string.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void onResourceBind( const std::string& resource ) { (void)resource; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called (by a Client object) if an error occurs while trying to bind a resource.
|
||||||
|
* @param error A pointer to an Error object that contains more
|
||||||
|
* information. May be 0.
|
||||||
|
*/
|
||||||
|
virtual void onResourceBindError( const Error* error ) { (void) (error); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called (by a Client object) if an error occurs while trying to establish
|
||||||
|
* a session.
|
||||||
|
* @param error A pointer to an Error object that contains more
|
||||||
|
* information. May be 0.
|
||||||
|
*/
|
||||||
|
virtual void onSessionCreateError( const Error* error ) { (void) (error); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when the connection was TLS/SSL secured.
|
||||||
|
* @param info Comprehensive info on the certificate.
|
||||||
|
* @return @b True if cert credentials are accepted, @b false otherwise. If @b false is returned
|
||||||
|
* the connection is terminated.
|
||||||
|
*/
|
||||||
|
virtual bool onTLSConnect( const CertInfo& info ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called for certain stream events. Notifications are purely informational
|
||||||
|
* and implementation is optional. Not all StreamEvents will necessarily be emitted for
|
||||||
|
* a given connection.
|
||||||
|
* @param event A stream event.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
virtual void onStreamEvent( StreamEvent event ) { (void) (event); }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONLISTENER_H__
|
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectionsocks5proxy.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server,
|
||||||
|
int port, bool ip )
|
||||||
|
: ConnectionBase( 0 ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh,
|
||||||
|
ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server,
|
||||||
|
int port, bool ip )
|
||||||
|
: ConnectionBase( cdh ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy::~ConnectionSOCKS5Proxy()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
delete m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionSOCKS5Proxy::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
|
||||||
|
return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::setConnectionImpl( ConnectionBase* connection )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
delete m_connection;
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionSOCKS5Proxy::connect()
|
||||||
|
{
|
||||||
|
// FIXME CHECKME
|
||||||
|
if( m_connection && m_connection->state() == StateConnected && m_handler )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_s5state = S5StateConnected;
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_connection && m_handler )
|
||||||
|
{
|
||||||
|
m_state = StateConnecting;
|
||||||
|
m_s5state = S5StateConnecting;
|
||||||
|
return m_connection->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::disconnect()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->disconnect();
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionSOCKS5Proxy::recv( int timeout )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->recv( timeout );
|
||||||
|
else
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionSOCKS5Proxy::receive()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->receive();
|
||||||
|
else
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionSOCKS5Proxy::send( const std::string& data )
|
||||||
|
{
|
||||||
|
// if( m_s5state != S5StateConnected )
|
||||||
|
// {
|
||||||
|
// printf( "p data sent: " );
|
||||||
|
// const char* x = data.c_str();
|
||||||
|
// for( unsigned int i = 0; i < data.length(); ++i )
|
||||||
|
// printf( "%02X ", (const char)x[i] );
|
||||||
|
// printf( "\n" );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->send( data );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::cleanup()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_s5state = S5StateDisconnected;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::getStatistics( long int &totalIn, long int &totalOut )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->getStatistics( totalIn, totalOut );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalIn = 0;
|
||||||
|
totalOut = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::handleReceivedData( const ConnectionBase* /*connection*/,
|
||||||
|
const std::string& data )
|
||||||
|
{
|
||||||
|
// if( m_s5state != S5StateConnected )
|
||||||
|
// {
|
||||||
|
// printf( "data recv: " );
|
||||||
|
// const char* x = data.c_str();
|
||||||
|
// for( unsigned int i = 0; i < data.length(); ++i )
|
||||||
|
// printf( "%02X ", (const char)x[i] );
|
||||||
|
// printf( "\n" );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if( !m_connection || !m_handler )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ConnectionError connError = ConnNoError;
|
||||||
|
|
||||||
|
switch( m_s5state )
|
||||||
|
{
|
||||||
|
case S5StateConnecting:
|
||||||
|
if( data.length() != 2 || data[0] != 0x05 )
|
||||||
|
connError = ConnIoError;
|
||||||
|
|
||||||
|
if( data[1] == 0x00 ) // no auth
|
||||||
|
{
|
||||||
|
negotiate();
|
||||||
|
}
|
||||||
|
else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
|
||||||
|
"authenticating to socks5 proxy as user " + m_proxyUser );
|
||||||
|
m_s5state = S5StateAuthenticating;
|
||||||
|
char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()];
|
||||||
|
size_t pos = 0;
|
||||||
|
d[pos++] = 0x01;
|
||||||
|
d[pos++] = (char)m_proxyUser.length();
|
||||||
|
strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
|
||||||
|
pos += m_proxyUser.length();
|
||||||
|
d[pos++] = (char)m_proxyPwd.length();
|
||||||
|
strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() );
|
||||||
|
pos += m_proxyPwd.length();
|
||||||
|
|
||||||
|
if( !send( std::string( d, pos ) ) )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
delete[] d;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( data[1] == (char)(unsigned char)0xFF && !m_proxyUser.empty() && !m_proxyPwd.empty() )
|
||||||
|
connError = ConnProxyNoSupportedAuth;
|
||||||
|
else
|
||||||
|
connError = ConnProxyAuthRequired;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case S5StateNegotiating:
|
||||||
|
if( data.length() >= 6 && data[0] == 0x05 )
|
||||||
|
{
|
||||||
|
if( data[1] == 0x00 )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_s5state = S5StateConnected;
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
}
|
||||||
|
else // connection refused
|
||||||
|
connError = ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
connError = ConnIoError;
|
||||||
|
break;
|
||||||
|
case S5StateAuthenticating:
|
||||||
|
if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 )
|
||||||
|
negotiate();
|
||||||
|
else
|
||||||
|
connError = ConnProxyAuthFailed;
|
||||||
|
break;
|
||||||
|
case S5StateConnected:
|
||||||
|
m_handler->handleReceivedData( this, data );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( connError != ConnNoError )
|
||||||
|
{
|
||||||
|
m_connection->disconnect();
|
||||||
|
m_handler->handleDisconnect( this, connError );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::negotiate()
|
||||||
|
{
|
||||||
|
m_s5state = S5StateNegotiating;
|
||||||
|
char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1];
|
||||||
|
size_t pos = 0;
|
||||||
|
d[pos++] = 0x05; // SOCKS version 5
|
||||||
|
d[pos++] = 0x01; // command CONNECT
|
||||||
|
d[pos++] = 0x00; // reserved
|
||||||
|
int port = m_port;
|
||||||
|
std::string server = m_server;
|
||||||
|
if( m_ip ) // IP address
|
||||||
|
{
|
||||||
|
d[pos++] = 0x01; // IPv4 address
|
||||||
|
std::string s;
|
||||||
|
const size_t j = server.length();
|
||||||
|
size_t l = 0;
|
||||||
|
for( size_t k = 0; k < j && l < 4; ++k )
|
||||||
|
{
|
||||||
|
if( server[k] != '.' )
|
||||||
|
s += server[k];
|
||||||
|
|
||||||
|
if( server[k] == '.' || k == j-1 )
|
||||||
|
{
|
||||||
|
d[pos++] = static_cast<char>( atoi( s.c_str() ) & 0xFF );
|
||||||
|
s = EmptyString;
|
||||||
|
++l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // hostname
|
||||||
|
{
|
||||||
|
if( port == -1 )
|
||||||
|
{
|
||||||
|
const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
|
||||||
|
if( servers.size() )
|
||||||
|
{
|
||||||
|
const std::pair< std::string, int >& host = *servers.begin();
|
||||||
|
server = host.first;
|
||||||
|
port = host.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d[pos++] = 0x03; // hostname
|
||||||
|
d[pos++] = (char)m_server.length();
|
||||||
|
strncpy( d + pos, m_server.c_str(), m_server.length() );
|
||||||
|
pos += m_server.length();
|
||||||
|
}
|
||||||
|
int nport = htons( port );
|
||||||
|
d[pos++] = static_cast<char>( nport );
|
||||||
|
d[pos++] = static_cast<char>( nport >> 8 );
|
||||||
|
|
||||||
|
std::string message = "Requesting socks5 proxy connection to " + server + ":"
|
||||||
|
+ util::int2string( port );
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message );
|
||||||
|
|
||||||
|
if( !send( std::string( d, pos ) ) )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
delete[] d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
{
|
||||||
|
std::string server = m_server;
|
||||||
|
int port = m_port;
|
||||||
|
if( port == -1 )
|
||||||
|
{
|
||||||
|
const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
|
||||||
|
if( !servers.empty() )
|
||||||
|
{
|
||||||
|
const std::pair< std::string, int >& host = *servers.begin();
|
||||||
|
server = host.first;
|
||||||
|
port = host.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
|
||||||
|
"Attempting to negotiate socks5 proxy connection" );
|
||||||
|
|
||||||
|
const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty();
|
||||||
|
const char d[4] = {
|
||||||
|
0x05, // SOCKS version 5
|
||||||
|
static_cast<char>( auth ? 0x02 // two methods
|
||||||
|
: 0x01 ), // one method
|
||||||
|
0x00, // method: no auth
|
||||||
|
0x02 // method: username/password auth
|
||||||
|
};
|
||||||
|
|
||||||
|
if( !send( std::string( d, auth ? 4 : 3 ) ) )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::handleDisconnect( const ConnectionBase* /*connection*/,
|
||||||
|
ConnectionError reason )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
|
||||||
|
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONSOCKS5PROXY_H__
|
||||||
|
#define CONNECTIONSOCKS5PROXY_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple SOCKS5 Proxying connection (RFC 1928 + RFC 1929).
|
||||||
|
*
|
||||||
|
* To use with a SOCKS5 proxy:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Client* c = new Client( ... );
|
||||||
|
* c->setConnectionImpl( new ConnectionSOCKS5Proxy( c,
|
||||||
|
* new ConnectionTCPClient( c->logInstance(), proxyHost, proxyPort ),
|
||||||
|
* c->logInstance(), xmppHost, xmppPort ) );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Make sure to pass the proxy host/port to the transport connection (ConnectionTCPClient in this case),
|
||||||
|
* and the XMPP host/port to the proxy connection.
|
||||||
|
*
|
||||||
|
* The reason why ConnectionSOCKS5Proxy doesn't manage its own ConnectionTCPClient is that it allows it
|
||||||
|
* to be used with other transports (like IPv6 or chained HTTP/SOCKS5 proxies).
|
||||||
|
*
|
||||||
|
* @note This class is also used by the SOCKS5 bytestreams implementation (with slightly different
|
||||||
|
* semantics).
|
||||||
|
*
|
||||||
|
* @note Simple @b plain-text username/password authentication is supported. GSSAPI authentication
|
||||||
|
* is not supported.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionSOCKS5Proxy : public ConnectionBase, public ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionSOCKS5Proxy object.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the (XMPP) host. ConnectionSOCKS5Proxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The proxy's port to connect to. This is the (XMPP) server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
* @param ip Indicates whether @c server is an IP address (true) or a host name (false).
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionHTTPProxy.
|
||||||
|
*/
|
||||||
|
ConnectionSOCKS5Proxy( ConnectionBase* connection, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1, bool ip = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionSOCKS5Proxy object.
|
||||||
|
* @param cdh A ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the (XMPP) host. ConnectionSOCKS5Proxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The proxy's port to connect to. This is the (XMPP) server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
* @param ip Indicates whether @c server is an IP address (true) or a host name (false).
|
||||||
|
*/
|
||||||
|
ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1, bool ip = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionSOCKS5Proxy();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the server to proxy to.
|
||||||
|
* @param host The server hostname (IP address).
|
||||||
|
* @param port The server port. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
* @param ip Indicates whether @c host is an IP address (true) or a host name (false).
|
||||||
|
*/
|
||||||
|
void setServer( const std::string& host, int port = -1, bool ip = false )
|
||||||
|
{ m_server = host; m_port = port; m_ip = ip; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets proxy authorization credentials.
|
||||||
|
* @param user The user name to use for proxy authorization.
|
||||||
|
* @param password The password to use for proxy authorization.
|
||||||
|
*/
|
||||||
|
void setProxyAuth( const std::string& user, const std::string& password )
|
||||||
|
{ m_proxyUser = user; m_proxyPwd = password; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying transport connection. A possibly existing connection will be deleted.
|
||||||
|
* @param connection The ConnectionBase to replace the current connection, if any.
|
||||||
|
*/
|
||||||
|
void setConnectionImpl( ConnectionBase* connection );
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Socks5State
|
||||||
|
{
|
||||||
|
S5StateDisconnected,
|
||||||
|
S5StateConnecting,
|
||||||
|
S5StateNegotiating,
|
||||||
|
S5StateAuthenticating,
|
||||||
|
S5StateConnected
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy &operator=( const ConnectionSOCKS5Proxy& );
|
||||||
|
void negotiate();
|
||||||
|
|
||||||
|
ConnectionBase* m_connection;
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
Socks5State m_s5state;
|
||||||
|
|
||||||
|
std::string m_proxyUser;
|
||||||
|
std::string m_proxyPwd;
|
||||||
|
std::string m_proxyHandshakeBuffer;
|
||||||
|
bool m_ip;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONSOCKS5PROXY_H__
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectiontcpbase.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "mutexguard.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <string.h>
|
||||||
|
#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
typedef int socklen_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTCPBase::ConnectionTCPBase( const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( 0 ),
|
||||||
|
m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ),
|
||||||
|
m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
|
||||||
|
{
|
||||||
|
init( server, port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPBase::ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( cdh ),
|
||||||
|
m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ),
|
||||||
|
m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
|
||||||
|
{
|
||||||
|
init( server, port );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::init( const std::string& server, int port )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPBase::~ConnectionTCPBase()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
free( m_buf );
|
||||||
|
m_buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::disconnect()
|
||||||
|
{
|
||||||
|
util::MutexGuard rm( m_recvMutex );
|
||||||
|
m_cancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionTCPBase::dataAvailable( int timeout )
|
||||||
|
{
|
||||||
|
if( m_socket < 0 )
|
||||||
|
return true; // let recv() catch the closed fd
|
||||||
|
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
FD_ZERO( &fds );
|
||||||
|
// the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
|
||||||
|
// however, the reason for the warning can't be fixed in gloox.
|
||||||
|
FD_SET( m_socket, &fds );
|
||||||
|
|
||||||
|
tv.tv_sec = timeout / 1000000;
|
||||||
|
tv.tv_usec = timeout % 1000000;
|
||||||
|
|
||||||
|
return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
|
||||||
|
&& FD_ISSET( m_socket, &fds ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPBase::receive()
|
||||||
|
{
|
||||||
|
if( m_socket < 0 )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
ConnectionError err = ConnNoError;
|
||||||
|
while( !m_cancel && ( err = recv( 10 ) ) == ConnNoError )
|
||||||
|
;
|
||||||
|
return err == ConnNoError ? ConnNotConnected : err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionTCPBase::send( const std::string& data )
|
||||||
|
{
|
||||||
|
m_sendMutex.lock();
|
||||||
|
|
||||||
|
if( data.empty() || ( m_socket < 0 ) )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sent = 0;
|
||||||
|
for( size_t num = 0, len = data.length(); sent != -1 && num < len; num += sent )
|
||||||
|
{
|
||||||
|
sent = static_cast<int>( ::send( m_socket, (data.c_str()+num), (int)(len - num), 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_totalBytesOut += (int)data.length();
|
||||||
|
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
|
||||||
|
if( sent == -1 && m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
|
||||||
|
return sent != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::getStatistics( long int &totalIn, long int &totalOut )
|
||||||
|
{
|
||||||
|
totalIn = m_totalBytesIn;
|
||||||
|
totalOut = m_totalBytesOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::cleanup()
|
||||||
|
{
|
||||||
|
if( !m_sendMutex.trylock() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( !m_recvMutex.trylock() )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_socket >= 0 )
|
||||||
|
{
|
||||||
|
DNS::closeSocket( m_socket, m_logInstance );
|
||||||
|
m_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_cancel = true;
|
||||||
|
m_totalBytesIn = 0;
|
||||||
|
m_totalBytesOut = 0;
|
||||||
|
|
||||||
|
m_recvMutex.unlock(),
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionTCPBase::localPort() const
|
||||||
|
{
|
||||||
|
struct sockaddr local;
|
||||||
|
socklen_t len = (socklen_t)sizeof( local );
|
||||||
|
if( getsockname ( m_socket, &local, &len ) < 0 )
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return ntohs( ((struct sockaddr_in *)&local)->sin_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ConnectionTCPBase::localInterface() const
|
||||||
|
{
|
||||||
|
struct sockaddr_in local;
|
||||||
|
socklen_t len = (socklen_t)sizeof( local );
|
||||||
|
if( getsockname ( m_socket, (reinterpret_cast<struct sockaddr*>( &local )), &len ) < 0 )
|
||||||
|
return EmptyString;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// char addr[INET_ADDRSTRLEN];
|
||||||
|
// return inet_ntop( AF_INET, &(local.sin_addr), addr, sizeof( addr ) ); //FIXME is this portable?
|
||||||
|
return inet_ntoa( local.sin_addr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTCPBASE_H__
|
||||||
|
#define CONNECTIONTCPBASE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
class Mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a base class for a simple TCP connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTCPBase : public ConnectionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPBase object.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that XMPP SRV records
|
||||||
|
* will be used to find out about the actual host:port.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionHTTPProxy.
|
||||||
|
*/
|
||||||
|
ConnectionTCPBase( const LogSink& logInstance, const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPBase object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTCPBase();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives access to the raw socket of this connection. Use it wisely. You can
|
||||||
|
* select()/poll() it and use ConnectionTCPBase::recv( -1 ) to fetch the data.
|
||||||
|
* @return The socket of the active connection, or -1 if no connection is established.
|
||||||
|
*/
|
||||||
|
int socket() const { return m_socket; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function allows to set an existing socket with an established
|
||||||
|
* connection to use in this connection. You will still need to call connect() in order to
|
||||||
|
* negotiate the XMPP stream. You should not set a new socket after having called connect().
|
||||||
|
* @param socket The existing socket.
|
||||||
|
*/
|
||||||
|
void setSocket( int socket ) { m_cancel = false; m_state = StateConnected; m_socket = socket; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local port.
|
||||||
|
* @return The local port.
|
||||||
|
*/
|
||||||
|
virtual int localPort() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locally bound IP address.
|
||||||
|
* @return The locally bound IP address.
|
||||||
|
*/
|
||||||
|
virtual const std::string localInterface() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ConnectionTCPBase& operator=( const ConnectionTCPBase& );
|
||||||
|
void init( const std::string& server, int port );
|
||||||
|
bool dataAvailable( int timeout = -1 );
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
util::Mutex m_sendMutex;
|
||||||
|
util::Mutex m_recvMutex;
|
||||||
|
|
||||||
|
char* m_buf;
|
||||||
|
int m_socket;
|
||||||
|
long int m_totalBytesIn;
|
||||||
|
long int m_totalBytesOut;
|
||||||
|
const int m_bufsize;
|
||||||
|
bool m_cancel;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTCPBASE_H__
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectiontcpclient.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "mutexguard.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTCPClient::ConnectionTCPClient( const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionTCPBase( logInstance, server, port )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPClient::ConnectionTCPClient( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionTCPBase( cdh, logInstance, server, port )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ConnectionTCPClient::~ConnectionTCPClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTCPClient::newInstance() const
|
||||||
|
{
|
||||||
|
return new ConnectionTCPClient( m_handler, m_logInstance, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPClient::connect()
|
||||||
|
{
|
||||||
|
m_sendMutex.lock();
|
||||||
|
// FIXME CHECKME
|
||||||
|
if( !m_handler )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_socket >= 0 && m_state > StateDisconnected )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
{
|
||||||
|
if( m_port == -1 )
|
||||||
|
m_socket = DNS::connect( m_server, m_logInstance );
|
||||||
|
else
|
||||||
|
m_socket = DNS::connect( m_server, m_port, m_logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
{
|
||||||
|
switch( m_socket )
|
||||||
|
{
|
||||||
|
case -ConnConnectionRefused:
|
||||||
|
m_logInstance.err( LogAreaClassConnectionTCPClient,
|
||||||
|
m_server + ": connection refused" );
|
||||||
|
break;
|
||||||
|
case -ConnDnsError:
|
||||||
|
m_logInstance.err( LogAreaClassConnectionTCPClient,
|
||||||
|
m_server + ": host not found" );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_logInstance.err( LogAreaClassConnectionTCPClient,
|
||||||
|
"Unknown error condition" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_handler->handleDisconnect( this, (ConnectionError)-m_socket );
|
||||||
|
return (ConnectionError)-m_socket;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cancel = false;
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPClient::recv( int timeout )
|
||||||
|
{
|
||||||
|
m_recvMutex.lock();
|
||||||
|
|
||||||
|
if( m_cancel || m_socket < 0 )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !dataAvailable( timeout ) )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = static_cast<int>( ::recv( m_socket, m_buf, m_bufsize, 0 ) );
|
||||||
|
if( size > 0 )
|
||||||
|
m_totalBytesIn += size;
|
||||||
|
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
|
||||||
|
if( size <= 0 )
|
||||||
|
{
|
||||||
|
ConnectionError error = ( size ? ConnIoError : ConnStreamClosed );
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, error );
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buf[size] = '\0';
|
||||||
|
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleReceivedData( this, std::string( m_buf, size ) );
|
||||||
|
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTCPCLIENT_H__
|
||||||
|
#define CONNECTIONTCPCLIENT_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectiontcpbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple TCP connection.
|
||||||
|
*
|
||||||
|
* You should only need to use this class directly if you need access to some special feature, like
|
||||||
|
* the raw socket(), or if you need HTTP proxy support (see @ref gloox::ConnectionHTTPProxy for more
|
||||||
|
* information).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTCPClient : public ConnectionTCPBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPClient object.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that XMPP SRV records
|
||||||
|
* will be used to find out about the actual host:port.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionHTTPProxy.
|
||||||
|
*/
|
||||||
|
ConnectionTCPClient( const LogSink& logInstance, const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPClient object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
ConnectionTCPClient( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTCPClient();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTCPClient &operator=( const ConnectionTCPClient & );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTCPCLIENT_H__
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectiontcpserver.h"
|
||||||
|
#include "connectiontcpclient.h"
|
||||||
|
#include "connectionhandler.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "mutexguard.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
# include <resolv.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/un.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <errno.h>
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef _WIN32_WCE
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTCPServer::ConnectionTCPServer( ConnectionHandler* ch, const LogSink& logInstance,
|
||||||
|
const std::string& ip, int port )
|
||||||
|
: ConnectionTCPBase( 0, logInstance, ip, port ),
|
||||||
|
m_connectionHandler( ch )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPServer::~ConnectionTCPServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTCPServer::newInstance() const
|
||||||
|
{
|
||||||
|
return new ConnectionTCPServer( m_connectionHandler, m_logInstance, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPServer::connect()
|
||||||
|
{
|
||||||
|
util::MutexGuard mg( &m_sendMutex );
|
||||||
|
|
||||||
|
if( m_socket >= 0 || m_state > StateDisconnected )
|
||||||
|
return ConnNoError;
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
m_socket = DNS::getSocket( m_logInstance );
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
return ConnIoError;
|
||||||
|
|
||||||
|
struct sockaddr_in local;
|
||||||
|
local.sin_family = AF_INET;
|
||||||
|
local.sin_port = static_cast<unsigned short int>( htons( m_port ) );
|
||||||
|
local.sin_addr.s_addr = m_server.empty() ? INADDR_ANY : inet_addr( m_server.c_str() );
|
||||||
|
memset( local.sin_zero, '\0', 8 );
|
||||||
|
|
||||||
|
if( bind( m_socket, (struct sockaddr*)&local, sizeof( struct sockaddr ) ) < 0 )
|
||||||
|
{
|
||||||
|
std::string message = "bind() to " + ( m_server.empty() ? std::string( "*" ) : m_server )
|
||||||
|
+ " (" + inet_ntoa( local.sin_addr ) + ":" + util::int2string( m_port ) + ") failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionTCPServer, message );
|
||||||
|
|
||||||
|
return ConnIoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( listen( m_socket, 10 ) < 0 )
|
||||||
|
{
|
||||||
|
std::string message = "listen on " + ( m_server.empty() ? std::string( "*" ) : m_server )
|
||||||
|
+ " (" + inet_ntoa( local.sin_addr ) + ":" + util::int2string( m_port ) + ") failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionTCPServer, message );
|
||||||
|
|
||||||
|
return ConnIoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cancel = false;
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPServer::recv( int timeout )
|
||||||
|
{
|
||||||
|
m_recvMutex.lock();
|
||||||
|
|
||||||
|
if( m_cancel || m_socket < 0 || !m_connectionHandler )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !dataAvailable( timeout ) )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in they;
|
||||||
|
int sin_size = sizeof( struct sockaddr_in );
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
int newfd = static_cast<int>( accept( static_cast<SOCKET>( m_socket ), (struct sockaddr*)&they, &sin_size ) );
|
||||||
|
#else
|
||||||
|
int newfd = accept( m_socket, (struct sockaddr*)&they, (socklen_t*)&sin_size );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
|
||||||
|
ConnectionTCPClient* conn = new ConnectionTCPClient( m_logInstance, inet_ntoa( they.sin_addr ),
|
||||||
|
ntohs( they.sin_port ) );
|
||||||
|
conn->setSocket( newfd );
|
||||||
|
m_connectionHandler->handleIncomingConnection( this, conn );
|
||||||
|
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTCPSERVER_H__
|
||||||
|
#define CONNECTIONTCPSERVER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectiontcpbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ConnectionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple listening TCP connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTCPServer : public ConnectionTCPBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPServer object.
|
||||||
|
* @param ch An ConnectionHandler-derived object that will handle incoming connections.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param ip The local IP address to listen on. This must @b not be a hostname.
|
||||||
|
* Leave this empty to listen on all local interfaces.
|
||||||
|
* @param port The port to listen on.
|
||||||
|
*/
|
||||||
|
ConnectionTCPServer( ConnectionHandler* ch, const LogSink& logInstance,
|
||||||
|
const std::string& ip, int port );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTCPServer();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function actually starts @c listening on the port given in the
|
||||||
|
* constructor.
|
||||||
|
*/
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTCPServer &operator=( const ConnectionTCPServer & );
|
||||||
|
|
||||||
|
ConnectionHandler* m_connectionHandler;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTCPSERVER_H__
|
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "connectiontls.h"
|
||||||
|
#include "tlsdefault.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTLS::ConnectionTLS( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log )
|
||||||
|
: ConnectionBase( cdh ),
|
||||||
|
m_connection( conn ), m_tls( 0 ), m_tlsHandler( 0 ),
|
||||||
|
m_log( log )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLS::ConnectionTLS( ConnectionBase* conn, const LogSink& log )
|
||||||
|
: ConnectionBase( 0 ),
|
||||||
|
m_connection( conn ), m_tls( 0 ), m_tlsHandler( 0 ), m_log( log )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLS::~ConnectionTLS()
|
||||||
|
{
|
||||||
|
delete m_connection;
|
||||||
|
delete m_tls;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::setConnectionImpl( ConnectionBase* connection )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( 0 );
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTLS::connect()
|
||||||
|
{
|
||||||
|
if( !m_connection )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
if( m_state == StateConnected )
|
||||||
|
return ConnNoError;
|
||||||
|
|
||||||
|
if( !m_tls )
|
||||||
|
m_tls = getTLSBase( this, m_connection->server() );
|
||||||
|
|
||||||
|
if( !m_tls )
|
||||||
|
return ConnTlsNotAvailable;
|
||||||
|
|
||||||
|
if( !m_tls->init( m_clientKey, m_clientCerts, m_cacerts ) )
|
||||||
|
return ConnTlsFailed;
|
||||||
|
|
||||||
|
// m_tls->setCACerts( m_cacerts );
|
||||||
|
// m_tls->setClientCert( m_clientKey, m_clientCerts );
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
|
||||||
|
if( m_connection->state() != StateConnected )
|
||||||
|
return m_connection->connect();
|
||||||
|
|
||||||
|
if( m_tls->handshake() )
|
||||||
|
return ConnNoError;
|
||||||
|
else
|
||||||
|
return ConnTlsFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTLS::recv( int timeout )
|
||||||
|
{
|
||||||
|
if( m_connection->state() == StateConnected )
|
||||||
|
{
|
||||||
|
return m_connection->recv( timeout );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.log( LogLevelWarning, LogAreaClassConnectionTLS,
|
||||||
|
"Attempt to receive data on a connection that is not connected (or is connecting)" );
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionTLS::send( const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_state != StateConnected )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_tls->encrypt( data );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTLS::receive()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->receive();
|
||||||
|
else
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::disconnect()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->disconnect();
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::cleanup()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->cleanup();
|
||||||
|
if( m_tls )
|
||||||
|
m_tls->cleanup();
|
||||||
|
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::getStatistics( long int& totalIn, long int& totalOut )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->getStatistics( totalIn, totalOut );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTLS::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* newConn = 0;
|
||||||
|
if( m_connection )
|
||||||
|
newConn = m_connection->newInstance();
|
||||||
|
return new ConnectionTLS( m_handler, newConn, m_log );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_tls )
|
||||||
|
m_tls->decrypt( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_tls )
|
||||||
|
m_tls->handshake();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
|
||||||
|
{
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleEncryptedData( const TLSBase* /*tls*/, const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->send( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleDecryptedData( const TLSBase* /*tls*/, const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleReceivedData( this, data );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.log( LogLevelDebug, LogAreaClassConnectionTLS, "Data received and decrypted but no handler" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleHandshakeResult( const TLSBase* tls, bool success, CertInfo& certinfo )
|
||||||
|
{
|
||||||
|
if( success )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_log.log( LogLevelDebug, LogAreaClassConnectionTLS, "TLS handshake succeeded" );
|
||||||
|
if( m_tlsHandler )
|
||||||
|
m_tlsHandler->handleHandshakeResult( tls, success, certinfo );
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_log.log( LogLevelWarning, LogAreaClassConnectionTLS, "TLS handshake failed" );
|
||||||
|
if( m_tlsHandler )
|
||||||
|
m_tlsHandler->handleHandshakeResult( tls, success, certinfo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTLS_H__
|
||||||
|
#define CONNECTIONTLS_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "tlsdefault.h"
|
||||||
|
#include "connectiondatahandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a TLS/SSL connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this function directly. However,
|
||||||
|
* you can use it to connect to the legacy Jabber SSL port,
|
||||||
|
* 5223.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* @code
|
||||||
|
* Client *c = new Client( ... );
|
||||||
|
* c->setConnectionImpl( new ConnectionTLS( c,
|
||||||
|
* new ConnectionTCP( c->logInstance(), server, 5223 ),
|
||||||
|
* c->logInstance()) );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Due to the need for handshaking data to be sent/received before the connection is fully
|
||||||
|
* established, be sure not to use the connection until ConnectionDataHandler::handleConnect()
|
||||||
|
* of the specified ConnectionDataHandler is called.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @author Matthew Wild <mwild1@gmail.com>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GLOOX_API ConnectionTLS : public TLSHandler, public ConnectionBase, public ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLS object.
|
||||||
|
* @param cdh The ConnectionDataHandler that will be notified of events from this connection
|
||||||
|
* @param conn A transport connection. It should be configured to connect to
|
||||||
|
* the server and port you wish to make the encrypted connection to.
|
||||||
|
* ConnectionTLS will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLS( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLS object.
|
||||||
|
* @param conn A transport connection. It should be configured to connect to
|
||||||
|
* the server and port you wish to make the encrypted connection to.
|
||||||
|
* ConnectionTLS will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLS( ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTLS();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set a number of trusted root CA certificates which shall be
|
||||||
|
* used to verify a servers certificate.
|
||||||
|
* @param cacerts A list of absolute paths to CA root certificate files in PEM format.
|
||||||
|
* @note This function is a wrapper for TLSBase::setCACerts().
|
||||||
|
*/
|
||||||
|
void setCACerts( const StringList& cacerts )
|
||||||
|
{
|
||||||
|
m_cacerts = cacerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to retrieve certificate and connection info of a encrypted connection.
|
||||||
|
* @return Certificate information.
|
||||||
|
* @note This funcztion is a wrapper around TLSBase::fetchTLSInfo().
|
||||||
|
*/
|
||||||
|
const CertInfo& fetchTLSInfo() const { return m_certInfo; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the user's certificate and private key. The certificate will
|
||||||
|
* be presented to the server upon request and can be used for SASL EXTERNAL authentication.
|
||||||
|
* The user's certificate file should be a bundle of more than one certificate in PEM format.
|
||||||
|
* The first one in the file should be the user's certificate, each cert following that one
|
||||||
|
* should have signed the previous one.
|
||||||
|
* @note These certificates are not necessarily the same as those used to verify the server's
|
||||||
|
* certificate.
|
||||||
|
* @param clientKey The absolute path to the user's private key in PEM format.
|
||||||
|
* @param clientCerts A path to a certificate bundle in PEM format.
|
||||||
|
* @note This function is a wrapper around TLSBase::setClientCert().
|
||||||
|
*/
|
||||||
|
void setClientCert( const std::string& clientKey, const std::string& clientCerts )
|
||||||
|
{
|
||||||
|
m_clientKey = clientKey;
|
||||||
|
m_clientCerts = clientCerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the transport connection.
|
||||||
|
* @param connection The transport connection to use.
|
||||||
|
*/
|
||||||
|
void setConnectionImpl( ConnectionBase* connection );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an TLSHandler derived object. Only the handleHandshakeResult()
|
||||||
|
* function will be used after a handshake took place.
|
||||||
|
* You can review certificate info there.
|
||||||
|
* @param th The TLSHandler to register.
|
||||||
|
* @note If no handler is set, ConnectionTLS will accept
|
||||||
|
* any certificate and continue with the connection.
|
||||||
|
*/
|
||||||
|
void registerTLSHandler( TLSHandler* th ) { m_tlsHandler = th; }
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int& totalIn, long int& totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
// reimplemented from TLSHandler
|
||||||
|
virtual void handleEncryptedData( const TLSBase*, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from TLSHandler
|
||||||
|
virtual void handleDecryptedData( const TLSBase*, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from TLSHandler
|
||||||
|
virtual void handleHandshakeResult( const TLSBase* base, bool success, CertInfo& certinfo );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Returns a TLS object (client). Reimplement to change the
|
||||||
|
* type of the object.
|
||||||
|
* @return A TLS object.
|
||||||
|
*/
|
||||||
|
virtual TLSBase* getTLSBase( TLSHandler* th, const std::string server )
|
||||||
|
{
|
||||||
|
return new TLSDefault( th, server, TLSDefault::VerifyingClient );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* m_connection;
|
||||||
|
TLSBase* m_tls;
|
||||||
|
TLSHandler* m_tlsHandler;
|
||||||
|
CertInfo m_certInfo;
|
||||||
|
const LogSink& m_log;
|
||||||
|
StringList m_cacerts;
|
||||||
|
std::string m_clientCerts;
|
||||||
|
std::string m_clientKey;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTLS& operator=( const ConnectionTLS& );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTLS_H__
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "connectiontlsserver.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTLSServer::ConnectionTLSServer( ConnectionDataHandler* cdh, ConnectionBase* conn,
|
||||||
|
const LogSink& log )
|
||||||
|
: ConnectionTLS( cdh, conn, log )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLSServer::ConnectionTLSServer( ConnectionBase* conn, const LogSink& log )
|
||||||
|
: ConnectionTLS( conn, log )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLSServer::~ConnectionTLSServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TLSBase* ConnectionTLSServer::getTLSBase( TLSHandler* th, const std::string server )
|
||||||
|
{
|
||||||
|
return new TLSDefault( th, server, TLSDefault::VerifyingServer );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTLSServer::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* newConn = 0;
|
||||||
|
if( m_connection )
|
||||||
|
newConn = m_connection->newInstance();
|
||||||
|
return new ConnectionTLSServer( m_handler, newConn, m_log );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTLSSERVER_H__
|
||||||
|
#define CONNECTIONTLSSERVER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "connectiontls.h"
|
||||||
|
#include "tlsdefault.h"
|
||||||
|
#include "tlshandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ConnectionDataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of the server-side of a TLS/SSL connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTLSServer : public ConnectionTLS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLSServer object.
|
||||||
|
* @param cdh The ConnectionDataHandler that will be notified of events from this connection
|
||||||
|
* @param conn A transport connection. It should be an established connection from
|
||||||
|
* a client that is about to perform a TLS handshake.
|
||||||
|
* ConnectionTLSServer will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLSServer( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLSServer object.
|
||||||
|
* @param conn A transport connection. It should be an established connection from
|
||||||
|
* a client that is about to perform a TLS handshake.
|
||||||
|
* ConnectionTLSServer will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLSServer( ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTLSServer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a TLS server.
|
||||||
|
* @return A TLS server.
|
||||||
|
*/
|
||||||
|
virtual TLSBase* getTLSBase( TLSHandler* th, const std::string server );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionTLS
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTLSServer& operator=( const ConnectionTLSServer& );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTLSSERVER_H__
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "dataformfield.h"
|
||||||
|
#include "dataformitem.h"
|
||||||
|
#include "dataformreported.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataForm::DataForm( FormType type, const StringList& instructions, const std::string& title )
|
||||||
|
: StanzaExtension( ExtDataForm ),
|
||||||
|
m_type( type ), m_instructions( instructions ), m_title( title ), m_reported( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::DataForm( FormType type, const std::string& title )
|
||||||
|
: StanzaExtension( ExtDataForm ),
|
||||||
|
m_type( type ), m_title( title ), m_reported( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::DataForm( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDataForm ),
|
||||||
|
m_type( TypeInvalid ), m_reported( 0 )
|
||||||
|
{
|
||||||
|
parse( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::DataForm( const DataForm& form )
|
||||||
|
: StanzaExtension( ExtDataForm ), DataFormFieldContainer( form ),
|
||||||
|
m_type( form.m_type ), m_instructions( form.m_instructions ),
|
||||||
|
m_title( form.m_title ), m_reported( form.m_reported ? new DataFormReported( form.m_reported->tag() ) : 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::~DataForm()
|
||||||
|
{
|
||||||
|
util::clearList( m_items );
|
||||||
|
delete m_reported;
|
||||||
|
m_reported = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* dfTypeValues[] =
|
||||||
|
{
|
||||||
|
"form", "submit", "cancel", "result"
|
||||||
|
};
|
||||||
|
|
||||||
|
bool DataForm::parse( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( !tag || tag->xmlns() != XMLNS_X_DATA || tag->name() != "x" )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::string& type = tag->findAttribute( TYPE );
|
||||||
|
if( type.empty() )
|
||||||
|
m_type = TypeForm;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_type = (FormType)util::lookup( type, dfTypeValues );
|
||||||
|
if( m_type == TypeInvalid )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "title" )
|
||||||
|
m_title = (*it)->cdata();
|
||||||
|
else if( (*it)->name() == "instructions" )
|
||||||
|
m_instructions.push_back( (*it)->cdata() );
|
||||||
|
else if( (*it)->name() == "field" )
|
||||||
|
m_fields.push_back( new DataFormField( (*it) ) );
|
||||||
|
else if( (*it)->name() == "reported" )
|
||||||
|
{
|
||||||
|
if( m_reported == NULL )
|
||||||
|
m_reported = new DataFormReported( (*it) );
|
||||||
|
// else - Invalid data form - only one "reported" is allowed
|
||||||
|
}
|
||||||
|
else if( (*it)->name() == "item" )
|
||||||
|
m_items.push_back( new DataFormItem( (*it) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DataForm::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/x[@xmlns='" + XMLNS_X_DATA + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataForm::tag() const
|
||||||
|
{
|
||||||
|
if( m_type == TypeInvalid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* x = new Tag( "x" );
|
||||||
|
x->setXmlns( XMLNS_X_DATA );
|
||||||
|
x->addAttribute( TYPE, util::lookup( m_type, dfTypeValues ) );
|
||||||
|
if( !m_title.empty() )
|
||||||
|
new Tag( x, "title", m_title );
|
||||||
|
|
||||||
|
StringList::const_iterator it_i = m_instructions.begin();
|
||||||
|
for( ; it_i != m_instructions.end(); ++it_i )
|
||||||
|
new Tag( x, "instructions", (*it_i) );
|
||||||
|
|
||||||
|
FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end(); ++it )
|
||||||
|
x->addChild( (*it)->tag() );
|
||||||
|
|
||||||
|
if( m_reported != NULL )
|
||||||
|
{
|
||||||
|
x->addChild( m_reported->tag() );
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemList::const_iterator iti = m_items.begin();
|
||||||
|
for( ; iti != m_items.end(); ++iti )
|
||||||
|
x->addChild( (*iti)->tag() );
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORM_H__
|
||||||
|
#define DATAFORM_H__
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
class DataFormItem;
|
||||||
|
class DataFormReported;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the possible Form Types.
|
||||||
|
*/
|
||||||
|
enum FormType
|
||||||
|
{
|
||||||
|
TypeForm, /**< The forms-processing entity is asking the forms-submitting
|
||||||
|
* entity to complete a form. */
|
||||||
|
TypeSubmit, /**< The forms-submitting entity is submitting data to the
|
||||||
|
* forms-processing entity. */
|
||||||
|
TypeCancel, /**< The forms-submitting entity has cancelled submission of data
|
||||||
|
* to the forms-processing entity. */
|
||||||
|
TypeResult, /**< The forms-processing entity is returning data (e.g., search
|
||||||
|
* results) to the forms-submitting entity, or the data is a
|
||||||
|
* generic data set. */
|
||||||
|
TypeInvalid /**< The form is invalid. Only possible if the form was created
|
||||||
|
* from an Tag which doesn't correctly describe a Data Form. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a XEP-0004 Data Form.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataForm : public StanzaExtension, public DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* A list of DataFormItems.
|
||||||
|
*/
|
||||||
|
typedef std::list<DataFormItem*> ItemList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new, empty form.
|
||||||
|
* @param type The form type.
|
||||||
|
* @param instructions Natural-language instructions for filling out the form. Should not contain
|
||||||
|
* newlines (\\n, \\r).
|
||||||
|
* @param title The natural-language title of the form. Should not contain newlines (\\n, \\r).
|
||||||
|
*/
|
||||||
|
DataForm( FormType type, const StringList& instructions, const std::string& title = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new, empty form without any instructions or title set. Probably best suited for
|
||||||
|
* result forms.
|
||||||
|
* @param type The form type.
|
||||||
|
* @param title The natural-language title of the form. Should not contain newlines (\\n, \\r).
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
DataForm( FormType type, const std::string& title = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataForm from an existing Tag/XML representation.
|
||||||
|
* @param tag The existing form to parse.
|
||||||
|
*/
|
||||||
|
DataForm( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DataForm, copying the given one.
|
||||||
|
* @param form The form to copy.
|
||||||
|
*/
|
||||||
|
DataForm( const DataForm& form );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataForm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the title of the form.
|
||||||
|
* @return The title of the form.
|
||||||
|
*/
|
||||||
|
const std::string& title() const { return m_title; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the title of the form.
|
||||||
|
* @param title The new title of the form.
|
||||||
|
* @note The title should not contain newlines (\\n, \\r).
|
||||||
|
*/
|
||||||
|
void setTitle( const std::string& title ) { m_title = title; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the natural-language instructions for the form.
|
||||||
|
* @return The fill-in instructions for the form.
|
||||||
|
*/
|
||||||
|
const StringList& instructions() const { return m_instructions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set natural-language instructions for the form.
|
||||||
|
* @param instructions The instructions for the form.
|
||||||
|
* @note The instructions should not contain newlines (\\n, \\r). Instead, every line should be an
|
||||||
|
* element of the StringMap. This allows for platform dependent newline handling on the target
|
||||||
|
* platform.
|
||||||
|
*/
|
||||||
|
void setInstructions( const StringList& instructions ) { m_instructions = instructions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reported field list in a DataForm.
|
||||||
|
* @return The reported section, containing 0..n fields.
|
||||||
|
*/
|
||||||
|
const DataFormReported* reported() const { return m_reported; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of items in a DataForm.
|
||||||
|
* @return A list of items.
|
||||||
|
*/
|
||||||
|
const ItemList& items() const { return m_items; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the form's type.
|
||||||
|
* @return The form's type.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
FormType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the form's type.
|
||||||
|
* @param type The form's new type.
|
||||||
|
*/
|
||||||
|
void setType( FormType type ) { m_type = type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given Tag and creates an appropriate DataForm representation.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
* @return @b True on success, @b false otherwise.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
bool parse( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts to @b true if the DataForm is valid, @b false otherwise.
|
||||||
|
*/
|
||||||
|
operator bool() const { return m_type != TypeInvalid; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new DataForm( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new DataForm( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FormType m_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringList m_instructions;
|
||||||
|
|
||||||
|
std::string m_title;
|
||||||
|
DataFormReported* m_reported;
|
||||||
|
ItemList m_items;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORM_H__
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dataformfield.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* fieldTypeValues[] =
|
||||||
|
{
|
||||||
|
"boolean", "fixed", "hidden", "jid-multi", "jid-single",
|
||||||
|
"list-multi", "list-single", "text-multi", "text-private", "text-single", ""
|
||||||
|
};
|
||||||
|
|
||||||
|
DataFormField::DataFormField( FieldType type )
|
||||||
|
: m_type( type ), m_required( false )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField::DataFormField( const std::string& name, const std::string& value,
|
||||||
|
const std::string& label, FieldType type )
|
||||||
|
: m_type( type ), m_name( name ), m_label( label ), m_required( false )
|
||||||
|
{
|
||||||
|
m_values.push_back( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField::DataFormField( const Tag* tag )
|
||||||
|
: m_type( TypeInvalid ), m_required( false )
|
||||||
|
{
|
||||||
|
if( !tag )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string& type = tag->findAttribute( TYPE );
|
||||||
|
if( type.empty() )
|
||||||
|
{
|
||||||
|
if( !tag->name().empty() )
|
||||||
|
m_type = TypeNone;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_type = (FieldType)util::lookup( type, fieldTypeValues );
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "var" ) )
|
||||||
|
m_name = tag->findAttribute( "var" );
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "label" ) )
|
||||||
|
m_label = tag->findAttribute( "label" );
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "desc" )
|
||||||
|
m_desc = (*it)->cdata();
|
||||||
|
else if( (*it)->name() == "required" )
|
||||||
|
m_required = true;
|
||||||
|
else if( (*it)->name() == "value" )
|
||||||
|
{
|
||||||
|
if( m_type == TypeTextMulti || m_type == TypeListMulti || m_type == TypeJidMulti )
|
||||||
|
addValue( (*it)->cdata() );
|
||||||
|
else
|
||||||
|
setValue( (*it)->cdata() );
|
||||||
|
}
|
||||||
|
else if( (*it)->name() == "option" )
|
||||||
|
{
|
||||||
|
Tag* v = (*it)->findChild( "value" );
|
||||||
|
if( v )
|
||||||
|
m_options.insert( std::make_pair( (*it)->findAttribute( "label" ), v->cdata() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField::~DataFormField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataFormField::tag() const
|
||||||
|
{
|
||||||
|
if( m_type == TypeInvalid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* field = new Tag( "field" );
|
||||||
|
field->addAttribute( TYPE, util::lookup( m_type, fieldTypeValues ) );
|
||||||
|
field->addAttribute( "var", m_name );
|
||||||
|
field->addAttribute( "label", m_label );
|
||||||
|
if( m_required )
|
||||||
|
new Tag( field, "required" );
|
||||||
|
|
||||||
|
if( !m_desc.empty() )
|
||||||
|
new Tag( field, "desc", m_desc );
|
||||||
|
|
||||||
|
if( m_type == TypeListSingle || m_type == TypeListMulti )
|
||||||
|
{
|
||||||
|
StringMultiMap::const_iterator it = m_options.begin();
|
||||||
|
for( ; it != m_options.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* option = new Tag( field, "option", "label", (*it).first );
|
||||||
|
new Tag( option, "value", (*it).second );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( m_type == TypeBoolean )
|
||||||
|
{
|
||||||
|
if( m_values.size() == 0 || m_values.front() == "false" || m_values.front() == "0" )
|
||||||
|
new Tag( field, "value", "0" );
|
||||||
|
else
|
||||||
|
new Tag( field, "value", "1" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_type == TypeTextMulti || m_type == TypeListMulti || m_type == TypeJidMulti )
|
||||||
|
{
|
||||||
|
StringList::const_iterator it = m_values.begin();
|
||||||
|
for( ; it != m_values.end() ; ++it )
|
||||||
|
new Tag( field, "value", (*it) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_values.size() && !( m_type == TypeTextMulti || m_type == TypeListMulti
|
||||||
|
|| m_type == TypeBoolean || m_type == TypeJidMulti ) )
|
||||||
|
new Tag( field, "value", m_values.front() );
|
||||||
|
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMFIELD_H__
|
||||||
|
#define DATAFORMFIELD_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a single field in a XEP-0004 Data Form.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormField
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Describes the possible types of a Data Form Field.
|
||||||
|
*/
|
||||||
|
enum FieldType
|
||||||
|
{
|
||||||
|
TypeBoolean, /**< The field enables an entity to gather or provide an either-or
|
||||||
|
* choice between two options. The default value is "false". */
|
||||||
|
TypeFixed, /**< The field is intended for data description (e.g.,
|
||||||
|
* human-readable text such as "section" headers) rather than data
|
||||||
|
* gathering or provision. The <value/> child SHOULD NOT contain
|
||||||
|
* newlines (the \\n and \\r characters); instead an application SHOULD
|
||||||
|
* generate multiple fixed fields, each with one <value/> child. */
|
||||||
|
TypeHidden, /**< The field is not shown to the entity providing information, but
|
||||||
|
* instead is returned with the form. */
|
||||||
|
TypeJidMulti, /**< The field enables an entity to gather or provide multiple Jabber
|
||||||
|
* IDs.*/
|
||||||
|
TypeJidSingle, /**< The field enables an entity to gather or provide a single Jabber
|
||||||
|
* ID.*/
|
||||||
|
TypeListMulti, /**< The field enables an entity to gather or provide one or more options
|
||||||
|
* from among many. */
|
||||||
|
TypeListSingle, /**< The field enables an entity to gather or provide one option from
|
||||||
|
* among many. */
|
||||||
|
TypeTextMulti, /**< The field enables an entity to gather or provide multiple lines of
|
||||||
|
* text. */
|
||||||
|
TypeTextPrivate, /**< The field enables an entity to gather or provide a single line or
|
||||||
|
* word of text, which shall be obscured in an interface
|
||||||
|
* (e.g., *****). */
|
||||||
|
TypeTextSingle, /**< The field enables an entity to gather or provide a single line or
|
||||||
|
* word of text, which may be shown in an interface. This field type is
|
||||||
|
* the default and MUST be assumed if an entity receives a field type it
|
||||||
|
* does not understand.*/
|
||||||
|
TypeNone, /**< The field is child of either a <reported> or <item>
|
||||||
|
* element or has no type attribute. */
|
||||||
|
TypeInvalid /**< The field is invalid. Only possible if the field was created from
|
||||||
|
* a Tag not correctly describing a Data Form Field. */
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataForm field.
|
||||||
|
* @param type The type of the field. Default: text-single.
|
||||||
|
*/
|
||||||
|
DataFormField( FieldType type = TypeTextSingle );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataForm field and fills it with the given values.
|
||||||
|
* @param name The field's name (the value of the 'var' attribute).
|
||||||
|
* @param value The field's value.
|
||||||
|
* @param label The field's label.
|
||||||
|
* @param type The field's type.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
DataFormField( const std::string& name, const std::string& value = EmptyString,
|
||||||
|
const std::string& label = EmptyString, FieldType type = TypeTextSingle );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Data Form Field from an existing tag that describes a field.
|
||||||
|
* @param tag The tag to parse.
|
||||||
|
*/
|
||||||
|
DataFormField( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormField();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the optional values of a field.
|
||||||
|
* @return The options of a field.
|
||||||
|
*/
|
||||||
|
const StringMultiMap& options() const { return m_options; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to create a Tag representation of the form field. This is usually called by
|
||||||
|
* DataForm.
|
||||||
|
* @return A Tag hierarchically describing the form field, or NULL if the field is invalid (i.e.
|
||||||
|
* created from a Tag not correctly describing a Data Form Field).
|
||||||
|
*/
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the name of the field (the content of the 'var' attribute).
|
||||||
|
* @return The name of the field.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name (the content of the 'var' attribute) of the field. The name identifies the
|
||||||
|
* field uniquely in the form.
|
||||||
|
* @param name The new name of the field.
|
||||||
|
* @note Fields of type other than 'fixed' MUST have a name, if it is 'fixed', it MAY.
|
||||||
|
*/
|
||||||
|
void setName( const std::string& name ) { m_name = name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the optional values of the field. The key of the map
|
||||||
|
* will be used as the label of the option, while the value will be used as ... the
|
||||||
|
* value. ;)
|
||||||
|
* @param options The optional values of a list* or *multi type of field.
|
||||||
|
*/
|
||||||
|
void setOptions( const StringMultiMap& options ) { m_options = options; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single option to the list of options.
|
||||||
|
* @param label The label of the option.
|
||||||
|
* @param value The value of the option.
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
void addOption( const std::string& label, const std::string& value )
|
||||||
|
{ m_options.insert( std::make_pair( label, value ) ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to determine whether or not this field is required.
|
||||||
|
* @return Whether or not this field is required.
|
||||||
|
*/
|
||||||
|
bool required() const { return m_required; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this field to set this field to be required.
|
||||||
|
* @param required Whether or not this field is required.
|
||||||
|
*/
|
||||||
|
void setRequired( bool required ) { m_required = required; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the describing label of this field.
|
||||||
|
* @return The describing label of this field.
|
||||||
|
*/
|
||||||
|
const std::string& label() const { return m_label; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the describing label of this field.
|
||||||
|
* @param label The describing label of this field.
|
||||||
|
*/
|
||||||
|
void setLabel( const std::string& label ) { m_label = label; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the description of this field.
|
||||||
|
* @return The description of this field
|
||||||
|
*/
|
||||||
|
const std::string& description() const { return m_desc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the description of this field.
|
||||||
|
* @param desc The description of this field.
|
||||||
|
*/
|
||||||
|
void setDescription( const std::string& desc ) { m_desc = desc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the value of this field.
|
||||||
|
* @return The value of this field.
|
||||||
|
*/
|
||||||
|
const std::string& value() const { return ( m_values.size() > 0 ) ? m_values.front() : EmptyString; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the value of this field.
|
||||||
|
* @param value The new value of this field.
|
||||||
|
*/
|
||||||
|
void setValue( const std::string& value ) { m_values.clear(); addValue( value ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the values of this field, if its of type 'text-multi'.
|
||||||
|
* @return The value of this field.
|
||||||
|
*/
|
||||||
|
const StringList& values() const { return m_values; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set multiple values of this field, if it is of type 'text-multi'. If its not,
|
||||||
|
* use @ref setValue() instead.
|
||||||
|
* @param values The new values of this field.
|
||||||
|
*/
|
||||||
|
void setValues( const StringList& values ) { m_values = values; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single value to the list of values.
|
||||||
|
* @param value The value to add.
|
||||||
|
*/
|
||||||
|
void addValue( const std::string& value ) { m_values.push_back( value ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the type of this field.
|
||||||
|
* @return The type of this field.
|
||||||
|
*/
|
||||||
|
FieldType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts to @b true if the FormBase is valid, @b false otherwise.
|
||||||
|
*/
|
||||||
|
operator bool() const { return m_type != TypeInvalid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FieldType m_type;
|
||||||
|
|
||||||
|
StringMultiMap m_options;
|
||||||
|
StringList m_values;
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_desc;
|
||||||
|
std::string m_label;
|
||||||
|
|
||||||
|
bool m_required;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMFIELD_H__
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataFormFieldContainer::DataFormFieldContainer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormFieldContainer::DataFormFieldContainer( const DataFormFieldContainer& dffc )
|
||||||
|
{
|
||||||
|
FieldList::const_iterator it = dffc.m_fields.begin();
|
||||||
|
for( ; it != dffc.m_fields.end(); ++it )
|
||||||
|
{
|
||||||
|
m_fields.push_back( new DataFormField( *(*it) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormFieldContainer::~DataFormFieldContainer()
|
||||||
|
{
|
||||||
|
util::clearList( m_fields );
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField* DataFormFieldContainer::field( const std::string& field ) const
|
||||||
|
{
|
||||||
|
FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end() && (*it)->name() != field; ++it )
|
||||||
|
;
|
||||||
|
return it != m_fields.end() ? (*it) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMFIELDCONTAINER_H__
|
||||||
|
#define DATAFORMFIELDCONTAINER_H__
|
||||||
|
|
||||||
|
#include "dataformfield.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class DataFormField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstract base class for a XEP-0004 Data Form.
|
||||||
|
*
|
||||||
|
* You shouldn't need to use this class directly. Use DataForm instead.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new FieldContainer.
|
||||||
|
*/
|
||||||
|
DataFormFieldContainer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new FieldContainer, copying all fields from the given FieldContainer.
|
||||||
|
* @param dffc The FieldContainer to copy.
|
||||||
|
*/
|
||||||
|
DataFormFieldContainer( const DataFormFieldContainer& dffc );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormFieldContainer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of XEP-0004 Data Form Fields.
|
||||||
|
*/
|
||||||
|
typedef std::list<DataFormField*> FieldList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to check whether this form contains a field with the given name.
|
||||||
|
* @param field The name of the field (the content of the 'var' attribute).
|
||||||
|
* @return Whether or not the form contains the named field.
|
||||||
|
*/
|
||||||
|
bool hasField( const std::string& field ) const
|
||||||
|
{ return DataFormFieldContainer::field( field ) != 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to fetch a pointer to a field of the form. If no such field exists,
|
||||||
|
* 0 is returned.
|
||||||
|
* @param field The name of the field (the content of the 'var' attribute).
|
||||||
|
* @return A copy of the field with the given name if it exists, 0 otherwise.
|
||||||
|
*/
|
||||||
|
DataFormField* field( const std::string& field ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the list of fields of a form.
|
||||||
|
* @return The list of fields the form contains.
|
||||||
|
*/
|
||||||
|
FieldList& fields() { return m_fields; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the const list of fields of a form.
|
||||||
|
* @return The const list of fields the form contains.
|
||||||
|
*/
|
||||||
|
const FieldList& fields() const { return m_fields; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the fields the form contains.
|
||||||
|
* @param fields The list of fields.
|
||||||
|
* @note Any previously set fields will be deleted. Always set all fields, not a delta.
|
||||||
|
*/
|
||||||
|
virtual void setFields( FieldList& fields ) { m_fields = fields; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to add a single field to the list of existing fields.
|
||||||
|
* @param field The field to add.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
virtual void addField( DataFormField* field ) { m_fields.push_back( field ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single new Field and returns a pointer to that field.
|
||||||
|
* @param type The field's type.
|
||||||
|
* @param name The field's name (the value of the 'var' attribute).
|
||||||
|
* @param value The field's value.
|
||||||
|
* @param label The field's label.
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
DataFormField* addField( DataFormField::FieldType type, const std::string& name,
|
||||||
|
const std::string& value = EmptyString,
|
||||||
|
const std::string& label = EmptyString )
|
||||||
|
{
|
||||||
|
DataFormField* field = new DataFormField( name, value, label, type );
|
||||||
|
m_fields.push_back( field );
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FieldList m_fields;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMFIELDCONTAINER_H__
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataformitem.h"
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataFormItem::DataFormItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormItem::DataFormItem( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "item" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const TagList &l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
DataFormField* f = new DataFormField( (*it) );
|
||||||
|
m_fields.push_back( f );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormItem::~DataFormItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataFormItem::tag() const
|
||||||
|
{
|
||||||
|
Tag* i = new Tag ( "item" );
|
||||||
|
DataFormFieldContainer::FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end(); ++it )
|
||||||
|
{
|
||||||
|
i->addChild( (*it)->tag() );
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMITEM_H__
|
||||||
|
#define DATAFORMITEM_H__
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of an <item> element in a XEP-0004 Data Form of type result.
|
||||||
|
*
|
||||||
|
* There are some constraints regarding usage of this element you should be aware of. Check XEP-0004
|
||||||
|
* section 3.4. This class does not enforce correct usage at this point.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormItem : public DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an empty 'item' element you can add fields to.
|
||||||
|
*/
|
||||||
|
DataFormItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a 'item' element and fills it with the 'field' elements contained in the given Tag.
|
||||||
|
* The Tag's root element must be a 'item' element. Its child element should be 'field' elements.
|
||||||
|
* @param tag The tag to read the 'field' elements from.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
DataFormItem( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of the current object.
|
||||||
|
* @return A Tag representation of the current object.
|
||||||
|
*/
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMITEM_H__
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataformreported.h"
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataFormReported::DataFormReported()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormReported::DataFormReported( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "reported" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const TagList &l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
DataFormField* f = new DataFormField( (*it) );
|
||||||
|
m_fields.push_back( f );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormReported::~DataFormReported()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataFormReported::tag() const
|
||||||
|
{
|
||||||
|
Tag* r = new Tag ( "reported" );
|
||||||
|
DataFormFieldContainer::FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end(); ++it )
|
||||||
|
{
|
||||||
|
r->addChild( (*it)->tag() );
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMREPORTED_H__
|
||||||
|
#define DATAFORMREPORTED_H__
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a <reported> element in a XEP-0004 Data Form of type result.
|
||||||
|
*
|
||||||
|
* There are some constraints regarding usage of this element you should be aware of. Check XEP-0004
|
||||||
|
* section 3.4. This class does not enforce correct usage at this point.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormReported : public DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an empty 'reported' element you can add fields to.
|
||||||
|
*/
|
||||||
|
DataFormReported();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a 'reported' element and fills it with the 'field' elements contained in the given Tag.
|
||||||
|
* The Tag's root element must be a 'reported' element. Its child element should be 'field' elements.
|
||||||
|
* @param tag The tag to read the 'field' elements from.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
DataFormReported( Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormReported();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of the current object.
|
||||||
|
* @return A Tag representation of the current object.
|
||||||
|
*/
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMREPORTED_H__
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "delayeddelivery.h"
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DelayedDelivery::DelayedDelivery( const JID& from, const std::string stamp, const std::string& reason )
|
||||||
|
: StanzaExtension( ExtDelay ), m_from( from ), m_stamp( stamp ), m_reason( reason ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( !m_stamp.empty() )
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DelayedDelivery::DelayedDelivery( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDelay ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( !tag || !tag->hasAttribute( "stamp" ) )
|
||||||
|
return;
|
||||||
|
if( !( tag->name() == "x" && tag->hasAttribute( XMLNS, XMLNS_X_DELAY ) ) )
|
||||||
|
if( !( tag->name() == "delay" && tag->hasAttribute( XMLNS, XMLNS_DELAY ) ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_reason = tag->cdata();
|
||||||
|
m_stamp = tag->findAttribute( "stamp" );
|
||||||
|
m_from = tag->findAttribute( "from" );
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayedDelivery::~DelayedDelivery()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DelayedDelivery::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter =
|
||||||
|
"/presence/delay[@xmlns='" + XMLNS_DELAY + "']"
|
||||||
|
"|/message/delay[@xmlns='" + XMLNS_DELAY + "']"
|
||||||
|
"|/presence/x[@xmlns='" + XMLNS_X_DELAY + "']"
|
||||||
|
"|/message/x[@xmlns='" + XMLNS_X_DELAY + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DelayedDelivery::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( "delay" );
|
||||||
|
t->addAttribute( XMLNS, XMLNS_DELAY );
|
||||||
|
if( m_from )
|
||||||
|
t->addAttribute( "from", m_from.full() );
|
||||||
|
if( !m_stamp.empty() )
|
||||||
|
t->addAttribute( "stamp", m_stamp );
|
||||||
|
if( !m_reason.empty() )
|
||||||
|
t->setCData( m_reason );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DELAYEDDELIVERY_H__
|
||||||
|
#define DELAYEDDELIVERY_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "jid.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0203 (Delayed Delivery).
|
||||||
|
*
|
||||||
|
* The class also implements the deprecated XEP-0091 (Delayed Delivery) in a read-only fashion.
|
||||||
|
* It understands both XEP formats for input, but any output will conform to XEP-0203.
|
||||||
|
*
|
||||||
|
* XEP Version: 0.1
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API DelayedDelivery : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object and fills it according to the parameters.
|
||||||
|
* @param from The JID of the original sender or the entity that delayed the sending.
|
||||||
|
* @param stamp The datetime stamp of the original send.
|
||||||
|
* @param reason An optional natural language reason for the delay.
|
||||||
|
*/
|
||||||
|
DelayedDelivery( const JID& from, const std::string stamp,
|
||||||
|
const std::string& reason = "" );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
DelayedDelivery( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DelayedDelivery();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the datetime when the stanza was originally sent.
|
||||||
|
* The format MUST adhere to the dateTime format specified in XEP-0082 and MUST
|
||||||
|
* be expressed in UTC.
|
||||||
|
* @return The original datetime.
|
||||||
|
*/
|
||||||
|
const std::string& stamp() const { return m_stamp; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the original datetime.
|
||||||
|
* @param stamp The original datetime.
|
||||||
|
*/
|
||||||
|
void setStamp( const std::string& stamp ) { m_stamp = stamp; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JID of the original sender of the stanza or the entity that
|
||||||
|
* delayed the sending.
|
||||||
|
* The format MUST adhere to the dateTime format specified in XEP-0082 and MUST
|
||||||
|
* be expressed in UTC.
|
||||||
|
* @return The JID.
|
||||||
|
*/
|
||||||
|
const JID& from() const { return m_from; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the JID of the origianl sender or the entity that delayed the sending.
|
||||||
|
* @param from The JID.
|
||||||
|
*/
|
||||||
|
void setFrom( const JID& from ) { m_from = from; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a natural language reason for the delay.
|
||||||
|
* @return A natural language reason for the delay.
|
||||||
|
*/
|
||||||
|
const std::string& reason() const { return m_reason; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the reason for the delay.
|
||||||
|
* @param reason The reason for the delay.
|
||||||
|
*/
|
||||||
|
void setReason( const std::string& reason ) { m_reason = reason; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new DelayedDelivery( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new DelayedDelivery( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
JID m_from;
|
||||||
|
std::string m_stamp;
|
||||||
|
std::string m_reason;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DELAYEDDELIVERY_H__
|
|
@ -0,0 +1,535 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "disco.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "disconodehandler.h"
|
||||||
|
#include "softwareversion.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
// ---- Disco::Identity ----
|
||||||
|
Disco::Identity::Identity( const std::string& category,
|
||||||
|
const std::string& type,
|
||||||
|
const std::string& name )
|
||||||
|
: m_category( category ), m_type( type ), m_name( name )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Identity::Identity( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "identity" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_category = tag->findAttribute( "category" );
|
||||||
|
m_type = tag->findAttribute( "type" );
|
||||||
|
m_name = tag->findAttribute( "name" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Identity::Identity( const Identity& id )
|
||||||
|
: m_category( id.m_category ), m_type( id.m_type ), m_name( id.m_name )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Identity::~Identity()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Identity::tag() const
|
||||||
|
{
|
||||||
|
if( m_category.empty() || m_type.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* i = new Tag( "identity" );
|
||||||
|
i->addAttribute( "category", m_category );
|
||||||
|
i->addAttribute( "type", m_type );
|
||||||
|
|
||||||
|
if( !m_name.empty() )
|
||||||
|
i->addAttribute( "name", m_name );
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Identity ----
|
||||||
|
|
||||||
|
// ---- Disco::Info ----
|
||||||
|
Disco::Info::Info( const std::string& node, bool defaultFeatures )
|
||||||
|
: StanzaExtension( ExtDiscoInfo ), m_node( node ), m_form( 0 )
|
||||||
|
{
|
||||||
|
if( defaultFeatures )
|
||||||
|
{
|
||||||
|
m_features.push_back( XMLNS_DISCO_INFO );
|
||||||
|
m_features.push_back( XMLNS_DISCO_ITEMS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Info::Info( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDiscoInfo ), m_form( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_INFO )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
const std::string& name = (*it)->name();
|
||||||
|
if( name == "identity" )
|
||||||
|
m_identities.push_back( new Identity( (*it) ) );
|
||||||
|
else if( name == "feature" && (*it)->hasAttribute( "var" ) )
|
||||||
|
m_features.push_back( (*it)->findAttribute( "var" ) );
|
||||||
|
else if( !m_form && name == "x" && (*it)->xmlns() == XMLNS_X_DATA )
|
||||||
|
m_form = new DataForm( (*it) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Info::Info( const Info& info )
|
||||||
|
: StanzaExtension( ExtDiscoInfo ), m_node( info.m_node ), m_features( info.m_features ),
|
||||||
|
m_identities( info.m_identities ), m_form( info.m_form ? new DataForm( *(info.m_form) ) : 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Info::~Info()
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
util::clearList( m_identities );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::Info::setForm( DataForm* form )
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Disco::Info::hasFeature( const std::string& feature ) const
|
||||||
|
{
|
||||||
|
StringList::const_iterator it = m_features.begin();
|
||||||
|
for( ; it != m_features.end() && (*it) != feature; ++it )
|
||||||
|
;
|
||||||
|
return it != m_features.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Disco::Info::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_INFO + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Info::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_INFO );
|
||||||
|
|
||||||
|
if( !m_node.empty() )
|
||||||
|
t->addAttribute( "node", m_node );
|
||||||
|
|
||||||
|
IdentityList::const_iterator it_i = m_identities.begin();
|
||||||
|
for( ; it_i != m_identities.end(); ++it_i )
|
||||||
|
t->addChild( (*it_i)->tag() );
|
||||||
|
|
||||||
|
StringList::const_iterator it_f = m_features.begin();
|
||||||
|
for( ; it_f != m_features.end(); ++it_f )
|
||||||
|
new Tag( t, "feature", "var", (*it_f) );
|
||||||
|
|
||||||
|
if( m_form )
|
||||||
|
t->addChild( m_form->tag() );
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Info ----
|
||||||
|
|
||||||
|
// ---- Disco::Item ----
|
||||||
|
Disco::Item::Item( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "item" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_jid = tag->findAttribute( "jid" );
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
m_name = tag->findAttribute( "name" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Item::tag() const
|
||||||
|
{
|
||||||
|
if( !m_jid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* i = new Tag( "item" );
|
||||||
|
i->addAttribute( "jid", m_jid.full() );
|
||||||
|
|
||||||
|
if( !m_node.empty() )
|
||||||
|
i->addAttribute( "node", m_node );
|
||||||
|
if( !m_name.empty() )
|
||||||
|
i->addAttribute( "name", m_name );
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Item ----
|
||||||
|
|
||||||
|
// ---- Disco::Items ----
|
||||||
|
Disco::Items::Items( const std::string& node )
|
||||||
|
: StanzaExtension( ExtDiscoItems ), m_node( node )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Items::Items( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDiscoItems )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_ITEMS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
const std::string& name = (*it)->name();
|
||||||
|
if( name == "item" )
|
||||||
|
m_items.push_back( new Item( (*it) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Items::~Items()
|
||||||
|
{
|
||||||
|
util::clearList( m_items );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::Items::setItems( const ItemList& items )
|
||||||
|
{
|
||||||
|
util::clearList( m_items );
|
||||||
|
m_items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const std::string& Disco::Items::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_ITEMS + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Items::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_ITEMS );
|
||||||
|
|
||||||
|
if( !m_node.empty() )
|
||||||
|
t->addAttribute( "node", m_node );
|
||||||
|
|
||||||
|
ItemList::const_iterator it_i = m_items.begin();
|
||||||
|
for( ; it_i != m_items.end(); ++it_i )
|
||||||
|
t->addChild( (*it_i)->tag() );
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Items ----
|
||||||
|
|
||||||
|
// ---- Disco ----
|
||||||
|
Disco::Disco( ClientBase* parent )
|
||||||
|
: m_parent( parent ), m_form( 0 )
|
||||||
|
{
|
||||||
|
addFeature( XMLNS_VERSION );
|
||||||
|
// addFeature( XMLNS_DISCO_INFO ); //handled by Disco::Info now
|
||||||
|
// addFeature( XMLNS_DISCO_ITEMS ); //handled by Disco::Info now
|
||||||
|
if( m_parent )
|
||||||
|
{
|
||||||
|
m_parent->registerIqHandler( this, ExtDiscoInfo );
|
||||||
|
m_parent->registerIqHandler( this, ExtDiscoItems );
|
||||||
|
m_parent->registerIqHandler( this, ExtVersion );
|
||||||
|
m_parent->registerStanzaExtension( new Disco::Info() );
|
||||||
|
m_parent->registerStanzaExtension( new Disco::Items() );
|
||||||
|
m_parent->registerStanzaExtension( new SoftwareVersion() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::~Disco()
|
||||||
|
{
|
||||||
|
util::clearList( m_identities );
|
||||||
|
delete m_form;
|
||||||
|
|
||||||
|
if( m_parent )
|
||||||
|
{
|
||||||
|
m_parent->removeIqHandler( this, ExtDiscoInfo );
|
||||||
|
m_parent->removeIqHandler( this, ExtDiscoItems );
|
||||||
|
m_parent->removeIqHandler( this, ExtVersion );
|
||||||
|
m_parent->removeStanzaExtension( ExtDiscoInfo );
|
||||||
|
m_parent->removeStanzaExtension( ExtDiscoItems );
|
||||||
|
m_parent->removeStanzaExtension( ExtVersion );
|
||||||
|
m_parent->removeIDHandler( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::setForm( DataForm* form )
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Disco::handleIq( const IQ& iq )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Get:
|
||||||
|
{
|
||||||
|
IQ re( IQ::Result, iq.from(), iq.id() );
|
||||||
|
re.setFrom( iq.to() );
|
||||||
|
|
||||||
|
const SoftwareVersion* sv = iq.findExtension<SoftwareVersion>( ExtVersion );
|
||||||
|
if( sv )
|
||||||
|
{
|
||||||
|
re.addExtension( new SoftwareVersion( m_versionName, m_versionVersion, m_versionOs ) );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Info *info = iq.findExtension<Info>( ExtDiscoInfo );
|
||||||
|
if( info )
|
||||||
|
{
|
||||||
|
Info *i = new Info( EmptyString, true );
|
||||||
|
if( !info->node().empty() )
|
||||||
|
{
|
||||||
|
i->setNode( info->node() );
|
||||||
|
IdentityList identities;
|
||||||
|
StringList features;
|
||||||
|
DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( info->node() );
|
||||||
|
if( it == m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
delete i;
|
||||||
|
IQ re( IQ::Error, iq.from(), iq.id() );
|
||||||
|
re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
|
||||||
|
for( ; in != (*it).second.end(); ++in )
|
||||||
|
{
|
||||||
|
IdentityList il = (*in)->handleDiscoNodeIdentities( iq.from(), info->node() );
|
||||||
|
il.sort(); // needed on win32
|
||||||
|
identities.merge( il );
|
||||||
|
StringList fl = (*in)->handleDiscoNodeFeatures( iq.from(), info->node() );
|
||||||
|
fl.sort(); // needed on win32
|
||||||
|
features.merge( fl );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i->setIdentities( identities );
|
||||||
|
i->setFeatures( features );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IdentityList il;
|
||||||
|
IdentityList::const_iterator it = m_identities.begin();
|
||||||
|
for( ; it != m_identities.end(); ++it )
|
||||||
|
{
|
||||||
|
il.push_back( new Identity( *(*it) ) );
|
||||||
|
}
|
||||||
|
i->setIdentities( il );
|
||||||
|
i->setFeatures( m_features );
|
||||||
|
if( m_form )
|
||||||
|
i->setForm( new DataForm( *m_form ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
re.addExtension( i );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Items *items = iq.findExtension<Items>( ExtDiscoItems );
|
||||||
|
if( items )
|
||||||
|
{
|
||||||
|
Items *i = new Items( items->node() );
|
||||||
|
if( !items->node().empty() )
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( items->node() );
|
||||||
|
if( it == m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
delete i;
|
||||||
|
IQ re( IQ::Error, iq.from(), iq.id() );
|
||||||
|
re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ItemList itemlist;
|
||||||
|
DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
|
||||||
|
for( ; in != (*it).second.end(); ++in )
|
||||||
|
{
|
||||||
|
ItemList il = (*in)->handleDiscoNodeItems( iq.from(), iq.to(), items->node() );
|
||||||
|
il.sort(); // needed on win32
|
||||||
|
itemlist.merge( il );
|
||||||
|
}
|
||||||
|
i->setItems( itemlist );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
re.addExtension( i );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IQ::Set:
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
DiscoHandlerList::const_iterator it = m_discoHandlers.begin();
|
||||||
|
for( ; it != m_discoHandlers.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->handleDiscoSet( iq ) )
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::handleIqID( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
DiscoHandlerMap::iterator it = m_track.find( iq.id() );
|
||||||
|
if( it != m_track.end() && (*it).second.dh )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
switch( context )
|
||||||
|
{
|
||||||
|
case GetDiscoInfo:
|
||||||
|
{
|
||||||
|
const Info* di = iq.findExtension<Info>( ExtDiscoInfo );
|
||||||
|
if( di )
|
||||||
|
(*it).second.dh->handleDiscoInfo( iq.from(), *di, (*it).second.context );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GetDiscoItems:
|
||||||
|
{
|
||||||
|
const Items* di = iq.findExtension<Items>( ExtDiscoItems );
|
||||||
|
if( di )
|
||||||
|
(*it).second.dh->handleDiscoItems( iq.from(), *di, (*it).second.context );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IQ::Error:
|
||||||
|
{
|
||||||
|
(*it).second.dh->handleDiscoError( iq.from(), iq.error(), (*it).second.context );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_track.erase( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::getDisco( const JID& to, const std::string& node, DiscoHandler* dh, int context,
|
||||||
|
IdType idType, const std::string& tid )
|
||||||
|
{
|
||||||
|
const std::string& id = tid.empty() ? m_parent->getID() : tid;
|
||||||
|
|
||||||
|
IQ iq( IQ::Get, to, id );
|
||||||
|
if( idType == GetDiscoInfo )
|
||||||
|
iq.addExtension( new Info( node ) );
|
||||||
|
else
|
||||||
|
iq.addExtension( new Items( node ) );
|
||||||
|
|
||||||
|
DiscoHandlerContext ct;
|
||||||
|
ct.dh = dh;
|
||||||
|
ct.context = context;
|
||||||
|
m_track[id] = ct;
|
||||||
|
m_parent->send( iq, this, idType );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::setVersion( const std::string& name, const std::string& version, const std::string& os )
|
||||||
|
{
|
||||||
|
m_versionName = name;
|
||||||
|
m_versionVersion = version;
|
||||||
|
m_versionOs = os;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::setIdentity( const std::string& category, const std::string& type,
|
||||||
|
const std::string& name )
|
||||||
|
{
|
||||||
|
util::clearList( m_identities );
|
||||||
|
addIdentity( category, type, name );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::removeDiscoHandler( DiscoHandler* dh )
|
||||||
|
{
|
||||||
|
m_discoHandlers.remove( dh );
|
||||||
|
DiscoHandlerMap::iterator t;
|
||||||
|
DiscoHandlerMap::iterator it = m_track.begin();
|
||||||
|
while( it != m_track.end() )
|
||||||
|
{
|
||||||
|
t = it;
|
||||||
|
++it;
|
||||||
|
if( dh == (*t).second.dh )
|
||||||
|
{
|
||||||
|
m_track.erase( t );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::registerNodeHandler( DiscoNodeHandler* nh, const std::string& node )
|
||||||
|
{
|
||||||
|
m_nodeHandlers[node].push_back( nh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::removeNodeHandler( DiscoNodeHandler* nh, const std::string& node )
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerMap::iterator it = m_nodeHandlers.find( node );
|
||||||
|
if( it != m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
(*it).second.remove( nh );
|
||||||
|
if( (*it).second.empty() )
|
||||||
|
m_nodeHandlers.erase( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::removeNodeHandlers( DiscoNodeHandler* nh )
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerMap::iterator it = m_nodeHandlers.begin();
|
||||||
|
DiscoNodeHandlerMap::iterator it2;
|
||||||
|
while( it != m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
it2 = it++;
|
||||||
|
removeNodeHandler( nh, (*it2).first );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StringList Disco::features( bool defaultFeatures ) const
|
||||||
|
{
|
||||||
|
StringList f = m_features;
|
||||||
|
if( defaultFeatures )
|
||||||
|
{
|
||||||
|
f.push_back( XMLNS_DISCO_INFO );
|
||||||
|
f.push_back( XMLNS_DISCO_ITEMS );
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,636 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISCO_H__
|
||||||
|
#define DISCO_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "iqhandler.h"
|
||||||
|
#include "jid.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClientBase;
|
||||||
|
class DataForm;
|
||||||
|
class DiscoHandler;
|
||||||
|
class DiscoNodeHandler;
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class implements XEP-0030 (Service Discovery) and XEP-0092 (Software Version).
|
||||||
|
*
|
||||||
|
* ClientBase will automatically instantiate a Disco object. It can be used to
|
||||||
|
* announce special features of your client, or its version, or...
|
||||||
|
*
|
||||||
|
* XEP version: 2.2
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API Disco : public IqHandler
|
||||||
|
{
|
||||||
|
friend class ClientBase;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Identity; // declared below class Info
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of pointers to Identity objects. Used with Disco::Info.
|
||||||
|
*/
|
||||||
|
typedef std::list<Identity*> IdentityList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco Info element (from Service Discovery, XEP-0030)
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Info : public StanzaExtension
|
||||||
|
{
|
||||||
|
friend class Disco;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Returns the queried node identifier, if any.
|
||||||
|
* @return The node identifier. May be empty.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's supported features.
|
||||||
|
* @return A list of supported features/namespaces.
|
||||||
|
*/
|
||||||
|
const StringList& features() const { return m_features; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to check if the entity the Info came from supports agiven feature.
|
||||||
|
* @param feature The feature to check for.
|
||||||
|
* @return @b True if the entity announces support for the feature, @b false otherwise.
|
||||||
|
*/
|
||||||
|
bool hasFeature( const std::string& feature ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's identities.
|
||||||
|
* @return A list of pointers to Identity objects.
|
||||||
|
*/
|
||||||
|
const IdentityList& identities() const { return m_identities; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an optionally included data form. This is used by e.g. MUC (XEP-0045).
|
||||||
|
* @return An optional data form included in the disco#info. May be 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an optional DataForm, e.g. for XEP-0232. Only one form can be added
|
||||||
|
* at this point.
|
||||||
|
* @param form An optional DataForm to include in the Info reply.
|
||||||
|
* The form will be owned by and deleted on destruction of the Info object.
|
||||||
|
* @note If called more than once the previously set form will be deleted.
|
||||||
|
*/
|
||||||
|
void setForm( DataForm* form );
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Info( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Info( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef DISCO_INFO_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Creates a empty Info object, suitable for making disco#info requests.
|
||||||
|
* @param node The node identifier to query (optional).
|
||||||
|
* @param defaultFeatures Indicates whether or not the default features should be
|
||||||
|
* included in the Info. Should be @b false for requests, @b true for replies.
|
||||||
|
* Defaults to @b false.
|
||||||
|
*/
|
||||||
|
Info( const std::string& node = EmptyString, bool defaultFeatures = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Info object from the given Tag.
|
||||||
|
* @param tag A <query> tag in the disco#info namespace, (possibly) containing
|
||||||
|
* a disco#info reply.
|
||||||
|
*/
|
||||||
|
Info( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor.
|
||||||
|
* @param info An Info object to copy.
|
||||||
|
*/
|
||||||
|
Info( const Info& info );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Info();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current info node.
|
||||||
|
* @param node The info node.
|
||||||
|
*/
|
||||||
|
void setNode( const std::string& node ) { m_node = node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to set the entity's features.
|
||||||
|
* @param features A list of supported features/namespaces.
|
||||||
|
*/
|
||||||
|
void setFeatures( const StringList& features )
|
||||||
|
{
|
||||||
|
StringList fl( features );
|
||||||
|
fl.sort(); // needed on win32
|
||||||
|
m_features.merge( fl );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to set the entity's identities.
|
||||||
|
* @param identities A list of pointers to the entity's identities.
|
||||||
|
* @note The Identity objects pointed to will be owned by the Info object. The
|
||||||
|
* list should neither be used again nor should the Identity objects be deleted.
|
||||||
|
*/
|
||||||
|
void setIdentities( const IdentityList& identities ) { m_identities = identities; }
|
||||||
|
|
||||||
|
std::string m_node;
|
||||||
|
StringList m_features;
|
||||||
|
IdentityList m_identities;
|
||||||
|
DataForm* m_form;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco identity (Service Discovery, XEP-0030).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Identity
|
||||||
|
{
|
||||||
|
friend class Info;
|
||||||
|
friend class Disco;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a Disco Identity from a category, type and name.
|
||||||
|
* See http://www.xmpp.org/registrar/disco-categories.html for more info.
|
||||||
|
* @param category The identity's category.
|
||||||
|
* @param type The identity's type.
|
||||||
|
* @param name The identity's name.
|
||||||
|
*/
|
||||||
|
Identity( const std::string& category,
|
||||||
|
const std::string& type,
|
||||||
|
const std::string& name );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy Contructor.
|
||||||
|
* @param id An Identity to create a new Identity object from.
|
||||||
|
*/
|
||||||
|
Identity( const Identity& id );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Identity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity's category.
|
||||||
|
* @return The identity's category.
|
||||||
|
*/
|
||||||
|
const std::string& category() const { return m_category; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity's type.
|
||||||
|
* @return The identity's type.
|
||||||
|
*/
|
||||||
|
const std::string& type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity's name.
|
||||||
|
* @return The identity's name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of this identity.
|
||||||
|
* @return A Tag, or 0.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Creates a Disco Identity from the given Tag.
|
||||||
|
* @param tag A Tag representation of a disco identity.
|
||||||
|
*/
|
||||||
|
Identity( const Tag* tag );
|
||||||
|
|
||||||
|
std::string m_category; /**< The identity's category. */
|
||||||
|
std::string m_type; /**< The identity's type. */
|
||||||
|
std::string m_name; /**< The identity's name. */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Item; // declared below class Items
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of pointers to Item objects. Used with Disco::Items.
|
||||||
|
*/
|
||||||
|
typedef std::list<Item*> ItemList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco query element (from Service Discovery, XEP-0030)
|
||||||
|
* in the disco#items namespace, implemented as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Items : public StanzaExtension
|
||||||
|
{
|
||||||
|
friend class Disco;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// This needs to be public so one can proactively send a list of adhoc commands
|
||||||
|
// see XEP-0050
|
||||||
|
/**
|
||||||
|
* Creates an empty Items object, suitable for making disco#items requests.
|
||||||
|
* @param node The node identifier to query (optional).
|
||||||
|
*/
|
||||||
|
Items( const std::string& node = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Items();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to set the entity's/node's items.
|
||||||
|
* @param items A list of pointers to the entity's/node's items.
|
||||||
|
* @note The Item objects pointed to will be owned by the Items object. The
|
||||||
|
* list should neither be used again nor should the Item objects be deleted.
|
||||||
|
*/
|
||||||
|
void setItems( const ItemList& items );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the queried node identifier, if any.
|
||||||
|
* @return The node identifier. May be empty.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's/node's items.
|
||||||
|
* @return A list of pointers to Item objects.
|
||||||
|
*/
|
||||||
|
const ItemList& items() const { return m_items; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Items( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Items( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef DISCO_ITEMS_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Creates an Items object from the given Tag.
|
||||||
|
* @param tag A <query> tag in the disco#items namespace, (possibly) containing
|
||||||
|
* a disco#items reply.
|
||||||
|
*/
|
||||||
|
Items( const Tag* tag );
|
||||||
|
|
||||||
|
std::string m_node;
|
||||||
|
ItemList m_items;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco item (Service Discovery, XEP-0030).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Item
|
||||||
|
{
|
||||||
|
friend class Items;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a Disco Item from a JID, node and name.
|
||||||
|
* @param jid The item's JID.
|
||||||
|
* @param node The item's type.
|
||||||
|
* @param name The item's name.
|
||||||
|
*/
|
||||||
|
Item( const JID& jid,
|
||||||
|
const std::string& node,
|
||||||
|
const std::string& name )
|
||||||
|
: m_jid( jid ), m_node( node ), m_name( name ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Item() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item's JID.
|
||||||
|
* @return The item's JID.
|
||||||
|
*/
|
||||||
|
const JID& jid() const { return m_jid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item's node.
|
||||||
|
* @return The item's node.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item's name.
|
||||||
|
* @return The item's name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of this item.
|
||||||
|
* @return A Tag, or 0.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Creates a Disco Item from the given Tag.
|
||||||
|
* @param tag A Tag representation of a Disco item.
|
||||||
|
*/
|
||||||
|
Item( const Tag* tag );
|
||||||
|
|
||||||
|
JID m_jid; /**< The item's jid. */
|
||||||
|
std::string m_node; /**< The item's type. */
|
||||||
|
std::string m_name; /**< The item's name. */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a feature to the list of supported Jabber features.
|
||||||
|
* The list will be posted as an answer to IQ queries in the
|
||||||
|
* "http://jabber.org/protocol/disco#info" namespace.
|
||||||
|
* These IQ packets will also be forwarded to the
|
||||||
|
* application's IqHandler, if it listens to the @c disco\#info namespace.
|
||||||
|
* By default, disco(very) queries are handled by the library.
|
||||||
|
* By default, all supported, not disabled features are announced.
|
||||||
|
* @param feature A feature (namespace) the host app supports.
|
||||||
|
* @note Use this function for non-queryable features. For nodes that shall
|
||||||
|
* answer to @c disco\#info queries, use registerNodeHandler().
|
||||||
|
*/
|
||||||
|
void addFeature( const std::string& feature )
|
||||||
|
{ m_features.push_back( feature ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given feature from the list of advertised client features.
|
||||||
|
* @param feature The feature to remove.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
void removeFeature( const std::string& feature )
|
||||||
|
{ m_features.remove( feature ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you retrieve the features this Disco instance supports.
|
||||||
|
* @param defaultFeatures Include default features. Defaults to @b false.
|
||||||
|
* @return A list of supported features/namespaces.
|
||||||
|
*/
|
||||||
|
const StringList features( bool defaultFeatures = false ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the given JID for general infomation according to
|
||||||
|
* XEP-0030 (Service Discovery).
|
||||||
|
* To receive the results inherit from DiscoHandler and register with the Disco object.
|
||||||
|
* @param to The destination-JID of the query.
|
||||||
|
* @param node An optional node to query. Not inserted if empty.
|
||||||
|
* @param dh The DiscoHandler to notify about results.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @param tid An optional id that is going to be used as the IQ request's id. Only
|
||||||
|
* necessary if you need to know the request's id.
|
||||||
|
*/
|
||||||
|
void getDiscoInfo( const JID& to, const std::string& node, DiscoHandler* dh, int context,
|
||||||
|
const std::string& tid = EmptyString )
|
||||||
|
{ getDisco( to, node, dh, context, GetDiscoInfo, tid ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the given JID for its items according to
|
||||||
|
* XEP-0030 (Service Discovery).
|
||||||
|
* To receive the results inherit from DiscoHandler and register with the Disco object.
|
||||||
|
* @param to The destination-JID of the query.
|
||||||
|
* @param node An optional node to query. Not inserted if empty.
|
||||||
|
* @param dh The DiscoHandler to notify about results.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @param tid An optional id that is going to be used as the IQ request's id. Only
|
||||||
|
* necessary if you need to know the request's id.
|
||||||
|
*/
|
||||||
|
void getDiscoItems( const JID& to, const std::string& node, DiscoHandler* dh, int context,
|
||||||
|
const std::string& tid = EmptyString )
|
||||||
|
{ getDisco( to, node, dh, context, GetDiscoItems, tid ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the version of the host application using this library.
|
||||||
|
* The library takes care of jabber:iq:version requests. These
|
||||||
|
* IQ packets will not be forwarded to the IqHandlers.
|
||||||
|
* @param name The name to be returned to inquireing clients.
|
||||||
|
* @param version The version to be returned to inquireing clients.
|
||||||
|
* @param os The operating system to announce. Default: don't include.
|
||||||
|
*/
|
||||||
|
void setVersion( const std::string& name, const std::string& version,
|
||||||
|
const std::string& os = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application's advertised name.
|
||||||
|
* @return The application's advertised name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_versionName; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application's advertised version.
|
||||||
|
* @return The application's advertised version.
|
||||||
|
*/
|
||||||
|
const std::string& version() const { return m_versionVersion; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application's advertised operating system.
|
||||||
|
* @return The application's advertised operating system.
|
||||||
|
*/
|
||||||
|
const std::string& os() const { return m_versionOs; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the identity of this entity.
|
||||||
|
* The library uses this information to answer disco#info requests
|
||||||
|
* with a correct identity.
|
||||||
|
* XEP-0030 requires an entity to have at least one identity. See XEP-0030
|
||||||
|
* for more information on categories and types.
|
||||||
|
* @param category The entity category of this client. Default: client.
|
||||||
|
* @param type The type of this entity. Default: bot.
|
||||||
|
* @param name The name of the entity. Default: empty.
|
||||||
|
* @note An entity can have more than one identity. You cann add more identities
|
||||||
|
* using addIdentity(). A call to setIdentity() will clear the list of identities
|
||||||
|
* and, after that, add the new identity given by the arguments to setIdentity().
|
||||||
|
*/
|
||||||
|
void setIdentity( const std::string& category, const std::string& type,
|
||||||
|
const std::string& name = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds another identity to the list of identities.
|
||||||
|
* @param category The entity category of this client. Default: client.
|
||||||
|
* @param type The type of this entity. Default: bot.
|
||||||
|
* @param name The name of the entity. Default: empty.
|
||||||
|
*/
|
||||||
|
void addIdentity( const std::string& category, const std::string& type,
|
||||||
|
const std::string& name = EmptyString )
|
||||||
|
{ m_identities.push_back( new Identity( category, type, name ) ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's identities.
|
||||||
|
* @return The entity's identities.
|
||||||
|
*/
|
||||||
|
const IdentityList& identities() const { return m_identities; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an optional DataForm to Disco:Info replies, e.g. for XEP-0232.
|
||||||
|
* Only one form can be added at this point.
|
||||||
|
* @param form An optional DataForm to include in the Info reply.
|
||||||
|
* The form will be owned by and deleted on destruction of the Disco object.
|
||||||
|
* @note If called more than once the previously set form will be deleted.
|
||||||
|
*/
|
||||||
|
void setForm( DataForm* form );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the DataForm set by setForm(). Used by Capabilities.
|
||||||
|
* @return The DataForm, or 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register an @ref DiscoHandler with the Disco
|
||||||
|
* object. This is only necessary if you want to receive Disco-set requests. Else
|
||||||
|
* a one-time registration happens when calling getDiscoInfo() and getDiscoItems(), respectively.
|
||||||
|
* @param dh The DiscoHandler-derived object to register.
|
||||||
|
*/
|
||||||
|
void registerDiscoHandler( DiscoHandler* dh )
|
||||||
|
{ m_discoHandlers.push_back( dh ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the given DiscoHandler.
|
||||||
|
* @param dh The DiscoHandler to unregister.
|
||||||
|
*/
|
||||||
|
void removeDiscoHandler( DiscoHandler* dh );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a @ref DiscoNodeHandler with the Disco
|
||||||
|
* object. The DiscoNodeHandler will receive disco#items queries which are
|
||||||
|
* directed to the corresponding node registered for the handler.
|
||||||
|
* @param nh The NodeHandler-derived object to register.
|
||||||
|
* @param node The node name to associate with this handler. Use an empty string to
|
||||||
|
* register for the root node.
|
||||||
|
*/
|
||||||
|
void registerNodeHandler( DiscoNodeHandler* nh, const std::string& node );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the node handler for the given node.
|
||||||
|
* @param nh The NodeHandler to unregister.
|
||||||
|
* @param node The node for which the handler shall be removed. Use an empty string to
|
||||||
|
* remove the root node's handler.
|
||||||
|
*/
|
||||||
|
void removeNodeHandler( DiscoNodeHandler* nh, const std::string& node );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all registered nodes of the given node handler.
|
||||||
|
* @param nh The NodeHandler to unregister.
|
||||||
|
*/
|
||||||
|
void removeNodeHandlers( DiscoNodeHandler* nh );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler.
|
||||||
|
virtual bool handleIq( const IQ& iq );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler.
|
||||||
|
virtual void handleIqID( const IQ& iq, int context );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef DISCO_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
Disco( ClientBase* parent );
|
||||||
|
virtual ~Disco();
|
||||||
|
|
||||||
|
enum IdType
|
||||||
|
{
|
||||||
|
GetDiscoInfo,
|
||||||
|
GetDiscoItems
|
||||||
|
};
|
||||||
|
|
||||||
|
void getDisco( const JID& to, const std::string& node, DiscoHandler* dh,
|
||||||
|
int context, IdType idType, const std::string& tid );
|
||||||
|
|
||||||
|
struct DiscoHandlerContext
|
||||||
|
{
|
||||||
|
DiscoHandler* dh;
|
||||||
|
int context;
|
||||||
|
};
|
||||||
|
|
||||||
|
ClientBase* m_parent;
|
||||||
|
|
||||||
|
typedef std::list<DiscoHandler*> DiscoHandlerList;
|
||||||
|
typedef std::list<DiscoNodeHandler*> DiscoNodeHandlerList;
|
||||||
|
typedef std::map<std::string, DiscoNodeHandlerList> DiscoNodeHandlerMap;
|
||||||
|
typedef std::map<std::string, DiscoHandlerContext> DiscoHandlerMap;
|
||||||
|
|
||||||
|
DiscoHandlerList m_discoHandlers;
|
||||||
|
DiscoNodeHandlerMap m_nodeHandlers;
|
||||||
|
DiscoHandlerMap m_track;
|
||||||
|
IdentityList m_identities;
|
||||||
|
StringList m_features;
|
||||||
|
StringMap m_queryIDs;
|
||||||
|
DataForm* m_form;
|
||||||
|
|
||||||
|
std::string m_versionName;
|
||||||
|
std::string m_versionVersion;
|
||||||
|
std::string m_versionOs;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISCO_H__
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISCOHANDLER_H__
|
||||||
|
#define DISCOHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "disco.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that enables objects to receive Service Discovery (XEP-0030) events.
|
||||||
|
*
|
||||||
|
* A class implementing this interface can receive the results of sent disco queries.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API DiscoHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DiscoHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function if you want to be notified about the result
|
||||||
|
* of an disco#info query.
|
||||||
|
* @param from The sender of the disco#info result.
|
||||||
|
* @param info The Info.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function if you want to be notified about the result
|
||||||
|
* of a disco#items query.
|
||||||
|
* @param from The sender of the disco#items result.
|
||||||
|
* @param items The Items.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function to receive disco error notifications.
|
||||||
|
* @param from The sender of the error result.
|
||||||
|
* @param error The Error. May be 0.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void handleDiscoError( const JID& from, const Error* error, int context ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function to receive notifications about incoming IQ
|
||||||
|
* stanzas of type 'set' in the disco namespace.
|
||||||
|
* @param iq The full IQ.
|
||||||
|
* @return Returns @b true if the stanza was handled and answered, @b false otherwise.
|
||||||
|
*/
|
||||||
|
virtual bool handleDiscoSet( const IQ& iq ) { (void)iq; return false; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISCOHANDLER_H__
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISCONODEHANDLER_H__
|
||||||
|
#define DISCONODEHANDLER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "disco.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Derived classes can be registered as NodeHandlers for certain nodes with the Disco object.
|
||||||
|
*
|
||||||
|
* Incoming disco#info and disco#items queries are delegated to their respective handlers.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API DiscoNodeHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DiscoNodeHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In addition to @c handleDiscoNodeIdentities, this function is used to gather
|
||||||
|
* more information on a specific node. It is called when a disco#info query
|
||||||
|
* arrives with a node attribute that matches the one registered for this handler.
|
||||||
|
* @param from The sender of the request.
|
||||||
|
* @param node The node this handler is supposed to handle.
|
||||||
|
* @return A list of features supported by this node.
|
||||||
|
*/
|
||||||
|
virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In addition to @c handleDiscoNodeFeatures, this function is used to gather
|
||||||
|
* more information on a specific node. It is called when a disco#info query
|
||||||
|
* arrives with a node attribute that matches the one registered for this handler.
|
||||||
|
* @param from The sender of the request.
|
||||||
|
* @param node The node this handler is supposed to handle.
|
||||||
|
* @return A list of identities for the given node. The caller will own the identities.
|
||||||
|
*/
|
||||||
|
virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from,
|
||||||
|
const std::string& node ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to gather more information on a specific node.
|
||||||
|
* It is called when a disco#items query arrives with a node attribute that
|
||||||
|
* matches the one registered for this handler. If node is empty, items for the
|
||||||
|
* root node (no node) shall be returned.
|
||||||
|
* @param from The sender of the request.
|
||||||
|
* @param to The receiving JID (useful for transports).
|
||||||
|
* @param node The node this handler is supposed to handle.
|
||||||
|
* @return A list of items supported by this node.
|
||||||
|
*/
|
||||||
|
virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to,
|
||||||
|
const std::string& node = EmptyString ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISCONODEHANDLER_H__
|
|
@ -0,0 +1,488 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32_WCE
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
# include <resolv.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/un.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_WINDNS_H
|
||||||
|
# include <windns.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SRV_COST (RRFIXEDSZ+0)
|
||||||
|
#define SRV_WEIGHT (RRFIXEDSZ+2)
|
||||||
|
#define SRV_PORT (RRFIXEDSZ+4)
|
||||||
|
#define SRV_SERVER (RRFIXEDSZ+6)
|
||||||
|
#define SRV_FIXEDSZ (RRFIXEDSZ+6)
|
||||||
|
|
||||||
|
#ifndef T_SRV
|
||||||
|
# define T_SRV 33
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// mingw
|
||||||
|
#ifndef DNS_TYPE_SRV
|
||||||
|
# define DNS_TYPE_SRV 33
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_CMPRSFLGS
|
||||||
|
# define NS_CMPRSFLGS 0xc0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef C_IN
|
||||||
|
# define C_IN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INVALID_SOCKET
|
||||||
|
# define INVALID_SOCKET -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define XMPP_PORT 5222
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined( HAVE_RES_QUERYDOMAIN ) && defined( HAVE_DN_SKIPNAME ) && defined( HAVE_RES_QUERY )
|
||||||
|
DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
buffer srvbuf;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
const std::string dname = "_" + service + "._" + proto;
|
||||||
|
|
||||||
|
if( !domain.empty() )
|
||||||
|
srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ),
|
||||||
|
C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
|
||||||
|
else
|
||||||
|
srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
|
||||||
|
|
||||||
|
if( srvbuf.len < 0 )
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
|
||||||
|
HEADER* hdr = (HEADER*)srvbuf.buf;
|
||||||
|
unsigned char* here = srvbuf.buf + NS_HFIXEDSZ;
|
||||||
|
|
||||||
|
if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
if( hdr->rcode >= 1 && hdr->rcode <= 5 )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
if( ntohs( hdr->ancount ) == 0 )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
if( ntohs( hdr->ancount ) > NS_PACKETSZ )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
int cnt;
|
||||||
|
for( cnt = ntohs( hdr->qdcount ); cnt > 0; --cnt )
|
||||||
|
{
|
||||||
|
int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
|
||||||
|
here += strlen + NS_QFIXEDSZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* srv[NS_PACKETSZ];
|
||||||
|
int srvnum = 0;
|
||||||
|
for( cnt = ntohs( hdr->ancount ); cnt > 0; --cnt )
|
||||||
|
{
|
||||||
|
int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
|
||||||
|
here += strlen;
|
||||||
|
srv[srvnum++] = here;
|
||||||
|
here += SRV_FIXEDSZ;
|
||||||
|
here += dn_skipname( here, srvbuf.buf + srvbuf.len );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( error )
|
||||||
|
{
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
// (q)sort here
|
||||||
|
|
||||||
|
HostMap servers;
|
||||||
|
for( cnt = 0; cnt < srvnum; ++cnt )
|
||||||
|
{
|
||||||
|
char srvname[NS_MAXDNAME];
|
||||||
|
srvname[0] = '\0';
|
||||||
|
|
||||||
|
if( dn_expand( srvbuf.buf, srvbuf.buf + NS_PACKETSZ,
|
||||||
|
srv[cnt] + SRV_SERVER, srvname, NS_MAXDNAME ) < 0
|
||||||
|
|| !(*srvname) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsigned char* c = srv[cnt] + SRV_PORT;
|
||||||
|
servers.insert( std::make_pair( (char*)srvname, ntohs( c[1] << 8 | c[0] ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !servers.size() )
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined( _WIN32 ) && defined( HAVE_WINDNS_H )
|
||||||
|
DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
const std::string dname = "_" + service + "._" + proto + "." + domain;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
DNS::HostMap servers;
|
||||||
|
DNS_RECORD* pRecord = NULL;
|
||||||
|
DNS_STATUS status = DnsQuery_UTF8( dname.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pRecord, NULL );
|
||||||
|
if( status == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
DNS_RECORD* pRec = pRecord;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( pRec->wType == DNS_TYPE_SRV )
|
||||||
|
{
|
||||||
|
servers[pRec->Data.SRV.pNameTarget] = pRec->Data.SRV.wPort;
|
||||||
|
}
|
||||||
|
pRec = pRec->pNext;
|
||||||
|
}
|
||||||
|
while( pRec != NULL );
|
||||||
|
DnsRecordListFree( pRecord, DnsFreeRecordList );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logInstance.warn( LogAreaClassDns, "DnsQuery_UTF8() failed: " + util::int2string( status ) );
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( error || !servers.size() )
|
||||||
|
{
|
||||||
|
servers = defaultHostMap( domain, logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
DNS::HostMap DNS::resolve( const std::string& /*service*/, const std::string& /*proto*/,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
logInstance.warn( LogAreaClassDns, "Notice: gloox does not support SRV "
|
||||||
|
"records on this platform. Using A records instead." );
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DNS::HostMap DNS::defaultHostMap( const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
HostMap server;
|
||||||
|
|
||||||
|
logInstance.warn( LogAreaClassDns, "Notice: no SRV record found for "
|
||||||
|
+ domain + ", using default port." );
|
||||||
|
|
||||||
|
if( !domain.empty() )
|
||||||
|
server[domain] = XMPP_PORT;
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_GETADDRINFO
|
||||||
|
void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Resolving: _" + service + "._" + proto + "." + domain );
|
||||||
|
struct addrinfo hints;
|
||||||
|
if( proto == "tcp" )
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
else if( proto == "udp" )
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto );
|
||||||
|
}
|
||||||
|
memset( &hints, '\0', sizeof( hints ) );
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res );
|
||||||
|
if( e )
|
||||||
|
logInstance.err( LogAreaClassDns, "getaddrinfo() failed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::connect( const std::string& host, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
struct addrinfo* results = 0;
|
||||||
|
|
||||||
|
resolve( &results, host, logInstance );
|
||||||
|
if( !results )
|
||||||
|
{
|
||||||
|
logInstance.err( LogAreaClassDns, "host not found: " + host );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo* runp = results;
|
||||||
|
while( runp )
|
||||||
|
{
|
||||||
|
int fd = DNS::connect( runp, logInstance );
|
||||||
|
if( fd >= 0 )
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
runp = runp->ai_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo( results );
|
||||||
|
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::connect( struct addrinfo* res, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
if( !res )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol, logInstance );
|
||||||
|
if( fd < 0 )
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 )
|
||||||
|
{
|
||||||
|
char ip[NI_MAXHOST];
|
||||||
|
char port[NI_MAXSERV];
|
||||||
|
|
||||||
|
if( getnameinfo( res->ai_addr, sizeof( sockaddr ),
|
||||||
|
ip, sizeof( ip ),
|
||||||
|
port, sizeof( port ),
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV ) )
|
||||||
|
{
|
||||||
|
//FIXME do we need to handle this? How? Can it actually happen at all?
|
||||||
|
// printf( "could not get numeric hostname");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( res->ai_canonname )
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connecting to " + std::string( res->ai_canonname )
|
||||||
|
+ " (" + ip + "), port " + port );
|
||||||
|
else
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connecting to " + ip + ":" + port );
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message = "connect() failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
closeSocket( fd, logInstance );
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int DNS::connect( const std::string& host, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
HostMap hosts = resolve( host, logInstance );
|
||||||
|
if( hosts.size() == 0 )
|
||||||
|
return -ConnDnsError;
|
||||||
|
|
||||||
|
HostMap::const_iterator it = hosts.begin();
|
||||||
|
for( ; it != hosts.end(); ++it )
|
||||||
|
{
|
||||||
|
int fd = DNS::connect( (*it).first, (*it).second, logInstance );
|
||||||
|
if( fd >= 0 )
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int DNS::getSocket( const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
WSADATA wsaData;
|
||||||
|
if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "WSAStartup() failed. WSAGetLastError: "
|
||||||
|
+ util::int2string( ::WSAGetLastError() ) );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int protocol = IPPROTO_TCP;
|
||||||
|
struct protoent* prot;
|
||||||
|
if( ( prot = getprotobyname( "tcp" ) ) != 0 )
|
||||||
|
{
|
||||||
|
protocol = prot->p_proto;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string message = "getprotobyname( \"tcp\" ) failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() )
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
+ ". Falling back to IPPROTO_TCP: " + util::int2string( IPPROTO_TCP );
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
// Do not return an error. We'll fall back to IPPROTO_TCP.
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSocket( PF_INET, SOCK_STREAM, protocol, logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::getSocket( int af, int socktype, int proto, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
SOCKET fd;
|
||||||
|
#else
|
||||||
|
int fd;
|
||||||
|
#endif
|
||||||
|
if( ( fd = socket( af, socktype, proto ) ) == INVALID_SOCKET )
|
||||||
|
{
|
||||||
|
std::string message = "getSocket( "
|
||||||
|
+ util::int2string( af ) + ", "
|
||||||
|
+ util::int2string( socktype ) + ", "
|
||||||
|
+ util::int2string( proto )
|
||||||
|
+ " ) failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
cleanup( logInstance );
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SETSOCKOPT
|
||||||
|
int timeout = 5000;
|
||||||
|
setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) );
|
||||||
|
setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&timeout, sizeof( timeout ) );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return (int)fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::connect( const std::string& host, int port, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
int fd = getSocket( logInstance );
|
||||||
|
if( fd < 0 )
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
struct hostent* h;
|
||||||
|
if( ( h = gethostbyname( host.c_str() ) ) == 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "gethostbyname() failed for " + host + "." );
|
||||||
|
cleanup( logInstance );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in target;
|
||||||
|
target.sin_family = AF_INET;
|
||||||
|
target.sin_port = htons( static_cast<unsigned short int>( port ) );
|
||||||
|
|
||||||
|
if( h->h_length != sizeof( struct in_addr ) )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "gethostbyname() returned unexpected structure." );
|
||||||
|
cleanup( logInstance );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connecting to " + host
|
||||||
|
+ " (" + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" );
|
||||||
|
|
||||||
|
memset( target.sin_zero, '\0', 8 );
|
||||||
|
if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connected to " + host + " ("
|
||||||
|
+ inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" );
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message = "Connection to " + host + " ("
|
||||||
|
+ inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ") failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
closeSocket( fd, logInstance );
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNS::closeSocket( int fd, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
int result = closesocket( fd );
|
||||||
|
#else
|
||||||
|
int result = close( fd );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if( result != 0 )
|
||||||
|
{
|
||||||
|
std::string message = "closeSocket() failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNS::cleanup( const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
if( WSACleanup() != 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "WSACleanup() failed. WSAGetLastError: "
|
||||||
|
+ util::int2string( ::WSAGetLastError() ) );
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)logInstance;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DNS_H__
|
||||||
|
#define DNS_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <windows.h>
|
||||||
|
# include <windns.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ARPA_NAMESER_H
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <arpa/nameser_compat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_MAXDNAME
|
||||||
|
# define NS_MAXDNAME 1025
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_PACKETSZ
|
||||||
|
# define NS_PACKETSZ 512
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_GETADDRINFO
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class holds a number of static functions used for DNS related stuff.
|
||||||
|
*
|
||||||
|
* You should not need to use these functions directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API DNS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of strings (used for server addresses) and ints (used for port numbers).
|
||||||
|
*/
|
||||||
|
typedef std::map<std::string, int> HostMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function resolves a service/protocol/domain tuple.
|
||||||
|
* @param service The SRV service type.
|
||||||
|
* @param proto The SRV protocol.
|
||||||
|
* @param domain The domain to search for SRV records.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A list of weighted hostname/port pairs from SRV records, or A records if no SRV
|
||||||
|
* records where found.
|
||||||
|
*/
|
||||||
|
static HostMap resolve( const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience funtion which uses @ref resolve() to resolve SRV records
|
||||||
|
* for a given domain, using a service of @b xmpp-client and a proto of @b tcp.
|
||||||
|
* @param domain The domain to resolve SRV records for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A list of weighted hostname/port pairs from SRV records, or A records if no SRV
|
||||||
|
* records where found.
|
||||||
|
*/
|
||||||
|
static HostMap resolve( const std::string& domain, const LogSink& logInstance )
|
||||||
|
{ return resolve( "xmpp-client", "tcp", domain, logInstance ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience function which uses @ref resolve() to get a list of hosts
|
||||||
|
* and connects to one of them.
|
||||||
|
* @param host The host to resolve SRV records for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A file descriptor for the established connection.
|
||||||
|
*/
|
||||||
|
static int connect( const std::string& host, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience function which connects to the given host and port. No SRV
|
||||||
|
* records are resolved. Use this function for special setups.
|
||||||
|
* @param host The host/IP address to connect to.
|
||||||
|
* @param port A custom port to connect to.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A file descriptor for the established connection.
|
||||||
|
*/
|
||||||
|
static int connect( const std::string& host, int port, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience function that prepares and returnes a simple, unconnected TCP socket.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A TCP socket.
|
||||||
|
*/
|
||||||
|
static int getSocket( const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the given socket.
|
||||||
|
* @param fd The socket to close.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
*/
|
||||||
|
static void closeSocket( int fd, const LogSink& logInstance );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef HAVE_GETADDRINFO
|
||||||
|
/**
|
||||||
|
* Resolves the given service for the given domain and protocol, using the IPv6-ready
|
||||||
|
* getaddrinfo(). The result is put into the first parameter.
|
||||||
|
* @param res A pointer to a pointer holding the query results.
|
||||||
|
* @param service A service string to query for, e.g. xmpp-client.
|
||||||
|
* @param proto A protocol name.
|
||||||
|
* @param domain The domain to query for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
*/
|
||||||
|
static void resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience funtion which uses @ref resolve() to resolve SRV records
|
||||||
|
* for a given domain, using a service of @b xmpp-client and a proto of @b tcp.
|
||||||
|
* @param res A pointer to a pointer holding the query results.
|
||||||
|
* @param domain The domain to resolve SRV records for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
*/
|
||||||
|
static void resolve( struct addrinfo** res, const std::string& domain, const LogSink& logInstance )
|
||||||
|
{ resolve( res, "xmpp-client", "tcp", domain, logInstance ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to connect to the host/address contained in the addrinfo structure.
|
||||||
|
* @param res The connection parameters.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A file descriptor for the established connection.
|
||||||
|
*/
|
||||||
|
static int connect( struct addrinfo* res, const LogSink& logInstance );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function prepares and returns a socket with the given parameters.
|
||||||
|
* @param af The address family. E.g. PF_INET.
|
||||||
|
* @param socktype The socket type. E.g. SOCK_STREAM.
|
||||||
|
* @param proto The protocol number. E.g. 6 (TCP).
|
||||||
|
*/
|
||||||
|
static int getSocket( int af, int socktype, int proto, const LogSink& logInstance );
|
||||||
|
|
||||||
|
static HostMap defaultHostMap( const std::string& domain, const LogSink& logInstance );
|
||||||
|
static void cleanup( const LogSink& logInstance );
|
||||||
|
|
||||||
|
struct buffer
|
||||||
|
{
|
||||||
|
unsigned char buf[NS_PACKETSZ];
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DNS_H__
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Error type values */
|
||||||
|
static const char* errValues [] = {
|
||||||
|
"auth",
|
||||||
|
"cancel",
|
||||||
|
"continue",
|
||||||
|
"modify",
|
||||||
|
"wait"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Stanza error values */
|
||||||
|
static const char* stanzaErrValues [] = {
|
||||||
|
"bad-request",
|
||||||
|
"conflict",
|
||||||
|
"feature-not-implemented",
|
||||||
|
"forbidden",
|
||||||
|
"gone",
|
||||||
|
"internal-server-error",
|
||||||
|
"item-not-found",
|
||||||
|
"jid-malformed",
|
||||||
|
"not-acceptable",
|
||||||
|
"not-allowed",
|
||||||
|
"not-authorized",
|
||||||
|
"not-modified",
|
||||||
|
"payment-required",
|
||||||
|
"recipient-unavailable",
|
||||||
|
"redirect",
|
||||||
|
"registration-required",
|
||||||
|
"remote-server-not-found",
|
||||||
|
"remote-server-timeout",
|
||||||
|
"resource-constraint",
|
||||||
|
"service-unavailable",
|
||||||
|
"subscription-required",
|
||||||
|
"undefined-condition",
|
||||||
|
"unexpected-request",
|
||||||
|
"unknown-sender"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline StanzaErrorType stanzaErrorType( const std::string& type )
|
||||||
|
{
|
||||||
|
return (StanzaErrorType)util::lookup( type, errValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline StanzaError stanzaError( const std::string& type )
|
||||||
|
{
|
||||||
|
return (StanzaError)util::lookup( type, stanzaErrValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtError ),
|
||||||
|
m_error( StanzaErrorUndefined ), m_appError( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "error" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_type = stanzaErrorType( tag->findAttribute( TYPE ) );
|
||||||
|
|
||||||
|
TagList::const_iterator it = tag->children().begin();
|
||||||
|
for( ; it != tag->children().end(); ++it )
|
||||||
|
{
|
||||||
|
StanzaError srt = gloox::stanzaError( (*it)->name() );
|
||||||
|
if( srt != StanzaErrorUndefined )
|
||||||
|
m_error = srt;
|
||||||
|
else if( (*it)->name() == "text" )
|
||||||
|
m_text[(*it)->findAttribute("xml:lang")] = (*it)->cdata();
|
||||||
|
else
|
||||||
|
m_appError = (*it)->clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error( const Error& error )
|
||||||
|
: StanzaExtension( ExtError ), m_type( error.m_type ),
|
||||||
|
m_error( error.m_error ), m_appError( error.m_appError ? m_appError->clone() : 0 )
|
||||||
|
{}
|
||||||
|
|
||||||
|
Error::~Error()
|
||||||
|
{
|
||||||
|
delete m_appError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Error::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/error"
|
||||||
|
"|/message/error"
|
||||||
|
"|/presence/error"
|
||||||
|
"|/subscription/error";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Tag* Error::tag() const
|
||||||
|
{
|
||||||
|
if( m_type == StanzaErrorTypeUndefined || m_error == StanzaErrorUndefined )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* error = new Tag( "error", TYPE, util::lookup( m_type, errValues ) );
|
||||||
|
new Tag( error, util::lookup( m_error, stanzaErrValues ), XMLNS, XMLNS_XMPP_STANZAS );
|
||||||
|
|
||||||
|
StringMap::const_iterator it = m_text.begin();
|
||||||
|
for( ; it != m_text.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* txt = new Tag( error, "text" );
|
||||||
|
txt->setXmlns( XMLNS_XMPP_STANZAS );
|
||||||
|
txt->addAttribute( "xml:lang", (*it).first );
|
||||||
|
txt->setCData( (*it).second );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_appError )
|
||||||
|
error->addChild( m_appError->clone() );
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Error::text( const std::string& lang ) const
|
||||||
|
{
|
||||||
|
StringMap::const_iterator it = m_text.find( lang );
|
||||||
|
return it != m_text.end() ? (*it).second : EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ERROR_H__
|
||||||
|
#define ERROR_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A stanza error abstraction implemented as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Vincent Thomasset
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Error : public StanzaExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Error()
|
||||||
|
// : StanzaExtension( ExtError ), m_type( StanzaErrorTypeUndefined ),
|
||||||
|
// m_error( StanzaErrorUndefined ), m_appError( 0 )
|
||||||
|
// {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Error object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Error( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Error object.
|
||||||
|
* @param type The error type.
|
||||||
|
* @param error The actual stanza error.
|
||||||
|
* @param appError An optional application-specific error.
|
||||||
|
*/
|
||||||
|
Error( StanzaErrorType type, StanzaError error, Tag* appError = 0 )
|
||||||
|
: StanzaExtension( ExtError ), m_type( type ),
|
||||||
|
m_error( error ), m_appError( appError )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Error();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error type.
|
||||||
|
* @return The error type.
|
||||||
|
*/
|
||||||
|
StanzaErrorType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the stanza error.
|
||||||
|
* @return The actual error.
|
||||||
|
*/
|
||||||
|
StanzaError error() const { return m_error; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to retrieve the application-specific error
|
||||||
|
* condition of a stanza error.
|
||||||
|
* @return The application-specific error element of a stanza error.
|
||||||
|
* 0 if no respective element was found or no error occured.
|
||||||
|
*/
|
||||||
|
const Tag* appError() const { return m_appError; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text of a error stanza for the given language if available.
|
||||||
|
* If the requested language is not available, the default text (without
|
||||||
|
* a xml:lang attribute) will be returned.
|
||||||
|
* @param lang The language identifier for the desired language. It must
|
||||||
|
* conform to section 2.12 of the XML specification and RFC 3066. If
|
||||||
|
* empty, the default text will be returned, if any.
|
||||||
|
* @return The text of an error stanza.
|
||||||
|
*/
|
||||||
|
const std::string& text( const std::string& lang = EmptyString ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text of a error stanza for the given language.
|
||||||
|
* @param text The error text to set.
|
||||||
|
* @param lang The language identifier for the desired language. It must
|
||||||
|
* conform to section 2.12 of the XML specification and RFC 3066. If
|
||||||
|
* empty, the default text will be set.
|
||||||
|
*/
|
||||||
|
void setText( const std::string& text, const std::string& lang = EmptyString )
|
||||||
|
{
|
||||||
|
m_text[lang] = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Error( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Error( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Error( const Error& error );
|
||||||
|
|
||||||
|
void setValues( const Tag* tag );
|
||||||
|
|
||||||
|
StanzaErrorType m_type;
|
||||||
|
StanzaError m_error;
|
||||||
|
Tag* m_appError;
|
||||||
|
StringMap m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ERROR_H__ */
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENT_H__
|
||||||
|
#define EVENT_H__
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Stanza;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A base class for events.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Event types.
|
||||||
|
*/
|
||||||
|
enum EventType
|
||||||
|
{
|
||||||
|
PingPing, /**< Incoming Ping (XEP-0199). */
|
||||||
|
PingPong, /**< Incoming Pong (XEP-0199). */
|
||||||
|
PingError /**< Incoming Error Pong (XEP-0199). */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Event of the given type.
|
||||||
|
* @param type The Event type.
|
||||||
|
*/
|
||||||
|
Event( EventType type ) : m_eventType( type ), m_stanza( 0 ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Event of the given type, referencing the given Stanza.
|
||||||
|
* @param type The Event type.
|
||||||
|
* @param stanza A Stanza to point at. No copy of the Stanza is taken, just its address.
|
||||||
|
*/
|
||||||
|
Event( EventType type, const Stanza& stanza ) : m_eventType( type ), m_stanza( &stanza ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Event() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Event's type.
|
||||||
|
* @return The Event's type.
|
||||||
|
*/
|
||||||
|
EventType eventType() const { return m_eventType; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pointer to a Stanza-derived object.
|
||||||
|
* @return A pointer to a Stanza that caused the event. May be 0.
|
||||||
|
* @note You should @b not delete the Stanza object.
|
||||||
|
*/
|
||||||
|
const Stanza* stanza() const { return m_stanza; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EventType m_eventType;
|
||||||
|
const Stanza* m_stanza;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVENT_H__
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "eventdispatcher.h"
|
||||||
|
#include "eventhandler.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
EventDispatcher::EventDispatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
EventDispatcher::~EventDispatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::dispatch( const Event& event, const std::string& context, bool remove )
|
||||||
|
{
|
||||||
|
typedef ContextHandlerMap::iterator Ei;
|
||||||
|
std::pair<Ei, Ei> g = m_contextHandlers.equal_range( context );
|
||||||
|
Ei it = g.first;
|
||||||
|
Ei it2;
|
||||||
|
while( it != g.second )
|
||||||
|
{
|
||||||
|
it2 = it++;
|
||||||
|
(*it2).second->handleEvent( event );
|
||||||
|
if( remove )
|
||||||
|
m_contextHandlers.erase( it2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::dispatch( const Event& event )
|
||||||
|
{
|
||||||
|
TypeHandlerMap::iterator it = m_typeHandlers.begin();
|
||||||
|
for( ; it != m_typeHandlers.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it).first == event.eventType() )
|
||||||
|
(*it).second->handleEvent( event );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::registerEventHandler( EventHandler* eh, const std::string& context )
|
||||||
|
{
|
||||||
|
if( !eh || context.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_contextHandlers.insert( std::make_pair( context, eh ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::removeEventHandler( EventHandler* eh )
|
||||||
|
{
|
||||||
|
ContextHandlerMap::iterator it = m_contextHandlers.begin();
|
||||||
|
ContextHandlerMap::iterator it2;
|
||||||
|
while( it != m_contextHandlers.end() )
|
||||||
|
{
|
||||||
|
it2 = it++;
|
||||||
|
if( (*it2).second == eh )
|
||||||
|
m_contextHandlers.erase( it2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENTDISPATCHER_H__
|
||||||
|
#define EVENTDISPATCHER_H__
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class EventHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An Event dispatcher.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class EventDispatcher
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new EventDispatcher object. You should not need to use this class directly.
|
||||||
|
*/
|
||||||
|
EventDispatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~EventDispatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for handlers for the given Event, and removes the handlers if requested.
|
||||||
|
* @param event The Event to dispatch.
|
||||||
|
* @param context An identifier that limits the EventHandlers that will get notified to
|
||||||
|
* those that are specifically interested in this context.
|
||||||
|
* @param remove Whether or not to remove the context from the list of known contexts. Useful for
|
||||||
|
* IQ IDs.
|
||||||
|
*/
|
||||||
|
void dispatch( const Event& event, const std::string& context, bool remove );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for handlers for the given Event, identified by its type.
|
||||||
|
* @param event The event to dispatch.
|
||||||
|
*/
|
||||||
|
void dispatch( const Event& event );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given EventHandler to be notified about Events with the given context.
|
||||||
|
* The context will usually be an IQ ID.
|
||||||
|
* @param eh The EventHandler to register.
|
||||||
|
* @param context The context to register the EventHandler for.
|
||||||
|
*/
|
||||||
|
void registerEventHandler( EventHandler* eh, const std::string& context );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given EventHandler.
|
||||||
|
* @param eh The EventHandler to remove.
|
||||||
|
*/
|
||||||
|
void removeEventHandler( EventHandler* eh );
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::multimap<const std::string, EventHandler*> ContextHandlerMap;
|
||||||
|
typedef std::multimap<Event::EventType, EventHandler*> TypeHandlerMap;
|
||||||
|
|
||||||
|
ContextHandlerMap m_contextHandlers;
|
||||||
|
TypeHandlerMap m_typeHandlers;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVENTDISPATCHER_H__
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENTHANDLER_H__
|
||||||
|
#define EVENTHANDLER_H__
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An base class for event handlers.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class EventHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~EventHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gets called for Events this handler was registered for.
|
||||||
|
* @param event The Event.
|
||||||
|
*/
|
||||||
|
virtual void handleEvent( const Event& event ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVENTHANDLER_H__
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "featureneg.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
FeatureNeg::FeatureNeg( DataForm* form )
|
||||||
|
: StanzaExtension( ExtFeatureNeg ), m_form( form )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FeatureNeg::FeatureNeg( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtFeatureNeg ), m_form( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "feature" || tag->xmlns() != XMLNS_FEATURE_NEG )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Tag* f = tag->findTag( "feature/x[@xmlns='" + XMLNS_X_DATA + "']" );
|
||||||
|
if( f )
|
||||||
|
m_form = new DataForm( f );
|
||||||
|
}
|
||||||
|
|
||||||
|
FeatureNeg::~FeatureNeg()
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& FeatureNeg::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/feature[@xmlns='" + XMLNS_FEATURE_NEG + "']"
|
||||||
|
"|/iq/feature[@xmlns='" + XMLNS_FEATURE_NEG + "']" ;
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* FeatureNeg::tag() const
|
||||||
|
{
|
||||||
|
if( !m_form )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( "feature" );
|
||||||
|
t->setXmlns( XMLNS_FEATURE_NEG );
|
||||||
|
t->addChild( m_form->tag() );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FEATURENEG_H__
|
||||||
|
#define FEATURENEG_H__
|
||||||
|
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class DataForm;
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of Feature Negotiation (XEP-0020), implemented
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* XEP Version: 1.5
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API FeatureNeg : public StanzaExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new wrapper object using the given DataForm.
|
||||||
|
* @param form The DataForm to embed. The FeatureNeg object will own the DataForm.
|
||||||
|
*/
|
||||||
|
FeatureNeg( DataForm* form );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new wrapper object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
FeatureNeg( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~FeatureNeg();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the wrapped DataForm.
|
||||||
|
* @return The wrapped DataForm. May be 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new FeatureNeg( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new FeatureNeg( m_form );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataForm* m_form;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FEATURENEG_H__
|
|
@ -0,0 +1,207 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "flexoff.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
// ---- FlexibleOffline::Offline ----
|
||||||
|
FlexibleOffline::Offline::Offline( const Tag* /*tag*/ )
|
||||||
|
: StanzaExtension( ExtFlexOffline )
|
||||||
|
{
|
||||||
|
// FIXME what to do here?
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexibleOffline::Offline::Offline( int context, const StringList& msgs )
|
||||||
|
: StanzaExtension( ExtFlexOffline ), m_context( context ), m_msgs( msgs )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexibleOffline::Offline::~Offline()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& FlexibleOffline::Offline::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/offline[@xmlns='" + XMLNS_OFFLINE + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* FlexibleOffline::Offline::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "offline" );
|
||||||
|
t->setXmlns( XMLNS_OFFLINE );
|
||||||
|
|
||||||
|
if( m_msgs.empty() )
|
||||||
|
new Tag( t, m_context == FORequestMsgs ? "fetch" : "purge" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string action = m_context == FORequestMsgs ? "view" : "remove";
|
||||||
|
StringList::const_iterator it = m_msgs.begin();
|
||||||
|
for( ; it != m_msgs.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* i = new Tag( t, "item", "action", action );
|
||||||
|
i->addAttribute( "node", (*it) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~FlexibleOffline::Offline ----
|
||||||
|
|
||||||
|
// ---- FlexibleOffline ----
|
||||||
|
FlexibleOffline::FlexibleOffline( ClientBase* parent )
|
||||||
|
: m_parent( parent ), m_flexibleOfflineHandler( 0 )
|
||||||
|
{
|
||||||
|
if( m_parent )
|
||||||
|
m_parent->registerStanzaExtension( new Offline() );
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexibleOffline::~FlexibleOffline()
|
||||||
|
{
|
||||||
|
if( m_parent )
|
||||||
|
m_parent->removeIDHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::checkSupport()
|
||||||
|
{
|
||||||
|
m_parent->disco()->getDiscoInfo( m_parent->jid().server(), EmptyString, this, FOCheckSupport );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::getMsgCount()
|
||||||
|
{
|
||||||
|
m_parent->disco()->getDiscoInfo( m_parent->jid().server(), XMLNS_OFFLINE, this, FORequestNum );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::fetchHeaders()
|
||||||
|
{
|
||||||
|
m_parent->disco()->getDiscoItems( m_parent->jid().server(), XMLNS_OFFLINE, this, FORequestHeaders );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::messageOperation( int context, const StringList& msgs )
|
||||||
|
{
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
IQ::IqType iqType = context == FORequestMsgs ? IQ::Get : IQ::Set;
|
||||||
|
IQ iq( iqType, JID(), id );
|
||||||
|
iq.addExtension( new Offline( context, msgs ) );
|
||||||
|
m_parent->send( iq, this, context );
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::registerFlexibleOfflineHandler( FlexibleOfflineHandler* foh )
|
||||||
|
{
|
||||||
|
m_flexibleOfflineHandler = foh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::removeFlexibleOfflineHandler()
|
||||||
|
{
|
||||||
|
m_flexibleOfflineHandler = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::handleDiscoInfo( const JID& /*from*/, const Disco::Info& info, int context )
|
||||||
|
{
|
||||||
|
if( !m_flexibleOfflineHandler )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( context )
|
||||||
|
{
|
||||||
|
case FOCheckSupport:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineSupport( info.hasFeature( XMLNS_OFFLINE ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FORequestNum:
|
||||||
|
int num = -1;
|
||||||
|
if( info.form() && info.form()->hasField( "number_of_messages" ) )
|
||||||
|
num = atoi( info.form()->field( "number_of_messages" )->value().c_str() );
|
||||||
|
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineMsgNum( num );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::handleDiscoItems( const JID& /*from*/, const Disco::Items& items, int context )
|
||||||
|
{
|
||||||
|
if( context == FORequestHeaders && m_flexibleOfflineHandler )
|
||||||
|
{
|
||||||
|
if( items.node() == XMLNS_OFFLINE )
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineMessageHeaders( items.items() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::handleDiscoError( const JID& /*from*/, const Error* /*error*/, int /*context*/ )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexibleOffline::handleIqID( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
if( !m_flexibleOfflineHandler )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( context )
|
||||||
|
{
|
||||||
|
case FORequestMsgs:
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrRequestSuccess );
|
||||||
|
break;
|
||||||
|
case IQ::Error:
|
||||||
|
switch( iq.error()->error() )
|
||||||
|
{
|
||||||
|
case StanzaErrorForbidden:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrForbidden );
|
||||||
|
break;
|
||||||
|
case StanzaErrorItemNotFound:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrItemNotFound );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrUnknownError );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORemoveMsgs:
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrRemoveSuccess );
|
||||||
|
break;
|
||||||
|
case IQ::Error:
|
||||||
|
switch( iq.error()->error() )
|
||||||
|
{
|
||||||
|
case StanzaErrorForbidden:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrForbidden );
|
||||||
|
break;
|
||||||
|
case StanzaErrorItemNotFound:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrItemNotFound );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrUnknownError );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FLEXOFF_H__
|
||||||
|
#define FLEXOFF_H__
|
||||||
|
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "flexoffhandler.h"
|
||||||
|
#include "iqhandler.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An implementation of XEP-0013 (Flexible Offline Message Retrieval).
|
||||||
|
*
|
||||||
|
* Use the FlexibleOfflineHandler to receive results.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API FlexibleOffline : public DiscoHandler, public IqHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new FlexibleOffline object that manages retrieval of offline messages.
|
||||||
|
* @param parent The ClientBase to use for communication.
|
||||||
|
*/
|
||||||
|
FlexibleOffline( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~FlexibleOffline();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates querying the server for Flexible Offline Message Retrieval-support.
|
||||||
|
* The result is announced through the FlexibleOfflineHandler.
|
||||||
|
* An application could cache the result on a per-server basis to eliminate the associated delay.
|
||||||
|
*/
|
||||||
|
void checkSupport();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the server for the number of stored offline messages.
|
||||||
|
* The result is announced through the FlexibleOfflineHandler.
|
||||||
|
*/
|
||||||
|
void getMsgCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates fetching the offline message headers.
|
||||||
|
* The result is announced through the FlexibleOfflineHandler.
|
||||||
|
*/
|
||||||
|
void fetchHeaders();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates fetching of one or more specific messages, or all messages.
|
||||||
|
* The result is announced through the FlexibleOfflineHandler.
|
||||||
|
* If the list of message nodes contains one or more nodes, the corresponding messages are
|
||||||
|
* fetched. If the list is empty all messages are fetched (<fetch>).
|
||||||
|
* @param msgs A list of message nodes to fetch.
|
||||||
|
*/
|
||||||
|
void fetchMessages( const StringList& msgs )
|
||||||
|
{ messageOperation( FORequestMsgs, msgs ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates removing of one or more specific messages, or all messages.
|
||||||
|
* The result is announced through the FlexibleOfflineHandler.
|
||||||
|
* If the list of message nodes contains one or more nodes, the corresponding messages are
|
||||||
|
* removed. If the list is empty all messages are removed (<purge>).
|
||||||
|
*/
|
||||||
|
void removeMessages( const StringList& msgs )
|
||||||
|
{ messageOperation( FORemoveMsgs, msgs ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a FlexibleOfflineHandler as object that receives results of XEP-0013 queries.
|
||||||
|
* Only one Handler at a time is possible.
|
||||||
|
* @param foh The Handler object to register.
|
||||||
|
*/
|
||||||
|
void registerFlexibleOfflineHandler( FlexibleOfflineHandler* foh );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the registered handler.
|
||||||
|
*/
|
||||||
|
void removeFlexibleOfflineHandler();
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoError( const JID& from, const Error* error, int context );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler.
|
||||||
|
virtual bool handleIq( const IQ& iq ) { (void)iq; return false; }
|
||||||
|
|
||||||
|
// reimplemented from IqHandler.
|
||||||
|
virtual void handleIqID( const IQ& iq, int context );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef FLEXOFF_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
class Offline : public StanzaExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new Offline object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Offline( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Offline object for the given context and messages.
|
||||||
|
* @param context The context.
|
||||||
|
* @param msgs The messages.
|
||||||
|
*/
|
||||||
|
Offline( int context, const StringList& msgs );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Offline();
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Offline( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Offline( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_context;
|
||||||
|
StringList m_msgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
void messageOperation( int context, const StringList& msgs );
|
||||||
|
|
||||||
|
enum FOContext
|
||||||
|
{
|
||||||
|
FOCheckSupport,
|
||||||
|
FORequestNum,
|
||||||
|
FORequestHeaders,
|
||||||
|
FORequestMsgs,
|
||||||
|
FORemoveMsgs
|
||||||
|
};
|
||||||
|
|
||||||
|
ClientBase* m_parent;
|
||||||
|
FlexibleOfflineHandler* m_flexibleOfflineHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FLEXOFF_H__
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FLEXOFFHANDLER_H__
|
||||||
|
#define FLEXOFFHANDLER_H__
|
||||||
|
|
||||||
|
#include "disco.h"
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the possible results of a message retrieval or deletion request.
|
||||||
|
*/
|
||||||
|
enum FlexibleOfflineResult
|
||||||
|
{
|
||||||
|
FomrRemoveSuccess, /**< Message(s) were removed successfully. */
|
||||||
|
FomrRequestSuccess, /**< Message(s) were fetched successfully. */
|
||||||
|
FomrForbidden, /**< The requester is a JID other than an authorized resource of the
|
||||||
|
* user. Something wnet serieously wrong */
|
||||||
|
FomrItemNotFound, /**< The requested node (message ID) does not exist. */
|
||||||
|
FomrUnknownError /**< An error occurred which is not specified in XEP-0013. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implementation of this virtual interface allows for retrieval of offline messages following
|
||||||
|
* XEP-0030.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API FlexibleOfflineHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~FlexibleOfflineHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called to indicate whether the server supports XEP-0013 or not.
|
||||||
|
* Call @ref FlexibleOffline::checkSupport() to trigger the check.
|
||||||
|
* @param support Whether the server support XEP-0013 or not.
|
||||||
|
*/
|
||||||
|
virtual void handleFlexibleOfflineSupport( bool support ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called to announce the number of available offline messages.
|
||||||
|
* Call @ref FlexibleOffline::getMsgCount() to trigger the check.
|
||||||
|
* @param num The number of stored offline messages.
|
||||||
|
*/
|
||||||
|
virtual void handleFlexibleOfflineMsgNum( int num ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when the offline message headers arrive.
|
||||||
|
* Call @ref FlexibleOffline::fetchHeaders() to trigger the check.
|
||||||
|
* @param headers A map of ID/sender pairs describing the offline messages.
|
||||||
|
*/
|
||||||
|
virtual void handleFlexibleOfflineMessageHeaders( const Disco::ItemList& headers ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called to indicate the result of a fetch or delete instruction.
|
||||||
|
* @param foResult The result of the operation.
|
||||||
|
*/
|
||||||
|
virtual void handleFlexibleOfflineResult( FlexibleOfflineResult foResult ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FLEXOFFHANDLER_H__
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
const std::string XMLNS_CLIENT = "jabber:client";
|
||||||
|
const std::string XMLNS_COMPONENT_ACCEPT = "jabber:component:accept";
|
||||||
|
const std::string XMLNS_COMPONENT_CONNECT = "jabber:component:connect";
|
||||||
|
|
||||||
|
const std::string XMLNS_DISCO_INFO = "http://jabber.org/protocol/disco#info";
|
||||||
|
const std::string XMLNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items";
|
||||||
|
const std::string XMLNS_DISCO_PUBLISH = "http://jabber.org/protocol/disco#publish";
|
||||||
|
const std::string XMLNS_ADHOC_COMMANDS = "http://jabber.org/protocol/commands";
|
||||||
|
const std::string XMLNS_COMPRESSION = "http://jabber.org/protocol/compress";
|
||||||
|
const std::string XMLNS_OFFLINE = "http://jabber.org/protocol/offline";
|
||||||
|
|
||||||
|
const std::string XMLNS_CHAT_STATES = "http://jabber.org/protocol/chatstates";
|
||||||
|
const std::string XMLNS_AMP = "http://jabber.org/protocol/amp";
|
||||||
|
const std::string XMLNS_IBB = "http://jabber.org/protocol/ibb";
|
||||||
|
const std::string XMLNS_FEATURE_NEG = "http://jabber.org/protocol/feature-neg";
|
||||||
|
const std::string XMLNS_CHATNEG = "http://jabber.org/protocol/chatneg";
|
||||||
|
|
||||||
|
const std::string XMLNS_XHTML_IM = "http://jabber.org/protocol/xhtml-im";
|
||||||
|
const std::string XMLNS_DELAY = "urn:xmpp:delay";
|
||||||
|
const std::string XMLNS_ROSTER = "jabber:iq:roster";
|
||||||
|
const std::string XMLNS_VERSION = "jabber:iq:version";
|
||||||
|
const std::string XMLNS_REGISTER = "jabber:iq:register";
|
||||||
|
|
||||||
|
const std::string XMLNS_PRIVACY = "jabber:iq:privacy";
|
||||||
|
const std::string XMLNS_AUTH = "jabber:iq:auth";
|
||||||
|
const std::string XMLNS_PRIVATE_XML = "jabber:iq:private";
|
||||||
|
const std::string XMLNS_LAST = "jabber:iq:last";
|
||||||
|
const std::string XMLNS_SEARCH = "jabber:iq:search";
|
||||||
|
|
||||||
|
const std::string XMLNS_IQ_OOB = "jabber:iq:oob";
|
||||||
|
const std::string XMLNS_X_DATA = "jabber:x:data";
|
||||||
|
const std::string XMLNS_X_EVENT = "jabber:x:event";
|
||||||
|
const std::string XMLNS_X_OOB = "jabber:x:oob";
|
||||||
|
const std::string XMLNS_X_DELAY = "jabber:x:delay";
|
||||||
|
|
||||||
|
const std::string XMLNS_X_GPGSIGNED = "jabber:x:signed";
|
||||||
|
const std::string XMLNS_X_GPGENCRYPTED = "jabber:x:encrypted";
|
||||||
|
const std::string XMLNS_VCARD_TEMP = "vcard-temp";
|
||||||
|
const std::string XMLNS_X_VCARD_UPDATE = "vcard-temp:x:update";
|
||||||
|
const std::string XMLNS_BOOKMARKS = "storage:bookmarks";
|
||||||
|
|
||||||
|
const std::string XMLNS_ANNOTATIONS = "storage:rosternotes";
|
||||||
|
const std::string XMLNS_ROSTER_DELIMITER = "roster:delimiter";
|
||||||
|
const std::string XMLNS_XMPP_PING = "urn:xmpp:ping";
|
||||||
|
const std::string XMLNS_SI = "http://jabber.org/protocol/si";
|
||||||
|
const std::string XMLNS_SI_FT = "http://jabber.org/protocol/si/profile/file-transfer";
|
||||||
|
|
||||||
|
const std::string XMLNS_BYTESTREAMS = "http://jabber.org/protocol/bytestreams";
|
||||||
|
const std::string XMLNS_MUC = "http://jabber.org/protocol/muc";
|
||||||
|
const std::string XMLNS_MUC_USER = "http://jabber.org/protocol/muc#user";
|
||||||
|
const std::string XMLNS_MUC_ADMIN = "http://jabber.org/protocol/muc#admin";
|
||||||
|
const std::string XMLNS_MUC_UNIQUE = "http://jabber.org/protocol/muc#unique";
|
||||||
|
|
||||||
|
const std::string XMLNS_MUC_OWNER = "http://jabber.org/protocol/muc#owner";
|
||||||
|
const std::string XMLNS_MUC_ROOMINFO = "http://jabber.org/protocol/muc#roominfo";
|
||||||
|
const std::string XMLNS_MUC_ROOMS = "http://jabber.org/protocol/muc#rooms";
|
||||||
|
const std::string XMLNS_MUC_REQUEST = "http://jabber.org/protocol/muc#request";
|
||||||
|
|
||||||
|
const std::string XMLNS_PUBSUB = "http://jabber.org/protocol/pubsub";
|
||||||
|
const std::string XMLNS_PUBSUB_ERRORS = "http://jabber.org/protocol/pubsub#errors";
|
||||||
|
const std::string XMLNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event";
|
||||||
|
const std::string XMLNS_PUBSUB_OWNER = "http://jabber.org/protocol/pubsub#owner";
|
||||||
|
|
||||||
|
const std::string XMLNS_CAPS = "http://jabber.org/protocol/caps";
|
||||||
|
const std::string XMLNS_FT_FASTMODE = "http://affinix.com/jabber/stream";
|
||||||
|
|
||||||
|
const std::string XMLNS_STREAM = "http://etherx.jabber.org/streams";
|
||||||
|
const std::string XMLNS_XMPP_STREAM = "urn:ietf:params:xml:ns:xmpp-streams";
|
||||||
|
const std::string XMLNS_XMPP_STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas";
|
||||||
|
const std::string XMLNS_STREAM_TLS = "urn:ietf:params:xml:ns:xmpp-tls";
|
||||||
|
const std::string XMLNS_STREAM_SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||||
|
|
||||||
|
const std::string XMLNS_STREAM_BIND = "urn:ietf:params:xml:ns:xmpp-bind";
|
||||||
|
const std::string XMLNS_STREAM_SESSION = "urn:ietf:params:xml:ns:xmpp-session";
|
||||||
|
const std::string XMLNS_STREAM_IQAUTH = "http://jabber.org/features/iq-auth";
|
||||||
|
const std::string XMLNS_STREAM_IQREGISTER = "http://jabber.org/features/iq-register";
|
||||||
|
const std::string XMLNS_STREAM_COMPRESS = "http://jabber.org/features/compress";
|
||||||
|
|
||||||
|
const std::string XMLNS_HTTPBIND = "http://jabber.org/protocol/httpbind";
|
||||||
|
const std::string XMLNS_XMPP_BOSH = "urn:xmpp:xbosh";
|
||||||
|
const std::string XMLNS_RECEIPTS = "urn:xmpp:receipts";
|
||||||
|
const std::string XMLNS_NICKNAME = "http://jabber.org/protocol/nick";
|
||||||
|
|
||||||
|
const std::string XMLNS_JINGLE = "urn:xmpp:tmp:jingle";
|
||||||
|
const std::string XMLNS_JINGLE_AUDIO_RTP = "urn:xmpp:tmp:jingle:apps:audio-rtp";
|
||||||
|
const std::string XMLNS_JINGLE_ICE_UDP = "urn:xmpp:tmp:jingle:transports:ice-udp";
|
||||||
|
const std::string XMLNS_JINGLE_RAW_UDP = "urn:xmpp:tmp:jingle:transports:raw-udp";
|
||||||
|
const std::string XMLNS_JINGLE_VIDEO_RTP = "urn:xmpp:tmp:jingle:apps:video-rtp";
|
||||||
|
|
||||||
|
const std::string XMLNS_SHIM = "http://jabber.org/protocol/shim";
|
||||||
|
const std::string XMLNS_ATTENTION = "urn:xmpp:attention:0";
|
||||||
|
|
||||||
|
const std::string XMPP_STREAM_VERSION_MAJOR = "1";
|
||||||
|
const std::string XMPP_STREAM_VERSION_MINOR = "0";
|
||||||
|
const std::string GLOOX_VERSION = "1.0";
|
||||||
|
const std::string GLOOX_CAPS_NODE = "http://camaya.net/gloox";
|
||||||
|
|
||||||
|
const std::string XMLNS = "xmlns";
|
||||||
|
const std::string TYPE = "type";
|
||||||
|
const std::string EmptyString = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* gloox_version()
|
||||||
|
{
|
||||||
|
return gloox::GLOOX_VERSION.c_str();
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Ŝarĝante…
Reference in New Issue