Move the xmpp library to its new name.
This commit is contained in:
10
CHANGES.txt
Normal file
10
CHANGES.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Version 0.7 (unreleased)
|
||||
|
||||
* first public release
|
||||
* basic connection support
|
||||
* event handling through callbacks
|
||||
* stanza creation through local mini-DOM
|
||||
* support SASL DIGEST-MD5 authentication
|
||||
* support SASL PLAIN authentication
|
||||
* support fallback to jabber:auth
|
||||
* support resource binding
|
||||
340
COPYING.txt
Normal file
340
COPYING.txt
Normal file
@@ -0,0 +1,340 @@
|
||||
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 Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<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 Library General
|
||||
Public License instead of this License.
|
||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
libstrophe XMPP client library
|
||||
Copyright (C) 2005 OGG, LLC.
|
||||
|
||||
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 (in a file named COPYING). If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
|
||||
For information about purchasing an exemption for use with
|
||||
proprietary software under terms other than the above, please
|
||||
contact <sales@onlinegamegroup.com>.
|
||||
31
README.txt
Normal file
31
README.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
This is libstrophe, our experimental XMPP C client library.
|
||||
|
||||
Our goals are:
|
||||
|
||||
(a) usable quickly
|
||||
(b) well documented
|
||||
(c) reliable
|
||||
(d) not suck
|
||||
|
||||
And to implement the complete XMPP spec, but not the historical Jabber
|
||||
bits.
|
||||
|
||||
== Build Instructions ==
|
||||
|
||||
We use the 'scons' tool to build the library, unit tests,
|
||||
documentation and examples. You'll need to obtain a copy
|
||||
from http://www.scons.org/ or from your system distributor.
|
||||
|
||||
Once scons is installed, invoke 'scons' in the top-level
|
||||
directory to build the library. This will create a static
|
||||
library (also in the top-level) directory which can be
|
||||
linked into other programs. The public api is defined
|
||||
in <xmpp.h> which is also in the top-level directory.
|
||||
|
||||
Invoke 'scons test' in the top-level directory to execute
|
||||
the unit and self tests.
|
||||
|
||||
The examples/ directory contains some examples of how to
|
||||
use the library; these may be helpful in addition to the
|
||||
API documentation in doc/.
|
||||
|
||||
151
SConstruct
Normal file
151
SConstruct
Normal file
@@ -0,0 +1,151 @@
|
||||
# -*- Mode: python -*-
|
||||
# SCons build specification
|
||||
# see http://www.scons.org if you do not have this tool
|
||||
|
||||
# Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
# This software is provided AS-IS with no warranty, either express or
|
||||
# implied.
|
||||
#
|
||||
# This software is distributed under license and may not be copied,
|
||||
# modified or distributed except as expressly authorized under the
|
||||
# terms of the license contained in the file LICENSE.txt in this
|
||||
# distribution.
|
||||
|
||||
# This source code is distributed under specific license
|
||||
# terms. See LICENSE.txt for details.
|
||||
|
||||
# invoke with 'scons' to build the library
|
||||
|
||||
LIBXMPP_VERSION_MAJOR=0
|
||||
LIBXMPP_VERSION_MINOR=0
|
||||
|
||||
from os.path import join
|
||||
|
||||
# TODO: should use lamda and map to work on python 1.5
|
||||
def path(prefix, list): return [join(prefix, x) for x in list]
|
||||
|
||||
ExpatSources = Split("""
|
||||
xmlparse.c
|
||||
xmltok.c
|
||||
xmlrole.c
|
||||
""")
|
||||
|
||||
Sources = Split("""
|
||||
ctx.c
|
||||
conn.c
|
||||
parser.c
|
||||
handler.c
|
||||
auth.c
|
||||
event.c
|
||||
stanza.c
|
||||
jid.c
|
||||
sock.c
|
||||
hash.c
|
||||
sasl.c
|
||||
sha1.c
|
||||
md5.c
|
||||
util.c
|
||||
""")
|
||||
|
||||
Headers = Split("""
|
||||
xmpp.h
|
||||
common.h
|
||||
sock.h
|
||||
hash.h
|
||||
sha1.h
|
||||
md5.h
|
||||
util.h
|
||||
""")
|
||||
|
||||
Examples = Split("""
|
||||
basic.c
|
||||
active.c
|
||||
roster.c
|
||||
""")
|
||||
|
||||
env = Environment()
|
||||
if env['CC'] == 'gcc':
|
||||
env.Append(CCFLAGS=["-g", "-Wall"])
|
||||
|
||||
expatenv = env.Copy()
|
||||
# feature defs
|
||||
expatenv.Append(CCFLAGS=" -DXML_DTD")
|
||||
expatenv.Append(CCFLAGS=" -DXML_NS")
|
||||
expatenv.Append(CCFLAGS=" -DXML_CONTEXT_BYTES=1024")
|
||||
# sys config defs (these should be autodetected)
|
||||
expatenv.Append(CCFLAGS=" -DBYTEORDER=4321")
|
||||
expatenv.Append(CCFLAGS=" -DHAVE_MEMMOVE")
|
||||
expatenv.Append(CCFLAGS=" -DHAVE_UNISTD")
|
||||
|
||||
expat = expatenv.Library('expat', path(join('expat', 'lib'), ExpatSources))
|
||||
Default(expat)
|
||||
|
||||
xmppenv = env.Copy()
|
||||
xmppenv.Append(CPPPATH=['.', 'src', join('expat','lib')])
|
||||
|
||||
xmpp = xmppenv.Library('xmpp', path("src", Sources))
|
||||
Default(xmpp)
|
||||
|
||||
exenv = xmppenv.Copy()
|
||||
exenv.Append(LIBS=["xmpp", "expat"])
|
||||
exenv.Append(LIBPATH=["."])
|
||||
for e in path("examples", Examples):
|
||||
example = exenv.Program(e)
|
||||
Default(example)
|
||||
|
||||
# generate a MSVC project for convenience
|
||||
# TODO: this doesn't seem to be implemented?
|
||||
#env.MSVSProject(target = 'xmpp' + env['MSVSPROJECTSUFFIX'],
|
||||
# srcs = path("src", Sources), incs = path("src", Headers),
|
||||
# variant = 'Release', buildtarget = xmpp)
|
||||
|
||||
### unit and regression tests
|
||||
|
||||
import unittest
|
||||
from os import popen
|
||||
|
||||
class RunnableTestCase(unittest.TestCase):
|
||||
def __init__(self, file):
|
||||
self.file = str(file)
|
||||
unittest.TestCase.__init__(self)
|
||||
def shortDescription(self):
|
||||
return "Running '%s'" % self.file
|
||||
def runTest(self):
|
||||
'''C tests should return '0' on exit'''
|
||||
test = popen(self.file)
|
||||
self.failIf(test.close(), "unit '%s' did not exit normally" % self.file)
|
||||
|
||||
def testcase_runner(target, source, env):
|
||||
suite = unittest.TestSuite()
|
||||
for test in source:
|
||||
#suite.addTest(RunnableTestCase(test[0]))
|
||||
suite.addTest(RunnableTestCase(test))
|
||||
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
env.Execute(Touch(str(target[0])))
|
||||
|
||||
testenv = env.Copy()
|
||||
testenv.Append(CPPPATH=['.', 'src', join('expat','lib')])
|
||||
testenv.Append(LIBS=['xmpp', 'expat'])
|
||||
testenv.Append(LIBPATH=['.'])
|
||||
|
||||
import SCons.Node
|
||||
test_builder = Builder(action = testcase_runner,
|
||||
multi = 1)
|
||||
|
||||
testenv.Append(BUILDERS = {'TestCase' : test_builder})
|
||||
|
||||
# application source files for unit tests
|
||||
tests = Split("""
|
||||
test_ctx.c
|
||||
test_sock.c
|
||||
test_hash.c
|
||||
test_base64.c
|
||||
test_sasl.c
|
||||
test_jid.c
|
||||
""")
|
||||
|
||||
# build each test and add it to the 'test' pseudo target
|
||||
for test in path("tests", tests):
|
||||
tests = testenv.TestCase('test_stamp', testenv.Program(test))
|
||||
testenv.AlwaysBuild(tests)
|
||||
testenv.Alias('test', tests)
|
||||
4
TODO.txt
Normal file
4
TODO.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
* thread safety: specifically the reference counting is NOT THREAD SAFE
|
||||
and we should fix this later to make reference count operations atomic.
|
||||
|
||||
* SRV lookups
|
||||
122
examples/active.c
Normal file
122
examples/active.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/* active.c
|
||||
** XMPP client library -- basic usage example
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
/* This example demonstrates basic handler functions by printing out
|
||||
** active resources on a jabberd 2.x server. This program requires
|
||||
** an admin account on a jabberd 2.x account in order to run.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xmpp.h>
|
||||
|
||||
int handle_reply(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *query, *item;
|
||||
char *type;
|
||||
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
if (strcmp("type", "error") == 0)
|
||||
fprintf(stderr, "ERROR: query failed\n");
|
||||
else {
|
||||
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
||||
printf("Active Sessions:\n");
|
||||
for (item = xmpp_stanza_get_children(query); item;
|
||||
item = xmpp_stanza_get_next(item))
|
||||
printf("\t %s\n", xmpp_stanza_get_attribute(item, "jid"));
|
||||
printf("END OF LIST\n");
|
||||
}
|
||||
|
||||
/* disconnect */
|
||||
xmpp_disconnect(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
||||
const int error, xmpp_stream_error_t * const stream_error,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
xmpp_stanza_t *iq, *query;
|
||||
|
||||
if (status == XMPP_CONN_CONNECT) {
|
||||
fprintf(stderr, "DEBUG: connected\n");
|
||||
|
||||
/* create iq stanza for request */
|
||||
iq = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(iq, "iq");
|
||||
xmpp_stanza_set_type(iq, "get");
|
||||
xmpp_stanza_set_id(iq, "active1");
|
||||
xmpp_stanza_set_attribute(iq, "to", "xxxxxxxxx.com");
|
||||
|
||||
query = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(query, "query");
|
||||
xmpp_stanza_set_ns(query, XMPP_NS_DISCO_ITEMS);
|
||||
xmpp_stanza_set_attribute(query, "node", "sessions");
|
||||
|
||||
xmpp_stanza_add_child(iq, query);
|
||||
|
||||
/* we can release the stanza since it belongs to iq now */
|
||||
xmpp_stanza_release(query);
|
||||
|
||||
/* set up reply handler */
|
||||
xmpp_id_handler_add(conn, handle_reply, "active1", ctx);
|
||||
|
||||
/* send out the stanza */
|
||||
xmpp_send(conn, iq);
|
||||
|
||||
/* release the stanza */
|
||||
xmpp_stanza_release(iq);
|
||||
} else {
|
||||
fprintf(stderr, "DEBUG: disconnected\n");
|
||||
xmpp_stop(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Usage: active <jid> <pass> <server>\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create a context */
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
|
||||
/* create a connection */
|
||||
conn = xmpp_conn_new(ctx);
|
||||
|
||||
/* setup authentication information */
|
||||
xmpp_conn_set_jid(conn, argv[1]);
|
||||
xmpp_conn_set_pass(conn, argv[2]);
|
||||
|
||||
/* initiate connection */
|
||||
xmpp_connect_client(conn, argv[3], conn_handler, ctx);
|
||||
|
||||
/* start the event loop */
|
||||
xmpp_run(ctx);
|
||||
|
||||
/* release our connection and context */
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
66
examples/basic.c
Normal file
66
examples/basic.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/* basic.c
|
||||
** XMPP client library -- basic usage example
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <xmpp.h>
|
||||
|
||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
||||
const int error, xmpp_stream_error_t * const stream_error,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
|
||||
if (status == XMPP_CONN_CONNECT) {
|
||||
fprintf(stderr, "DEBUG: connected\n");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "DEBUG: disconnected\n");
|
||||
xmpp_stop(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Usage: basic <jid> <pass> <server>\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create a context */
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
|
||||
/* create a connection */
|
||||
conn = xmpp_conn_new(ctx);
|
||||
|
||||
/* setup authentication information */
|
||||
xmpp_conn_set_jid(conn, argv[1]);
|
||||
xmpp_conn_set_pass(conn, argv[2]);
|
||||
|
||||
/* initiate connection */
|
||||
xmpp_connect_client(conn, argv[3], conn_handler, ctx);
|
||||
|
||||
/* start the event loop */
|
||||
xmpp_run(ctx);
|
||||
|
||||
/* release our connection and context */
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
127
examples/roster.c
Normal file
127
examples/roster.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/* roster.c
|
||||
** XMPP client library -- handler example
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
/* This example demonstrates basic handler functions by printing out
|
||||
** the user's roster.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xmpp.h>
|
||||
|
||||
int handle_reply(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *query, *item;
|
||||
char *type, *name;
|
||||
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
if (strcmp("type", "error") == 0)
|
||||
fprintf(stderr, "ERROR: query failed\n");
|
||||
else {
|
||||
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
||||
printf("Roster:\n");
|
||||
for (item = xmpp_stanza_get_children(query); item;
|
||||
item = xmpp_stanza_get_next(item))
|
||||
if ((name = xmpp_stanza_get_attribute(item, "name")))
|
||||
printf("\t %s (%s) sub=%s\n",
|
||||
name,
|
||||
xmpp_stanza_get_attribute(item, "jid"),
|
||||
xmpp_stanza_get_attribute(item, "subscription"));
|
||||
else
|
||||
printf("\t %s sub=%s\n",
|
||||
xmpp_stanza_get_attribute(item, "jid"),
|
||||
xmpp_stanza_get_attribute(item, "subscription"));
|
||||
printf("END OF LIST\n");
|
||||
}
|
||||
|
||||
/* disconnect */
|
||||
xmpp_disconnect(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
||||
const int error, xmpp_stream_error_t * const stream_error,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||
xmpp_stanza_t *iq, *query;
|
||||
|
||||
if (status == XMPP_CONN_CONNECT) {
|
||||
fprintf(stderr, "DEBUG: connected\n");
|
||||
|
||||
/* create iq stanza for request */
|
||||
iq = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(iq, "iq");
|
||||
xmpp_stanza_set_type(iq, "get");
|
||||
xmpp_stanza_set_id(iq, "roster1");
|
||||
|
||||
query = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(query, "query");
|
||||
xmpp_stanza_set_ns(query, XMPP_NS_ROSTER);
|
||||
|
||||
xmpp_stanza_add_child(iq, query);
|
||||
|
||||
/* we can release the stanza since it belongs to iq now */
|
||||
xmpp_stanza_release(query);
|
||||
|
||||
/* set up reply handler */
|
||||
xmpp_id_handler_add(conn, handle_reply, "roster1", ctx);
|
||||
|
||||
/* send out the stanza */
|
||||
xmpp_send(conn, iq);
|
||||
|
||||
/* release the stanza */
|
||||
xmpp_stanza_release(iq);
|
||||
} else {
|
||||
fprintf(stderr, "DEBUG: disconnected\n");
|
||||
xmpp_stop(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_t *conn;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Usage: active <jid> <pass> <server>\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create a context */
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
|
||||
/* create a connection */
|
||||
conn = xmpp_conn_new(ctx);
|
||||
|
||||
/* setup authentication information */
|
||||
xmpp_conn_set_jid(conn, argv[1]);
|
||||
xmpp_conn_set_pass(conn, argv[2]);
|
||||
|
||||
/* initiate connection */
|
||||
xmpp_connect_client(conn, argv[3], conn_handler, ctx);
|
||||
|
||||
/* start the event loop */
|
||||
xmpp_run(ctx);
|
||||
|
||||
/* release our connection and context */
|
||||
xmpp_conn_release(conn);
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
9
names.txt
Normal file
9
names.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Melissa's suggestions:
|
||||
something to do with note passing in high school
|
||||
something related to telephones/switchboards, etc
|
||||
|
||||
ganglion
|
||||
thalamus
|
||||
|
||||
jack's suggestion:
|
||||
eloquence and eloq for sure. libeloq etc.
|
||||
634
src/auth.c
Normal file
634
src/auth.c
Normal file
@@ -0,0 +1,634 @@
|
||||
/* auth.c
|
||||
** XMPP client library -- auth functions and handlers
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
|
||||
#define FEATURES_TIMEOUT 2000 /* 2 seconds */
|
||||
#define BIND_TIMEOUT 2000 /* 2 seconds */
|
||||
#define LEGACY_TIMEOUT 2000 /* 2 seconds */
|
||||
|
||||
static void _auth(xmpp_conn_t * const conn);
|
||||
static void _handle_open_sasl(xmpp_conn_t * const conn);
|
||||
static int _handle_missing_legacy(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
static int _handle_legacy(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_features_sasl(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_digestmd5_default(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
|
||||
static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
static int _handle_missing_bind(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
static int _handle_bind(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
|
||||
/* stream:features handlers */
|
||||
|
||||
static int _handle_missing_features(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_debug(conn->ctx, "xmpp", "didn't get stream features");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_features(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *child, *mech;
|
||||
char *text;
|
||||
|
||||
/* remove the handler that detects missing stream:features */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_features);
|
||||
|
||||
/* check for TLS */
|
||||
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
|
||||
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0))
|
||||
conn->tls_support = 1;
|
||||
|
||||
/* check for SASL */
|
||||
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
|
||||
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_SASL) == 0)) {
|
||||
for (mech = xmpp_stanza_get_children(child); mech;
|
||||
mech = xmpp_stanza_get_next(mech)) {
|
||||
if (strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
|
||||
text = xmpp_stanza_get_text(mech);
|
||||
if (strcasecmp(text, "PLAIN") == 0)
|
||||
conn->sasl_support |= SASL_MASK_PLAIN;
|
||||
else if (strcasecmp(text, "DIGEST-MD5") == 0)
|
||||
conn->sasl_support |= SASL_MASK_DIGESTMD5;
|
||||
|
||||
xmpp_free(conn->ctx, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Implement TLS */
|
||||
|
||||
_auth(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns the correct auth id for a component or a client.
|
||||
* returned string must be freed by caller */
|
||||
static char *_get_authid(xmpp_conn_t * const conn)
|
||||
{
|
||||
char *authid = NULL;
|
||||
char *at;
|
||||
|
||||
if (conn->type == XMPP_CLIENT) {
|
||||
/* authid is the node portion of jid */
|
||||
if (!conn->jid) return NULL;
|
||||
authid = xmpp_strdup(conn->ctx, conn->jid);
|
||||
if (!authid) return NULL;
|
||||
at = strchr(authid, '@');
|
||||
at[0] = 0;
|
||||
}
|
||||
|
||||
return authid;
|
||||
}
|
||||
|
||||
static int _handle_digestmd5_default(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *name;
|
||||
int ret = 1;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp",
|
||||
"handle digest-md5 (default) called for %s", name);
|
||||
|
||||
if (strcmp(name, "failure") == 0) {
|
||||
/* TODO: handle errors */
|
||||
xmpp_debug(conn->ctx, "xmpp", "SASL DIGEST-MD5 auth failed!");
|
||||
/* fall back to next auth method */
|
||||
_auth(conn);
|
||||
|
||||
/* remove handler */
|
||||
ret = 0;
|
||||
} else if (strcmp(name, "success") == 0) {
|
||||
/* SASL DIGEST-MD5 auth successful, we need to restart the stream */
|
||||
xmpp_debug(conn->ctx, "xmpp", "SASL DIGEST-MD5 auth successful");
|
||||
|
||||
/* reset parser */
|
||||
parser_prepare_reset(conn, _handle_open_sasl);
|
||||
|
||||
/* send stream tag */
|
||||
conn_open_stream(conn);
|
||||
|
||||
/* remove handler */
|
||||
ret = 0;
|
||||
} else {
|
||||
/* got unexpected reply */
|
||||
xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL "
|
||||
"DIGEST-MD5 authentication.");
|
||||
xmpp_disconnect(conn);
|
||||
|
||||
/* remove handler */
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* handle the challenge phase of digest auth */
|
||||
static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *text;
|
||||
char *response;
|
||||
xmpp_stanza_t *auth, *authdata;
|
||||
char *name;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp",\
|
||||
"handle digest-md5 (challenge) called for %s", name);
|
||||
|
||||
if (strcmp(name, "challenge") == 0) {
|
||||
text = xmpp_stanza_get_text(stanza);
|
||||
response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
|
||||
if (!response) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_free(conn->ctx, text);
|
||||
|
||||
auth = xmpp_stanza_new(conn->ctx);
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_stanza_set_name(auth, "response");
|
||||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xmpp_stanza_set_text(authdata, response);
|
||||
xmpp_free(conn->ctx, response);
|
||||
|
||||
xmpp_stanza_add_child(auth, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
handler_add(conn, _handle_digestmd5_rspauth,
|
||||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||||
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
|
||||
} else {
|
||||
return _handle_digestmd5_default(conn, stanza, userdata);
|
||||
}
|
||||
|
||||
/* remove ourselves */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle the rspauth phase of digest auth */
|
||||
static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *auth;
|
||||
char *name;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp",
|
||||
"handle digest-md5 (rspauth) called for %s", name);
|
||||
|
||||
|
||||
if (strcmp(name, "challenge") == 0) {
|
||||
/* assume it's an rspauth response */
|
||||
auth = xmpp_stanza_new(conn->ctx);
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_stanza_set_name(auth, "response");
|
||||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
} else {
|
||||
return _handle_digestmd5_default(conn, stanza, userdata);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _handle_plain(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *name;
|
||||
int ret = 1;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp", "handle plain called for %s", name);
|
||||
|
||||
/* the server should send a <success> or <failure> stanza */
|
||||
if (strcmp(name, "failure") == 0) {
|
||||
/* TODO: handle errors */
|
||||
|
||||
/* fall back to next auth method */
|
||||
_auth(conn);
|
||||
} else if (strcmp(name, "success") == 0) {
|
||||
/* SASL PLAIN auth successful, we need to restart the stream */
|
||||
xmpp_debug(conn->ctx, "xmpp", "SASL PLAIN auth successful");
|
||||
|
||||
/* reset parser */
|
||||
parser_prepare_reset(conn, _handle_open_sasl);
|
||||
|
||||
/* remove this handler */
|
||||
ret = 0;
|
||||
|
||||
/* send stream tag */
|
||||
conn_open_stream(conn);
|
||||
} else {
|
||||
/* got unexpected reply */
|
||||
xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL PLAIN "\
|
||||
"authentication.");
|
||||
xmpp_disconnect(conn);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
|
||||
const char * const mechanism)
|
||||
{
|
||||
xmpp_stanza_t *auth;
|
||||
|
||||
/* build auth stanza */
|
||||
auth = xmpp_stanza_new(conn->ctx);
|
||||
if (auth) {
|
||||
xmpp_stanza_set_name(auth, "auth");
|
||||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||||
xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
/* authenticate the connection
|
||||
* this may get called multiple times. if any auth method fails,
|
||||
* this will get called again until one auth method succeeds or every
|
||||
* method fails */
|
||||
static void _auth(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_stanza_t *auth, *authdata, *query, *child, *iq;
|
||||
char *str, *authid;
|
||||
|
||||
if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
|
||||
auth = _make_sasl_auth(conn, "DIGEST-MD5");
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
handler_add(conn, _handle_digestmd5_challenge,
|
||||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||||
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
|
||||
/* SASL DIGEST-MD5 was tried, unset flag */
|
||||
conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
|
||||
} else if (conn->sasl_support & SASL_MASK_PLAIN) {
|
||||
auth = _make_sasl_auth(conn, "PLAIN");
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
authid = _get_authid(conn);
|
||||
if (!authid) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
str = sasl_plain(conn->ctx, authid, conn->pass);
|
||||
if (!str) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_text(authdata, str);
|
||||
xmpp_free(conn->ctx, str);
|
||||
|
||||
xmpp_stanza_add_child(auth, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
handler_add(conn, _handle_plain,
|
||||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||||
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
|
||||
/* SASL PLAIN was tried */
|
||||
conn->sasl_support &= ~SASL_MASK_PLAIN;
|
||||
} else if (conn->type == XMPP_CLIENT) {
|
||||
/* legacy client authentication */
|
||||
|
||||
iq = xmpp_stanza_new(conn->ctx);
|
||||
if (!iq) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(iq, "iq");
|
||||
xmpp_stanza_set_type(iq, "set");
|
||||
xmpp_stanza_set_id(iq, "_xmpp_auth1");
|
||||
|
||||
query = xmpp_stanza_new(conn->ctx);
|
||||
if (!query) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(query, "query");
|
||||
xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
|
||||
xmpp_stanza_add_child(iq, query);
|
||||
xmpp_stanza_release(query);
|
||||
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(child, "username");
|
||||
xmpp_stanza_add_child(query, child);
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
str = xmpp_jid_node(conn->ctx, conn->jid);
|
||||
xmpp_stanza_set_text(authdata, str);
|
||||
xmpp_free(conn->ctx, str);
|
||||
xmpp_stanza_add_child(child, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(child, "password");
|
||||
xmpp_stanza_add_child(query, child);
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_text(authdata, conn->pass);
|
||||
xmpp_stanza_add_child(child, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(child, "resource");
|
||||
xmpp_stanza_add_child(query, child);
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
str = xmpp_jid_resource(conn->ctx, conn->jid);
|
||||
if (str)
|
||||
xmpp_stanza_set_text(authdata, str);
|
||||
xmpp_free(conn->ctx, str);
|
||||
xmpp_stanza_add_child(child, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
|
||||
handler_add_timed(conn, _handle_missing_legacy,
|
||||
LEGACY_TIMEOUT, NULL);
|
||||
|
||||
xmpp_send(conn, iq);
|
||||
xmpp_stanza_release(iq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* this is called on initial stream open */
|
||||
void auth_handle_open(xmpp_conn_t * const conn)
|
||||
{
|
||||
/* reset all timed handlers */
|
||||
handler_reset_timed(conn, 0);
|
||||
|
||||
/* setup handlers for incoming <stream:features> */
|
||||
handler_add(conn, _handle_features,
|
||||
NULL, "stream:features", NULL, NULL);
|
||||
handler_add_timed(conn, _handle_missing_features,
|
||||
FEATURES_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
/* called when stream:stream tag received after SASL auth */
|
||||
static void _handle_open_sasl(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
|
||||
|
||||
/* setup stream:features handlers */
|
||||
handler_add(conn, _handle_features_sasl,
|
||||
NULL, "stream:features", NULL, NULL);
|
||||
handler_add_timed(conn, _handle_missing_features_sasl,
|
||||
FEATURES_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
static int _handle_features_sasl(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *bind, *iq;
|
||||
|
||||
/* remove missing features handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
|
||||
|
||||
/* we are expecting <bind/> */
|
||||
bind = xmpp_stanza_get_child_by_name(stanza, "bind");
|
||||
if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) {
|
||||
/* bind resource */
|
||||
|
||||
/* setup response handlers */
|
||||
handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL);
|
||||
handler_add_timed(conn, _handle_missing_bind,
|
||||
BIND_TIMEOUT, NULL);
|
||||
|
||||
/* send bind response */
|
||||
/* FIXME: this should detect whether a resource is present in
|
||||
conn->jid and bind that instead. right now we let the server bind
|
||||
us */
|
||||
iq = xmpp_stanza_new(conn->ctx);
|
||||
if (!iq) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xmpp_stanza_set_name(iq, "iq");
|
||||
xmpp_stanza_set_type(iq, "set");
|
||||
xmpp_stanza_set_id(iq, "_xmpp_bind1");
|
||||
|
||||
bind = xmpp_stanza_copy(bind);
|
||||
if (!bind) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_stanza_add_child(iq, bind);
|
||||
xmpp_stanza_release(bind);
|
||||
|
||||
/* send bind request */
|
||||
xmpp_send(conn, iq);
|
||||
xmpp_stanza_release(iq);
|
||||
} else {
|
||||
/* can't bind, disconnect */
|
||||
xmpp_error(conn->ctx, "xmpp", "Stream features does not allow "\
|
||||
"resource bind.");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Did not receive stream features "\
|
||||
"after SASL authentication.");
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_bind(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *type;
|
||||
|
||||
/* delete missing bind handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_bind);
|
||||
|
||||
/* server has replied to bind request */
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
if (!type) {
|
||||
xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "error") == 0) {
|
||||
xmpp_error(conn->ctx, "xmpp", "Binding failed.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "result") == 0) {
|
||||
/* TODO: extract resource if present */
|
||||
xmpp_debug(conn->ctx, "xmpp", "Bind successful.");
|
||||
conn->authenticated = 1;
|
||||
|
||||
/* call connection handler */
|
||||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_bind(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_legacy(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *type, *name;
|
||||
|
||||
/* delete missing handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_legacy);
|
||||
|
||||
/* server responded to legacy auth request */
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
if (!type || strcmp(name, "iq") != 0) {
|
||||
xmpp_error(conn->ctx, "xmpp", "Server sent us an unexpected response "\
|
||||
"to legacy authentication request.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "error") == 0) {
|
||||
/* legacy client auth failed, no more fallbacks */
|
||||
xmpp_error(conn->ctx, "xmpp", "Legacy client authentication failed.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "result") == 0) {
|
||||
/* auth succeeded */
|
||||
xmpp_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
|
||||
|
||||
conn->authenticated = 1;
|
||||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||||
} else {
|
||||
xmpp_error(conn->ctx, "xmpp", "Server sent us a legacy authentication "\
|
||||
"response with a bad type.");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_legacy(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\
|
||||
"authentication request.");
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
272
src/common.h
Normal file
272
src/common.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/* common.h
|
||||
** XMPP client library -- internal common structures
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_XMPP_COMMON_H__
|
||||
#define __LIBXMPP_XMPP_COMMON_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#ifndef _WIN32
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "sock.h"
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "expat.h"
|
||||
|
||||
/** run-time context **/
|
||||
|
||||
typedef enum {
|
||||
XMPP_LOOP_NOTSTARTED,
|
||||
XMPP_LOOP_RUNNING,
|
||||
XMPP_LOOP_QUIT
|
||||
} xmpp_loop_status_t;
|
||||
|
||||
typedef struct _xmpp_connlist_t {
|
||||
xmpp_conn_t *conn;
|
||||
struct _xmpp_connlist_t *next;
|
||||
} xmpp_connlist_t;
|
||||
|
||||
struct _xmpp_ctx_t {
|
||||
const xmpp_mem_t *mem;
|
||||
const xmpp_log_t *log;
|
||||
|
||||
xmpp_loop_status_t loop_status;
|
||||
xmpp_connlist_t *connlist;
|
||||
};
|
||||
|
||||
|
||||
/* convenience functions for accessing the context */
|
||||
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size);
|
||||
void xmpp_free(const xmpp_ctx_t * const ctx, void *p);
|
||||
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
||||
const size_t size);
|
||||
char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s);
|
||||
|
||||
void xmpp_log(const xmpp_ctx_t * const ctx,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
va_list ap);
|
||||
|
||||
/* wrappers for xmpp_log at specific levels */
|
||||
void xmpp_error(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
void xmpp_warn(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
void xmpp_info(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
void xmpp_debug(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
|
||||
/** jid */
|
||||
/* these return new strings that must be xmpp_free()'d */
|
||||
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
||||
const char *domain,
|
||||
const char *resource);
|
||||
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
|
||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
|
||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
|
||||
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
|
||||
|
||||
|
||||
/** connection **/
|
||||
|
||||
/* opaque connection object */
|
||||
typedef enum {
|
||||
XMPP_STATE_DISCONNECTED,
|
||||
XMPP_STATE_CONNECTING,
|
||||
XMPP_STATE_CONNECTED
|
||||
} xmpp_conn_state_t;
|
||||
|
||||
typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
|
||||
struct _xmpp_send_queue_t {
|
||||
char *data;
|
||||
size_t len;
|
||||
size_t written;
|
||||
|
||||
xmpp_send_queue_t *next;
|
||||
};
|
||||
|
||||
typedef struct _xmpp_handlist_t xmpp_handlist_t;
|
||||
struct _xmpp_handlist_t {
|
||||
/* common members */
|
||||
int user_handler;
|
||||
void *handler;
|
||||
void *userdata;
|
||||
int enabled; /* handlers are added disabled and enabled after the
|
||||
* handler chain is processed to prevent stanzas from
|
||||
* getting processed by newly added handlers */
|
||||
xmpp_handlist_t *next;
|
||||
|
||||
union {
|
||||
/* timed handlers */
|
||||
struct {
|
||||
unsigned long period;
|
||||
uint64_t last_stamp;
|
||||
};
|
||||
/* id handlers */
|
||||
struct {
|
||||
char *id;
|
||||
};
|
||||
/* normal handlers */
|
||||
struct {
|
||||
char *ns;
|
||||
char *name;
|
||||
char *type;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#define SASL_MASK_PLAIN 0x01
|
||||
#define SASL_MASK_DIGESTMD5 0x02
|
||||
#define SASL_MASK_ANONYMOUS 0x04
|
||||
|
||||
typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn);
|
||||
|
||||
struct _xmpp_conn_t {
|
||||
unsigned int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_type_t type;
|
||||
|
||||
xmpp_conn_state_t state;
|
||||
int error;
|
||||
xmpp_stream_error_t *stream_error;
|
||||
sock_t sock;
|
||||
|
||||
int tls_support;
|
||||
int sasl_support; /* if true, field is a bitfield of supported
|
||||
mechanisms */
|
||||
|
||||
char *lang;
|
||||
char *domain;
|
||||
char *jid;
|
||||
char *pass;
|
||||
char *stream_id;
|
||||
|
||||
/* send queue and parameters */
|
||||
int blocking_send;
|
||||
int send_queue_max;
|
||||
int send_queue_len;
|
||||
xmpp_send_queue_t *send_queue_head;
|
||||
xmpp_send_queue_t *send_queue_tail;
|
||||
|
||||
/* xml parser */
|
||||
int reset_parser;
|
||||
XML_Parser parser;
|
||||
int depth;
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
/* event handlers */
|
||||
|
||||
/* stream open handler */
|
||||
xmpp_open_handler open_handler;
|
||||
|
||||
/* user handlers only get called after authentication */
|
||||
int authenticated;
|
||||
|
||||
/* connection events handler */
|
||||
xmpp_conn_handler conn_handler;
|
||||
void *userdata;
|
||||
|
||||
/* other handlers */
|
||||
xmpp_handlist_t *timed_handlers;
|
||||
hash_t *id_handlers;
|
||||
xmpp_handlist_t *handlers;
|
||||
};
|
||||
|
||||
void conn_disconnect(xmpp_conn_t * const conn);
|
||||
void conn_disconnect_clean(xmpp_conn_t * const conn);
|
||||
void conn_open_stream(xmpp_conn_t * const conn);
|
||||
|
||||
void xmpp_send_raw_string(xmpp_conn_t * const conn,
|
||||
const char * const fmt, ...);
|
||||
void xmpp_send_raw(xmpp_conn_t * const conn,
|
||||
const char * const data, const size_t len);
|
||||
|
||||
|
||||
typedef enum {
|
||||
XMPP_STANZA_UNKNOWN,
|
||||
XMPP_STANZA_TEXT,
|
||||
XMPP_STANZA_TAG
|
||||
} xmpp_stanza_type_t;
|
||||
|
||||
struct _xmpp_stanza_t {
|
||||
int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
|
||||
xmpp_stanza_type_t type;
|
||||
|
||||
xmpp_stanza_t *prev;
|
||||
xmpp_stanza_t *next;
|
||||
xmpp_stanza_t *children;
|
||||
xmpp_stanza_t *parent;
|
||||
|
||||
char *data;
|
||||
|
||||
hash_t *attributes;
|
||||
};
|
||||
|
||||
void xmpp_stanza_set_attributes(xmpp_stanza_t * const stanza,
|
||||
const char * const * const attr);
|
||||
|
||||
/* parser functions */
|
||||
void parser_handle_start(void *userdata,
|
||||
const XML_Char *name,
|
||||
const XML_Char **attr);
|
||||
void parser_handle_character(void *userdata, const XML_Char *s, int len);
|
||||
void parser_handle_end(void *userdata, const XML_Char *name);
|
||||
void parser_prepare_reset(xmpp_conn_t * const conn,
|
||||
xmpp_open_handler handler);
|
||||
int parser_reset(xmpp_conn_t * const conn);
|
||||
|
||||
/* handler management */
|
||||
void handler_fire_stanza(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza);
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t * const ctx);
|
||||
void handler_reset_timed(xmpp_conn_t *conn, int user_only);
|
||||
void handler_add_timed(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata);
|
||||
void handler_add_id(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata);
|
||||
void handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata);
|
||||
|
||||
/* utility functions */
|
||||
void disconnect_mem_error(xmpp_conn_t * const conn);
|
||||
|
||||
/* auth functions */
|
||||
void auth_handle_open(xmpp_conn_t * const conn);
|
||||
|
||||
#endif /* __LIBXMPP_XMPP_COMMON_H__ */
|
||||
398
src/conn.c
Normal file
398
src/conn.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/* conn.c
|
||||
** connection object functions
|
||||
**
|
||||
** Copyright 2005 OGG, LLC
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
#define DEFAULT_SEND_QUEUE_MAX 64
|
||||
#define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
|
||||
|
||||
static int _disconnect_cleanup(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
|
||||
|
||||
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
xmpp_conn_t *conn = NULL;
|
||||
xmpp_connlist_t *tail, *item;
|
||||
|
||||
if (ctx == NULL) return NULL;
|
||||
conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
|
||||
|
||||
if (conn != NULL) {
|
||||
conn->ctx = ctx;
|
||||
|
||||
conn->type = XMPP_UNKNOWN;
|
||||
conn->sock = -1;
|
||||
conn->error = 0;
|
||||
conn->stream_error = NULL;
|
||||
|
||||
/* default send parameters */
|
||||
conn->blocking_send = 0;
|
||||
conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
|
||||
conn->send_queue_len = 0;
|
||||
conn->send_queue_head = NULL;
|
||||
conn->send_queue_tail = NULL;
|
||||
|
||||
conn->lang = xmpp_strdup(conn->ctx, "en");
|
||||
if (!conn->lang) {
|
||||
xmpp_free(conn->ctx, conn);
|
||||
return NULL;
|
||||
}
|
||||
conn->domain = NULL;
|
||||
conn->jid = NULL;
|
||||
conn->pass = NULL;
|
||||
conn->stream_id = NULL;
|
||||
|
||||
conn->tls_support = 0;
|
||||
conn->sasl_support = 0;
|
||||
|
||||
conn->parser = NULL;
|
||||
conn->stanza = NULL;
|
||||
parser_prepare_reset(conn, auth_handle_open);
|
||||
|
||||
conn->authenticated = 0;
|
||||
conn->conn_handler = NULL;
|
||||
conn->userdata = NULL;
|
||||
conn->timed_handlers = NULL;
|
||||
/* we own (and will free) the hash values */
|
||||
conn->id_handlers = hash_new(conn->ctx, 32, NULL);
|
||||
conn->handlers = NULL;
|
||||
|
||||
/* give the caller a reference to connection */
|
||||
conn->ref = 1;
|
||||
|
||||
/* add connection to ctx->connlist */
|
||||
tail = conn->ctx->connlist;
|
||||
while (tail && tail->next) tail = tail->next;
|
||||
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t));
|
||||
if (!item) {
|
||||
xmpp_error(conn->ctx, "xmpp", "failed to allocate memory");
|
||||
xmpp_free(conn->ctx, conn->lang);
|
||||
XML_ParserFree(conn->parser);
|
||||
xmpp_free(conn->ctx, conn);
|
||||
conn = NULL;
|
||||
} else {
|
||||
item->conn = conn;
|
||||
item->next = NULL;
|
||||
|
||||
if (tail) tail->next = item;
|
||||
else conn->ctx->connlist = item;
|
||||
}
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
xmpp_conn_t * xmpp_conn_clone(xmpp_conn_t * const conn)
|
||||
{
|
||||
conn->ref++;
|
||||
return conn;
|
||||
}
|
||||
|
||||
void xmpp_conn_release(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_connlist_t *item, *prev;
|
||||
xmpp_handlist_t *hlitem, *thli;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
|
||||
if (conn->ref > 1)
|
||||
conn->ref--;
|
||||
else {
|
||||
ctx = conn->ctx;
|
||||
|
||||
/* remove connection from context's connlist */
|
||||
if (ctx->connlist->conn == conn) {
|
||||
item = ctx->connlist;
|
||||
ctx->connlist = item->next;
|
||||
xmpp_free(ctx, item);
|
||||
} else {
|
||||
prev = NULL;
|
||||
item = ctx->connlist;
|
||||
while (item && item->conn != conn) {
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
xmpp_error(ctx, "xmpp", "Connection not in context's list\n");
|
||||
} else {
|
||||
prev->next = item->next;
|
||||
xmpp_free(ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
/* free handler stuff
|
||||
* note that userdata is the responsibility of the client
|
||||
* and the handler pointers don't need to be freed since they
|
||||
* are pointers to functions */
|
||||
|
||||
hlitem = conn->timed_handlers;
|
||||
while (hlitem) {
|
||||
thli = hlitem;
|
||||
hlitem = hlitem->next;
|
||||
|
||||
xmpp_free(ctx, thli);
|
||||
}
|
||||
|
||||
/* id handlers
|
||||
* we have to traverse the hash table freeing list elements
|
||||
* then release the hash table */
|
||||
iter = hash_iter_new(conn->id_handlers);
|
||||
while ((key = hash_iter_next(iter))) {
|
||||
hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
|
||||
while (hlitem) {
|
||||
thli = hlitem;
|
||||
hlitem = hlitem->next;
|
||||
xmpp_free(conn->ctx, thli->id);
|
||||
xmpp_free(conn->ctx, thli);
|
||||
}
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
hash_release(conn->id_handlers);
|
||||
|
||||
hlitem = conn->handlers;
|
||||
while (hlitem) {
|
||||
thli = hlitem;
|
||||
hlitem = hlitem->next;
|
||||
|
||||
if (hlitem->ns) xmpp_free(ctx, hlitem->ns);
|
||||
if (hlitem->name) xmpp_free(ctx, hlitem->name);
|
||||
if (hlitem->type) xmpp_free(ctx, hlitem->type);
|
||||
xmpp_free(ctx, thli);
|
||||
}
|
||||
|
||||
if (conn->stream_error) {
|
||||
xmpp_stanza_release(conn->stream_error->stanza);
|
||||
xmpp_free(ctx, conn->stream_error);
|
||||
}
|
||||
|
||||
XML_ParserFree(conn->parser);
|
||||
|
||||
if (conn->domain) xmpp_free(ctx, conn->domain);
|
||||
if (conn->jid) xmpp_free(ctx, conn->jid);
|
||||
if (conn->pass) xmpp_free(ctx, conn->pass);
|
||||
if (conn->stream_id) xmpp_free(ctx, conn->stream_id);
|
||||
xmpp_free(ctx, conn);
|
||||
}
|
||||
}
|
||||
|
||||
const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn)
|
||||
{
|
||||
return conn->jid;
|
||||
}
|
||||
|
||||
/* set the jid of the user or the component name. in the first case,
|
||||
* this can be a full jid, or a bare jid. in the second case, this will
|
||||
* probably be a domain only.
|
||||
*/
|
||||
void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid)
|
||||
{
|
||||
if (conn->jid) xmpp_free(conn->ctx, conn->jid);
|
||||
conn->jid = xmpp_strdup(conn->ctx, jid);
|
||||
}
|
||||
|
||||
const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn)
|
||||
{
|
||||
return conn->pass;
|
||||
}
|
||||
|
||||
void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass)
|
||||
{
|
||||
if (conn->pass) xmpp_free(conn->ctx, conn->pass);
|
||||
conn->pass = xmpp_strdup(conn->ctx, pass);
|
||||
}
|
||||
|
||||
int xmpp_connect_client(xmpp_conn_t * const conn,
|
||||
const char * const domain,
|
||||
xmpp_conn_handler callback,
|
||||
void * const userdata)
|
||||
{
|
||||
conn->type = XMPP_CLIENT;
|
||||
|
||||
conn->domain = xmpp_strdup(conn->ctx, domain);
|
||||
if (!conn->domain) return -1;
|
||||
|
||||
conn->sock = sock_connect(domain, 5222);
|
||||
if (conn->sock < 0) return -1;
|
||||
|
||||
/* setup handler */
|
||||
conn->conn_handler = callback;
|
||||
conn->userdata = userdata;
|
||||
|
||||
/* FIXME: it could happen that the connect returns immediately as
|
||||
* successful, though this is pretty unlikely. This would be a little
|
||||
* hard to fix, since we'd have to detect and fire off the callback
|
||||
* from within the event loop */
|
||||
|
||||
conn->state = XMPP_STATE_CONNECTING;
|
||||
xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function is only called by the end tag handler. it is
|
||||
* the only place where a conn_disconnect would be called during a clean
|
||||
* disconnect sequence */
|
||||
void conn_disconnect_clean(xmpp_conn_t * const conn)
|
||||
{
|
||||
/* remove the timed handler */
|
||||
xmpp_timed_handler_delete(conn, _disconnect_cleanup);
|
||||
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
|
||||
void conn_disconnect(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_info(conn->ctx, "xmpp", "Disconnected from server");
|
||||
conn->state = XMPP_STATE_DISCONNECTED;
|
||||
sock_close(conn->sock);
|
||||
|
||||
/* fire off connection handler */
|
||||
conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
|
||||
conn->stream_error, conn->userdata);
|
||||
}
|
||||
|
||||
/* timed handler for cleanup if normal disconnect procedure takes too long */
|
||||
static int _disconnect_cleanup(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_debug(conn->ctx, "xmpp",
|
||||
"disconnection forced by cleanup timeout");
|
||||
|
||||
conn_disconnect(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* terminates the XMPP stream, closing the underlying socket,
|
||||
* and calls the conn_handler. this function returns immediately
|
||||
* without calling the handler if the connection is not active */
|
||||
void xmpp_disconnect(xmpp_conn_t * const conn)
|
||||
{
|
||||
if (conn->state != XMPP_STATE_CONNECTING &&
|
||||
conn->state != XMPP_STATE_CONNECTED)
|
||||
return;
|
||||
|
||||
/* close the stream */
|
||||
xmpp_send_raw_string(conn, "</stream:stream>");
|
||||
|
||||
/* setup timed handler in case disconnect takes too long */
|
||||
handler_add_timed(conn, _disconnect_cleanup,
|
||||
DISCONNECT_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
/* convinience function for sending data to a connection */
|
||||
void xmpp_send_raw_string(xmpp_conn_t * const conn,
|
||||
const char * const fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
size_t len;
|
||||
char buf[1024]; /* small buffer for common case */
|
||||
char *bigbuf;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, 1024, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len >= 1024) {
|
||||
/* we need more space for this data, so we allocate a big
|
||||
* enough buffer and print to that */
|
||||
len++; /* account for trailing \0 */
|
||||
bigbuf = xmpp_alloc(conn->ctx, len);
|
||||
if (!bigbuf) {
|
||||
xmpp_debug(conn->ctx, "xmpp", "Could not allocate memory for send_raw_string");
|
||||
return;
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(bigbuf, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
xmpp_debug(conn->ctx, "conn", "SENT: %s", bigbuf);
|
||||
|
||||
/* len - 1 so we don't send trailing \0 */
|
||||
xmpp_send_raw(conn, bigbuf, len - 1);
|
||||
|
||||
xmpp_free(conn->ctx, bigbuf);
|
||||
} else {
|
||||
xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
|
||||
|
||||
xmpp_send_raw(conn, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* adds data to the send queue for a connection */
|
||||
void xmpp_send_raw(xmpp_conn_t * const conn,
|
||||
const char * const data, const size_t len)
|
||||
{
|
||||
xmpp_send_queue_t *item;
|
||||
|
||||
if (conn->state != XMPP_STATE_CONNECTED) return;
|
||||
|
||||
/* create send queue item for queue */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
|
||||
if (!item) return;
|
||||
|
||||
item->data = xmpp_alloc(conn->ctx, len);
|
||||
if (!item->data) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
memcpy(item->data, data, len);
|
||||
item->len = len;
|
||||
item->next = NULL;
|
||||
item->written = 0;
|
||||
|
||||
/* add item to the send queue */
|
||||
if (!conn->send_queue_tail) {
|
||||
/* first item, set head and tail */
|
||||
conn->send_queue_head = item;
|
||||
conn->send_queue_tail = item;
|
||||
} else {
|
||||
/* add to the tail */
|
||||
conn->send_queue_tail->next = item;
|
||||
conn->send_queue_tail = item;
|
||||
}
|
||||
conn->send_queue_len++;
|
||||
}
|
||||
|
||||
void xmpp_send(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza)
|
||||
{
|
||||
char *buf;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
if (conn->state == XMPP_STATE_CONNECTED) {
|
||||
if ((ret = xmpp_stanza_to_text(stanza, &buf, &len)) == 0) {
|
||||
xmpp_send_raw(conn, buf, len);
|
||||
xmpp_debug(conn->ctx, "xmpp", "SENT: %s", buf);
|
||||
xmpp_free(conn->ctx, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void conn_open_stream(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_send_raw_string(conn,
|
||||
"<?xml version=\"1.0\"?>" \
|
||||
"<stream:stream to=\"%s\" " \
|
||||
"xml:lang=\"%s\" " \
|
||||
"version=\"1.0\" " \
|
||||
"xmlns=\"%s\" " \
|
||||
"xmlns:stream=\"%s\">",
|
||||
conn->domain,
|
||||
conn->lang,
|
||||
conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT,
|
||||
XMPP_NS_STREAMS);
|
||||
}
|
||||
187
src/ctx.c
Normal file
187
src/ctx.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/* ctx.c
|
||||
** XMPP client library -- run-time context implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
|
||||
/** version **/
|
||||
|
||||
/* TODO: update from the build system? */
|
||||
#ifndef LIBXMPP_VERSION_MAJOR
|
||||
#define LIBXMPP_VERSION_MAJOR (0)
|
||||
#endif
|
||||
#ifndef LIBXMPP_VERSION_MINOR
|
||||
#define LIBXMPP_VERSION_MINOR (0)
|
||||
#endif
|
||||
|
||||
int xmpp_version_check(int major, int minor)
|
||||
{
|
||||
return (major == LIBXMPP_VERSION_MAJOR) &&
|
||||
(minor >= LIBXMPP_VERSION_MINOR);
|
||||
}
|
||||
|
||||
/** run-time contexts **/
|
||||
|
||||
/* define the global default allocator, logger and context here */
|
||||
|
||||
static xmpp_mem_t xmpp_default_mem = {
|
||||
malloc, /* use the stdlib routines by default */
|
||||
free,
|
||||
realloc
|
||||
};
|
||||
|
||||
static const char * const xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
||||
|
||||
void xmpp_default_logger(void * const userdata,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const msg)
|
||||
{
|
||||
fprintf(stderr, "%s %s %s\n", area, xmpp_log_level_name[level], msg);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static xmpp_log_t xmpp_default_log = { NULL, NULL };
|
||||
#else
|
||||
static xmpp_log_t xmpp_default_log = { xmpp_default_logger, NULL };
|
||||
#endif
|
||||
|
||||
|
||||
/** convenience functions for accessing the context **/
|
||||
|
||||
/* allocator */
|
||||
|
||||
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size)
|
||||
{
|
||||
return ctx->mem->alloc(size);
|
||||
}
|
||||
|
||||
void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
|
||||
{
|
||||
return ctx->mem->free(p);
|
||||
}
|
||||
|
||||
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
||||
const size_t size)
|
||||
{
|
||||
return ctx->mem->realloc(p, size);
|
||||
}
|
||||
|
||||
/* logger */
|
||||
|
||||
void xmpp_log(const xmpp_ctx_t * const ctx,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
va_list ap)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
/* FIXME: we don't send log lines > 1024 chars */
|
||||
|
||||
vsnprintf(buf, 1023, fmt, ap);
|
||||
|
||||
ctx->log->handler(ctx->log->userdata, level, area, buf);
|
||||
}
|
||||
|
||||
void xmpp_error(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void xmpp_warn(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void xmpp_info(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void xmpp_debug(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** allocate and initialize a new ctx object */
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
||||
const xmpp_log_t * const log)
|
||||
{
|
||||
xmpp_ctx_t *ctx = NULL;
|
||||
|
||||
if (mem == NULL)
|
||||
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t));
|
||||
else
|
||||
ctx = mem->alloc(sizeof(xmpp_ctx_t));
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (mem != NULL)
|
||||
ctx->mem = mem;
|
||||
else
|
||||
ctx->mem = &xmpp_default_mem;
|
||||
|
||||
if (log == NULL)
|
||||
ctx->log = &xmpp_default_log;
|
||||
else
|
||||
ctx->log = log;
|
||||
|
||||
ctx->connlist = NULL;
|
||||
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** free a ctx object no longer in use */
|
||||
void xmpp_ctx_free(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
/* mem and log are owned by their suppliers */
|
||||
xmpp_free(ctx, ctx); /* pull the hole in after us */
|
||||
}
|
||||
|
||||
220
src/event.c
Normal file
220
src/event.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* event.c
|
||||
** XMPP client library -- event loop and management
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
#define DEFAULT_TIMEOUT 1
|
||||
|
||||
/* send data and check all connections for their events
|
||||
* and call event handlers. timeout is in milliseconds */
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
{
|
||||
xmpp_connlist_t *connitem;
|
||||
xmpp_conn_t *conn;
|
||||
fd_set rfds, wfds;
|
||||
sock_t max = 0;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
xmpp_send_queue_t *sq, *tsq;
|
||||
size_t towrite;
|
||||
char buf[4096];
|
||||
uint64_t next;
|
||||
|
||||
if (ctx->loop_status == XMPP_LOOP_QUIT) return;
|
||||
ctx->loop_status = XMPP_LOOP_RUNNING;
|
||||
|
||||
/* send queued data */
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
if (conn->state != XMPP_STATE_CONNECTED) {
|
||||
connitem = connitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
sq = conn->send_queue_head;
|
||||
while (sq) {
|
||||
towrite = sq->len - sq->written;
|
||||
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
|
||||
|
||||
if (ret < 0 && sock_is_recoverable(sock_error())) {
|
||||
/* an error occured */
|
||||
conn->error = sock_error();
|
||||
break;
|
||||
} else if (ret < towrite) {
|
||||
/* not all data could be sent now */
|
||||
sq->written += ret;
|
||||
break;
|
||||
}
|
||||
|
||||
/* all data for this queue item written, delete and move on */
|
||||
xmpp_free(ctx, sq->data);
|
||||
tsq = sq;
|
||||
sq = sq->next;
|
||||
xmpp_free(ctx, tsq);
|
||||
|
||||
/* pop the top item */
|
||||
conn->send_queue_head = sq;
|
||||
/* if we've sent everything update the tail */
|
||||
if (!sq) conn->send_queue_tail = NULL;
|
||||
}
|
||||
|
||||
/* tear down connection on error */
|
||||
if (conn->error) {
|
||||
/* FIXME: need to tear down send queues and random other things
|
||||
* maybe this should be abstracted */
|
||||
xmpp_debug(ctx, "xmpp", "send error occured, tear down socket");
|
||||
sock_close(conn->sock);
|
||||
conn->state = XMPP_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
/* reset parsers if needed */
|
||||
for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
|
||||
if (connitem->conn->reset_parser)
|
||||
parser_reset(connitem->conn);
|
||||
}
|
||||
|
||||
|
||||
/* fire any ready timed handlers, then
|
||||
make sure we don't wait past the time when timed handlers need
|
||||
to be called */
|
||||
next = handler_fire_timed(ctx);
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = ((next < timeout) ? next : timeout) * 1000;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
|
||||
/* find events to watch */
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
/* connect has been called and we're waiting for it to complete */
|
||||
/* connection will give us write or error events */
|
||||
FD_SET(conn->sock, &wfds);
|
||||
break;
|
||||
case XMPP_STATE_CONNECTED:
|
||||
FD_SET(conn->sock, &rfds);
|
||||
break;
|
||||
case XMPP_STATE_DISCONNECTED:
|
||||
/* do nothing */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->sock > max) max = conn->sock;
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
/* check for events */
|
||||
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
|
||||
|
||||
/* select errored */
|
||||
if (ret < 0) {
|
||||
xmpp_error(ctx, "xmpp", "event watcher internal error");
|
||||
return;
|
||||
}
|
||||
|
||||
/* no events happened */
|
||||
if (ret == 0) return;
|
||||
|
||||
/* process events */
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
if (FD_ISSET(conn->sock, &wfds)) {
|
||||
/* connection complete */
|
||||
|
||||
/* check for error */
|
||||
if (sock_connect_error(conn->sock) != 0) {
|
||||
/* connection failed */
|
||||
xmpp_debug(ctx, "xmpp", "connection failed");
|
||||
conn->state = XMPP_STATE_DISCONNECTED;
|
||||
sock_close(conn->sock);
|
||||
break;
|
||||
}
|
||||
|
||||
conn->state = XMPP_STATE_CONNECTED;
|
||||
xmpp_debug(ctx, "xmpp", "connection successful");
|
||||
|
||||
|
||||
/* send stream init */
|
||||
conn_open_stream(conn);
|
||||
}
|
||||
|
||||
break;
|
||||
case XMPP_STATE_CONNECTED:
|
||||
if (FD_ISSET(conn->sock, &rfds)) {
|
||||
ret = sock_read(conn->sock, buf, 4096);
|
||||
if (ret > 0) {
|
||||
ret = XML_Parse(conn->parser, buf, ret, 0);
|
||||
if (!ret) {
|
||||
/* parse error, we need to shut down */
|
||||
/* FIXME */
|
||||
}
|
||||
}
|
||||
/* return of 0 means socket closed by server */
|
||||
}
|
||||
|
||||
break;
|
||||
case XMPP_STATE_DISCONNECTED:
|
||||
/* do nothing */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
/* fire any ready handlers */
|
||||
handler_fire_timed(ctx);
|
||||
}
|
||||
|
||||
void xmpp_run(xmpp_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
|
||||
|
||||
ctx->loop_status = XMPP_LOOP_RUNNING;
|
||||
while (ctx->loop_status == XMPP_LOOP_RUNNING) {
|
||||
xmpp_run_once(ctx, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
xmpp_debug(ctx, "event", "Event loop completed.");
|
||||
}
|
||||
|
||||
void xmpp_stop(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_debug(ctx, "event", "Stopping event loop.");
|
||||
|
||||
if (ctx->loop_status == XMPP_LOOP_RUNNING)
|
||||
ctx->loop_status = XMPP_LOOP_QUIT;
|
||||
}
|
||||
446
src/handler.c
Normal file
446
src/handler.c
Normal file
@@ -0,0 +1,446 @@
|
||||
/* handler.c
|
||||
** XMPP client library -- event handler management
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
void handler_fire_stanza(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev;
|
||||
char *id, *ns, *name, *type;
|
||||
|
||||
/* call id handlers */
|
||||
id = xmpp_stanza_get_id(stanza);
|
||||
if (id) {
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
for (; item; item = item->next) {
|
||||
if (item->user_handler && !conn->authenticated)
|
||||
continue;
|
||||
|
||||
((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* call handlers */
|
||||
ns = xmpp_stanza_get_ns(stanza);
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
|
||||
/* enable all added handlers */
|
||||
for (item = conn->handlers; item; item = item->next)
|
||||
item->enabled = 1;
|
||||
|
||||
prev = NULL;
|
||||
item = conn->handlers;
|
||||
while (item) {
|
||||
/* skip newly added handlers */
|
||||
if (!item->enabled) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* don't call user handlers until authentication succeeds */
|
||||
if (item->user_handler && !conn->authenticated) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!item->ns || (ns && strcmp(ns, item->ns) == 0)) &&
|
||||
(!item->name || (name && strcmp(name, item->name) == 0)) &&
|
||||
(!item->type || (type && strcmp(type, item->type) == 0)))
|
||||
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
|
||||
/* handler is one-shot, so delete it */
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else
|
||||
conn->handlers = item->next;
|
||||
xmpp_free(conn->ctx, item);
|
||||
item = NULL;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
prev = item;
|
||||
item = item->next;
|
||||
} else if (prev)
|
||||
item = prev->next;
|
||||
else
|
||||
item = conn->handlers;
|
||||
}
|
||||
}
|
||||
|
||||
/* helper function to fire timed handlers. returns the
|
||||
* time until the next handler would be fired */
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
xmpp_connlist_t *connitem;
|
||||
xmpp_handlist_t *handitem, *temp;
|
||||
int ret, fired;
|
||||
uint64_t elapsed, min;
|
||||
|
||||
min = (uint64_t)(-1);
|
||||
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
if (connitem->conn->state != XMPP_STATE_CONNECTED) {
|
||||
connitem = connitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* enable all handlers that were added */
|
||||
for (handitem = connitem->conn->timed_handlers; handitem;
|
||||
handitem = handitem->next)
|
||||
handitem->enabled = 1;
|
||||
|
||||
handitem = connitem->conn->timed_handlers;
|
||||
while (handitem) {
|
||||
/* skip newly added handlers */
|
||||
if (!handitem->enabled) {
|
||||
handitem = handitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only fire user handlers after authentication */
|
||||
if (handitem->user_handler && !connitem->conn->authenticated) {
|
||||
handitem = handitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
fired = 0;
|
||||
elapsed = time_elapsed(handitem->last_stamp, time_stamp());
|
||||
if (elapsed >= handitem->period) {
|
||||
/* fire! */
|
||||
fired = 1;
|
||||
handitem->last_stamp = time_stamp();
|
||||
ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
|
||||
} else if (min > (handitem->period - elapsed))
|
||||
min = handitem->period - elapsed;
|
||||
|
||||
temp = handitem;
|
||||
handitem = handitem->next;
|
||||
|
||||
/* delete handler if it returned false */
|
||||
if (fired && !ret)
|
||||
xmpp_timed_handler_delete(connitem->conn, temp->handler);
|
||||
}
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/* reset timed handlers for a connection. this is called
|
||||
* whenever connection is successful */
|
||||
void handler_reset_timed(xmpp_conn_t *conn, int user_only)
|
||||
{
|
||||
xmpp_handlist_t *handitem;
|
||||
|
||||
handitem = conn->timed_handlers;
|
||||
while (handitem) {
|
||||
if ((user_only && handitem->user_handler) || !user_only)
|
||||
handitem->last_stamp = time_stamp();
|
||||
|
||||
handitem = handitem->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void _timed_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata,
|
||||
const int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
|
||||
/* check if handler is already in the list */
|
||||
for (item = conn->timed_handlers; item; item = item->next) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
}
|
||||
if (item) return;
|
||||
|
||||
/* build new item */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item) return;
|
||||
|
||||
item->user_handler = user_handler;
|
||||
item->handler = (void *)handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
item->period = period;
|
||||
item->last_stamp = time_stamp();
|
||||
|
||||
/* append item to list */
|
||||
if (!conn->timed_handlers)
|
||||
conn->timed_handlers = item;
|
||||
else {
|
||||
tail = conn->timed_handlers;
|
||||
while (tail->next)
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev;
|
||||
|
||||
if (!conn->timed_handlers) return;
|
||||
|
||||
prev = NULL;
|
||||
item = conn->timed_handlers;
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else
|
||||
conn->timed_handlers = item->next;
|
||||
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
static void _id_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata, int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
|
||||
/* check if handler is already in the list */
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
item = item->next;
|
||||
}
|
||||
if (item) return;
|
||||
|
||||
/* build new item */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item) return;
|
||||
|
||||
item->user_handler = user_handler;
|
||||
item->handler = (void *)handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
item->id = xmpp_strdup(conn->ctx, id);
|
||||
if (!item->id) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
|
||||
/* put on list in hash table */
|
||||
tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
if (!tail)
|
||||
hash_add(conn->id_handlers, id, item);
|
||||
else {
|
||||
while (tail->next)
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev;
|
||||
|
||||
prev = NULL;
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
if (!item) return;
|
||||
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else {
|
||||
hash_drop(conn->id_handlers, id);
|
||||
hash_add(conn->id_handlers, id, item->next);
|
||||
}
|
||||
xmpp_free(conn->ctx, item->id);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
static void _handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata, int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
|
||||
/* check if handler already in list */
|
||||
for (item = conn->handlers; item; item = item->next) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
}
|
||||
if (item) return;
|
||||
|
||||
/* build new item */
|
||||
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item) return;
|
||||
|
||||
item->user_handler = user_handler;
|
||||
item->handler = (void *)handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
if (ns) {
|
||||
item->ns = xmpp_strdup(conn->ctx, ns);
|
||||
if (!item->ns) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
item->ns = NULL;
|
||||
if (name) {
|
||||
item->name = xmpp_strdup(conn->ctx, name);
|
||||
if (!item->name) {
|
||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
item->name = NULL;
|
||||
if (type) {
|
||||
item->type = xmpp_strdup(conn->ctx, type);
|
||||
if (!item->type) {
|
||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
} else
|
||||
item->type = NULL;
|
||||
|
||||
/* append to list */
|
||||
if (!conn->handlers)
|
||||
conn->handlers = item;
|
||||
else {
|
||||
tail = conn->handlers;
|
||||
while (tail->next)
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler)
|
||||
{
|
||||
xmpp_handlist_t *prev, *item;
|
||||
|
||||
if (!conn->handlers) return;
|
||||
|
||||
prev = NULL;
|
||||
item = conn->handlers;
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else
|
||||
conn->handlers = item->next;
|
||||
|
||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
||||
if (item->type) xmpp_free(conn->ctx, item->type);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_timed_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata)
|
||||
{
|
||||
_timed_handler_add(conn, handler, period, userdata, 1);
|
||||
}
|
||||
|
||||
void handler_add_timed(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata)
|
||||
{
|
||||
_timed_handler_add(conn, handler, period, userdata, 0);
|
||||
}
|
||||
|
||||
void xmpp_id_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata)
|
||||
{
|
||||
_id_handler_add(conn, handler, id, userdata, 1);
|
||||
}
|
||||
|
||||
void handler_add_id(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata)
|
||||
{
|
||||
_id_handler_add(conn, handler, id, userdata, 0);
|
||||
}
|
||||
|
||||
void xmpp_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata)
|
||||
{
|
||||
_handler_add(conn, handler, ns, name, type, userdata, 1);
|
||||
}
|
||||
|
||||
void handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata)
|
||||
{
|
||||
_handler_add(conn, handler, ns, name, type, userdata, 0);
|
||||
}
|
||||
272
src/hash.c
Normal file
272
src/hash.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/* hash.c
|
||||
** XMPP client library
|
||||
** hash table implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* private types */
|
||||
typedef struct _hashentry_t hashentry_t;
|
||||
|
||||
struct _hashentry_t {
|
||||
hashentry_t *next;
|
||||
char *key;
|
||||
void *value;
|
||||
};
|
||||
|
||||
struct _hash_t {
|
||||
unsigned int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
hash_free_func free;
|
||||
int length;
|
||||
int num_keys;
|
||||
hashentry_t **entries;
|
||||
};
|
||||
|
||||
struct _hash_iterator_t {
|
||||
unsigned int ref;
|
||||
hash_t *table;
|
||||
hashentry_t *entry;
|
||||
int index;
|
||||
};
|
||||
|
||||
/** allocate and initialize a new hash table */
|
||||
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
|
||||
hash_free_func free)
|
||||
{
|
||||
hash_t *result = NULL;
|
||||
|
||||
result = xmpp_alloc(ctx, sizeof(hash_t));
|
||||
if (result != NULL) {
|
||||
result->entries = xmpp_alloc(ctx, size * sizeof(hashentry_t *));
|
||||
if (result->entries == NULL) {
|
||||
xmpp_free(ctx, result);
|
||||
return NULL;
|
||||
}
|
||||
memset(result->entries, 0, size * sizeof(hashentry_t *));
|
||||
result->length = size;
|
||||
|
||||
result->ctx = ctx;
|
||||
result->free = free;
|
||||
result->num_keys = 0;
|
||||
/* give the caller a reference */
|
||||
result->ref = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** obtain a new reference to an existing hash table */
|
||||
hash_t *hash_clone(hash_t * const table)
|
||||
{
|
||||
table->ref++;
|
||||
return table;
|
||||
}
|
||||
|
||||
/** release a hash table that is no longer needed */
|
||||
void hash_release(hash_t * const table)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry, *next;
|
||||
int i;
|
||||
|
||||
if (table->ref > 1)
|
||||
table->ref--;
|
||||
else {
|
||||
for (i = 0; i < table->length; i++) {
|
||||
entry = table->entries[i];
|
||||
while (entry != NULL) {
|
||||
next = entry->next;
|
||||
xmpp_free(ctx, entry->key);
|
||||
if (table->free) table->free(ctx, entry->value);
|
||||
xmpp_free(ctx, entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
xmpp_free(ctx, table->entries);
|
||||
xmpp_free(ctx, table);
|
||||
}
|
||||
}
|
||||
|
||||
/** hash a key for our table lookup */
|
||||
static int _hash_key(hash_t *table, const char *key)
|
||||
{
|
||||
int hash = 0;
|
||||
int shift = 0;
|
||||
const char *c = key;
|
||||
|
||||
while (*c != '\0') {
|
||||
/* assume 32 bit ints */
|
||||
hash ^= ((int)*c++ << shift);
|
||||
shift += 8;
|
||||
if (shift > 24) shift = 0;
|
||||
}
|
||||
|
||||
return hash % table->length;
|
||||
}
|
||||
|
||||
/** add a key, value pair to a hash table.
|
||||
* each key can appear only once; the value of any
|
||||
* identical key will be replaced
|
||||
*/
|
||||
int hash_add(hash_t *table, const char * const key, void *data)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry = NULL;
|
||||
int index = _hash_key(table, key);
|
||||
|
||||
/* allocate and fill a new entry */
|
||||
entry = xmpp_alloc(ctx, sizeof(hashentry_t));
|
||||
if (!entry) return -1;
|
||||
entry->key = xmpp_strdup(ctx, key);
|
||||
if (!entry->key) {
|
||||
xmpp_free(ctx, entry);
|
||||
return -1;
|
||||
}
|
||||
entry->value = data;
|
||||
/* insert ourselves in the linked list */
|
||||
/* TODO: this leaks duplicate keys */
|
||||
entry->next = table->entries[index];
|
||||
table->entries[index] = entry;
|
||||
table->num_keys++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** look up a key in a hash table */
|
||||
void *hash_get(hash_t *table, const char *key)
|
||||
{
|
||||
hashentry_t *entry;
|
||||
int index = _hash_key(table, key);
|
||||
void *result = NULL;
|
||||
|
||||
/* look up the hash entry */
|
||||
entry = table->entries[index];
|
||||
while (entry != NULL) {
|
||||
/* traverse the linked list looking for the key */
|
||||
if (!strcmp(key, entry->key)) {
|
||||
/* match */
|
||||
result = entry->value;
|
||||
return result;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
/* no match */
|
||||
return result;
|
||||
}
|
||||
|
||||
/** delete a key from a hash table */
|
||||
int hash_drop(hash_t *table, const char *key)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry, *prev;
|
||||
int index = _hash_key(table, key);
|
||||
|
||||
/* look up the hash entry */
|
||||
entry = table->entries[index];
|
||||
prev = NULL;
|
||||
while (entry != NULL) {
|
||||
/* traverse the linked list looking for the key */
|
||||
if (!strcmp(key, entry->key)) {
|
||||
/* match, remove the entry */
|
||||
xmpp_free(ctx, entry->key);
|
||||
if (table->free) table->free(ctx, entry->value);
|
||||
if (prev == NULL) {
|
||||
table->entries[index] = entry->next;
|
||||
} else {
|
||||
prev->next = entry->next;
|
||||
}
|
||||
xmpp_free(ctx, entry);
|
||||
table->num_keys--;
|
||||
return 0;
|
||||
}
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
/* no match */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hash_num_keys(hash_t *table)
|
||||
{
|
||||
return table->num_keys;
|
||||
}
|
||||
|
||||
/** allocate and initialize a new iterator */
|
||||
hash_iterator_t *hash_iter_new(hash_t *table)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hash_iterator_t *iter;
|
||||
|
||||
iter = xmpp_alloc(ctx, sizeof(*iter));
|
||||
if (iter != NULL) {
|
||||
iter->ref = 1;
|
||||
iter->table = hash_clone(table);
|
||||
iter->entry = NULL;
|
||||
iter->index = 0;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
/** release an iterator that is no longer needed */
|
||||
void hash_iter_release(hash_iterator_t *iter)
|
||||
{
|
||||
xmpp_ctx_t *ctx = iter->table->ctx;
|
||||
|
||||
iter->ref--;
|
||||
|
||||
if (iter->ref <= 0) {
|
||||
hash_release(iter->table);
|
||||
xmpp_free(ctx, iter);
|
||||
}
|
||||
}
|
||||
|
||||
/** return the next hash table key from the iterator.
|
||||
the returned key should not be freed */
|
||||
const char * hash_iter_next(hash_iterator_t *iter)
|
||||
{
|
||||
hash_t *table = iter->table;
|
||||
hashentry_t *entry = iter->entry;
|
||||
int i = iter->index + 1;
|
||||
|
||||
/* advance until we find the next entry */
|
||||
if (entry != NULL) entry = entry->next;
|
||||
if (entry == NULL) {
|
||||
/* we're off the end of list, search for a new entry */
|
||||
while (i < iter->table->length) {
|
||||
entry = table->entries[i];
|
||||
if (entry != NULL) {
|
||||
iter->index = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((entry == NULL) || (i >= table->length)) {
|
||||
/* no more keys! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* remember our current match */
|
||||
iter->entry = entry;
|
||||
return entry->key;
|
||||
}
|
||||
|
||||
60
src/hash.h
Normal file
60
src/hash.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* hash.h
|
||||
** XMPP client library -- hash table interface
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_HASH_H__
|
||||
#define __LIBXMPP_HASH_H__
|
||||
|
||||
typedef struct _hash_t hash_t;
|
||||
|
||||
typedef void (*hash_free_func)(const xmpp_ctx_t * const ctx, void *p);
|
||||
|
||||
/** allocate and initialize a new hash table */
|
||||
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
|
||||
hash_free_func free);
|
||||
|
||||
/** allocate a new reference to an existing hash table */
|
||||
hash_t *hash_clone(hash_t * const table);
|
||||
|
||||
/** release a hash table when no longer needed */
|
||||
void hash_release(hash_t * const table);
|
||||
|
||||
/** add a key, value pair to a hash table.
|
||||
* each key can appear only once; the value of any
|
||||
* identical key will be replaced
|
||||
*/
|
||||
int hash_add(hash_t *table, const char * const key, void *data);
|
||||
|
||||
/** look up a key in a hash table */
|
||||
void *hash_get(hash_t *table, const char *key);
|
||||
|
||||
/** delete a key from a hash table */
|
||||
int hash_drop(hash_t *table, const char *key);
|
||||
|
||||
/** return the number of keys in a hash */
|
||||
int hash_num_keys(hash_t *table);
|
||||
|
||||
/** hash key iterator functions */
|
||||
typedef struct _hash_iterator_t hash_iterator_t;
|
||||
|
||||
/** allocate and initialize a new iterator */
|
||||
hash_iterator_t *hash_iter_new(hash_t *table);
|
||||
|
||||
/** release an iterator that is no longer needed */
|
||||
void hash_iter_release(hash_iterator_t *iter);
|
||||
|
||||
/** return the next hash table key from the iterator.
|
||||
the returned key should not be freed */
|
||||
const char * hash_iter_next(hash_iterator_t *iter);
|
||||
|
||||
#endif /* __LIBXMPPP_HASH_H__ */
|
||||
139
src/jid.c
Normal file
139
src/jid.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/* jid.c
|
||||
** XMPP client library
|
||||
** helper functions for parsing jid's
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
/** join jid component parts to form a new jid string */
|
||||
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
||||
const char *domain,
|
||||
const char *resource)
|
||||
{
|
||||
char *result;
|
||||
int len,nlen,dlen,rlen;
|
||||
|
||||
/* jid must at least have a domain */
|
||||
if (domain == NULL) return NULL;
|
||||
|
||||
/* accumulate lengths */
|
||||
dlen = strlen(domain);
|
||||
nlen = (node) ? strlen(node) + 1 : 0;
|
||||
rlen = (resource) ? strlen(resource) + 1 : 0;
|
||||
len = nlen + dlen + rlen;
|
||||
|
||||
/* concat components */
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
if (node != NULL) {
|
||||
memcpy(result, node, nlen - 1);
|
||||
result[nlen-1] = '@';
|
||||
}
|
||||
memcpy(result + nlen, domain, dlen);
|
||||
if (resource != NULL) {
|
||||
result[nlen+dlen] = '/';
|
||||
memcpy(result+nlen+dlen+1, resource, rlen - 1);
|
||||
}
|
||||
result[nlen+dlen+rlen] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return a bare jid */
|
||||
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result;
|
||||
const char *c;
|
||||
|
||||
c = strchr(jid, '/');
|
||||
if (c == NULL) return xmpp_strdup(ctx, jid);
|
||||
|
||||
result = xmpp_alloc(ctx, c-jid+1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, jid, c-jid);
|
||||
result[c-jid] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return the node portion of a jid */
|
||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c;
|
||||
|
||||
c = strchr(jid, '@');
|
||||
if (c != NULL) {
|
||||
result = xmpp_alloc(ctx, (c-jid) + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, jid, (c-jid));
|
||||
result[c-jid] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return the domain portion of a jid */
|
||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c,*s;
|
||||
|
||||
c = strchr(jid, '@');
|
||||
if (c == NULL) {
|
||||
/* no node, assume domain */
|
||||
c = jid;
|
||||
} else {
|
||||
/* advance past the separator */
|
||||
c++;
|
||||
}
|
||||
s = strchr(c, '/');
|
||||
if (s == NULL) {
|
||||
/* no resource */
|
||||
s = c + strlen(c);
|
||||
}
|
||||
result = xmpp_alloc(ctx, (s-c) + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, c, (s-c));
|
||||
result[s-c] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return the node portion of a jid */
|
||||
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c;
|
||||
int len;
|
||||
|
||||
c = strchr(jid, '/');
|
||||
if (c != NULL) {
|
||||
c++;
|
||||
len = strlen(c);
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, c, len);
|
||||
result[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
285
src/md5.c
Normal file
285
src/md5.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/* md5.c
|
||||
** MD5 hash function implemention, adapted for local use
|
||||
**
|
||||
** This code is in the Public Domain
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
#include <string.h> /* memcpy(), memset() */
|
||||
#include "md5.h"
|
||||
|
||||
/* little-endian word access macros */
|
||||
#define GET_32BIT_LSB_FIRST(cp) \
|
||||
(((uint32_t)(unsigned char)(cp)[0]) | \
|
||||
((uint32_t)(unsigned char)(cp)[1] << 8 ) | \
|
||||
((uint32_t)(unsigned char)(cp)[2] << 16) | \
|
||||
((uint32_t)(unsigned char)(cp)[3] << 24))
|
||||
|
||||
#define PUT_32BIT_LSB_FIRST(cp, value) \
|
||||
do { \
|
||||
(cp)[0] = (value) & 0xFF; \
|
||||
(cp)[1] = ((value) >> 8) & 0xFF; \
|
||||
(cp)[2] = ((value) >> 16) & 0xFF; \
|
||||
(cp)[3] = ((value) >> 24) & 0xFF; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void MD5Init(struct MD5Context *ctx)
|
||||
{
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
|
||||
memset(ctx->in, 0, 64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
|
||||
{
|
||||
uint32_t t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = (t + ((uint32_t)len << 3)) & 0xffffffff) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
|
||||
{
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
|
||||
/* Append length in bits and transform */
|
||||
PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
|
||||
PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
|
||||
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
|
||||
PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
|
||||
PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
|
||||
PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
#ifndef ASM_MD5
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
/* debugging version: */
|
||||
/*
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
printf("MD5STEP: w: %x x: %x y: %x z: %x data: %x s: %x\n", \
|
||||
w, x, y, z, data, s); \
|
||||
printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
|
||||
( w += f(x, y, z) + data, printf(" - w: %x ", w), \
|
||||
w = w<<s | w>>(32-s), printf(" - w: %x\n", w), w += x )
|
||||
*/
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
|
||||
struct MD5Context *ctx)
|
||||
{
|
||||
register uint32_t a, b, c, d, i;
|
||||
uint32_t in[16];
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MD5
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void MD5DumpBytes(unsigned char *b, int len)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<len; i++) {
|
||||
if (i%32==0 && i!=0) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("%02x", b[i]&0xff);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#endif /* DEBUG_MD5 */
|
||||
|
||||
#endif /* !MD5_ASM */
|
||||
37
src/md5.h
Normal file
37
src/md5.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* md5.h
|
||||
** interface to MD5 hash function
|
||||
**
|
||||
** This code is in the Public Domain.
|
||||
*/
|
||||
|
||||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
|
||||
/* we use the uint32_t type from stdint.h
|
||||
* if it is not available, add a typedef here:
|
||||
*/
|
||||
/* make sure the stdint.h types are available */
|
||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
struct MD5Context {
|
||||
uint32_t buf[4];
|
||||
uint32_t bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
|
||||
void MD5Init(struct MD5Context *context);
|
||||
void MD5Update(struct MD5Context *context, unsigned char const *buf,
|
||||
uint32_t len);
|
||||
void MD5Final(unsigned char digest[16], struct MD5Context *context);
|
||||
void MD5Transform(uint32_t buf[4], const unsigned char in[64],
|
||||
struct MD5Context *ctx);
|
||||
|
||||
#ifdef DEBUG_MD5
|
||||
void MD5DumpBytes(unsigned char *b, int len);
|
||||
#endif
|
||||
|
||||
#endif /* !MD5_H */
|
||||
202
src/parser.c
Normal file
202
src/parser.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/* parser.c
|
||||
** XMPP client library -- xml parser handlers and utility functions
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "expat.h"
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
static void _log_open_tag(xmpp_conn_t * const conn,
|
||||
const XML_Char **attr)
|
||||
{
|
||||
char buf[4096];
|
||||
size_t len, pos;
|
||||
int i;
|
||||
|
||||
pos = 0;
|
||||
len = snprintf(buf, 4096, "<stream:stream");
|
||||
if (len < 0) return;
|
||||
|
||||
pos += len;
|
||||
|
||||
for (i = 0; attr[i]; i += 2) {
|
||||
len = snprintf(&buf[pos], 4096 - pos, " %s=\"%s\"",
|
||||
attr[i], attr[i+1]);
|
||||
if (len < 0) return;
|
||||
|
||||
pos += len;
|
||||
}
|
||||
|
||||
len = snprintf(&buf[pos], 4096 - pos, ">");
|
||||
if (len < 0) return;
|
||||
|
||||
xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
|
||||
}
|
||||
|
||||
void parser_handle_start(void *userdata,
|
||||
const XML_Char *name,
|
||||
const XML_Char **attr)
|
||||
{
|
||||
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
if (conn->depth == 0) {
|
||||
/* we're expecting a stream:stream tag. */
|
||||
if (strcmp(name, "stream:stream") != 0) {
|
||||
xmpp_error(conn->ctx, "xmpp",
|
||||
"Server did not open valid stream.");
|
||||
conn_disconnect(conn);
|
||||
} else {
|
||||
_log_open_tag(conn, attr);
|
||||
|
||||
if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id);
|
||||
conn->stream_id = xmpp_strdup(conn->ctx, "foo");
|
||||
if (!conn->stream_id) {
|
||||
xmpp_error(conn->ctx, "xmpp",
|
||||
"Memory allocation failure.");
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
|
||||
/* call stream open handler */
|
||||
conn->open_handler(conn);
|
||||
}
|
||||
} else {
|
||||
/* build stanzas at depth 1 */
|
||||
if (!conn->stanza && conn->depth != 1) {
|
||||
/* something terrible happened */
|
||||
/* FIXME: shutdown disconnect */
|
||||
xmpp_debug(conn->ctx, "xmpp", "oops, where did our stanza go?");
|
||||
} else if (!conn->stanza) {
|
||||
/* starting a new toplevel stanza */
|
||||
conn->stanza = xmpp_stanza_new(conn->ctx);
|
||||
if (!conn->stanza) {
|
||||
/* FIXME: can't allocate, disconnect */
|
||||
}
|
||||
xmpp_stanza_set_name(conn->stanza, name);
|
||||
xmpp_stanza_set_attributes(conn->stanza, attr);
|
||||
} else {
|
||||
/* starting a child of conn->stanza */
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
/* FIXME: can't allocate, disconnect */
|
||||
}
|
||||
xmpp_stanza_set_name(child, name);
|
||||
xmpp_stanza_set_attributes(child, attr);
|
||||
|
||||
/* add child to parent */
|
||||
xmpp_stanza_add_child(conn->stanza, child);
|
||||
|
||||
/* the child is owned by the toplevel stanza now */
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
/* make child the current stanza */
|
||||
conn->stanza = child;
|
||||
}
|
||||
}
|
||||
|
||||
conn->depth++;
|
||||
}
|
||||
|
||||
void parser_handle_end(void *userdata, const XML_Char *name)
|
||||
{
|
||||
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
||||
char *buf;
|
||||
size_t len;
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
conn->depth--;
|
||||
|
||||
if (conn->depth == 0) {
|
||||
/* got a closing stream tag */
|
||||
xmpp_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
|
||||
conn_disconnect_clean(conn);
|
||||
} else {
|
||||
if (conn->stanza->parent) {
|
||||
/* we're finishing a child stanza, so set current to the parent */
|
||||
conn->stanza = conn->stanza->parent;
|
||||
} else {
|
||||
/* we're finishing a toplevel stanza, so fire off handler */
|
||||
if (xmpp_stanza_to_text(conn->stanza, &buf, &len) == 0) {
|
||||
xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
|
||||
xmpp_free(conn->ctx, buf);
|
||||
}
|
||||
|
||||
stanza = xmpp_stanza_clone(conn->stanza);
|
||||
xmpp_stanza_release(conn->stanza);
|
||||
conn->stanza = NULL;
|
||||
|
||||
/* fire handlers */
|
||||
handler_fire_stanza(conn, stanza);
|
||||
|
||||
xmpp_stanza_release(stanza);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parser_handle_character(void *userdata, const XML_Char *s, int len)
|
||||
{
|
||||
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
if (conn->depth < 2) return;
|
||||
|
||||
/* create and populate stanza */
|
||||
stanza = xmpp_stanza_new(conn->ctx);
|
||||
if (!stanza) {
|
||||
/* FIXME: allocation error, disconnect */
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_text_with_size(stanza, s, len);
|
||||
|
||||
xmpp_stanza_add_child(conn->stanza, stanza);
|
||||
xmpp_stanza_release(stanza);
|
||||
}
|
||||
|
||||
/* prepares a parser reset. this is called from handlers. we can't
|
||||
* reset the parser immediately as it is not reentrant. */
|
||||
void parser_prepare_reset(xmpp_conn_t * const conn,
|
||||
xmpp_open_handler handler)
|
||||
{
|
||||
conn->reset_parser = 1;
|
||||
conn->open_handler = handler;
|
||||
}
|
||||
|
||||
/* shuts down and restarts XML parser. true on success */
|
||||
int parser_reset(xmpp_conn_t * const conn)
|
||||
{
|
||||
conn->reset_parser = 0;
|
||||
|
||||
if (conn->parser)
|
||||
XML_ParserFree(conn->parser);
|
||||
|
||||
if (conn->stanza)
|
||||
xmpp_stanza_release(conn->stanza);
|
||||
|
||||
conn->parser = XML_ParserCreate(NULL);
|
||||
if (!conn->parser) return 0;
|
||||
|
||||
conn->depth = 0;
|
||||
conn->stanza = NULL;
|
||||
XML_SetUserData(conn->parser, conn);
|
||||
XML_SetElementHandler(conn->parser, parser_handle_start,
|
||||
parser_handle_end);
|
||||
XML_SetCharacterDataHandler(conn->parser, parser_handle_character);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
593
src/sasl.c
Normal file
593
src/sasl.c
Normal file
@@ -0,0 +1,593 @@
|
||||
/* sasl.c
|
||||
** XMPP client library -- SASL authentication helpers
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
#include "md5.h"
|
||||
|
||||
/* make sure the stdint.h types are available */
|
||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
typedef signed char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef __int64 int64_t;
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short int uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
/* no uint64_t */
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
|
||||
/** generate authentication string for the SASL PLAIN mechanism */
|
||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
||||
int idlen, passlen;
|
||||
char *result = NULL;
|
||||
char *msg;
|
||||
|
||||
/* our message is Base64(authzid,\0,authid,\0,password)
|
||||
if there is no authzid, that field is left empty */
|
||||
|
||||
idlen = strlen(authid);
|
||||
passlen = strlen(password);
|
||||
msg = xmpp_alloc(ctx, 2 + idlen + passlen);
|
||||
if (msg != NULL) {
|
||||
msg[0] = '\0';
|
||||
memcpy(msg+1, authid, idlen);
|
||||
msg[1+idlen] = '\0';
|
||||
memcpy(msg+1+idlen+1, password, passlen);
|
||||
result = base64_encode(ctx, msg, 2 + idlen + passlen);
|
||||
xmpp_free(ctx, msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** helpers for digest auth */
|
||||
|
||||
/* create a new, null-terminated string from a substring */
|
||||
static char *_make_string(xmpp_ctx_t *ctx, const char *s, const int len)
|
||||
{
|
||||
char *result;
|
||||
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, s, len);
|
||||
result[len] = '\0';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* create a new, null-terminated string quoting another string */
|
||||
static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
|
||||
{
|
||||
char *result;
|
||||
int len = strlen(s);
|
||||
|
||||
result = xmpp_alloc(ctx, len + 3);
|
||||
if (result != NULL) {
|
||||
result[0] = '"';
|
||||
memcpy(result+1, s, len);
|
||||
result[len+1] = '"';
|
||||
result[len+2] = '\0';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* split key, value pairs into a hash */
|
||||
static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
||||
{
|
||||
hash_t *result;
|
||||
char *text;
|
||||
char *key, *value;
|
||||
char *s, *t;
|
||||
|
||||
text = base64_decode(ctx, msg, strlen(msg));
|
||||
if (text == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = hash_new(ctx, 10, xmpp_free);
|
||||
if (result != NULL) {
|
||||
s = text;
|
||||
while (*s != '\0') {
|
||||
/* accumulate a key ending at '=' */
|
||||
t = s;
|
||||
while ((*t != '=') && (*t != '\0')) t++;
|
||||
if (*t == '\0') break; /* bad string */
|
||||
key = _make_string(ctx, s, (t-s));
|
||||
if (key == NULL) break;
|
||||
/* advance our start pointer past the key */
|
||||
s = t + 1;
|
||||
/* accumulate a value ending in ',' or '\0' */
|
||||
t = s;
|
||||
while ((*t != ',') && (*t != '\0')) t++;
|
||||
/* trim quotes if they occur */
|
||||
if (((*s == '\'') && (*(t-1) == '\'')) ||
|
||||
((*s == '"') && (*(t-1) == '"'))) {
|
||||
value = _make_string(ctx, s+1, (t-s-2));
|
||||
} else {
|
||||
value = _make_string(ctx, s, (t-s));
|
||||
}
|
||||
if (value == NULL) {
|
||||
xmpp_free(ctx, key);
|
||||
break;
|
||||
}
|
||||
/* TODO: check for collisions per spec */
|
||||
hash_add(result, key, value);
|
||||
/* hash table now owns the value, free the key */
|
||||
xmpp_free(ctx, key);
|
||||
/* advance past the value and any trailing comma */
|
||||
s = (*t) ? t + 1 : t;
|
||||
}
|
||||
}
|
||||
xmpp_free(ctx, text);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** expand a 16 byte MD5 digest to a 32 byte hex representation */
|
||||
static void _digest_to_hex(const char *digest, char *hex)
|
||||
{
|
||||
int i;
|
||||
const char hexdigit[] = "0123456789abcdef";
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
*hex++ = hexdigit[ (digest[i] >> 4) & 0x0F ];
|
||||
*hex++ = hexdigit[ digest[i] & 0x0F ];
|
||||
}
|
||||
}
|
||||
|
||||
/** append 'key="value"' to a buffer, growing as necessary */
|
||||
static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
|
||||
char *buf, int *len, int quote)
|
||||
{
|
||||
int olen,nlen;
|
||||
int keylen, valuelen;
|
||||
const char *value, *qvalue;
|
||||
char *c;
|
||||
|
||||
/* allocate a zero-length string if necessary */
|
||||
if (buf == NULL) {
|
||||
buf = xmpp_alloc(ctx, 1);
|
||||
buf[0] = '\0';
|
||||
}
|
||||
if (buf == NULL) return NULL;
|
||||
|
||||
/* get current string length */
|
||||
olen = strlen(buf);
|
||||
value = hash_get(table, key);
|
||||
if (value == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
||||
value = "\"\"";
|
||||
}
|
||||
if (quote) {
|
||||
qvalue = _make_quoted(ctx, value);
|
||||
} else {
|
||||
qvalue = value;
|
||||
}
|
||||
/* added length is key + '=' + value */
|
||||
/* (+ ',' if we're not the first entry */
|
||||
keylen = strlen(key);
|
||||
valuelen = strlen(qvalue);
|
||||
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
|
||||
buf = xmpp_realloc(ctx, buf, olen+nlen);
|
||||
|
||||
if (buf != NULL) {
|
||||
c = buf + olen;
|
||||
if (olen) *c++ = ',';
|
||||
memcpy(c, key, keylen); c += keylen;
|
||||
*c++ = '=';
|
||||
memcpy(c, qvalue, valuelen); c += valuelen;
|
||||
*c++ = '\0';
|
||||
}
|
||||
|
||||
if (quote) xmpp_free(ctx, (char *)qvalue);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** generate auth response string for the SASL DIGEST-MD5 mechanism */
|
||||
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
||||
const char *jid, const char *password) {
|
||||
hash_t *table;
|
||||
char *result = NULL;
|
||||
char *node, *domain;
|
||||
char *value;
|
||||
unsigned char *A1, *A2; /* MD5 hashed versions */
|
||||
char *response;
|
||||
int rlen;
|
||||
struct MD5Context MD5;
|
||||
unsigned char digest[16];
|
||||
char hex[32];
|
||||
|
||||
/* our digest response is
|
||||
Hex( KD( HEX(MD5(A1)),
|
||||
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
|
||||
))
|
||||
|
||||
where KD(k, s) = MD5(k ':' s),
|
||||
A1 = MD5( node ':' domain ':' password ) ':' nonce ':' cnonce
|
||||
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
||||
|
||||
If there is an authzid it is ':'-appended to A1 */
|
||||
|
||||
/* parse the challenge */
|
||||
table = _parse_digest_challenge(ctx, challenge);
|
||||
if (table == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = xmpp_jid_node(ctx, jid);
|
||||
domain = xmpp_jid_domain(ctx, jid);
|
||||
|
||||
/* add our response fields */
|
||||
hash_add(table, "username", xmpp_strdup(ctx, node));
|
||||
/* TODO: generate a random cnonce */
|
||||
hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
|
||||
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
|
||||
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
|
||||
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
|
||||
memcpy(value, "xmpp/", 5);
|
||||
memcpy(value+5, domain, strlen(domain));
|
||||
value[5+strlen(domain)] = '\0';
|
||||
hash_add(table, "digest-uri", value);
|
||||
|
||||
/* generate response */
|
||||
|
||||
/* construct MD5(A1) */
|
||||
MD5Init(&MD5);
|
||||
MD5Update(&MD5, node, strlen(node));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
MD5Update(&MD5, domain, strlen(domain));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
MD5Update(&MD5, password, strlen(password));
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
MD5Init(&MD5);
|
||||
MD5Update(&MD5, digest, 16);
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "nonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "cnonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
A1 = xmpp_alloc(ctx, 16);
|
||||
memcpy(A1, digest, 16);
|
||||
|
||||
/* construct MD5(A2) */
|
||||
MD5Init(&MD5);
|
||||
MD5Update(&MD5, "AUTHENTICATE:", 13);
|
||||
value = hash_get(table, "digest-uri");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
A2 = xmpp_alloc(ctx, 16);
|
||||
memcpy(A2, digest, 16);
|
||||
|
||||
/* construct response */
|
||||
MD5Init(&MD5);
|
||||
_digest_to_hex(A1, hex);
|
||||
MD5Update(&MD5, hex, 32);
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "nonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
MD5Update(&MD5, "00000001", 8);
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "cnonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "qop");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
_digest_to_hex(A2, hex);
|
||||
MD5Update(&MD5, hex, 32);
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
response = xmpp_alloc(ctx, 32+1);
|
||||
_digest_to_hex(digest, hex);
|
||||
memcpy(response, hex, 32);
|
||||
response[32] = '\0';
|
||||
hash_add(table, "response", response);
|
||||
|
||||
xmpp_free(ctx, A1);
|
||||
xmpp_free(ctx, A2);
|
||||
|
||||
/* construct reply */
|
||||
result = NULL;
|
||||
rlen = 0;
|
||||
result = _add_key(ctx, table, "username", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "realm", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "nonce", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "cnonce", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "nc", result, &rlen, 0);
|
||||
result = _add_key(ctx, table, "qop", result, &rlen, 0);
|
||||
result = _add_key(ctx, table, "digest-uri", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "response", result, &rlen, 0);
|
||||
result = _add_key(ctx, table, "charset", result, &rlen, 0);
|
||||
|
||||
xmpp_free(ctx, node);
|
||||
xmpp_free(ctx, domain);
|
||||
hash_release(table); /* also frees value strings */
|
||||
|
||||
/* reuse response for the base64 encode of our result */
|
||||
response = base64_encode(ctx, result, strlen(result));
|
||||
xmpp_free(ctx, result);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
||||
|
||||
/** map of all byte values to the base64 values, or to
|
||||
'65' which indicates an invalid character. '=' is '64' */
|
||||
static const char _base64_invcharmap[256] = {
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
|
||||
65, 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,65, 65,65,65,65,
|
||||
65,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,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
|
||||
};
|
||||
|
||||
/** map of all 6-bit values to their corresponding byte
|
||||
in the base64 alphabet. Padding char is the value '64' */
|
||||
static const char _base64_charmap[65] = {
|
||||
'A','B','C','D', 'E','F','G','H',
|
||||
'I','J','K','L', 'M','N','O','P',
|
||||
'Q','R','S','T', 'U','V','W','X',
|
||||
'Y','Z','a','b', 'c','d','e','f',
|
||||
'g','h','i','j', 'k','l','m','n',
|
||||
'o','p','q','r', 's','t','u','v',
|
||||
'w','x','y','z', '0','1','2','3',
|
||||
'4','5','6','7', '8','9','+','/',
|
||||
'='
|
||||
};
|
||||
|
||||
int base64_encoded_len(xmpp_ctx_t *ctx, const int len)
|
||||
{
|
||||
/* encoded steam is 4 bytes for every three, rounded up */
|
||||
return ((len + 2)/3) << 2;
|
||||
}
|
||||
|
||||
char *base64_encode(xmpp_ctx_t *ctx,
|
||||
const unsigned char * const buffer, const int len)
|
||||
{
|
||||
int clen;
|
||||
char *cbuf, *c;
|
||||
uint32_t word, hextet;
|
||||
int i;
|
||||
|
||||
clen = base64_encoded_len(ctx, len);
|
||||
cbuf = xmpp_alloc(ctx, clen + 1);
|
||||
if (cbuf != NULL) {
|
||||
c = cbuf;
|
||||
/* loop over data, turning every 3 bytes into 4 characters */
|
||||
for (i = 0; i < len - 2; i += 3) {
|
||||
word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
|
||||
hextet = (word & 0x00FC0000) >> 18;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (word & 0x0003F000) >> 12;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (word & 0x00000FC0) >> 6;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (word & 0x000003F);
|
||||
*c++ = _base64_charmap[hextet];
|
||||
}
|
||||
/* zero, one or two bytes left */
|
||||
switch (len - i) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
hextet = (buffer[len-1] & 0xFC) >> 2;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (buffer[len-1] & 0x02) << 6;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
*c++ = _base64_charmap[64]; /* pad */
|
||||
*c++ = _base64_charmap[64]; /* pad */
|
||||
break;
|
||||
case 2:
|
||||
hextet = (buffer[len-2] & 0xFC) >> 2;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = ((buffer[len-2] & 0x03) << 4) |
|
||||
((buffer[len-1] & 0xF0) >> 4);
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (buffer[len-1] & 0x0F) << 2;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
*c++ = _base64_charmap[64]; /* pad */
|
||||
break;
|
||||
}
|
||||
/* add a terminal null */
|
||||
*c = '\0';
|
||||
}
|
||||
|
||||
return cbuf;
|
||||
}
|
||||
|
||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len)
|
||||
{
|
||||
int nudge;
|
||||
int c;
|
||||
|
||||
/* count the padding characters for the remainder */
|
||||
nudge = -1;
|
||||
c = _base64_invcharmap[(int)buffer[len-1]];
|
||||
if (c < 64) nudge = 0;
|
||||
else if (c == 64) {
|
||||
c = _base64_invcharmap[(int)buffer[len-2]];
|
||||
if (c < 64) nudge = 2;
|
||||
else if (c == 64) {
|
||||
c = _base64_invcharmap[(int)buffer[len-3]];
|
||||
if (c < 64) nudge = 1;
|
||||
}
|
||||
}
|
||||
if (nudge < 0) return 0; /* reject bad coding */
|
||||
|
||||
/* decoded steam is 3 bytes for every four */
|
||||
return 3 * ((len + 3) >> 2) + nudge;
|
||||
}
|
||||
|
||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len)
|
||||
{
|
||||
int dlen;
|
||||
unsigned char *dbuf, *d;
|
||||
uint32_t word, hextet;
|
||||
int i;
|
||||
|
||||
/* len must be a multiple of 4 */
|
||||
if (len & 0x03) return NULL;
|
||||
|
||||
dlen = base64_decoded_len(ctx, buffer, len);
|
||||
dbuf = xmpp_alloc(ctx, dlen + 1);
|
||||
if (dbuf != NULL) {
|
||||
d = dbuf;
|
||||
/* loop over each set of 4 characters, decoding 3 bytes */
|
||||
for (i = 0; i < len - 3; i += 4) {
|
||||
hextet = _base64_invcharmap[(int)buffer[i]];
|
||||
if (hextet & 0xC0) break;
|
||||
word = hextet << 18;
|
||||
hextet = _base64_invcharmap[(int)buffer[i+1]];
|
||||
if (hextet & 0xC0) break;
|
||||
word |= hextet << 12;
|
||||
hextet = _base64_invcharmap[(int)buffer[i+2]];
|
||||
if (hextet & 0xC0) break;
|
||||
word |= hextet << 6;
|
||||
hextet = _base64_invcharmap[(int)buffer[i+3]];
|
||||
if (hextet & 0xC0) break;
|
||||
word |= hextet;
|
||||
*d++ = (word & 0x00FF0000) >> 16;
|
||||
*d++ = (word & 0x0000FF00) >> 8;
|
||||
*d++ = (word & 0x000000FF);
|
||||
}
|
||||
if (hextet > 64) goto _base64_decode_error;
|
||||
/* handle the remainder */
|
||||
switch (dlen % 3) {
|
||||
case 0:
|
||||
/* nothing to do */
|
||||
break;
|
||||
case 1:
|
||||
/* redo the last quartet, checking for correctness */
|
||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word = hextet << 2;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word |= hextet >> 4;
|
||||
*d++ = word & 0xFF;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
||||
if (hextet != 64) goto _base64_decode_error;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
||||
if (hextet != 64) goto _base64_decode_error;
|
||||
break;
|
||||
case 2:
|
||||
/* redo the last quartet, checking for correctness */
|
||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word = hextet << 10;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word |= hextet << 4;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word |= hextet >> 2;
|
||||
*d++ = (word & 0xFF00) >> 8;
|
||||
*d++ = (word & 0x00FF);
|
||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
||||
if (hextet != 64) goto _base64_decode_error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*d = '\0';
|
||||
return dbuf;
|
||||
|
||||
_base64_decode_error:
|
||||
/* invalid character; abort decoding! */
|
||||
xmpp_free(ctx, dbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*** self tests ***/
|
||||
#ifdef TEST
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int test_charmap_identity(void)
|
||||
{
|
||||
int i, v, u;
|
||||
|
||||
for (i = 0; i < 65; i++) {
|
||||
v = _base64_charmap[i];
|
||||
if (v > 255) return 1;
|
||||
u = _base64_invcharmap[v];
|
||||
/* printf("map: %d -> %d -> %d\n", i, v, u); */
|
||||
if (u != i) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_charmap_range(void)
|
||||
{
|
||||
int i, v;
|
||||
|
||||
for (i = 64; i < 256; i++) {
|
||||
v = _base64_invcharmap[i];
|
||||
if (i < 64) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printf("testing charmap identity...");
|
||||
ret = test_charmap_identity();
|
||||
if (ret) return ret;
|
||||
printf(" ok.\n");
|
||||
|
||||
printf("testing charmap range...");
|
||||
ret = test_charmap_range();
|
||||
if (ret) return ret;
|
||||
printf(" ok.\n");
|
||||
|
||||
printf("no error\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST */
|
||||
40
src/sasl.h
Normal file
40
src/sasl.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* sasl.h
|
||||
** XMPP client library -- SASL authentication helpers
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_SASL_H__
|
||||
#define __LIBXMPP_SASL_H__
|
||||
|
||||
#include "xmpp.h"
|
||||
|
||||
/** low-level sasl routines */
|
||||
|
||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
|
||||
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
||||
const char *jid, const char *password);
|
||||
|
||||
|
||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
||||
|
||||
int base64_encoded_len(xmpp_ctx_t *ctx, const int len);
|
||||
|
||||
char *base64_encode(xmpp_ctx_t *ctx,
|
||||
const unsigned char * const buffer, const int len);
|
||||
|
||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len);
|
||||
|
||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len);
|
||||
|
||||
#endif /* _LIBXMPP_SASL_H__ */
|
||||
385
src/sha1.c
Normal file
385
src/sha1.c
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
100% Public Domain
|
||||
|
||||
-----------------
|
||||
Modified 7/98
|
||||
By James H. Brown <jbrown@burgoyne.com>
|
||||
Still 100% Public Domain
|
||||
|
||||
Corrected a problem which generated improper hash values on 16 bit machines
|
||||
Routine SHA1Update changed from
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||
len)
|
||||
to
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||
long len)
|
||||
|
||||
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||
However, on 16 bit machines an int is too small for the shifts being done
|
||||
against
|
||||
it. This caused the hash function to generate incorrect values if len was
|
||||
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||
|
||||
Since the file IO in main() reads 16K at a time, any file 8K or larger would
|
||||
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
|
||||
"a"s).
|
||||
|
||||
I also changed the declaration of variables i & j in SHA1Update to
|
||||
unsigned long from unsigned int for the same reason.
|
||||
|
||||
These changes should make no difference to any 32 bit implementations since
|
||||
an
|
||||
int and a long are the same size in those environments.
|
||||
|
||||
--
|
||||
I also corrected a few compiler warnings generated by Borland C.
|
||||
1. Added #include <process.h> for exit() prototype
|
||||
2. Removed unused variable 'j' in SHA1Final
|
||||
3. Changed exit(0) to return(0) at end of main.
|
||||
|
||||
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||
-----------------
|
||||
Modified 8/98
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
Still 100% public domain
|
||||
|
||||
1- Removed #include <process.h> and used return() instead of exit()
|
||||
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||
|
||||
-----------------
|
||||
Modified 4/01
|
||||
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||
Still 100% PD
|
||||
Modified to run on Compaq Alpha hardware.
|
||||
|
||||
-----------------
|
||||
Modified 07/2002
|
||||
By Ralph Giles <giles@artofcode.com>
|
||||
Still 100% public domain
|
||||
modified for use with stdint types, autoconf
|
||||
code cleanup, removed attribution comments
|
||||
switched SHA1Final() argument order for consistency
|
||||
use SHA1_ prefix for public api
|
||||
move public api to sha1.h
|
||||
*/
|
||||
|
||||
/*
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define SHA1HANDSOFF */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* make sure the stdint.h types are available */
|
||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
typedef signed char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef __int64 int64_t;
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short int uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
/* no uint64_t */
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
/* FIXME: can we do this in an endian-proof way? */
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
#ifdef VERBOSE /* SAK */
|
||||
void SHAPrintContext(SHA1_CTX *context, char *msg){
|
||||
printf("%s (%d,%d) %x %x %x %x %x\n",
|
||||
msg,
|
||||
context->count[0], context->count[1],
|
||||
context->state[0],
|
||||
context->state[1],
|
||||
context->state[2],
|
||||
context->state[3],
|
||||
context->state[4]);
|
||||
}
|
||||
#endif /* VERBOSE */
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
CHAR64LONG16* block;
|
||||
|
||||
#ifdef SHA1HANDSOFF
|
||||
static uint8_t workspace[64];
|
||||
block = (CHAR64LONG16*)workspace;
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
block = (CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
void SHA1_Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "before");
|
||||
#endif
|
||||
|
||||
j = (context->count[0] >> 3) & 63;
|
||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1_Transform(context->state, data + i);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "after ");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t finalcount[8];
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
SHA1_Update(context, (uint8_t *)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
SHA1_Update(context, (uint8_t *)"\0", 1);
|
||||
}
|
||||
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
|
||||
digest[i] = (uint8_t)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
|
||||
/* Wipe variables */
|
||||
i = 0;
|
||||
memset(context->buffer, 0, 64);
|
||||
memset(context->state, 0, 20);
|
||||
memset(context->count, 0, 8);
|
||||
memset(finalcount, 0, 8); /* SWR */
|
||||
|
||||
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
#if 0
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int i, j;
|
||||
SHA1_CTX context;
|
||||
unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
|
||||
FILE* file;
|
||||
|
||||
if (argc > 2) {
|
||||
puts("Public domain SHA-1 implementation - by Steve Reid <sreid@sea-to-sky.net>");
|
||||
puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* JHB */
|
||||
puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
|
||||
return(0);
|
||||
}
|
||||
if (argc < 2) {
|
||||
file = stdin;
|
||||
}
|
||||
else {
|
||||
if (!(file = fopen(argv[1], "rb"))) {
|
||||
fputs("Unable to open file.", stderr);
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
SHA1_Init(&context);
|
||||
while (!feof(file)) { /* note: what if ferror(file) */
|
||||
i = fread(buffer, 1, 16384, file);
|
||||
SHA1_Update(&context, buffer, i);
|
||||
}
|
||||
SHA1_Final(&context, digest);
|
||||
fclose(file);
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
printf("%02X", digest[i*4+j]);
|
||||
}
|
||||
putchar(' ');
|
||||
}
|
||||
putchar('\n');
|
||||
return(0); /* JHB */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* self test */
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
static char *test_data[] = {
|
||||
"abc",
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||
"A million repetitions of 'a'"};
|
||||
static char *test_results[] = {
|
||||
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
|
||||
"84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
|
||||
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"};
|
||||
|
||||
|
||||
void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
|
||||
{
|
||||
int i,j;
|
||||
char *c = output;
|
||||
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
sprintf(c,"%02X", digest[i*4+j]);
|
||||
c += 2;
|
||||
}
|
||||
sprintf(c, " ");
|
||||
c += 1;
|
||||
}
|
||||
*(c - 1) = '\0';
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int k;
|
||||
SHA1_CTX context;
|
||||
uint8_t digest[20];
|
||||
char output[80];
|
||||
|
||||
fprintf(stdout, "verifying SHA-1 implementation... ");
|
||||
|
||||
for (k = 0; k < 2; k++){
|
||||
SHA1_Init(&context);
|
||||
SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k]));
|
||||
SHA1_Final(&context, digest);
|
||||
digest_to_hex(digest, output);
|
||||
|
||||
if (strcmp(output, test_results[k])) {
|
||||
fprintf(stdout, "FAIL\n");
|
||||
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]);
|
||||
fprintf(stderr,"\t%s returned\n", output);
|
||||
fprintf(stderr,"\t%s is correct\n", test_results[k]);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
/* million 'a' vector we feed separately */
|
||||
SHA1_Init(&context);
|
||||
for (k = 0; k < 1000000; k++)
|
||||
SHA1_Update(&context, (uint8_t*)"a", 1);
|
||||
SHA1_Final(&context, digest);
|
||||
digest_to_hex(digest, output);
|
||||
if (strcmp(output, test_results[2])) {
|
||||
fprintf(stdout, "FAIL\n");
|
||||
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]);
|
||||
fprintf(stderr,"\t%s returned\n", output);
|
||||
fprintf(stderr,"\t%s is correct\n", test_results[2]);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* success */
|
||||
fprintf(stdout, "ok\n");
|
||||
return(0);
|
||||
}
|
||||
#endif /* TEST */
|
||||
27
src/sha1.h
Normal file
27
src/sha1.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* public api for steve reid's public domain SHA-1 implementation */
|
||||
/* this file is in the public domain */
|
||||
|
||||
#ifndef __SHA1_H
|
||||
#define __SHA1_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
void SHA1_Init(SHA1_CTX* context);
|
||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
|
||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SHA1_H */
|
||||
164
src/sock.c
Normal file
164
src/sock.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/* sock.c
|
||||
** XMPP client library -- socket abstraction implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#define snprintf _snprintf
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "sock.h"
|
||||
|
||||
void sock_initialize(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsad;
|
||||
WSAStartup(0x0101, &wsad);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sock_shutdown(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_error(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _in_progress(int error)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
|
||||
#else
|
||||
return (errno == EINPROGRESS);
|
||||
#endif
|
||||
}
|
||||
|
||||
sock_t sock_connect(const char * const host, const unsigned int port)
|
||||
{
|
||||
sock_t sock;
|
||||
char service[6];
|
||||
struct addrinfo *res, *ainfo, hints;
|
||||
int err;
|
||||
|
||||
sock = -1;
|
||||
|
||||
snprintf(service, 6, "%u", port);
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((err = getaddrinfo(host, service, &hints, &res)) != 0)
|
||||
return -1;
|
||||
|
||||
ainfo = res;
|
||||
while (ainfo) {
|
||||
if ((sock = socket(ainfo->ai_family, ainfo->ai_socktype,
|
||||
ainfo->ai_protocol)) >= 0) {
|
||||
sock_set_nonblocking(sock);
|
||||
|
||||
err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
||||
|
||||
if ((err == 0) || (err < 0 && _in_progress(sock_error())))
|
||||
break;
|
||||
}
|
||||
|
||||
ainfo = ainfo->ai_next;
|
||||
}
|
||||
|
||||
if (res) freeaddrinfo(res);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int sock_close(const sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return closesocket(sock);
|
||||
#else
|
||||
return close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_set_blocking(const sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long block = 0;
|
||||
return ioctlsocket(sock, FIONBIO, &block);
|
||||
#else
|
||||
return fcntl(sock, F_SETFL, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_set_nonblocking(const sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long nonblock = 1;
|
||||
return ioctlsocket(sock, FIONBIO, &nonblock);
|
||||
#else
|
||||
return fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_read(const sock_t sock, void * const buff, const size_t len)
|
||||
{
|
||||
return recv(sock, buff, len, 0);
|
||||
}
|
||||
|
||||
int sock_write(const sock_t sock, const void * const buff, const size_t len)
|
||||
{
|
||||
return send(sock, buff, len, 0);
|
||||
}
|
||||
|
||||
int sock_is_recoverable(const int error)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
|
||||
error == WSAEINPROGRESS);
|
||||
#else
|
||||
return (error == EAGAIN || error == EINTR);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_connect_error(const sock_t sock)
|
||||
{
|
||||
socklen_t len;
|
||||
int error, ret;
|
||||
|
||||
len = sizeof(int);
|
||||
|
||||
ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (ret < 0) return ret;
|
||||
return error;
|
||||
}
|
||||
42
src/sock.h
Normal file
42
src/sock.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* sock.h
|
||||
** XMPP client library -- socket abstraction header
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_SOCK_H__
|
||||
#define __LIBXMPP_SOCK_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
typedef int sock_t;
|
||||
#else
|
||||
typedef SOCKET sock_t;
|
||||
#endif
|
||||
|
||||
void sock_initialize(void);
|
||||
void sock_shutdown(void);
|
||||
|
||||
int sock_error(void);
|
||||
|
||||
sock_t sock_connect(const char * const host, const unsigned int port);
|
||||
int sock_close(const sock_t sock);
|
||||
|
||||
int sock_set_blocking(const sock_t sock);
|
||||
int sock_set_nonblocking(const sock_t sock);
|
||||
int sock_read(const sock_t sock, void * const buff, const size_t len);
|
||||
int sock_write(const sock_t sock, const void * const buff, const size_t len);
|
||||
int sock_is_recoverable(const int error);
|
||||
/* checks for an error after connect, return 0 if connect successful */
|
||||
int sock_connect_error(const sock_t sock);
|
||||
|
||||
#endif /* __LIBXMPP_SOCK_H__ */
|
||||
505
src/stanza.c
Normal file
505
src/stanza.c
Normal file
@@ -0,0 +1,505 @@
|
||||
/* stanza.c
|
||||
** XMPP client library -- XMPP stanza object and utilities
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* allocate an initialize a blank stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t));
|
||||
if (stanza != NULL) {
|
||||
stanza->ref = 1;
|
||||
stanza->ctx = ctx;
|
||||
stanza->type = XMPP_STANZA_UNKNOWN;
|
||||
stanza->prev = NULL;
|
||||
stanza->next = NULL;
|
||||
stanza->children = NULL;
|
||||
stanza->parent = NULL;
|
||||
stanza->data = NULL;
|
||||
stanza->attributes = NULL;
|
||||
}
|
||||
|
||||
return stanza;
|
||||
}
|
||||
|
||||
/* clone a stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
stanza->ref++;
|
||||
|
||||
/* clone all children */
|
||||
for (child = stanza->children; child; child = child->next)
|
||||
xmpp_stanza_clone(child);
|
||||
|
||||
return stanza;
|
||||
}
|
||||
|
||||
/* copies and stanza and all children
|
||||
* this function returns a new stanza copied from stanza. the new
|
||||
* stanza will have no parent and no siblings. the caller is given
|
||||
* a reference to this new stanza. this function is used to extract
|
||||
* a child from one stanza for inclusion in another. */
|
||||
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_stanza_t *copy, *child, *copychild, *tail;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
void *val;
|
||||
|
||||
copy = xmpp_stanza_new(stanza->ctx);
|
||||
if (!copy) goto copy_error;
|
||||
|
||||
copy->type = stanza->type;
|
||||
|
||||
if (stanza->data) {
|
||||
copy->data = xmpp_strdup(stanza->ctx, stanza->data);
|
||||
if (!copy->data) goto copy_error;
|
||||
}
|
||||
|
||||
if (stanza->attributes) {
|
||||
copy->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
if (!copy->attributes) goto copy_error;
|
||||
iter = hash_iter_new(stanza->attributes);
|
||||
if (!iter) { printf("DEBUG HERE\n"); goto copy_error; }
|
||||
while ((key = hash_iter_next(iter))) {
|
||||
val = xmpp_strdup(stanza->ctx,
|
||||
(char *)hash_get(stanza->attributes, key));
|
||||
if (!val) goto copy_error;
|
||||
|
||||
if (hash_add(copy->attributes, key, val))
|
||||
goto copy_error;
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
}
|
||||
|
||||
tail = copy->children;
|
||||
for (child = stanza->children; child; child = child->next) {
|
||||
copychild = xmpp_stanza_copy(child);
|
||||
if (!copychild) goto copy_error;
|
||||
copychild->parent = copy;
|
||||
|
||||
if (tail) {
|
||||
copychild->prev = tail;
|
||||
tail->next = copychild;
|
||||
} else
|
||||
copy->children = copychild;
|
||||
tail = copychild;
|
||||
}
|
||||
|
||||
return copy;
|
||||
|
||||
copy_error:
|
||||
/* release all the hitherto allocated memory */
|
||||
if (copy) xmpp_stanza_release(copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* free a stanza object and it's contents */
|
||||
void xmpp_stanza_release(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_stanza_t *child, *tchild;
|
||||
|
||||
/* release all children */
|
||||
child = stanza->children;
|
||||
while (child) {
|
||||
tchild = child;
|
||||
child = child->next;
|
||||
xmpp_stanza_release(tchild);
|
||||
}
|
||||
|
||||
/* release stanza */
|
||||
if (stanza->ref > 1)
|
||||
stanza->ref--;
|
||||
else {
|
||||
if (stanza->attributes) hash_release(stanza->attributes);
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
xmpp_free(stanza->ctx, stanza);
|
||||
}
|
||||
}
|
||||
|
||||
/* small helper function */
|
||||
static inline void _render_update(int *written, const int length,
|
||||
const int lastwrite,
|
||||
size_t *left, char **ptr)
|
||||
{
|
||||
*written += lastwrite;
|
||||
|
||||
if (*written > length) {
|
||||
*left = 0;
|
||||
*ptr = NULL;
|
||||
} else {
|
||||
*left -= lastwrite;
|
||||
*ptr = &(*ptr)[lastwrite];
|
||||
}
|
||||
}
|
||||
|
||||
/* always returns number of bytes written or that would have been
|
||||
* written if the buffer was large enough
|
||||
* return values < 0 indicate some error occured,
|
||||
* and return values > buflen indicate buffer was not large enough
|
||||
*/
|
||||
static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
char * const buf, size_t const buflen)
|
||||
{
|
||||
char *ptr = buf;
|
||||
size_t left = buflen;
|
||||
int ret, written;
|
||||
xmpp_stanza_t *child;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
|
||||
written = 0;
|
||||
|
||||
if (stanza->type == XMPP_STANZA_UNKNOWN) return -1;
|
||||
|
||||
if (stanza->type == XMPP_STANZA_TEXT) {
|
||||
if (!stanza->data) return -2;
|
||||
|
||||
ret = snprintf(ptr, left, "%s", stanza->data);
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
} else { /* stanza->type == XMPP_STANZA_TAG */
|
||||
if (!stanza->data) return -2;
|
||||
|
||||
/* write begining of tag and attributes */
|
||||
ret = snprintf(ptr, left, "<%s", stanza->data);
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
|
||||
if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) {
|
||||
iter = hash_iter_new(stanza->attributes);
|
||||
while ((key = hash_iter_next(iter))) {
|
||||
ret = snprintf(ptr, left, " %s=\"%s\"", key,
|
||||
(char *)hash_get(stanza->attributes, key));
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
}
|
||||
|
||||
if (!stanza->children) {
|
||||
/* write end if singleton tag */
|
||||
ret = snprintf(ptr, left, "/>");
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
} else {
|
||||
/* this stanza has child stanzas */
|
||||
|
||||
/* write end of start tag */
|
||||
ret = snprintf(ptr, left, ">");
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
|
||||
/* iterate and recurse over child stanzas */
|
||||
child = stanza->children;
|
||||
while (child) {
|
||||
ret = _render_stanza_recursive(child, ptr, left);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
/* write end tag */
|
||||
ret = snprintf(ptr, left, "</%s>", stanza->data);
|
||||
if (ret < 0) return -4;
|
||||
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
/* render a stanza to text
|
||||
* a buffer is allocated big enough to hold the stanza
|
||||
* and *buf = buffer. the size of buffer filled with data
|
||||
* is returned in *buflen (does not include trailing \0).
|
||||
* the returned buffer contains a trailing \0 so the result is
|
||||
* a valid string.
|
||||
*/
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
char ** const buf,
|
||||
size_t * const buflen)
|
||||
{
|
||||
char *buffer, *tmp;
|
||||
size_t length;
|
||||
int ret;
|
||||
|
||||
/* allocate a default sized buffer and attempt to render */
|
||||
length = 1024;
|
||||
buffer = xmpp_alloc(stanza->ctx, length);
|
||||
if (!buffer) {
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return -5;
|
||||
}
|
||||
|
||||
ret = _render_stanza_recursive(stanza, buffer, length);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
if (ret > length - 1) {
|
||||
tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1);
|
||||
if (!tmp) {
|
||||
xmpp_free(stanza->ctx, buffer);
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return -5;
|
||||
}
|
||||
length = ret;
|
||||
buffer = tmp;
|
||||
buffer[length-1] = 0;
|
||||
|
||||
ret = _render_stanza_recursive(stanza, buffer, length);
|
||||
if (ret > length) return -6;
|
||||
}
|
||||
|
||||
length = ret + 1;
|
||||
buffer[length - 1] = 0;
|
||||
|
||||
*buf = buffer;
|
||||
*buflen = length - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_name(xmpp_stanza_t *stanza,
|
||||
const char * const name)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT) return;
|
||||
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
|
||||
stanza->type = XMPP_STANZA_TAG;
|
||||
stanza->data = xmpp_strdup(stanza->ctx, name);
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT) return NULL;
|
||||
return stanza->data;
|
||||
}
|
||||
|
||||
/* convinience function to copy attributes from the xml parser
|
||||
* callback into a stanza. this replaces all previous attributes */
|
||||
void xmpp_stanza_set_attributes(xmpp_stanza_t *stanza,
|
||||
const char * const * const attr)
|
||||
{
|
||||
int i;
|
||||
char *value;
|
||||
|
||||
if (stanza->attributes != NULL)
|
||||
hash_release(stanza->attributes);
|
||||
|
||||
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
if (!stanza->attributes) return;
|
||||
|
||||
for (i = 0; attr[i]; i += 2) {
|
||||
value = xmpp_strdup(stanza->ctx, attr[i + 1]);
|
||||
if (!value) {
|
||||
/* FIXME: memory allocation error */
|
||||
continue;
|
||||
}
|
||||
hash_add(stanza->attributes, attr[i], value);
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
|
||||
const char * const key,
|
||||
const char * const value)
|
||||
{
|
||||
char *val;
|
||||
|
||||
if (stanza->type != XMPP_STANZA_TAG) return;
|
||||
|
||||
if (!stanza->attributes) {
|
||||
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
if (!stanza->attributes) return;
|
||||
}
|
||||
|
||||
val = xmpp_strdup(stanza->ctx, value);
|
||||
if (!val) return;
|
||||
|
||||
hash_add(stanza->attributes, key, val);
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_ns(xmpp_stanza_t * const stanza,
|
||||
const char * const ns)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stanza, "xmlns", ns);
|
||||
}
|
||||
|
||||
void xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
|
||||
{
|
||||
xmpp_stanza_t *s;
|
||||
|
||||
/* get a reference to the child */
|
||||
xmpp_stanza_clone(child);
|
||||
|
||||
child->parent = stanza;
|
||||
|
||||
if (!stanza->children)
|
||||
stanza->children = child;
|
||||
else {
|
||||
s = stanza->children;
|
||||
while (s->next) s = s->next;
|
||||
s->next = child;
|
||||
child->prev = s;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_text(xmpp_stanza_t *stanza,
|
||||
const char * const text)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TAG) return;
|
||||
|
||||
stanza->type = XMPP_STANZA_TEXT;
|
||||
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
stanza->data = xmpp_strdup(stanza->ctx, text);
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
const char * const text,
|
||||
const size_t size)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TAG) return;
|
||||
|
||||
stanza->type = XMPP_STANZA_TEXT;
|
||||
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
stanza->data = xmpp_alloc(stanza->ctx, size + 1);
|
||||
if (!stanza->data) return;
|
||||
|
||||
memcpy(stanza->data, text, size);
|
||||
stanza->data[size] = 0;
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_id(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return (char *)hash_get(stanza->attributes, "id");
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_ns(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return (char *)hash_get(stanza->attributes, "xmlns");
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_type(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return (char *)hash_get(stanza->attributes, "type");
|
||||
}
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza,
|
||||
const char * const name)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
for (child = stanza->children; child; child = child->next) {
|
||||
if (child->type == XMPP_STANZA_TAG &&
|
||||
(strcmp(name, xmpp_stanza_get_name(child)) == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
return stanza->children;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
return stanza->next;
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
size_t len, clen;
|
||||
xmpp_stanza_t *child;
|
||||
char *text;
|
||||
|
||||
len = 0;
|
||||
for (child = stanza->children; child; child = child->next)
|
||||
if (child->type == XMPP_STANZA_TEXT)
|
||||
len += strlen(child->data);
|
||||
|
||||
text = (char *)xmpp_alloc(stanza->ctx, len + 1);
|
||||
if (!text) return NULL;
|
||||
|
||||
len = 0;
|
||||
for (child = stanza->children; child; child = child->next)
|
||||
if (child->type == XMPP_STANZA_TEXT) {
|
||||
clen = strlen(child->data);
|
||||
memcpy(&text[len], child->data, clen);
|
||||
len += clen;
|
||||
}
|
||||
|
||||
text[len] = 0;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_id(xmpp_stanza_t * const stanza,
|
||||
const char * const id)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stanza, "id", id);
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_type(xmpp_stanza_t * const stanza,
|
||||
const char * const type)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stanza, "type", type);
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
|
||||
const char * const name)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return hash_get(stanza->attributes, name);
|
||||
}
|
||||
70
src/util.c
Normal file
70
src/util.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* util.c
|
||||
** XMPP client library -- various utility functions
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
|
||||
/** implement our own strdup that uses the ctx allocator */
|
||||
char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s)
|
||||
{
|
||||
size_t len;
|
||||
char *copy;
|
||||
|
||||
len = strlen(s);
|
||||
copy = xmpp_alloc(ctx, len + 1);
|
||||
if (!copy) {
|
||||
xmpp_error(ctx, "xmpp", "failed to allocate required memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(copy, s, len + 1);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
uint64_t time_stamp(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return timeGetTime();
|
||||
#else
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t time_elapsed(uint64_t t1, uint64_t t2)
|
||||
{
|
||||
return (uint64_t)(t2 - t1);
|
||||
}
|
||||
|
||||
void disconnect_mem_error(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
26
src/util.h
Normal file
26
src/util.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* util.h
|
||||
** XMPP client library -- various utility functions
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_UTIL_H__
|
||||
#define __LIBXMPP_UTIL_H__
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* timing functions */
|
||||
uint64_t time_stamp(void);
|
||||
uint64_t time_elapsed(uint64_t t1, uint64_t t2);
|
||||
|
||||
#endif /* __LIBXMPP_UTIL_H__ */
|
||||
151
tests/test_base64.c
Normal file
151
tests/test_base64.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/* test_base64.c
|
||||
** XMPP client library -- test routines for the base64 codec
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
|
||||
static const unsigned char text_1[] = "How now brown cow?";
|
||||
static const char base64_1[] = "SG93IG5vdyBicm93biBjb3c/";
|
||||
static const unsigned char text_2[] = {0x14,0xfb,0x9c,0x03,0xd9,0x7e,0x00};
|
||||
static const char base64_2[] = "FPucA9l+";
|
||||
static const unsigned char text_3[] =
|
||||
"From rest and sleep, which but thy pictures be, "
|
||||
"Much pleasure; then from thee much more must flow, "
|
||||
"And soonest our best men with thee do go, "
|
||||
"Rest of their bones, and soul's delivery.";
|
||||
static const char base64_3[] =
|
||||
"RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
|
||||
"cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt"
|
||||
"b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp"
|
||||
"dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv"
|
||||
"dWwncyBkZWxpdmVyeS4=";
|
||||
static const unsigned char text_4[] =
|
||||
{0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd, 0xf3, 0xd5,
|
||||
0x41, 0xbc, 0x1b, 0xe9, 0xdf, 0xe9, 0xb3, 0x08, 0x00};
|
||||
static const char base64_4[] = "1i8nSX7d89VBvBvp3+mzCA==";
|
||||
static const char text_5[] =
|
||||
"realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
|
||||
static const char base64_5[] =
|
||||
"cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
|
||||
"YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo"
|
||||
"YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz";
|
||||
|
||||
int test_encode(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *result;
|
||||
result = base64_encode(ctx, text_1, strlen(text_1));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(base64_1)) return 1;
|
||||
if (strncmp(base64_1, result, strlen(base64_1))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_encode(ctx, text_2, strlen(text_2));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(base64_2)) return 1;
|
||||
if (strncmp(base64_2, result, strlen(base64_2))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_encode(ctx, text_3, strlen(text_3));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(base64_3)) return 1;
|
||||
if (strncmp(base64_3, result, strlen(base64_3))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_encode(ctx, text_4, strlen(text_4));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(base64_4)) return 1;
|
||||
if (strncmp(base64_4, result, strlen(base64_4))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_encode(ctx, text_5, strlen(text_5));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(base64_5)) return 1;
|
||||
if (strncmp(base64_5, result, strlen(base64_5))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int test_decode(xmpp_ctx_t *ctx)
|
||||
{
|
||||
unsigned char *result;
|
||||
|
||||
result = base64_decode(ctx, base64_1, strlen(base64_1));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(text_1)) return 1;
|
||||
if (strncmp(text_1, result, strlen(text_1))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_decode(ctx, base64_2, strlen(base64_2));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(text_2)) return 1;
|
||||
if (strncmp(text_2, result, strlen(text_2))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_decode(ctx, base64_3, strlen(base64_3));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(text_3)) return 1;
|
||||
if (strncmp(text_3, result, strlen(text_3))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_decode(ctx, base64_4, strlen(base64_4));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(text_4)) return 1;
|
||||
if (strncmp(text_4, result, strlen(text_4))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
result = base64_decode(ctx, base64_5, strlen(base64_5));
|
||||
if (result == NULL) return 2;
|
||||
if (strlen(result) != strlen(text_5)) return 1;
|
||||
if (strncmp(text_5, result, strlen(text_5))) return 1;
|
||||
xmpp_free(ctx,result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
int ret;
|
||||
|
||||
printf("allocating context... ");
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
if (ctx == NULL) printf("failed to create context\n");
|
||||
if (ctx == NULL) return -1;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("testing encode... ");
|
||||
ret = test_encode(ctx);
|
||||
if (ret) printf("base64_encode failed!\n");
|
||||
if (ret) return ret;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("testing decode... ");
|
||||
ret = test_decode(ctx);
|
||||
if (ret) printf("base64_decode failed!\n");
|
||||
if (ret) return ret;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("freeing context... ");
|
||||
xmpp_ctx_free(ctx);
|
||||
printf("ok.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
102
tests/test_ctx.c
Normal file
102
tests/test_ctx.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/* test_ctx.c
|
||||
** XMPP client library -- test routines for the library run-time context
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
static int log_called = 0;
|
||||
static int mem_alloc_called = 0;
|
||||
static int mem_free_called = 0;
|
||||
static int mem_realloc_called = 0;
|
||||
|
||||
void *my_alloc(const size_t size)
|
||||
{
|
||||
mem_alloc_called++;
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void my_free(void *p)
|
||||
{
|
||||
mem_free_called++;
|
||||
return free(p);
|
||||
}
|
||||
|
||||
void *my_realloc(void *p, const size_t size)
|
||||
{
|
||||
mem_realloc_called++;
|
||||
return realloc(p, size);
|
||||
}
|
||||
|
||||
void my_logger(void * const userdata, const xmpp_log_level_t level,
|
||||
const char * const area, const char * const msg)
|
||||
{
|
||||
if (strcmp((char *)userdata, "asdf") == 0 && level == XMPP_LEVEL_DEBUG
|
||||
&& strcmp(area, "test") == 0 && strcmp(msg, "hello") == 0)
|
||||
log_called++;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_mem_t mymem;
|
||||
xmpp_log_t mylog;
|
||||
char my_str[5] = "asdf";
|
||||
void *testptr1, *testptr2;
|
||||
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
if (ctx == NULL) return 1;
|
||||
|
||||
/* destroy context */
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
/* setup our memory handler */
|
||||
mymem.alloc = my_alloc;
|
||||
mymem.free = my_free;
|
||||
mymem.realloc = my_realloc;
|
||||
|
||||
/* setup our logger */
|
||||
mylog.handler = my_logger;
|
||||
mylog.userdata = my_str;
|
||||
|
||||
ctx = xmpp_ctx_new(&mymem, &mylog);
|
||||
xmpp_debug(ctx, "test", "hello");
|
||||
|
||||
testptr1 = xmpp_alloc(ctx, 1024);
|
||||
if (testptr1 == NULL) {
|
||||
xmpp_ctx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
testptr2 = xmpp_realloc(ctx, testptr1, 2048);
|
||||
if (testptr2 == NULL) {
|
||||
xmpp_free(ctx, testptr1);
|
||||
xmpp_ctx_free(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_free(ctx, testptr2);
|
||||
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
/* check for test failure */
|
||||
if (!(log_called && mem_alloc_called && mem_realloc_called &&
|
||||
mem_free_called))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
141
tests/test_hash.c
Normal file
141
tests/test_hash.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/* test_hash.c
|
||||
** XMPP client library -- self-test for the hash-table implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "hash.h"
|
||||
|
||||
#define TABLESIZE 100
|
||||
#define TESTSIZE 500
|
||||
|
||||
/* static test data */
|
||||
const int nkeys = 5;
|
||||
const char *keys[] = {
|
||||
"foo", "bar", "baz", "quux", "xyzzy"
|
||||
};
|
||||
const char *values[] = {
|
||||
"wuzzle", "mug", "canonical", "rosebud", "lottery"
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
hash_t *table, *clone;
|
||||
hash_iterator_t *iter;
|
||||
unsigned int seed;
|
||||
const char *key;
|
||||
char *result;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
/* initialize random numbers */
|
||||
if (argc > 2) {
|
||||
/* use a seed from the command line */
|
||||
seed = (unsigned int)atoi(argv[1]);
|
||||
} else {
|
||||
seed = (unsigned int)clock();
|
||||
}
|
||||
/* using random seed 'seed' */
|
||||
srand(seed);
|
||||
|
||||
/* allocate a default context */
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
if (ctx == NULL) {
|
||||
/* ctx allocation failed! */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* allocate a hash table */
|
||||
table = hash_new(ctx, TABLESIZE, NULL);
|
||||
if (table == NULL) {
|
||||
/* table allocation failed! */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* test insertion */
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
err = hash_add(table, keys[i], (void*)values[i]);
|
||||
if (err) return err;
|
||||
}
|
||||
|
||||
/* test key count */
|
||||
if (hash_num_keys(table) != nkeys) {
|
||||
/* wrong number of keys in table! */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* test cloning */
|
||||
clone = hash_clone(table);
|
||||
|
||||
/* test lookup */
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
result = hash_get(clone, keys[i]);
|
||||
if (result == NULL) {
|
||||
/* lookup failed! */
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(values[i], result)) {
|
||||
/* lookup returned incorrect value! */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* test key iterator */
|
||||
iter = hash_iter_new(clone);
|
||||
if (iter == NULL) {
|
||||
/* iterator allocation failed! */
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < nkeys; i++) {
|
||||
key = hash_iter_next(iter);
|
||||
printf("key: '%s'\n", key);
|
||||
}
|
||||
key = hash_iter_next(iter);
|
||||
if (key != NULL) {
|
||||
/* extra keys returned! */
|
||||
return 1;
|
||||
}
|
||||
key = hash_iter_next(iter);
|
||||
if (key != NULL) {
|
||||
/* extra keys returned! */
|
||||
return 1;
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
|
||||
/* release the hash table */
|
||||
hash_release(table);
|
||||
|
||||
/* test drops */
|
||||
hash_drop(clone, keys[2]);
|
||||
if (hash_get(clone, keys[2]) != NULL) return 1;
|
||||
hash_drop(clone, keys[1]);
|
||||
hash_drop(clone, keys[4]);
|
||||
if (hash_get(clone, keys[4]) != NULL) return 1;
|
||||
if (hash_get(clone, keys[1]) != NULL) return 1;
|
||||
/* keys 0,3 should still be available */
|
||||
if (hash_get(clone, keys[0]) == NULL) return 1;
|
||||
if (hash_get(clone, keys[3]) == NULL) return 1;
|
||||
|
||||
/* release our clone */
|
||||
hash_release(clone);
|
||||
|
||||
/* release our library context */
|
||||
xmpp_ctx_free(ctx);
|
||||
|
||||
return err;
|
||||
}
|
||||
128
tests/test_jid.c
Normal file
128
tests/test_jid.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/* test_base64.c
|
||||
** XMPP client library -- test routines for the jid utils
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
static const char jid1[] = "foo@bar.com";
|
||||
static const char jid2[] = "anyone@example.com/hullo";
|
||||
static const char jid3[] = "manic.porter@xyz.net/frob";
|
||||
static const char jid4[] = "domain.tld";
|
||||
|
||||
int test_jid(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *node;
|
||||
char *domain;
|
||||
char *resource;
|
||||
|
||||
node = xmpp_jid_node(ctx, jid1);
|
||||
domain = xmpp_jid_domain(ctx, jid1);
|
||||
resource = xmpp_jid_resource(ctx, jid1);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n",
|
||||
jid1, node, domain, resource);
|
||||
if (strcmp(node, "foo")) return 1;
|
||||
if (strcmp(domain, "bar.com")) return 1;
|
||||
if (resource != NULL) return 1;
|
||||
if (node) xmpp_free(ctx, node);
|
||||
if (domain) xmpp_free(ctx, domain);
|
||||
if (resource) xmpp_free(ctx, resource);
|
||||
|
||||
node = xmpp_jid_node(ctx, jid2);
|
||||
domain = xmpp_jid_domain(ctx, jid2);
|
||||
resource = xmpp_jid_resource(ctx, jid2);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n",
|
||||
jid2, node, domain, resource);
|
||||
if (strcmp(node, "anyone")) return 1;
|
||||
if (strcmp(domain, "example.com")) return 1;
|
||||
if (strcmp(resource, "hullo")) return 1;
|
||||
if (node) xmpp_free(ctx, node);
|
||||
if (domain) xmpp_free(ctx, domain);
|
||||
if (resource) xmpp_free(ctx, resource);
|
||||
|
||||
node = xmpp_jid_node(ctx, jid3);
|
||||
domain = xmpp_jid_domain(ctx, jid3);
|
||||
resource = xmpp_jid_resource(ctx, jid3);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n",
|
||||
jid3, node, domain, resource);
|
||||
if (strcmp(node, "manic.porter")) return 1;
|
||||
if (strcmp(domain, "xyz.net")) return 1;
|
||||
if (strcmp(resource, "frob")) return 1;
|
||||
if (node) xmpp_free(ctx, node);
|
||||
if (domain) xmpp_free(ctx, domain);
|
||||
if (resource) xmpp_free(ctx, resource);
|
||||
|
||||
node = xmpp_jid_node(ctx, jid4);
|
||||
domain = xmpp_jid_domain(ctx, jid4);
|
||||
resource = xmpp_jid_resource(ctx, jid4);
|
||||
printf("jid '%s' parsed to %s, %s, %s\n",
|
||||
jid4, node, domain, resource);
|
||||
if (node != NULL) return 1;
|
||||
if (strcmp(domain, "domain.tld")) return 1;
|
||||
if (resource != NULL) return 1;
|
||||
if (node) xmpp_free(ctx, node);
|
||||
if (domain) xmpp_free(ctx, domain);
|
||||
if (resource) xmpp_free(ctx, resource);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_jid_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *jid;
|
||||
|
||||
jid = xmpp_jid_new(ctx, "node", "domain", "resource");
|
||||
printf("new jid: '%s'\n", jid);
|
||||
if (strcmp(jid, "node@domain/resource")) return 1;
|
||||
xmpp_free(ctx, jid);
|
||||
|
||||
jid = xmpp_jid_new(ctx, "foo", "bar.com", NULL);
|
||||
printf("new jid: '%s'\n", jid);
|
||||
if (strcmp(jid, "foo@bar.com")) return 1;
|
||||
xmpp_free(ctx, jid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
int ret;
|
||||
|
||||
printf("allocating context... ");
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
if (ctx == NULL) printf("failed to create context\n");
|
||||
if (ctx == NULL) return -1;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("testing jid routines... ");
|
||||
ret = test_jid(ctx);
|
||||
if (ret) printf("failed!\n");
|
||||
if (ret) return ret;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("testing jid new routines... ");
|
||||
ret = test_jid_new(ctx);
|
||||
if (ret) printf("failed!\n");
|
||||
if (ret) return ret;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("freeing context... ");
|
||||
xmpp_ctx_free(ctx);
|
||||
printf("ok.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
114
tests/test_sasl.c
Normal file
114
tests/test_sasl.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/* test_sasl.c
|
||||
** XMPP client library -- test routines for the SASL implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
|
||||
static const unsigned char jid[] = "foo@bar.com";
|
||||
static const char password[] = "secret";
|
||||
static const char response_plain[] = "AGZvb0BiYXIuY29tAHNlY3JldA==";
|
||||
|
||||
static const char challenge_md5[] =
|
||||
"cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi"
|
||||
"LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==";
|
||||
static const char response_md5[] =
|
||||
"dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i"
|
||||
"T0E2TUc5dEVRR20yaGgiLGNub25jZT0iMDBERUFEQkVFRjAwIixuYz0wMDAw"
|
||||
"MDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL3NvbWVyZWFsbSIscmVz"
|
||||
"cG9uc2U9NGVhNmU4N2JjMDkzMzUwNzQzZGIyOGQ3MDIwOGNhZmIsY2hhcnNl"
|
||||
"dD11dGYtOA==";
|
||||
static const char response_md5_rfc[] =
|
||||
"dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i"
|
||||
"T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw"
|
||||
"MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i"
|
||||
"LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo"
|
||||
"YXJzZXQ9dXRmLTgK";
|
||||
|
||||
static const char challenge_md5_2[] =
|
||||
"cmVhbG09ImVsd29vZC5pbm5vc29mdC5jb20iLG5vbmNlPSJPQTZNRzl0"
|
||||
"RVFHbTJoaCIscW9wPSJhdXRoIixhbGdvcml0aG09bWQ1LXNlc3MsY2hh"
|
||||
"cnNldD11dGYtOA==";
|
||||
static const char response_md5_2[] =
|
||||
"Y2hhcnNldD11dGYtOCx1c2VybmFtZT0iY2hyaXMiLHJlYWxtPSJlbHdvb2"
|
||||
"QuaW5ub3NvZnQuY29tIixub25jZT0iT0E2TUc5dEVRR20yaGgiLG5jPTAw"
|
||||
"MDAwMDAxLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLGRpZ2VzdC11cmk9Im"
|
||||
"ltYXAvZWx3b29kLmlubm9zb2Z0LmNvbSIscmVzcG9uc2U9ZDM4OGRhZDkw"
|
||||
"ZDRiYmQ3NjBhMTUyMzIxZjIxNDNhZjcscW9wPWF1dGg=";
|
||||
|
||||
int test_plain(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *result;
|
||||
|
||||
result = sasl_plain(ctx, jid, password);
|
||||
if (result == NULL) {
|
||||
/* SASL PLAIN internal failure! */
|
||||
return 1;
|
||||
}
|
||||
if (strncmp(response_plain, result, strlen(response_plain))) {
|
||||
/* SASL PLAIN returned incorrect string! */
|
||||
return 2;
|
||||
}
|
||||
xmpp_free(ctx, result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_digest_md5(xmpp_ctx_t *ctx)
|
||||
{
|
||||
char *result;
|
||||
|
||||
result = sasl_digest_md5(ctx, challenge_md5,
|
||||
"somenode@somerealm", "secret");
|
||||
printf("response:\n%s\n", result);
|
||||
if (strcmp(response_md5, result)) {
|
||||
/* generated incorrect response to challenge */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
int ret;
|
||||
|
||||
printf("allocating context... ");
|
||||
ctx = xmpp_ctx_new(NULL, NULL);
|
||||
if (ctx == NULL) printf("failed to create context\n");
|
||||
if (ctx == NULL) return -1;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("testing SASL PLAIN... ");
|
||||
ret = test_plain(ctx);
|
||||
if (ret) printf("failed!\n");
|
||||
if (ret) return ret;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("testing SASL DIGEST-MD5... ");
|
||||
ret = test_digest_md5(ctx);
|
||||
if (ret) printf("failed!\n");
|
||||
if (ret) return ret;
|
||||
printf("ok.\n");
|
||||
|
||||
printf("freeing context... ");
|
||||
xmpp_ctx_free(ctx);
|
||||
printf("ok.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
64
tests/test_sock.c
Normal file
64
tests/test_sock.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/* test_sock.c
|
||||
** XMPP client library -- test routines for the socket abstraction
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "sock.h"
|
||||
|
||||
int wait_for_connect(sock_t sock)
|
||||
{
|
||||
fd_set wfds, efds;
|
||||
int ret;
|
||||
|
||||
FD_ZERO(&wfds); FD_SET(sock, &wfds);
|
||||
FD_ZERO(&efds); FD_SET(sock, &efds);
|
||||
|
||||
ret = select(sock + 1, NULL, &wfds, &efds, NULL);
|
||||
if (ret <= 0) return -1;
|
||||
|
||||
if (FD_ISSET(sock, &efds)) return 0;
|
||||
if (FD_ISSET(sock, &wfds)) return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
sock_t sock;
|
||||
int err;
|
||||
|
||||
sock_initialize();
|
||||
|
||||
sock = sock_connect("www.google.com", 80);
|
||||
|
||||
if (sock < 0) {
|
||||
sock_shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = wait_for_connect(sock);
|
||||
if (err < 0) {
|
||||
sock_close(sock);
|
||||
sock_shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
sock_close(sock);
|
||||
|
||||
sock_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
261
xmpp.h
Normal file
261
xmpp.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/* xmpp.h
|
||||
** XMPP client library API
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LLC. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_XMPP_H__
|
||||
#define __LIBXMPP_XMPP_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* namespace defines */
|
||||
#define XMPP_NS_CLIENT "jabber:client"
|
||||
#define XMPP_NS_COMPONENT "jabber:component:accept"
|
||||
#define XMPP_NS_STREAMS "http://etherx.jabber.org/streams"
|
||||
#define XMPP_NS_TLS "urn:ietf:params:xml:ns:xmpp-tls"
|
||||
#define XMPP_NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
|
||||
#define XMPP_NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
|
||||
#define XMPP_NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
|
||||
#define XMPP_NS_AUTH "jabber:iq:auth"
|
||||
#define XMPP_NS_DISCO_INFO "http://jabber.org/protocol/disco#info"
|
||||
#define XMPP_NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
|
||||
#define XMPP_NS_ROSTER "jabber:iq:roster"
|
||||
|
||||
/** version **/
|
||||
|
||||
int xmpp_version_check(int major, int minor);
|
||||
|
||||
/** run-time contexts **/
|
||||
|
||||
/* user-replaceable memory allocator */
|
||||
typedef struct _xmpp_mem_t xmpp_mem_t;
|
||||
|
||||
/* user-replaceable log object */
|
||||
typedef struct _xmpp_log_t xmpp_log_t;
|
||||
|
||||
/* opaque run time context containing the above hooks */
|
||||
typedef struct _xmpp_ctx_t xmpp_ctx_t;
|
||||
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
||||
const xmpp_log_t * const log);
|
||||
void xmpp_ctx_free(xmpp_ctx_t * const ctx);
|
||||
|
||||
struct _xmpp_mem_t {
|
||||
void *(*alloc)(const size_t size);
|
||||
void (*free)(void *p);
|
||||
void *(*realloc)(void *p, const size_t size);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
XMPP_LEVEL_DEBUG,
|
||||
XMPP_LEVEL_INFO,
|
||||
XMPP_LEVEL_WARN,
|
||||
XMPP_LEVEL_ERROR
|
||||
} xmpp_log_level_t;
|
||||
|
||||
typedef enum {
|
||||
XMPP_UNKNOWN,
|
||||
XMPP_CLIENT,
|
||||
XMPP_COMPONENT
|
||||
} xmpp_conn_type_t;
|
||||
|
||||
typedef void (*xmpp_log_handler)(void * const userdata,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const msg);
|
||||
|
||||
struct _xmpp_log_t {
|
||||
xmpp_log_handler handler;
|
||||
void * userdata;
|
||||
/* mutex_t lock; */
|
||||
};
|
||||
|
||||
/** connection **/
|
||||
|
||||
/* opaque connection object */
|
||||
typedef struct _xmpp_conn_t xmpp_conn_t;
|
||||
typedef struct _xmpp_stanza_t xmpp_stanza_t;
|
||||
|
||||
/* connect callback */
|
||||
typedef enum {
|
||||
XMPP_CONN_CONNECT,
|
||||
XMPP_CONN_DISCONNECT
|
||||
} xmpp_conn_event_t;
|
||||
|
||||
typedef enum {
|
||||
XMPP_SE_BAD_FORMAT,
|
||||
XMPP_SE_BAD_NS_PREFIX,
|
||||
XMPP_SE_CONFLICT,
|
||||
XMPP_SE_CONN_TIMEOUT,
|
||||
XMPP_SE_HOST_GONE,
|
||||
XMPP_SE_HOST_UNKNOWN,
|
||||
XMPP_SE_IMPROPER_ADDR,
|
||||
XMPP_SE_INTERNAL_SERVER_ERROR,
|
||||
XMPP_SE_INVALID_FROM,
|
||||
XMPP_SE_INVALID_ID,
|
||||
XMPP_SE_INVALID_NS,
|
||||
XMPP_SE_INVALID_XML,
|
||||
XMPP_SE_NOT_AUTHORIZED,
|
||||
XMPP_SE_POLICY_VIOLATION,
|
||||
XMPP_SE_REMOTE_CONN_FAILED,
|
||||
XMPP_SE_RESOURCE_CONSTRAINT,
|
||||
XMPP_SE_RESTRICTED_XML,
|
||||
XMPP_SE_SEE_OTHER_HOST,
|
||||
XMPP_SE_SYSTEM_SHUTDOWN,
|
||||
XMPP_SE_UNDEFINED_CONDITION,
|
||||
XMPP_SE_UNSUPPORTED_ENCODING,
|
||||
XMPP_SE_UNSUPPORTED_STANZA_TYPE,
|
||||
XMPP_SE_UNSUPPORTED_VERSION,
|
||||
XMPP_SE_XML_NOT_WELL_FORMED
|
||||
} xmpp_error_type_t;
|
||||
|
||||
typedef struct {
|
||||
xmpp_error_type_t type;
|
||||
char *text;
|
||||
xmpp_stanza_t *stanza;
|
||||
} xmpp_stream_error_t;
|
||||
|
||||
typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn,
|
||||
const xmpp_conn_event_t event,
|
||||
const int error,
|
||||
xmpp_stream_error_t * const stream_error,
|
||||
void * const userdata);
|
||||
|
||||
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx);
|
||||
xmpp_conn_t * xmpp_conn_clone(xmpp_conn_t * const conn);
|
||||
void xmpp_conn_release(xmpp_conn_t * const conn);
|
||||
|
||||
const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn);
|
||||
void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid);
|
||||
const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn);
|
||||
void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass);
|
||||
|
||||
int xmpp_connect_client(xmpp_conn_t * const conn,
|
||||
const char * const domain,
|
||||
xmpp_conn_handler callback,
|
||||
void * const userdata);
|
||||
|
||||
/*
|
||||
int xmpp_connect_component(conn, name)
|
||||
*/
|
||||
void xmpp_disconnect(xmpp_conn_t * const conn);
|
||||
|
||||
void xmpp_send(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza);
|
||||
|
||||
|
||||
/* handlers */
|
||||
|
||||
/* if the handle returns false it is removed */
|
||||
typedef int (*xmpp_timed_handler)(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
|
||||
void xmpp_timed_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata);
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler);
|
||||
|
||||
|
||||
/* if the handler returns false it is removed */
|
||||
typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
|
||||
void xmpp_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata);
|
||||
void xmpp_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler);
|
||||
|
||||
void xmpp_id_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata);
|
||||
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id);
|
||||
|
||||
/*
|
||||
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
|
||||
*/
|
||||
|
||||
/** stanzas **/
|
||||
|
||||
/** allocate an initialize a blank stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx);
|
||||
|
||||
/** clone a stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza);
|
||||
|
||||
/** copies a stanza and all children */
|
||||
xmpp_stanza_t * xmpp_stanza_copy(const xmpp_stanza_t * const stanza);
|
||||
|
||||
/** free a stanza object and it's contents */
|
||||
void xmpp_stanza_release(xmpp_stanza_t * const stanza);
|
||||
|
||||
/** marshall a stanza into text for transmission or display **/
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
char ** const buf, size_t * const buflen);
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t * const stanza);
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza,
|
||||
const char * const name);
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t * const stanza);
|
||||
char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
|
||||
const char * const name);
|
||||
char * xmpp_stanza_get_ns(xmpp_stanza_t * const stanza);
|
||||
/* concatenate all child text nodes. this function
|
||||
* returns a string that must be freed by the caller */
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza);
|
||||
char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
|
||||
|
||||
void xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
|
||||
void xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
|
||||
/* set_attribute adds/replaces attributes */
|
||||
void xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
|
||||
const char * const key,
|
||||
const char * const value);
|
||||
void xmpp_stanza_set_name(xmpp_stanza_t *stanza,
|
||||
const char * const name);
|
||||
void xmpp_stanza_set_text(xmpp_stanza_t *stanza,
|
||||
const char * const text);
|
||||
void xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
const char * const text,
|
||||
const size_t size);
|
||||
|
||||
/* common stanza helpers */
|
||||
char *xmpp_stanza_get_type(xmpp_stanza_t * const stanza);
|
||||
char *xmpp_stanza_get_id(xmpp_stanza_t * const stanza);
|
||||
void xmpp_stanza_get_to();
|
||||
void xmpp_stanza_get_from();
|
||||
void xmpp_stanza_set_id(xmpp_stanza_t * const stanza,
|
||||
const char * const id);
|
||||
void xmpp_stanza_set_type(xmpp_stanza_t * const stanza,
|
||||
const char * const type);
|
||||
void xmpp_stanza_set_to();
|
||||
void xmpp_stanza_set_from();
|
||||
|
||||
/** allocate and initialize a stanza in reply to another */
|
||||
xmpp_stanza_t *xmpp_stanza_reply(const xmpp_stanza_t *stanza);
|
||||
|
||||
/* stanza subclasses */
|
||||
void xmpp_message_new();
|
||||
void xmpp_message_get_body();
|
||||
void xmpp_message_set_body();
|
||||
|
||||
void xmpp_iq_new();
|
||||
void xmpp_presence_new();
|
||||
|
||||
|
||||
/** event loop **/
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout);
|
||||
void xmpp_run(xmpp_ctx_t *ctx);
|
||||
void xmpp_stop(xmpp_ctx_t *ctx);
|
||||
|
||||
#endif /* __LIBXMPP_XMPP_H__ */
|
||||
Reference in New Issue
Block a user