Move the xmpp library to its new name.

This commit is contained in:
Ralph Giles
2005-06-14 14:10:31 +00:00
commit f27235b862
38 changed files with 6846 additions and 0 deletions

10
CHANGES.txt Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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__ */