Move the xmpp library to its new name.
This commit is contained in:
634
src/auth.c
Normal file
634
src/auth.c
Normal file
@@ -0,0 +1,634 @@
|
||||
/* auth.c
|
||||
** XMPP client library -- auth functions and handlers
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
|
||||
#define FEATURES_TIMEOUT 2000 /* 2 seconds */
|
||||
#define BIND_TIMEOUT 2000 /* 2 seconds */
|
||||
#define LEGACY_TIMEOUT 2000 /* 2 seconds */
|
||||
|
||||
static void _auth(xmpp_conn_t * const conn);
|
||||
static void _handle_open_sasl(xmpp_conn_t * const conn);
|
||||
static int _handle_missing_legacy(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
static int _handle_legacy(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_features_sasl(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_digestmd5_default(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
|
||||
static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
static int _handle_missing_bind(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
static int _handle_bind(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata);
|
||||
|
||||
/* stream:features handlers */
|
||||
|
||||
static int _handle_missing_features(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_debug(conn->ctx, "xmpp", "didn't get stream features");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_features(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *child, *mech;
|
||||
char *text;
|
||||
|
||||
/* remove the handler that detects missing stream:features */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_features);
|
||||
|
||||
/* check for TLS */
|
||||
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
|
||||
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0))
|
||||
conn->tls_support = 1;
|
||||
|
||||
/* check for SASL */
|
||||
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
|
||||
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_SASL) == 0)) {
|
||||
for (mech = xmpp_stanza_get_children(child); mech;
|
||||
mech = xmpp_stanza_get_next(mech)) {
|
||||
if (strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
|
||||
text = xmpp_stanza_get_text(mech);
|
||||
if (strcasecmp(text, "PLAIN") == 0)
|
||||
conn->sasl_support |= SASL_MASK_PLAIN;
|
||||
else if (strcasecmp(text, "DIGEST-MD5") == 0)
|
||||
conn->sasl_support |= SASL_MASK_DIGESTMD5;
|
||||
|
||||
xmpp_free(conn->ctx, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Implement TLS */
|
||||
|
||||
_auth(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns the correct auth id for a component or a client.
|
||||
* returned string must be freed by caller */
|
||||
static char *_get_authid(xmpp_conn_t * const conn)
|
||||
{
|
||||
char *authid = NULL;
|
||||
char *at;
|
||||
|
||||
if (conn->type == XMPP_CLIENT) {
|
||||
/* authid is the node portion of jid */
|
||||
if (!conn->jid) return NULL;
|
||||
authid = xmpp_strdup(conn->ctx, conn->jid);
|
||||
if (!authid) return NULL;
|
||||
at = strchr(authid, '@');
|
||||
at[0] = 0;
|
||||
}
|
||||
|
||||
return authid;
|
||||
}
|
||||
|
||||
static int _handle_digestmd5_default(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *name;
|
||||
int ret = 1;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp",
|
||||
"handle digest-md5 (default) called for %s", name);
|
||||
|
||||
if (strcmp(name, "failure") == 0) {
|
||||
/* TODO: handle errors */
|
||||
xmpp_debug(conn->ctx, "xmpp", "SASL DIGEST-MD5 auth failed!");
|
||||
/* fall back to next auth method */
|
||||
_auth(conn);
|
||||
|
||||
/* remove handler */
|
||||
ret = 0;
|
||||
} else if (strcmp(name, "success") == 0) {
|
||||
/* SASL DIGEST-MD5 auth successful, we need to restart the stream */
|
||||
xmpp_debug(conn->ctx, "xmpp", "SASL DIGEST-MD5 auth successful");
|
||||
|
||||
/* reset parser */
|
||||
parser_prepare_reset(conn, _handle_open_sasl);
|
||||
|
||||
/* send stream tag */
|
||||
conn_open_stream(conn);
|
||||
|
||||
/* remove handler */
|
||||
ret = 0;
|
||||
} else {
|
||||
/* got unexpected reply */
|
||||
xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL "
|
||||
"DIGEST-MD5 authentication.");
|
||||
xmpp_disconnect(conn);
|
||||
|
||||
/* remove handler */
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* handle the challenge phase of digest auth */
|
||||
static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *text;
|
||||
char *response;
|
||||
xmpp_stanza_t *auth, *authdata;
|
||||
char *name;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp",\
|
||||
"handle digest-md5 (challenge) called for %s", name);
|
||||
|
||||
if (strcmp(name, "challenge") == 0) {
|
||||
text = xmpp_stanza_get_text(stanza);
|
||||
response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
|
||||
if (!response) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_free(conn->ctx, text);
|
||||
|
||||
auth = xmpp_stanza_new(conn->ctx);
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_stanza_set_name(auth, "response");
|
||||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xmpp_stanza_set_text(authdata, response);
|
||||
xmpp_free(conn->ctx, response);
|
||||
|
||||
xmpp_stanza_add_child(auth, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
handler_add(conn, _handle_digestmd5_rspauth,
|
||||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||||
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
|
||||
} else {
|
||||
return _handle_digestmd5_default(conn, stanza, userdata);
|
||||
}
|
||||
|
||||
/* remove ourselves */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle the rspauth phase of digest auth */
|
||||
static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *auth;
|
||||
char *name;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp",
|
||||
"handle digest-md5 (rspauth) called for %s", name);
|
||||
|
||||
|
||||
if (strcmp(name, "challenge") == 0) {
|
||||
/* assume it's an rspauth response */
|
||||
auth = xmpp_stanza_new(conn->ctx);
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_stanza_set_name(auth, "response");
|
||||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
} else {
|
||||
return _handle_digestmd5_default(conn, stanza, userdata);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _handle_plain(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *name;
|
||||
int ret = 1;
|
||||
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
xmpp_debug(conn->ctx, "xmpp", "handle plain called for %s", name);
|
||||
|
||||
/* the server should send a <success> or <failure> stanza */
|
||||
if (strcmp(name, "failure") == 0) {
|
||||
/* TODO: handle errors */
|
||||
|
||||
/* fall back to next auth method */
|
||||
_auth(conn);
|
||||
} else if (strcmp(name, "success") == 0) {
|
||||
/* SASL PLAIN auth successful, we need to restart the stream */
|
||||
xmpp_debug(conn->ctx, "xmpp", "SASL PLAIN auth successful");
|
||||
|
||||
/* reset parser */
|
||||
parser_prepare_reset(conn, _handle_open_sasl);
|
||||
|
||||
/* remove this handler */
|
||||
ret = 0;
|
||||
|
||||
/* send stream tag */
|
||||
conn_open_stream(conn);
|
||||
} else {
|
||||
/* got unexpected reply */
|
||||
xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL PLAIN "\
|
||||
"authentication.");
|
||||
xmpp_disconnect(conn);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
|
||||
const char * const mechanism)
|
||||
{
|
||||
xmpp_stanza_t *auth;
|
||||
|
||||
/* build auth stanza */
|
||||
auth = xmpp_stanza_new(conn->ctx);
|
||||
if (auth) {
|
||||
xmpp_stanza_set_name(auth, "auth");
|
||||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||||
xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
/* authenticate the connection
|
||||
* this may get called multiple times. if any auth method fails,
|
||||
* this will get called again until one auth method succeeds or every
|
||||
* method fails */
|
||||
static void _auth(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_stanza_t *auth, *authdata, *query, *child, *iq;
|
||||
char *str, *authid;
|
||||
|
||||
if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
|
||||
auth = _make_sasl_auth(conn, "DIGEST-MD5");
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
handler_add(conn, _handle_digestmd5_challenge,
|
||||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||||
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
|
||||
/* SASL DIGEST-MD5 was tried, unset flag */
|
||||
conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
|
||||
} else if (conn->sasl_support & SASL_MASK_PLAIN) {
|
||||
auth = _make_sasl_auth(conn, "PLAIN");
|
||||
if (!auth) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
authid = _get_authid(conn);
|
||||
if (!authid) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
str = sasl_plain(conn->ctx, authid, conn->pass);
|
||||
if (!str) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_text(authdata, str);
|
||||
xmpp_free(conn->ctx, str);
|
||||
|
||||
xmpp_stanza_add_child(auth, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
handler_add(conn, _handle_plain,
|
||||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||||
|
||||
xmpp_send(conn, auth);
|
||||
xmpp_stanza_release(auth);
|
||||
|
||||
/* SASL PLAIN was tried */
|
||||
conn->sasl_support &= ~SASL_MASK_PLAIN;
|
||||
} else if (conn->type == XMPP_CLIENT) {
|
||||
/* legacy client authentication */
|
||||
|
||||
iq = xmpp_stanza_new(conn->ctx);
|
||||
if (!iq) {
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(iq, "iq");
|
||||
xmpp_stanza_set_type(iq, "set");
|
||||
xmpp_stanza_set_id(iq, "_xmpp_auth1");
|
||||
|
||||
query = xmpp_stanza_new(conn->ctx);
|
||||
if (!query) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(query, "query");
|
||||
xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
|
||||
xmpp_stanza_add_child(iq, query);
|
||||
xmpp_stanza_release(query);
|
||||
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(child, "username");
|
||||
xmpp_stanza_add_child(query, child);
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
str = xmpp_jid_node(conn->ctx, conn->jid);
|
||||
xmpp_stanza_set_text(authdata, str);
|
||||
xmpp_free(conn->ctx, str);
|
||||
xmpp_stanza_add_child(child, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(child, "password");
|
||||
xmpp_stanza_add_child(query, child);
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_text(authdata, conn->pass);
|
||||
xmpp_stanza_add_child(child, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_name(child, "resource");
|
||||
xmpp_stanza_add_child(query, child);
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
authdata = xmpp_stanza_new(conn->ctx);
|
||||
if (!authdata) {
|
||||
xmpp_stanza_release(iq);
|
||||
disconnect_mem_error(conn);
|
||||
return;
|
||||
}
|
||||
str = xmpp_jid_resource(conn->ctx, conn->jid);
|
||||
if (str)
|
||||
xmpp_stanza_set_text(authdata, str);
|
||||
xmpp_free(conn->ctx, str);
|
||||
xmpp_stanza_add_child(child, authdata);
|
||||
xmpp_stanza_release(authdata);
|
||||
|
||||
handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
|
||||
handler_add_timed(conn, _handle_missing_legacy,
|
||||
LEGACY_TIMEOUT, NULL);
|
||||
|
||||
xmpp_send(conn, iq);
|
||||
xmpp_stanza_release(iq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* this is called on initial stream open */
|
||||
void auth_handle_open(xmpp_conn_t * const conn)
|
||||
{
|
||||
/* reset all timed handlers */
|
||||
handler_reset_timed(conn, 0);
|
||||
|
||||
/* setup handlers for incoming <stream:features> */
|
||||
handler_add(conn, _handle_features,
|
||||
NULL, "stream:features", NULL, NULL);
|
||||
handler_add_timed(conn, _handle_missing_features,
|
||||
FEATURES_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
/* called when stream:stream tag received after SASL auth */
|
||||
static void _handle_open_sasl(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
|
||||
|
||||
/* setup stream:features handlers */
|
||||
handler_add(conn, _handle_features_sasl,
|
||||
NULL, "stream:features", NULL, NULL);
|
||||
handler_add_timed(conn, _handle_missing_features_sasl,
|
||||
FEATURES_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
static int _handle_features_sasl(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_stanza_t *bind, *iq;
|
||||
|
||||
/* remove missing features handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
|
||||
|
||||
/* we are expecting <bind/> */
|
||||
bind = xmpp_stanza_get_child_by_name(stanza, "bind");
|
||||
if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) {
|
||||
/* bind resource */
|
||||
|
||||
/* setup response handlers */
|
||||
handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL);
|
||||
handler_add_timed(conn, _handle_missing_bind,
|
||||
BIND_TIMEOUT, NULL);
|
||||
|
||||
/* send bind response */
|
||||
/* FIXME: this should detect whether a resource is present in
|
||||
conn->jid and bind that instead. right now we let the server bind
|
||||
us */
|
||||
iq = xmpp_stanza_new(conn->ctx);
|
||||
if (!iq) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
xmpp_stanza_set_name(iq, "iq");
|
||||
xmpp_stanza_set_type(iq, "set");
|
||||
xmpp_stanza_set_id(iq, "_xmpp_bind1");
|
||||
|
||||
bind = xmpp_stanza_copy(bind);
|
||||
if (!bind) {
|
||||
disconnect_mem_error(conn);
|
||||
return 0;
|
||||
}
|
||||
xmpp_stanza_add_child(iq, bind);
|
||||
xmpp_stanza_release(bind);
|
||||
|
||||
/* send bind request */
|
||||
xmpp_send(conn, iq);
|
||||
xmpp_stanza_release(iq);
|
||||
} else {
|
||||
/* can't bind, disconnect */
|
||||
xmpp_error(conn->ctx, "xmpp", "Stream features does not allow "\
|
||||
"resource bind.");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Did not receive stream features "\
|
||||
"after SASL authentication.");
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_bind(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *type;
|
||||
|
||||
/* delete missing bind handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_bind);
|
||||
|
||||
/* server has replied to bind request */
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
if (!type) {
|
||||
xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "error") == 0) {
|
||||
xmpp_error(conn->ctx, "xmpp", "Binding failed.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "result") == 0) {
|
||||
/* TODO: extract resource if present */
|
||||
xmpp_debug(conn->ctx, "xmpp", "Bind successful.");
|
||||
conn->authenticated = 1;
|
||||
|
||||
/* call connection handler */
|
||||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_bind(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_legacy(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza,
|
||||
void * const userdata)
|
||||
{
|
||||
char *type, *name;
|
||||
|
||||
/* delete missing handler */
|
||||
xmpp_timed_handler_delete(conn, _handle_missing_legacy);
|
||||
|
||||
/* server responded to legacy auth request */
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
if (!type || strcmp(name, "iq") != 0) {
|
||||
xmpp_error(conn->ctx, "xmpp", "Server sent us an unexpected response "\
|
||||
"to legacy authentication request.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "error") == 0) {
|
||||
/* legacy client auth failed, no more fallbacks */
|
||||
xmpp_error(conn->ctx, "xmpp", "Legacy client authentication failed.");
|
||||
xmpp_disconnect(conn);
|
||||
} else if (strcmp(type, "result") == 0) {
|
||||
/* auth succeeded */
|
||||
xmpp_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
|
||||
|
||||
conn->authenticated = 1;
|
||||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||||
} else {
|
||||
xmpp_error(conn->ctx, "xmpp", "Server sent us a legacy authentication "\
|
||||
"response with a bad type.");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _handle_missing_legacy(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\
|
||||
"authentication request.");
|
||||
xmpp_disconnect(conn);
|
||||
return 0;
|
||||
}
|
||||
272
src/common.h
Normal file
272
src/common.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/* common.h
|
||||
** XMPP client library -- internal common structures
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express or
|
||||
** implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_XMPP_COMMON_H__
|
||||
#define __LIBXMPP_XMPP_COMMON_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#ifndef _WIN32
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "sock.h"
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "expat.h"
|
||||
|
||||
/** run-time context **/
|
||||
|
||||
typedef enum {
|
||||
XMPP_LOOP_NOTSTARTED,
|
||||
XMPP_LOOP_RUNNING,
|
||||
XMPP_LOOP_QUIT
|
||||
} xmpp_loop_status_t;
|
||||
|
||||
typedef struct _xmpp_connlist_t {
|
||||
xmpp_conn_t *conn;
|
||||
struct _xmpp_connlist_t *next;
|
||||
} xmpp_connlist_t;
|
||||
|
||||
struct _xmpp_ctx_t {
|
||||
const xmpp_mem_t *mem;
|
||||
const xmpp_log_t *log;
|
||||
|
||||
xmpp_loop_status_t loop_status;
|
||||
xmpp_connlist_t *connlist;
|
||||
};
|
||||
|
||||
|
||||
/* convenience functions for accessing the context */
|
||||
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size);
|
||||
void xmpp_free(const xmpp_ctx_t * const ctx, void *p);
|
||||
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
||||
const size_t size);
|
||||
char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s);
|
||||
|
||||
void xmpp_log(const xmpp_ctx_t * const ctx,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
va_list ap);
|
||||
|
||||
/* wrappers for xmpp_log at specific levels */
|
||||
void xmpp_error(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
void xmpp_warn(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
void xmpp_info(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
void xmpp_debug(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...);
|
||||
|
||||
/** jid */
|
||||
/* these return new strings that must be xmpp_free()'d */
|
||||
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
||||
const char *domain,
|
||||
const char *resource);
|
||||
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
|
||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
|
||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
|
||||
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
|
||||
|
||||
|
||||
/** connection **/
|
||||
|
||||
/* opaque connection object */
|
||||
typedef enum {
|
||||
XMPP_STATE_DISCONNECTED,
|
||||
XMPP_STATE_CONNECTING,
|
||||
XMPP_STATE_CONNECTED
|
||||
} xmpp_conn_state_t;
|
||||
|
||||
typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
|
||||
struct _xmpp_send_queue_t {
|
||||
char *data;
|
||||
size_t len;
|
||||
size_t written;
|
||||
|
||||
xmpp_send_queue_t *next;
|
||||
};
|
||||
|
||||
typedef struct _xmpp_handlist_t xmpp_handlist_t;
|
||||
struct _xmpp_handlist_t {
|
||||
/* common members */
|
||||
int user_handler;
|
||||
void *handler;
|
||||
void *userdata;
|
||||
int enabled; /* handlers are added disabled and enabled after the
|
||||
* handler chain is processed to prevent stanzas from
|
||||
* getting processed by newly added handlers */
|
||||
xmpp_handlist_t *next;
|
||||
|
||||
union {
|
||||
/* timed handlers */
|
||||
struct {
|
||||
unsigned long period;
|
||||
uint64_t last_stamp;
|
||||
};
|
||||
/* id handlers */
|
||||
struct {
|
||||
char *id;
|
||||
};
|
||||
/* normal handlers */
|
||||
struct {
|
||||
char *ns;
|
||||
char *name;
|
||||
char *type;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#define SASL_MASK_PLAIN 0x01
|
||||
#define SASL_MASK_DIGESTMD5 0x02
|
||||
#define SASL_MASK_ANONYMOUS 0x04
|
||||
|
||||
typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn);
|
||||
|
||||
struct _xmpp_conn_t {
|
||||
unsigned int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_conn_type_t type;
|
||||
|
||||
xmpp_conn_state_t state;
|
||||
int error;
|
||||
xmpp_stream_error_t *stream_error;
|
||||
sock_t sock;
|
||||
|
||||
int tls_support;
|
||||
int sasl_support; /* if true, field is a bitfield of supported
|
||||
mechanisms */
|
||||
|
||||
char *lang;
|
||||
char *domain;
|
||||
char *jid;
|
||||
char *pass;
|
||||
char *stream_id;
|
||||
|
||||
/* send queue and parameters */
|
||||
int blocking_send;
|
||||
int send_queue_max;
|
||||
int send_queue_len;
|
||||
xmpp_send_queue_t *send_queue_head;
|
||||
xmpp_send_queue_t *send_queue_tail;
|
||||
|
||||
/* xml parser */
|
||||
int reset_parser;
|
||||
XML_Parser parser;
|
||||
int depth;
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
/* event handlers */
|
||||
|
||||
/* stream open handler */
|
||||
xmpp_open_handler open_handler;
|
||||
|
||||
/* user handlers only get called after authentication */
|
||||
int authenticated;
|
||||
|
||||
/* connection events handler */
|
||||
xmpp_conn_handler conn_handler;
|
||||
void *userdata;
|
||||
|
||||
/* other handlers */
|
||||
xmpp_handlist_t *timed_handlers;
|
||||
hash_t *id_handlers;
|
||||
xmpp_handlist_t *handlers;
|
||||
};
|
||||
|
||||
void conn_disconnect(xmpp_conn_t * const conn);
|
||||
void conn_disconnect_clean(xmpp_conn_t * const conn);
|
||||
void conn_open_stream(xmpp_conn_t * const conn);
|
||||
|
||||
void xmpp_send_raw_string(xmpp_conn_t * const conn,
|
||||
const char * const fmt, ...);
|
||||
void xmpp_send_raw(xmpp_conn_t * const conn,
|
||||
const char * const data, const size_t len);
|
||||
|
||||
|
||||
typedef enum {
|
||||
XMPP_STANZA_UNKNOWN,
|
||||
XMPP_STANZA_TEXT,
|
||||
XMPP_STANZA_TAG
|
||||
} xmpp_stanza_type_t;
|
||||
|
||||
struct _xmpp_stanza_t {
|
||||
int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
|
||||
xmpp_stanza_type_t type;
|
||||
|
||||
xmpp_stanza_t *prev;
|
||||
xmpp_stanza_t *next;
|
||||
xmpp_stanza_t *children;
|
||||
xmpp_stanza_t *parent;
|
||||
|
||||
char *data;
|
||||
|
||||
hash_t *attributes;
|
||||
};
|
||||
|
||||
void xmpp_stanza_set_attributes(xmpp_stanza_t * const stanza,
|
||||
const char * const * const attr);
|
||||
|
||||
/* parser functions */
|
||||
void parser_handle_start(void *userdata,
|
||||
const XML_Char *name,
|
||||
const XML_Char **attr);
|
||||
void parser_handle_character(void *userdata, const XML_Char *s, int len);
|
||||
void parser_handle_end(void *userdata, const XML_Char *name);
|
||||
void parser_prepare_reset(xmpp_conn_t * const conn,
|
||||
xmpp_open_handler handler);
|
||||
int parser_reset(xmpp_conn_t * const conn);
|
||||
|
||||
/* handler management */
|
||||
void handler_fire_stanza(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza);
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t * const ctx);
|
||||
void handler_reset_timed(xmpp_conn_t *conn, int user_only);
|
||||
void handler_add_timed(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata);
|
||||
void handler_add_id(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata);
|
||||
void handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata);
|
||||
|
||||
/* utility functions */
|
||||
void disconnect_mem_error(xmpp_conn_t * const conn);
|
||||
|
||||
/* auth functions */
|
||||
void auth_handle_open(xmpp_conn_t * const conn);
|
||||
|
||||
#endif /* __LIBXMPP_XMPP_COMMON_H__ */
|
||||
398
src/conn.c
Normal file
398
src/conn.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/* conn.c
|
||||
** connection object functions
|
||||
**
|
||||
** Copyright 2005 OGG, LLC
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
#define DEFAULT_SEND_QUEUE_MAX 64
|
||||
#define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
|
||||
|
||||
static int _disconnect_cleanup(xmpp_conn_t * const conn,
|
||||
void * const userdata);
|
||||
|
||||
|
||||
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
xmpp_conn_t *conn = NULL;
|
||||
xmpp_connlist_t *tail, *item;
|
||||
|
||||
if (ctx == NULL) return NULL;
|
||||
conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
|
||||
|
||||
if (conn != NULL) {
|
||||
conn->ctx = ctx;
|
||||
|
||||
conn->type = XMPP_UNKNOWN;
|
||||
conn->sock = -1;
|
||||
conn->error = 0;
|
||||
conn->stream_error = NULL;
|
||||
|
||||
/* default send parameters */
|
||||
conn->blocking_send = 0;
|
||||
conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
|
||||
conn->send_queue_len = 0;
|
||||
conn->send_queue_head = NULL;
|
||||
conn->send_queue_tail = NULL;
|
||||
|
||||
conn->lang = xmpp_strdup(conn->ctx, "en");
|
||||
if (!conn->lang) {
|
||||
xmpp_free(conn->ctx, conn);
|
||||
return NULL;
|
||||
}
|
||||
conn->domain = NULL;
|
||||
conn->jid = NULL;
|
||||
conn->pass = NULL;
|
||||
conn->stream_id = NULL;
|
||||
|
||||
conn->tls_support = 0;
|
||||
conn->sasl_support = 0;
|
||||
|
||||
conn->parser = NULL;
|
||||
conn->stanza = NULL;
|
||||
parser_prepare_reset(conn, auth_handle_open);
|
||||
|
||||
conn->authenticated = 0;
|
||||
conn->conn_handler = NULL;
|
||||
conn->userdata = NULL;
|
||||
conn->timed_handlers = NULL;
|
||||
/* we own (and will free) the hash values */
|
||||
conn->id_handlers = hash_new(conn->ctx, 32, NULL);
|
||||
conn->handlers = NULL;
|
||||
|
||||
/* give the caller a reference to connection */
|
||||
conn->ref = 1;
|
||||
|
||||
/* add connection to ctx->connlist */
|
||||
tail = conn->ctx->connlist;
|
||||
while (tail && tail->next) tail = tail->next;
|
||||
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_connlist_t));
|
||||
if (!item) {
|
||||
xmpp_error(conn->ctx, "xmpp", "failed to allocate memory");
|
||||
xmpp_free(conn->ctx, conn->lang);
|
||||
XML_ParserFree(conn->parser);
|
||||
xmpp_free(conn->ctx, conn);
|
||||
conn = NULL;
|
||||
} else {
|
||||
item->conn = conn;
|
||||
item->next = NULL;
|
||||
|
||||
if (tail) tail->next = item;
|
||||
else conn->ctx->connlist = item;
|
||||
}
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
xmpp_conn_t * xmpp_conn_clone(xmpp_conn_t * const conn)
|
||||
{
|
||||
conn->ref++;
|
||||
return conn;
|
||||
}
|
||||
|
||||
void xmpp_conn_release(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_ctx_t *ctx;
|
||||
xmpp_connlist_t *item, *prev;
|
||||
xmpp_handlist_t *hlitem, *thli;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
|
||||
if (conn->ref > 1)
|
||||
conn->ref--;
|
||||
else {
|
||||
ctx = conn->ctx;
|
||||
|
||||
/* remove connection from context's connlist */
|
||||
if (ctx->connlist->conn == conn) {
|
||||
item = ctx->connlist;
|
||||
ctx->connlist = item->next;
|
||||
xmpp_free(ctx, item);
|
||||
} else {
|
||||
prev = NULL;
|
||||
item = ctx->connlist;
|
||||
while (item && item->conn != conn) {
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
xmpp_error(ctx, "xmpp", "Connection not in context's list\n");
|
||||
} else {
|
||||
prev->next = item->next;
|
||||
xmpp_free(ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
/* free handler stuff
|
||||
* note that userdata is the responsibility of the client
|
||||
* and the handler pointers don't need to be freed since they
|
||||
* are pointers to functions */
|
||||
|
||||
hlitem = conn->timed_handlers;
|
||||
while (hlitem) {
|
||||
thli = hlitem;
|
||||
hlitem = hlitem->next;
|
||||
|
||||
xmpp_free(ctx, thli);
|
||||
}
|
||||
|
||||
/* id handlers
|
||||
* we have to traverse the hash table freeing list elements
|
||||
* then release the hash table */
|
||||
iter = hash_iter_new(conn->id_handlers);
|
||||
while ((key = hash_iter_next(iter))) {
|
||||
hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
|
||||
while (hlitem) {
|
||||
thli = hlitem;
|
||||
hlitem = hlitem->next;
|
||||
xmpp_free(conn->ctx, thli->id);
|
||||
xmpp_free(conn->ctx, thli);
|
||||
}
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
hash_release(conn->id_handlers);
|
||||
|
||||
hlitem = conn->handlers;
|
||||
while (hlitem) {
|
||||
thli = hlitem;
|
||||
hlitem = hlitem->next;
|
||||
|
||||
if (hlitem->ns) xmpp_free(ctx, hlitem->ns);
|
||||
if (hlitem->name) xmpp_free(ctx, hlitem->name);
|
||||
if (hlitem->type) xmpp_free(ctx, hlitem->type);
|
||||
xmpp_free(ctx, thli);
|
||||
}
|
||||
|
||||
if (conn->stream_error) {
|
||||
xmpp_stanza_release(conn->stream_error->stanza);
|
||||
xmpp_free(ctx, conn->stream_error);
|
||||
}
|
||||
|
||||
XML_ParserFree(conn->parser);
|
||||
|
||||
if (conn->domain) xmpp_free(ctx, conn->domain);
|
||||
if (conn->jid) xmpp_free(ctx, conn->jid);
|
||||
if (conn->pass) xmpp_free(ctx, conn->pass);
|
||||
if (conn->stream_id) xmpp_free(ctx, conn->stream_id);
|
||||
xmpp_free(ctx, conn);
|
||||
}
|
||||
}
|
||||
|
||||
const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn)
|
||||
{
|
||||
return conn->jid;
|
||||
}
|
||||
|
||||
/* set the jid of the user or the component name. in the first case,
|
||||
* this can be a full jid, or a bare jid. in the second case, this will
|
||||
* probably be a domain only.
|
||||
*/
|
||||
void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid)
|
||||
{
|
||||
if (conn->jid) xmpp_free(conn->ctx, conn->jid);
|
||||
conn->jid = xmpp_strdup(conn->ctx, jid);
|
||||
}
|
||||
|
||||
const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn)
|
||||
{
|
||||
return conn->pass;
|
||||
}
|
||||
|
||||
void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass)
|
||||
{
|
||||
if (conn->pass) xmpp_free(conn->ctx, conn->pass);
|
||||
conn->pass = xmpp_strdup(conn->ctx, pass);
|
||||
}
|
||||
|
||||
int xmpp_connect_client(xmpp_conn_t * const conn,
|
||||
const char * const domain,
|
||||
xmpp_conn_handler callback,
|
||||
void * const userdata)
|
||||
{
|
||||
conn->type = XMPP_CLIENT;
|
||||
|
||||
conn->domain = xmpp_strdup(conn->ctx, domain);
|
||||
if (!conn->domain) return -1;
|
||||
|
||||
conn->sock = sock_connect(domain, 5222);
|
||||
if (conn->sock < 0) return -1;
|
||||
|
||||
/* setup handler */
|
||||
conn->conn_handler = callback;
|
||||
conn->userdata = userdata;
|
||||
|
||||
/* FIXME: it could happen that the connect returns immediately as
|
||||
* successful, though this is pretty unlikely. This would be a little
|
||||
* hard to fix, since we'd have to detect and fire off the callback
|
||||
* from within the event loop */
|
||||
|
||||
conn->state = XMPP_STATE_CONNECTING;
|
||||
xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function is only called by the end tag handler. it is
|
||||
* the only place where a conn_disconnect would be called during a clean
|
||||
* disconnect sequence */
|
||||
void conn_disconnect_clean(xmpp_conn_t * const conn)
|
||||
{
|
||||
/* remove the timed handler */
|
||||
xmpp_timed_handler_delete(conn, _disconnect_cleanup);
|
||||
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
|
||||
void conn_disconnect(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_info(conn->ctx, "xmpp", "Disconnected from server");
|
||||
conn->state = XMPP_STATE_DISCONNECTED;
|
||||
sock_close(conn->sock);
|
||||
|
||||
/* fire off connection handler */
|
||||
conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
|
||||
conn->stream_error, conn->userdata);
|
||||
}
|
||||
|
||||
/* timed handler for cleanup if normal disconnect procedure takes too long */
|
||||
static int _disconnect_cleanup(xmpp_conn_t * const conn,
|
||||
void * const userdata)
|
||||
{
|
||||
xmpp_debug(conn->ctx, "xmpp",
|
||||
"disconnection forced by cleanup timeout");
|
||||
|
||||
conn_disconnect(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* terminates the XMPP stream, closing the underlying socket,
|
||||
* and calls the conn_handler. this function returns immediately
|
||||
* without calling the handler if the connection is not active */
|
||||
void xmpp_disconnect(xmpp_conn_t * const conn)
|
||||
{
|
||||
if (conn->state != XMPP_STATE_CONNECTING &&
|
||||
conn->state != XMPP_STATE_CONNECTED)
|
||||
return;
|
||||
|
||||
/* close the stream */
|
||||
xmpp_send_raw_string(conn, "</stream:stream>");
|
||||
|
||||
/* setup timed handler in case disconnect takes too long */
|
||||
handler_add_timed(conn, _disconnect_cleanup,
|
||||
DISCONNECT_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
/* convinience function for sending data to a connection */
|
||||
void xmpp_send_raw_string(xmpp_conn_t * const conn,
|
||||
const char * const fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
size_t len;
|
||||
char buf[1024]; /* small buffer for common case */
|
||||
char *bigbuf;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, 1024, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len >= 1024) {
|
||||
/* we need more space for this data, so we allocate a big
|
||||
* enough buffer and print to that */
|
||||
len++; /* account for trailing \0 */
|
||||
bigbuf = xmpp_alloc(conn->ctx, len);
|
||||
if (!bigbuf) {
|
||||
xmpp_debug(conn->ctx, "xmpp", "Could not allocate memory for send_raw_string");
|
||||
return;
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(bigbuf, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
xmpp_debug(conn->ctx, "conn", "SENT: %s", bigbuf);
|
||||
|
||||
/* len - 1 so we don't send trailing \0 */
|
||||
xmpp_send_raw(conn, bigbuf, len - 1);
|
||||
|
||||
xmpp_free(conn->ctx, bigbuf);
|
||||
} else {
|
||||
xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
|
||||
|
||||
xmpp_send_raw(conn, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* adds data to the send queue for a connection */
|
||||
void xmpp_send_raw(xmpp_conn_t * const conn,
|
||||
const char * const data, const size_t len)
|
||||
{
|
||||
xmpp_send_queue_t *item;
|
||||
|
||||
if (conn->state != XMPP_STATE_CONNECTED) return;
|
||||
|
||||
/* create send queue item for queue */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
|
||||
if (!item) return;
|
||||
|
||||
item->data = xmpp_alloc(conn->ctx, len);
|
||||
if (!item->data) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
memcpy(item->data, data, len);
|
||||
item->len = len;
|
||||
item->next = NULL;
|
||||
item->written = 0;
|
||||
|
||||
/* add item to the send queue */
|
||||
if (!conn->send_queue_tail) {
|
||||
/* first item, set head and tail */
|
||||
conn->send_queue_head = item;
|
||||
conn->send_queue_tail = item;
|
||||
} else {
|
||||
/* add to the tail */
|
||||
conn->send_queue_tail->next = item;
|
||||
conn->send_queue_tail = item;
|
||||
}
|
||||
conn->send_queue_len++;
|
||||
}
|
||||
|
||||
void xmpp_send(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza)
|
||||
{
|
||||
char *buf;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
if (conn->state == XMPP_STATE_CONNECTED) {
|
||||
if ((ret = xmpp_stanza_to_text(stanza, &buf, &len)) == 0) {
|
||||
xmpp_send_raw(conn, buf, len);
|
||||
xmpp_debug(conn->ctx, "xmpp", "SENT: %s", buf);
|
||||
xmpp_free(conn->ctx, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void conn_open_stream(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_send_raw_string(conn,
|
||||
"<?xml version=\"1.0\"?>" \
|
||||
"<stream:stream to=\"%s\" " \
|
||||
"xml:lang=\"%s\" " \
|
||||
"version=\"1.0\" " \
|
||||
"xmlns=\"%s\" " \
|
||||
"xmlns:stream=\"%s\">",
|
||||
conn->domain,
|
||||
conn->lang,
|
||||
conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT,
|
||||
XMPP_NS_STREAMS);
|
||||
}
|
||||
187
src/ctx.c
Normal file
187
src/ctx.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/* ctx.c
|
||||
** XMPP client library -- run-time context implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
|
||||
/** version **/
|
||||
|
||||
/* TODO: update from the build system? */
|
||||
#ifndef LIBXMPP_VERSION_MAJOR
|
||||
#define LIBXMPP_VERSION_MAJOR (0)
|
||||
#endif
|
||||
#ifndef LIBXMPP_VERSION_MINOR
|
||||
#define LIBXMPP_VERSION_MINOR (0)
|
||||
#endif
|
||||
|
||||
int xmpp_version_check(int major, int minor)
|
||||
{
|
||||
return (major == LIBXMPP_VERSION_MAJOR) &&
|
||||
(minor >= LIBXMPP_VERSION_MINOR);
|
||||
}
|
||||
|
||||
/** run-time contexts **/
|
||||
|
||||
/* define the global default allocator, logger and context here */
|
||||
|
||||
static xmpp_mem_t xmpp_default_mem = {
|
||||
malloc, /* use the stdlib routines by default */
|
||||
free,
|
||||
realloc
|
||||
};
|
||||
|
||||
static const char * const xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
||||
|
||||
void xmpp_default_logger(void * const userdata,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const msg)
|
||||
{
|
||||
fprintf(stderr, "%s %s %s\n", area, xmpp_log_level_name[level], msg);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static xmpp_log_t xmpp_default_log = { NULL, NULL };
|
||||
#else
|
||||
static xmpp_log_t xmpp_default_log = { xmpp_default_logger, NULL };
|
||||
#endif
|
||||
|
||||
|
||||
/** convenience functions for accessing the context **/
|
||||
|
||||
/* allocator */
|
||||
|
||||
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size)
|
||||
{
|
||||
return ctx->mem->alloc(size);
|
||||
}
|
||||
|
||||
void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
|
||||
{
|
||||
return ctx->mem->free(p);
|
||||
}
|
||||
|
||||
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
||||
const size_t size)
|
||||
{
|
||||
return ctx->mem->realloc(p, size);
|
||||
}
|
||||
|
||||
/* logger */
|
||||
|
||||
void xmpp_log(const xmpp_ctx_t * const ctx,
|
||||
const xmpp_log_level_t level,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
va_list ap)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
/* FIXME: we don't send log lines > 1024 chars */
|
||||
|
||||
vsnprintf(buf, 1023, fmt, ap);
|
||||
|
||||
ctx->log->handler(ctx->log->userdata, level, area, buf);
|
||||
}
|
||||
|
||||
void xmpp_error(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void xmpp_warn(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void xmpp_info(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void xmpp_debug(const xmpp_ctx_t * const ctx,
|
||||
const char * const area,
|
||||
const char * const fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/** allocate and initialize a new ctx object */
|
||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
||||
const xmpp_log_t * const log)
|
||||
{
|
||||
xmpp_ctx_t *ctx = NULL;
|
||||
|
||||
if (mem == NULL)
|
||||
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t));
|
||||
else
|
||||
ctx = mem->alloc(sizeof(xmpp_ctx_t));
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (mem != NULL)
|
||||
ctx->mem = mem;
|
||||
else
|
||||
ctx->mem = &xmpp_default_mem;
|
||||
|
||||
if (log == NULL)
|
||||
ctx->log = &xmpp_default_log;
|
||||
else
|
||||
ctx->log = log;
|
||||
|
||||
ctx->connlist = NULL;
|
||||
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** free a ctx object no longer in use */
|
||||
void xmpp_ctx_free(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
/* mem and log are owned by their suppliers */
|
||||
xmpp_free(ctx, ctx); /* pull the hole in after us */
|
||||
}
|
||||
|
||||
220
src/event.c
Normal file
220
src/event.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* event.c
|
||||
** XMPP client library -- event loop and management
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
#define DEFAULT_TIMEOUT 1
|
||||
|
||||
/* send data and check all connections for their events
|
||||
* and call event handlers. timeout is in milliseconds */
|
||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
||||
{
|
||||
xmpp_connlist_t *connitem;
|
||||
xmpp_conn_t *conn;
|
||||
fd_set rfds, wfds;
|
||||
sock_t max = 0;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
xmpp_send_queue_t *sq, *tsq;
|
||||
size_t towrite;
|
||||
char buf[4096];
|
||||
uint64_t next;
|
||||
|
||||
if (ctx->loop_status == XMPP_LOOP_QUIT) return;
|
||||
ctx->loop_status = XMPP_LOOP_RUNNING;
|
||||
|
||||
/* send queued data */
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
if (conn->state != XMPP_STATE_CONNECTED) {
|
||||
connitem = connitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
sq = conn->send_queue_head;
|
||||
while (sq) {
|
||||
towrite = sq->len - sq->written;
|
||||
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
|
||||
|
||||
if (ret < 0 && sock_is_recoverable(sock_error())) {
|
||||
/* an error occured */
|
||||
conn->error = sock_error();
|
||||
break;
|
||||
} else if (ret < towrite) {
|
||||
/* not all data could be sent now */
|
||||
sq->written += ret;
|
||||
break;
|
||||
}
|
||||
|
||||
/* all data for this queue item written, delete and move on */
|
||||
xmpp_free(ctx, sq->data);
|
||||
tsq = sq;
|
||||
sq = sq->next;
|
||||
xmpp_free(ctx, tsq);
|
||||
|
||||
/* pop the top item */
|
||||
conn->send_queue_head = sq;
|
||||
/* if we've sent everything update the tail */
|
||||
if (!sq) conn->send_queue_tail = NULL;
|
||||
}
|
||||
|
||||
/* tear down connection on error */
|
||||
if (conn->error) {
|
||||
/* FIXME: need to tear down send queues and random other things
|
||||
* maybe this should be abstracted */
|
||||
xmpp_debug(ctx, "xmpp", "send error occured, tear down socket");
|
||||
sock_close(conn->sock);
|
||||
conn->state = XMPP_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
/* reset parsers if needed */
|
||||
for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
|
||||
if (connitem->conn->reset_parser)
|
||||
parser_reset(connitem->conn);
|
||||
}
|
||||
|
||||
|
||||
/* fire any ready timed handlers, then
|
||||
make sure we don't wait past the time when timed handlers need
|
||||
to be called */
|
||||
next = handler_fire_timed(ctx);
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = ((next < timeout) ? next : timeout) * 1000;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
|
||||
/* find events to watch */
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
/* connect has been called and we're waiting for it to complete */
|
||||
/* connection will give us write or error events */
|
||||
FD_SET(conn->sock, &wfds);
|
||||
break;
|
||||
case XMPP_STATE_CONNECTED:
|
||||
FD_SET(conn->sock, &rfds);
|
||||
break;
|
||||
case XMPP_STATE_DISCONNECTED:
|
||||
/* do nothing */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->sock > max) max = conn->sock;
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
/* check for events */
|
||||
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
|
||||
|
||||
/* select errored */
|
||||
if (ret < 0) {
|
||||
xmpp_error(ctx, "xmpp", "event watcher internal error");
|
||||
return;
|
||||
}
|
||||
|
||||
/* no events happened */
|
||||
if (ret == 0) return;
|
||||
|
||||
/* process events */
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
conn = connitem->conn;
|
||||
|
||||
switch (conn->state) {
|
||||
case XMPP_STATE_CONNECTING:
|
||||
if (FD_ISSET(conn->sock, &wfds)) {
|
||||
/* connection complete */
|
||||
|
||||
/* check for error */
|
||||
if (sock_connect_error(conn->sock) != 0) {
|
||||
/* connection failed */
|
||||
xmpp_debug(ctx, "xmpp", "connection failed");
|
||||
conn->state = XMPP_STATE_DISCONNECTED;
|
||||
sock_close(conn->sock);
|
||||
break;
|
||||
}
|
||||
|
||||
conn->state = XMPP_STATE_CONNECTED;
|
||||
xmpp_debug(ctx, "xmpp", "connection successful");
|
||||
|
||||
|
||||
/* send stream init */
|
||||
conn_open_stream(conn);
|
||||
}
|
||||
|
||||
break;
|
||||
case XMPP_STATE_CONNECTED:
|
||||
if (FD_ISSET(conn->sock, &rfds)) {
|
||||
ret = sock_read(conn->sock, buf, 4096);
|
||||
if (ret > 0) {
|
||||
ret = XML_Parse(conn->parser, buf, ret, 0);
|
||||
if (!ret) {
|
||||
/* parse error, we need to shut down */
|
||||
/* FIXME */
|
||||
}
|
||||
}
|
||||
/* return of 0 means socket closed by server */
|
||||
}
|
||||
|
||||
break;
|
||||
case XMPP_STATE_DISCONNECTED:
|
||||
/* do nothing */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
/* fire any ready handlers */
|
||||
handler_fire_timed(ctx);
|
||||
}
|
||||
|
||||
void xmpp_run(xmpp_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
|
||||
|
||||
ctx->loop_status = XMPP_LOOP_RUNNING;
|
||||
while (ctx->loop_status == XMPP_LOOP_RUNNING) {
|
||||
xmpp_run_once(ctx, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
xmpp_debug(ctx, "event", "Event loop completed.");
|
||||
}
|
||||
|
||||
void xmpp_stop(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_debug(ctx, "event", "Stopping event loop.");
|
||||
|
||||
if (ctx->loop_status == XMPP_LOOP_RUNNING)
|
||||
ctx->loop_status = XMPP_LOOP_QUIT;
|
||||
}
|
||||
446
src/handler.c
Normal file
446
src/handler.c
Normal file
@@ -0,0 +1,446 @@
|
||||
/* handler.c
|
||||
** XMPP client library -- event handler management
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
void handler_fire_stanza(xmpp_conn_t * const conn,
|
||||
xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev;
|
||||
char *id, *ns, *name, *type;
|
||||
|
||||
/* call id handlers */
|
||||
id = xmpp_stanza_get_id(stanza);
|
||||
if (id) {
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
for (; item; item = item->next) {
|
||||
if (item->user_handler && !conn->authenticated)
|
||||
continue;
|
||||
|
||||
((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* call handlers */
|
||||
ns = xmpp_stanza_get_ns(stanza);
|
||||
name = xmpp_stanza_get_name(stanza);
|
||||
type = xmpp_stanza_get_type(stanza);
|
||||
|
||||
/* enable all added handlers */
|
||||
for (item = conn->handlers; item; item = item->next)
|
||||
item->enabled = 1;
|
||||
|
||||
prev = NULL;
|
||||
item = conn->handlers;
|
||||
while (item) {
|
||||
/* skip newly added handlers */
|
||||
if (!item->enabled) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* don't call user handlers until authentication succeeds */
|
||||
if (item->user_handler && !conn->authenticated) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!item->ns || (ns && strcmp(ns, item->ns) == 0)) &&
|
||||
(!item->name || (name && strcmp(name, item->name) == 0)) &&
|
||||
(!item->type || (type && strcmp(type, item->type) == 0)))
|
||||
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
|
||||
/* handler is one-shot, so delete it */
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else
|
||||
conn->handlers = item->next;
|
||||
xmpp_free(conn->ctx, item);
|
||||
item = NULL;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
prev = item;
|
||||
item = item->next;
|
||||
} else if (prev)
|
||||
item = prev->next;
|
||||
else
|
||||
item = conn->handlers;
|
||||
}
|
||||
}
|
||||
|
||||
/* helper function to fire timed handlers. returns the
|
||||
* time until the next handler would be fired */
|
||||
uint64_t handler_fire_timed(xmpp_ctx_t * const ctx)
|
||||
{
|
||||
xmpp_connlist_t *connitem;
|
||||
xmpp_handlist_t *handitem, *temp;
|
||||
int ret, fired;
|
||||
uint64_t elapsed, min;
|
||||
|
||||
min = (uint64_t)(-1);
|
||||
|
||||
connitem = ctx->connlist;
|
||||
while (connitem) {
|
||||
if (connitem->conn->state != XMPP_STATE_CONNECTED) {
|
||||
connitem = connitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* enable all handlers that were added */
|
||||
for (handitem = connitem->conn->timed_handlers; handitem;
|
||||
handitem = handitem->next)
|
||||
handitem->enabled = 1;
|
||||
|
||||
handitem = connitem->conn->timed_handlers;
|
||||
while (handitem) {
|
||||
/* skip newly added handlers */
|
||||
if (!handitem->enabled) {
|
||||
handitem = handitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only fire user handlers after authentication */
|
||||
if (handitem->user_handler && !connitem->conn->authenticated) {
|
||||
handitem = handitem->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
fired = 0;
|
||||
elapsed = time_elapsed(handitem->last_stamp, time_stamp());
|
||||
if (elapsed >= handitem->period) {
|
||||
/* fire! */
|
||||
fired = 1;
|
||||
handitem->last_stamp = time_stamp();
|
||||
ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
|
||||
} else if (min > (handitem->period - elapsed))
|
||||
min = handitem->period - elapsed;
|
||||
|
||||
temp = handitem;
|
||||
handitem = handitem->next;
|
||||
|
||||
/* delete handler if it returned false */
|
||||
if (fired && !ret)
|
||||
xmpp_timed_handler_delete(connitem->conn, temp->handler);
|
||||
}
|
||||
|
||||
connitem = connitem->next;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/* reset timed handlers for a connection. this is called
|
||||
* whenever connection is successful */
|
||||
void handler_reset_timed(xmpp_conn_t *conn, int user_only)
|
||||
{
|
||||
xmpp_handlist_t *handitem;
|
||||
|
||||
handitem = conn->timed_handlers;
|
||||
while (handitem) {
|
||||
if ((user_only && handitem->user_handler) || !user_only)
|
||||
handitem->last_stamp = time_stamp();
|
||||
|
||||
handitem = handitem->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void _timed_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata,
|
||||
const int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
|
||||
/* check if handler is already in the list */
|
||||
for (item = conn->timed_handlers; item; item = item->next) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
}
|
||||
if (item) return;
|
||||
|
||||
/* build new item */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item) return;
|
||||
|
||||
item->user_handler = user_handler;
|
||||
item->handler = (void *)handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
item->period = period;
|
||||
item->last_stamp = time_stamp();
|
||||
|
||||
/* append item to list */
|
||||
if (!conn->timed_handlers)
|
||||
conn->timed_handlers = item;
|
||||
else {
|
||||
tail = conn->timed_handlers;
|
||||
while (tail->next)
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev;
|
||||
|
||||
if (!conn->timed_handlers) return;
|
||||
|
||||
prev = NULL;
|
||||
item = conn->timed_handlers;
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else
|
||||
conn->timed_handlers = item->next;
|
||||
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
static void _id_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata, int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
|
||||
/* check if handler is already in the list */
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
item = item->next;
|
||||
}
|
||||
if (item) return;
|
||||
|
||||
/* build new item */
|
||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item) return;
|
||||
|
||||
item->user_handler = user_handler;
|
||||
item->handler = (void *)handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
item->id = xmpp_strdup(conn->ctx, id);
|
||||
if (!item->id) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
|
||||
/* put on list in hash table */
|
||||
tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
if (!tail)
|
||||
hash_add(conn->id_handlers, id, item);
|
||||
else {
|
||||
while (tail->next)
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id)
|
||||
{
|
||||
xmpp_handlist_t *item, *prev;
|
||||
|
||||
prev = NULL;
|
||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||
if (!item) return;
|
||||
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else {
|
||||
hash_drop(conn->id_handlers, id);
|
||||
hash_add(conn->id_handlers, id, item->next);
|
||||
}
|
||||
xmpp_free(conn->ctx, item->id);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
static void _handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata, int user_handler)
|
||||
{
|
||||
xmpp_handlist_t *item, *tail;
|
||||
|
||||
/* check if handler already in list */
|
||||
for (item = conn->handlers; item; item = item->next) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
}
|
||||
if (item) return;
|
||||
|
||||
/* build new item */
|
||||
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||
if (!item) return;
|
||||
|
||||
item->user_handler = user_handler;
|
||||
item->handler = (void *)handler;
|
||||
item->userdata = userdata;
|
||||
item->enabled = 0;
|
||||
item->next = NULL;
|
||||
|
||||
if (ns) {
|
||||
item->ns = xmpp_strdup(conn->ctx, ns);
|
||||
if (!item->ns) {
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
item->ns = NULL;
|
||||
if (name) {
|
||||
item->name = xmpp_strdup(conn->ctx, name);
|
||||
if (!item->name) {
|
||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
||||
xmpp_free(conn->ctx, item);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
item->name = NULL;
|
||||
if (type) {
|
||||
item->type = xmpp_strdup(conn->ctx, type);
|
||||
if (!item->type) {
|
||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
} else
|
||||
item->type = NULL;
|
||||
|
||||
/* append to list */
|
||||
if (!conn->handlers)
|
||||
conn->handlers = item;
|
||||
else {
|
||||
tail = conn->handlers;
|
||||
while (tail->next)
|
||||
tail = tail->next;
|
||||
tail->next = item;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_handler_delete(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler)
|
||||
{
|
||||
xmpp_handlist_t *prev, *item;
|
||||
|
||||
if (!conn->handlers) return;
|
||||
|
||||
prev = NULL;
|
||||
item = conn->handlers;
|
||||
while (item) {
|
||||
if (item->handler == (void *)handler)
|
||||
break;
|
||||
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
if (prev)
|
||||
prev->next = item->next;
|
||||
else
|
||||
conn->handlers = item->next;
|
||||
|
||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
||||
if (item->type) xmpp_free(conn->ctx, item->type);
|
||||
xmpp_free(conn->ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_timed_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata)
|
||||
{
|
||||
_timed_handler_add(conn, handler, period, userdata, 1);
|
||||
}
|
||||
|
||||
void handler_add_timed(xmpp_conn_t * const conn,
|
||||
xmpp_timed_handler handler,
|
||||
const unsigned long period,
|
||||
void * const userdata)
|
||||
{
|
||||
_timed_handler_add(conn, handler, period, userdata, 0);
|
||||
}
|
||||
|
||||
void xmpp_id_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata)
|
||||
{
|
||||
_id_handler_add(conn, handler, id, userdata, 1);
|
||||
}
|
||||
|
||||
void handler_add_id(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const id,
|
||||
void * const userdata)
|
||||
{
|
||||
_id_handler_add(conn, handler, id, userdata, 0);
|
||||
}
|
||||
|
||||
void xmpp_handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata)
|
||||
{
|
||||
_handler_add(conn, handler, ns, name, type, userdata, 1);
|
||||
}
|
||||
|
||||
void handler_add(xmpp_conn_t * const conn,
|
||||
xmpp_handler handler,
|
||||
const char * const ns,
|
||||
const char * const name,
|
||||
const char * const type,
|
||||
void * const userdata)
|
||||
{
|
||||
_handler_add(conn, handler, ns, name, type, userdata, 0);
|
||||
}
|
||||
272
src/hash.c
Normal file
272
src/hash.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/* hash.c
|
||||
** XMPP client library
|
||||
** hash table implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* private types */
|
||||
typedef struct _hashentry_t hashentry_t;
|
||||
|
||||
struct _hashentry_t {
|
||||
hashentry_t *next;
|
||||
char *key;
|
||||
void *value;
|
||||
};
|
||||
|
||||
struct _hash_t {
|
||||
unsigned int ref;
|
||||
xmpp_ctx_t *ctx;
|
||||
hash_free_func free;
|
||||
int length;
|
||||
int num_keys;
|
||||
hashentry_t **entries;
|
||||
};
|
||||
|
||||
struct _hash_iterator_t {
|
||||
unsigned int ref;
|
||||
hash_t *table;
|
||||
hashentry_t *entry;
|
||||
int index;
|
||||
};
|
||||
|
||||
/** allocate and initialize a new hash table */
|
||||
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
|
||||
hash_free_func free)
|
||||
{
|
||||
hash_t *result = NULL;
|
||||
|
||||
result = xmpp_alloc(ctx, sizeof(hash_t));
|
||||
if (result != NULL) {
|
||||
result->entries = xmpp_alloc(ctx, size * sizeof(hashentry_t *));
|
||||
if (result->entries == NULL) {
|
||||
xmpp_free(ctx, result);
|
||||
return NULL;
|
||||
}
|
||||
memset(result->entries, 0, size * sizeof(hashentry_t *));
|
||||
result->length = size;
|
||||
|
||||
result->ctx = ctx;
|
||||
result->free = free;
|
||||
result->num_keys = 0;
|
||||
/* give the caller a reference */
|
||||
result->ref = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** obtain a new reference to an existing hash table */
|
||||
hash_t *hash_clone(hash_t * const table)
|
||||
{
|
||||
table->ref++;
|
||||
return table;
|
||||
}
|
||||
|
||||
/** release a hash table that is no longer needed */
|
||||
void hash_release(hash_t * const table)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry, *next;
|
||||
int i;
|
||||
|
||||
if (table->ref > 1)
|
||||
table->ref--;
|
||||
else {
|
||||
for (i = 0; i < table->length; i++) {
|
||||
entry = table->entries[i];
|
||||
while (entry != NULL) {
|
||||
next = entry->next;
|
||||
xmpp_free(ctx, entry->key);
|
||||
if (table->free) table->free(ctx, entry->value);
|
||||
xmpp_free(ctx, entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
xmpp_free(ctx, table->entries);
|
||||
xmpp_free(ctx, table);
|
||||
}
|
||||
}
|
||||
|
||||
/** hash a key for our table lookup */
|
||||
static int _hash_key(hash_t *table, const char *key)
|
||||
{
|
||||
int hash = 0;
|
||||
int shift = 0;
|
||||
const char *c = key;
|
||||
|
||||
while (*c != '\0') {
|
||||
/* assume 32 bit ints */
|
||||
hash ^= ((int)*c++ << shift);
|
||||
shift += 8;
|
||||
if (shift > 24) shift = 0;
|
||||
}
|
||||
|
||||
return hash % table->length;
|
||||
}
|
||||
|
||||
/** add a key, value pair to a hash table.
|
||||
* each key can appear only once; the value of any
|
||||
* identical key will be replaced
|
||||
*/
|
||||
int hash_add(hash_t *table, const char * const key, void *data)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry = NULL;
|
||||
int index = _hash_key(table, key);
|
||||
|
||||
/* allocate and fill a new entry */
|
||||
entry = xmpp_alloc(ctx, sizeof(hashentry_t));
|
||||
if (!entry) return -1;
|
||||
entry->key = xmpp_strdup(ctx, key);
|
||||
if (!entry->key) {
|
||||
xmpp_free(ctx, entry);
|
||||
return -1;
|
||||
}
|
||||
entry->value = data;
|
||||
/* insert ourselves in the linked list */
|
||||
/* TODO: this leaks duplicate keys */
|
||||
entry->next = table->entries[index];
|
||||
table->entries[index] = entry;
|
||||
table->num_keys++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** look up a key in a hash table */
|
||||
void *hash_get(hash_t *table, const char *key)
|
||||
{
|
||||
hashentry_t *entry;
|
||||
int index = _hash_key(table, key);
|
||||
void *result = NULL;
|
||||
|
||||
/* look up the hash entry */
|
||||
entry = table->entries[index];
|
||||
while (entry != NULL) {
|
||||
/* traverse the linked list looking for the key */
|
||||
if (!strcmp(key, entry->key)) {
|
||||
/* match */
|
||||
result = entry->value;
|
||||
return result;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
/* no match */
|
||||
return result;
|
||||
}
|
||||
|
||||
/** delete a key from a hash table */
|
||||
int hash_drop(hash_t *table, const char *key)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hashentry_t *entry, *prev;
|
||||
int index = _hash_key(table, key);
|
||||
|
||||
/* look up the hash entry */
|
||||
entry = table->entries[index];
|
||||
prev = NULL;
|
||||
while (entry != NULL) {
|
||||
/* traverse the linked list looking for the key */
|
||||
if (!strcmp(key, entry->key)) {
|
||||
/* match, remove the entry */
|
||||
xmpp_free(ctx, entry->key);
|
||||
if (table->free) table->free(ctx, entry->value);
|
||||
if (prev == NULL) {
|
||||
table->entries[index] = entry->next;
|
||||
} else {
|
||||
prev->next = entry->next;
|
||||
}
|
||||
xmpp_free(ctx, entry);
|
||||
table->num_keys--;
|
||||
return 0;
|
||||
}
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
/* no match */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hash_num_keys(hash_t *table)
|
||||
{
|
||||
return table->num_keys;
|
||||
}
|
||||
|
||||
/** allocate and initialize a new iterator */
|
||||
hash_iterator_t *hash_iter_new(hash_t *table)
|
||||
{
|
||||
xmpp_ctx_t *ctx = table->ctx;
|
||||
hash_iterator_t *iter;
|
||||
|
||||
iter = xmpp_alloc(ctx, sizeof(*iter));
|
||||
if (iter != NULL) {
|
||||
iter->ref = 1;
|
||||
iter->table = hash_clone(table);
|
||||
iter->entry = NULL;
|
||||
iter->index = 0;
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
/** release an iterator that is no longer needed */
|
||||
void hash_iter_release(hash_iterator_t *iter)
|
||||
{
|
||||
xmpp_ctx_t *ctx = iter->table->ctx;
|
||||
|
||||
iter->ref--;
|
||||
|
||||
if (iter->ref <= 0) {
|
||||
hash_release(iter->table);
|
||||
xmpp_free(ctx, iter);
|
||||
}
|
||||
}
|
||||
|
||||
/** return the next hash table key from the iterator.
|
||||
the returned key should not be freed */
|
||||
const char * hash_iter_next(hash_iterator_t *iter)
|
||||
{
|
||||
hash_t *table = iter->table;
|
||||
hashentry_t *entry = iter->entry;
|
||||
int i = iter->index + 1;
|
||||
|
||||
/* advance until we find the next entry */
|
||||
if (entry != NULL) entry = entry->next;
|
||||
if (entry == NULL) {
|
||||
/* we're off the end of list, search for a new entry */
|
||||
while (i < iter->table->length) {
|
||||
entry = table->entries[i];
|
||||
if (entry != NULL) {
|
||||
iter->index = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((entry == NULL) || (i >= table->length)) {
|
||||
/* no more keys! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* remember our current match */
|
||||
iter->entry = entry;
|
||||
return entry->key;
|
||||
}
|
||||
|
||||
60
src/hash.h
Normal file
60
src/hash.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* hash.h
|
||||
** XMPP client library -- hash table interface
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_HASH_H__
|
||||
#define __LIBXMPP_HASH_H__
|
||||
|
||||
typedef struct _hash_t hash_t;
|
||||
|
||||
typedef void (*hash_free_func)(const xmpp_ctx_t * const ctx, void *p);
|
||||
|
||||
/** allocate and initialize a new hash table */
|
||||
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
|
||||
hash_free_func free);
|
||||
|
||||
/** allocate a new reference to an existing hash table */
|
||||
hash_t *hash_clone(hash_t * const table);
|
||||
|
||||
/** release a hash table when no longer needed */
|
||||
void hash_release(hash_t * const table);
|
||||
|
||||
/** add a key, value pair to a hash table.
|
||||
* each key can appear only once; the value of any
|
||||
* identical key will be replaced
|
||||
*/
|
||||
int hash_add(hash_t *table, const char * const key, void *data);
|
||||
|
||||
/** look up a key in a hash table */
|
||||
void *hash_get(hash_t *table, const char *key);
|
||||
|
||||
/** delete a key from a hash table */
|
||||
int hash_drop(hash_t *table, const char *key);
|
||||
|
||||
/** return the number of keys in a hash */
|
||||
int hash_num_keys(hash_t *table);
|
||||
|
||||
/** hash key iterator functions */
|
||||
typedef struct _hash_iterator_t hash_iterator_t;
|
||||
|
||||
/** allocate and initialize a new iterator */
|
||||
hash_iterator_t *hash_iter_new(hash_t *table);
|
||||
|
||||
/** release an iterator that is no longer needed */
|
||||
void hash_iter_release(hash_iterator_t *iter);
|
||||
|
||||
/** return the next hash table key from the iterator.
|
||||
the returned key should not be freed */
|
||||
const char * hash_iter_next(hash_iterator_t *iter);
|
||||
|
||||
#endif /* __LIBXMPPP_HASH_H__ */
|
||||
139
src/jid.c
Normal file
139
src/jid.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/* jid.c
|
||||
** XMPP client library
|
||||
** helper functions for parsing jid's
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
/** join jid component parts to form a new jid string */
|
||||
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
||||
const char *domain,
|
||||
const char *resource)
|
||||
{
|
||||
char *result;
|
||||
int len,nlen,dlen,rlen;
|
||||
|
||||
/* jid must at least have a domain */
|
||||
if (domain == NULL) return NULL;
|
||||
|
||||
/* accumulate lengths */
|
||||
dlen = strlen(domain);
|
||||
nlen = (node) ? strlen(node) + 1 : 0;
|
||||
rlen = (resource) ? strlen(resource) + 1 : 0;
|
||||
len = nlen + dlen + rlen;
|
||||
|
||||
/* concat components */
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
if (node != NULL) {
|
||||
memcpy(result, node, nlen - 1);
|
||||
result[nlen-1] = '@';
|
||||
}
|
||||
memcpy(result + nlen, domain, dlen);
|
||||
if (resource != NULL) {
|
||||
result[nlen+dlen] = '/';
|
||||
memcpy(result+nlen+dlen+1, resource, rlen - 1);
|
||||
}
|
||||
result[nlen+dlen+rlen] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return a bare jid */
|
||||
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result;
|
||||
const char *c;
|
||||
|
||||
c = strchr(jid, '/');
|
||||
if (c == NULL) return xmpp_strdup(ctx, jid);
|
||||
|
||||
result = xmpp_alloc(ctx, c-jid+1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, jid, c-jid);
|
||||
result[c-jid] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return the node portion of a jid */
|
||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c;
|
||||
|
||||
c = strchr(jid, '@');
|
||||
if (c != NULL) {
|
||||
result = xmpp_alloc(ctx, (c-jid) + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, jid, (c-jid));
|
||||
result[c-jid] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return the domain portion of a jid */
|
||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c,*s;
|
||||
|
||||
c = strchr(jid, '@');
|
||||
if (c == NULL) {
|
||||
/* no node, assume domain */
|
||||
c = jid;
|
||||
} else {
|
||||
/* advance past the separator */
|
||||
c++;
|
||||
}
|
||||
s = strchr(c, '/');
|
||||
if (s == NULL) {
|
||||
/* no resource */
|
||||
s = c + strlen(c);
|
||||
}
|
||||
result = xmpp_alloc(ctx, (s-c) + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, c, (s-c));
|
||||
result[s-c] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** return the node portion of a jid */
|
||||
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
|
||||
{
|
||||
char *result = NULL;
|
||||
const char *c;
|
||||
int len;
|
||||
|
||||
c = strchr(jid, '/');
|
||||
if (c != NULL) {
|
||||
c++;
|
||||
len = strlen(c);
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, c, len);
|
||||
result[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
285
src/md5.c
Normal file
285
src/md5.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/* md5.c
|
||||
** MD5 hash function implemention, adapted for local use
|
||||
**
|
||||
** This code is in the Public Domain
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
#include <string.h> /* memcpy(), memset() */
|
||||
#include "md5.h"
|
||||
|
||||
/* little-endian word access macros */
|
||||
#define GET_32BIT_LSB_FIRST(cp) \
|
||||
(((uint32_t)(unsigned char)(cp)[0]) | \
|
||||
((uint32_t)(unsigned char)(cp)[1] << 8 ) | \
|
||||
((uint32_t)(unsigned char)(cp)[2] << 16) | \
|
||||
((uint32_t)(unsigned char)(cp)[3] << 24))
|
||||
|
||||
#define PUT_32BIT_LSB_FIRST(cp, value) \
|
||||
do { \
|
||||
(cp)[0] = (value) & 0xFF; \
|
||||
(cp)[1] = ((value) >> 8) & 0xFF; \
|
||||
(cp)[2] = ((value) >> 16) & 0xFF; \
|
||||
(cp)[3] = ((value) >> 24) & 0xFF; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void MD5Init(struct MD5Context *ctx)
|
||||
{
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
|
||||
memset(ctx->in, 0, 64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
|
||||
{
|
||||
uint32_t t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = (t + ((uint32_t)len << 3)) & 0xffffffff) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
|
||||
{
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
|
||||
/* Append length in bits and transform */
|
||||
PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
|
||||
PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
|
||||
|
||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
||||
PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
|
||||
PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
|
||||
PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
|
||||
PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
#ifndef ASM_MD5
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
/* debugging version: */
|
||||
/*
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
printf("MD5STEP: w: %x x: %x y: %x z: %x data: %x s: %x\n", \
|
||||
w, x, y, z, data, s); \
|
||||
printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
|
||||
( w += f(x, y, z) + data, printf(" - w: %x ", w), \
|
||||
w = w<<s | w>>(32-s), printf(" - w: %x\n", w), w += x )
|
||||
*/
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
|
||||
struct MD5Context *ctx)
|
||||
{
|
||||
register uint32_t a, b, c, d, i;
|
||||
uint32_t in[16];
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MD5
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void MD5DumpBytes(unsigned char *b, int len)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<len; i++) {
|
||||
if (i%32==0 && i!=0) {
|
||||
printf("\n");
|
||||
}
|
||||
printf("%02x", b[i]&0xff);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#endif /* DEBUG_MD5 */
|
||||
|
||||
#endif /* !MD5_ASM */
|
||||
37
src/md5.h
Normal file
37
src/md5.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* md5.h
|
||||
** interface to MD5 hash function
|
||||
**
|
||||
** This code is in the Public Domain.
|
||||
*/
|
||||
|
||||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
|
||||
/* we use the uint32_t type from stdint.h
|
||||
* if it is not available, add a typedef here:
|
||||
*/
|
||||
/* make sure the stdint.h types are available */
|
||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
struct MD5Context {
|
||||
uint32_t buf[4];
|
||||
uint32_t bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
|
||||
void MD5Init(struct MD5Context *context);
|
||||
void MD5Update(struct MD5Context *context, unsigned char const *buf,
|
||||
uint32_t len);
|
||||
void MD5Final(unsigned char digest[16], struct MD5Context *context);
|
||||
void MD5Transform(uint32_t buf[4], const unsigned char in[64],
|
||||
struct MD5Context *ctx);
|
||||
|
||||
#ifdef DEBUG_MD5
|
||||
void MD5DumpBytes(unsigned char *b, int len);
|
||||
#endif
|
||||
|
||||
#endif /* !MD5_H */
|
||||
202
src/parser.c
Normal file
202
src/parser.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/* parser.c
|
||||
** XMPP client library -- xml parser handlers and utility functions
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "expat.h"
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
|
||||
static void _log_open_tag(xmpp_conn_t * const conn,
|
||||
const XML_Char **attr)
|
||||
{
|
||||
char buf[4096];
|
||||
size_t len, pos;
|
||||
int i;
|
||||
|
||||
pos = 0;
|
||||
len = snprintf(buf, 4096, "<stream:stream");
|
||||
if (len < 0) return;
|
||||
|
||||
pos += len;
|
||||
|
||||
for (i = 0; attr[i]; i += 2) {
|
||||
len = snprintf(&buf[pos], 4096 - pos, " %s=\"%s\"",
|
||||
attr[i], attr[i+1]);
|
||||
if (len < 0) return;
|
||||
|
||||
pos += len;
|
||||
}
|
||||
|
||||
len = snprintf(&buf[pos], 4096 - pos, ">");
|
||||
if (len < 0) return;
|
||||
|
||||
xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
|
||||
}
|
||||
|
||||
void parser_handle_start(void *userdata,
|
||||
const XML_Char *name,
|
||||
const XML_Char **attr)
|
||||
{
|
||||
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
if (conn->depth == 0) {
|
||||
/* we're expecting a stream:stream tag. */
|
||||
if (strcmp(name, "stream:stream") != 0) {
|
||||
xmpp_error(conn->ctx, "xmpp",
|
||||
"Server did not open valid stream.");
|
||||
conn_disconnect(conn);
|
||||
} else {
|
||||
_log_open_tag(conn, attr);
|
||||
|
||||
if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id);
|
||||
conn->stream_id = xmpp_strdup(conn->ctx, "foo");
|
||||
if (!conn->stream_id) {
|
||||
xmpp_error(conn->ctx, "xmpp",
|
||||
"Memory allocation failure.");
|
||||
conn_disconnect(conn);
|
||||
}
|
||||
|
||||
/* call stream open handler */
|
||||
conn->open_handler(conn);
|
||||
}
|
||||
} else {
|
||||
/* build stanzas at depth 1 */
|
||||
if (!conn->stanza && conn->depth != 1) {
|
||||
/* something terrible happened */
|
||||
/* FIXME: shutdown disconnect */
|
||||
xmpp_debug(conn->ctx, "xmpp", "oops, where did our stanza go?");
|
||||
} else if (!conn->stanza) {
|
||||
/* starting a new toplevel stanza */
|
||||
conn->stanza = xmpp_stanza_new(conn->ctx);
|
||||
if (!conn->stanza) {
|
||||
/* FIXME: can't allocate, disconnect */
|
||||
}
|
||||
xmpp_stanza_set_name(conn->stanza, name);
|
||||
xmpp_stanza_set_attributes(conn->stanza, attr);
|
||||
} else {
|
||||
/* starting a child of conn->stanza */
|
||||
child = xmpp_stanza_new(conn->ctx);
|
||||
if (!child) {
|
||||
/* FIXME: can't allocate, disconnect */
|
||||
}
|
||||
xmpp_stanza_set_name(child, name);
|
||||
xmpp_stanza_set_attributes(child, attr);
|
||||
|
||||
/* add child to parent */
|
||||
xmpp_stanza_add_child(conn->stanza, child);
|
||||
|
||||
/* the child is owned by the toplevel stanza now */
|
||||
xmpp_stanza_release(child);
|
||||
|
||||
/* make child the current stanza */
|
||||
conn->stanza = child;
|
||||
}
|
||||
}
|
||||
|
||||
conn->depth++;
|
||||
}
|
||||
|
||||
void parser_handle_end(void *userdata, const XML_Char *name)
|
||||
{
|
||||
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
||||
char *buf;
|
||||
size_t len;
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
conn->depth--;
|
||||
|
||||
if (conn->depth == 0) {
|
||||
/* got a closing stream tag */
|
||||
xmpp_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
|
||||
conn_disconnect_clean(conn);
|
||||
} else {
|
||||
if (conn->stanza->parent) {
|
||||
/* we're finishing a child stanza, so set current to the parent */
|
||||
conn->stanza = conn->stanza->parent;
|
||||
} else {
|
||||
/* we're finishing a toplevel stanza, so fire off handler */
|
||||
if (xmpp_stanza_to_text(conn->stanza, &buf, &len) == 0) {
|
||||
xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
|
||||
xmpp_free(conn->ctx, buf);
|
||||
}
|
||||
|
||||
stanza = xmpp_stanza_clone(conn->stanza);
|
||||
xmpp_stanza_release(conn->stanza);
|
||||
conn->stanza = NULL;
|
||||
|
||||
/* fire handlers */
|
||||
handler_fire_stanza(conn, stanza);
|
||||
|
||||
xmpp_stanza_release(stanza);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parser_handle_character(void *userdata, const XML_Char *s, int len)
|
||||
{
|
||||
xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
if (conn->depth < 2) return;
|
||||
|
||||
/* create and populate stanza */
|
||||
stanza = xmpp_stanza_new(conn->ctx);
|
||||
if (!stanza) {
|
||||
/* FIXME: allocation error, disconnect */
|
||||
return;
|
||||
}
|
||||
xmpp_stanza_set_text_with_size(stanza, s, len);
|
||||
|
||||
xmpp_stanza_add_child(conn->stanza, stanza);
|
||||
xmpp_stanza_release(stanza);
|
||||
}
|
||||
|
||||
/* prepares a parser reset. this is called from handlers. we can't
|
||||
* reset the parser immediately as it is not reentrant. */
|
||||
void parser_prepare_reset(xmpp_conn_t * const conn,
|
||||
xmpp_open_handler handler)
|
||||
{
|
||||
conn->reset_parser = 1;
|
||||
conn->open_handler = handler;
|
||||
}
|
||||
|
||||
/* shuts down and restarts XML parser. true on success */
|
||||
int parser_reset(xmpp_conn_t * const conn)
|
||||
{
|
||||
conn->reset_parser = 0;
|
||||
|
||||
if (conn->parser)
|
||||
XML_ParserFree(conn->parser);
|
||||
|
||||
if (conn->stanza)
|
||||
xmpp_stanza_release(conn->stanza);
|
||||
|
||||
conn->parser = XML_ParserCreate(NULL);
|
||||
if (!conn->parser) return 0;
|
||||
|
||||
conn->depth = 0;
|
||||
conn->stanza = NULL;
|
||||
XML_SetUserData(conn->parser, conn);
|
||||
XML_SetElementHandler(conn->parser, parser_handle_start,
|
||||
parser_handle_end);
|
||||
XML_SetCharacterDataHandler(conn->parser, parser_handle_character);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
593
src/sasl.c
Normal file
593
src/sasl.c
Normal file
@@ -0,0 +1,593 @@
|
||||
/* sasl.c
|
||||
** XMPP client library -- SASL authentication helpers
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "sasl.h"
|
||||
#include "md5.h"
|
||||
|
||||
/* make sure the stdint.h types are available */
|
||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
typedef signed char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef __int64 int64_t;
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short int uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
/* no uint64_t */
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
|
||||
/** generate authentication string for the SASL PLAIN mechanism */
|
||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
||||
int idlen, passlen;
|
||||
char *result = NULL;
|
||||
char *msg;
|
||||
|
||||
/* our message is Base64(authzid,\0,authid,\0,password)
|
||||
if there is no authzid, that field is left empty */
|
||||
|
||||
idlen = strlen(authid);
|
||||
passlen = strlen(password);
|
||||
msg = xmpp_alloc(ctx, 2 + idlen + passlen);
|
||||
if (msg != NULL) {
|
||||
msg[0] = '\0';
|
||||
memcpy(msg+1, authid, idlen);
|
||||
msg[1+idlen] = '\0';
|
||||
memcpy(msg+1+idlen+1, password, passlen);
|
||||
result = base64_encode(ctx, msg, 2 + idlen + passlen);
|
||||
xmpp_free(ctx, msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** helpers for digest auth */
|
||||
|
||||
/* create a new, null-terminated string from a substring */
|
||||
static char *_make_string(xmpp_ctx_t *ctx, const char *s, const int len)
|
||||
{
|
||||
char *result;
|
||||
|
||||
result = xmpp_alloc(ctx, len + 1);
|
||||
if (result != NULL) {
|
||||
memcpy(result, s, len);
|
||||
result[len] = '\0';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* create a new, null-terminated string quoting another string */
|
||||
static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
|
||||
{
|
||||
char *result;
|
||||
int len = strlen(s);
|
||||
|
||||
result = xmpp_alloc(ctx, len + 3);
|
||||
if (result != NULL) {
|
||||
result[0] = '"';
|
||||
memcpy(result+1, s, len);
|
||||
result[len+1] = '"';
|
||||
result[len+2] = '\0';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* split key, value pairs into a hash */
|
||||
static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
||||
{
|
||||
hash_t *result;
|
||||
char *text;
|
||||
char *key, *value;
|
||||
char *s, *t;
|
||||
|
||||
text = base64_decode(ctx, msg, strlen(msg));
|
||||
if (text == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = hash_new(ctx, 10, xmpp_free);
|
||||
if (result != NULL) {
|
||||
s = text;
|
||||
while (*s != '\0') {
|
||||
/* accumulate a key ending at '=' */
|
||||
t = s;
|
||||
while ((*t != '=') && (*t != '\0')) t++;
|
||||
if (*t == '\0') break; /* bad string */
|
||||
key = _make_string(ctx, s, (t-s));
|
||||
if (key == NULL) break;
|
||||
/* advance our start pointer past the key */
|
||||
s = t + 1;
|
||||
/* accumulate a value ending in ',' or '\0' */
|
||||
t = s;
|
||||
while ((*t != ',') && (*t != '\0')) t++;
|
||||
/* trim quotes if they occur */
|
||||
if (((*s == '\'') && (*(t-1) == '\'')) ||
|
||||
((*s == '"') && (*(t-1) == '"'))) {
|
||||
value = _make_string(ctx, s+1, (t-s-2));
|
||||
} else {
|
||||
value = _make_string(ctx, s, (t-s));
|
||||
}
|
||||
if (value == NULL) {
|
||||
xmpp_free(ctx, key);
|
||||
break;
|
||||
}
|
||||
/* TODO: check for collisions per spec */
|
||||
hash_add(result, key, value);
|
||||
/* hash table now owns the value, free the key */
|
||||
xmpp_free(ctx, key);
|
||||
/* advance past the value and any trailing comma */
|
||||
s = (*t) ? t + 1 : t;
|
||||
}
|
||||
}
|
||||
xmpp_free(ctx, text);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** expand a 16 byte MD5 digest to a 32 byte hex representation */
|
||||
static void _digest_to_hex(const char *digest, char *hex)
|
||||
{
|
||||
int i;
|
||||
const char hexdigit[] = "0123456789abcdef";
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
*hex++ = hexdigit[ (digest[i] >> 4) & 0x0F ];
|
||||
*hex++ = hexdigit[ digest[i] & 0x0F ];
|
||||
}
|
||||
}
|
||||
|
||||
/** append 'key="value"' to a buffer, growing as necessary */
|
||||
static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
|
||||
char *buf, int *len, int quote)
|
||||
{
|
||||
int olen,nlen;
|
||||
int keylen, valuelen;
|
||||
const char *value, *qvalue;
|
||||
char *c;
|
||||
|
||||
/* allocate a zero-length string if necessary */
|
||||
if (buf == NULL) {
|
||||
buf = xmpp_alloc(ctx, 1);
|
||||
buf[0] = '\0';
|
||||
}
|
||||
if (buf == NULL) return NULL;
|
||||
|
||||
/* get current string length */
|
||||
olen = strlen(buf);
|
||||
value = hash_get(table, key);
|
||||
if (value == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
||||
value = "\"\"";
|
||||
}
|
||||
if (quote) {
|
||||
qvalue = _make_quoted(ctx, value);
|
||||
} else {
|
||||
qvalue = value;
|
||||
}
|
||||
/* added length is key + '=' + value */
|
||||
/* (+ ',' if we're not the first entry */
|
||||
keylen = strlen(key);
|
||||
valuelen = strlen(qvalue);
|
||||
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
|
||||
buf = xmpp_realloc(ctx, buf, olen+nlen);
|
||||
|
||||
if (buf != NULL) {
|
||||
c = buf + olen;
|
||||
if (olen) *c++ = ',';
|
||||
memcpy(c, key, keylen); c += keylen;
|
||||
*c++ = '=';
|
||||
memcpy(c, qvalue, valuelen); c += valuelen;
|
||||
*c++ = '\0';
|
||||
}
|
||||
|
||||
if (quote) xmpp_free(ctx, (char *)qvalue);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** generate auth response string for the SASL DIGEST-MD5 mechanism */
|
||||
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
||||
const char *jid, const char *password) {
|
||||
hash_t *table;
|
||||
char *result = NULL;
|
||||
char *node, *domain;
|
||||
char *value;
|
||||
unsigned char *A1, *A2; /* MD5 hashed versions */
|
||||
char *response;
|
||||
int rlen;
|
||||
struct MD5Context MD5;
|
||||
unsigned char digest[16];
|
||||
char hex[32];
|
||||
|
||||
/* our digest response is
|
||||
Hex( KD( HEX(MD5(A1)),
|
||||
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
|
||||
))
|
||||
|
||||
where KD(k, s) = MD5(k ':' s),
|
||||
A1 = MD5( node ':' domain ':' password ) ':' nonce ':' cnonce
|
||||
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
||||
|
||||
If there is an authzid it is ':'-appended to A1 */
|
||||
|
||||
/* parse the challenge */
|
||||
table = _parse_digest_challenge(ctx, challenge);
|
||||
if (table == NULL) {
|
||||
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = xmpp_jid_node(ctx, jid);
|
||||
domain = xmpp_jid_domain(ctx, jid);
|
||||
|
||||
/* add our response fields */
|
||||
hash_add(table, "username", xmpp_strdup(ctx, node));
|
||||
/* TODO: generate a random cnonce */
|
||||
hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
|
||||
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
|
||||
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
|
||||
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
|
||||
memcpy(value, "xmpp/", 5);
|
||||
memcpy(value+5, domain, strlen(domain));
|
||||
value[5+strlen(domain)] = '\0';
|
||||
hash_add(table, "digest-uri", value);
|
||||
|
||||
/* generate response */
|
||||
|
||||
/* construct MD5(A1) */
|
||||
MD5Init(&MD5);
|
||||
MD5Update(&MD5, node, strlen(node));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
MD5Update(&MD5, domain, strlen(domain));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
MD5Update(&MD5, password, strlen(password));
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
MD5Init(&MD5);
|
||||
MD5Update(&MD5, digest, 16);
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "nonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "cnonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
A1 = xmpp_alloc(ctx, 16);
|
||||
memcpy(A1, digest, 16);
|
||||
|
||||
/* construct MD5(A2) */
|
||||
MD5Init(&MD5);
|
||||
MD5Update(&MD5, "AUTHENTICATE:", 13);
|
||||
value = hash_get(table, "digest-uri");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
A2 = xmpp_alloc(ctx, 16);
|
||||
memcpy(A2, digest, 16);
|
||||
|
||||
/* construct response */
|
||||
MD5Init(&MD5);
|
||||
_digest_to_hex(A1, hex);
|
||||
MD5Update(&MD5, hex, 32);
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "nonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
MD5Update(&MD5, "00000001", 8);
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "cnonce");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
value = hash_get(table, "qop");
|
||||
MD5Update(&MD5, value, strlen(value));
|
||||
MD5Update(&MD5, ":", 1);
|
||||
_digest_to_hex(A2, hex);
|
||||
MD5Update(&MD5, hex, 32);
|
||||
MD5Final(digest, &MD5);
|
||||
|
||||
response = xmpp_alloc(ctx, 32+1);
|
||||
_digest_to_hex(digest, hex);
|
||||
memcpy(response, hex, 32);
|
||||
response[32] = '\0';
|
||||
hash_add(table, "response", response);
|
||||
|
||||
xmpp_free(ctx, A1);
|
||||
xmpp_free(ctx, A2);
|
||||
|
||||
/* construct reply */
|
||||
result = NULL;
|
||||
rlen = 0;
|
||||
result = _add_key(ctx, table, "username", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "realm", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "nonce", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "cnonce", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "nc", result, &rlen, 0);
|
||||
result = _add_key(ctx, table, "qop", result, &rlen, 0);
|
||||
result = _add_key(ctx, table, "digest-uri", result, &rlen, 1);
|
||||
result = _add_key(ctx, table, "response", result, &rlen, 0);
|
||||
result = _add_key(ctx, table, "charset", result, &rlen, 0);
|
||||
|
||||
xmpp_free(ctx, node);
|
||||
xmpp_free(ctx, domain);
|
||||
hash_release(table); /* also frees value strings */
|
||||
|
||||
/* reuse response for the base64 encode of our result */
|
||||
response = base64_encode(ctx, result, strlen(result));
|
||||
xmpp_free(ctx, result);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
||||
|
||||
/** map of all byte values to the base64 values, or to
|
||||
'65' which indicates an invalid character. '=' is '64' */
|
||||
static const char _base64_invcharmap[256] = {
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
|
||||
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65,
|
||||
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
|
||||
};
|
||||
|
||||
/** map of all 6-bit values to their corresponding byte
|
||||
in the base64 alphabet. Padding char is the value '64' */
|
||||
static const char _base64_charmap[65] = {
|
||||
'A','B','C','D', 'E','F','G','H',
|
||||
'I','J','K','L', 'M','N','O','P',
|
||||
'Q','R','S','T', 'U','V','W','X',
|
||||
'Y','Z','a','b', 'c','d','e','f',
|
||||
'g','h','i','j', 'k','l','m','n',
|
||||
'o','p','q','r', 's','t','u','v',
|
||||
'w','x','y','z', '0','1','2','3',
|
||||
'4','5','6','7', '8','9','+','/',
|
||||
'='
|
||||
};
|
||||
|
||||
int base64_encoded_len(xmpp_ctx_t *ctx, const int len)
|
||||
{
|
||||
/* encoded steam is 4 bytes for every three, rounded up */
|
||||
return ((len + 2)/3) << 2;
|
||||
}
|
||||
|
||||
char *base64_encode(xmpp_ctx_t *ctx,
|
||||
const unsigned char * const buffer, const int len)
|
||||
{
|
||||
int clen;
|
||||
char *cbuf, *c;
|
||||
uint32_t word, hextet;
|
||||
int i;
|
||||
|
||||
clen = base64_encoded_len(ctx, len);
|
||||
cbuf = xmpp_alloc(ctx, clen + 1);
|
||||
if (cbuf != NULL) {
|
||||
c = cbuf;
|
||||
/* loop over data, turning every 3 bytes into 4 characters */
|
||||
for (i = 0; i < len - 2; i += 3) {
|
||||
word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
|
||||
hextet = (word & 0x00FC0000) >> 18;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (word & 0x0003F000) >> 12;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (word & 0x00000FC0) >> 6;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (word & 0x000003F);
|
||||
*c++ = _base64_charmap[hextet];
|
||||
}
|
||||
/* zero, one or two bytes left */
|
||||
switch (len - i) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
hextet = (buffer[len-1] & 0xFC) >> 2;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (buffer[len-1] & 0x02) << 6;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
*c++ = _base64_charmap[64]; /* pad */
|
||||
*c++ = _base64_charmap[64]; /* pad */
|
||||
break;
|
||||
case 2:
|
||||
hextet = (buffer[len-2] & 0xFC) >> 2;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = ((buffer[len-2] & 0x03) << 4) |
|
||||
((buffer[len-1] & 0xF0) >> 4);
|
||||
*c++ = _base64_charmap[hextet];
|
||||
hextet = (buffer[len-1] & 0x0F) << 2;
|
||||
*c++ = _base64_charmap[hextet];
|
||||
*c++ = _base64_charmap[64]; /* pad */
|
||||
break;
|
||||
}
|
||||
/* add a terminal null */
|
||||
*c = '\0';
|
||||
}
|
||||
|
||||
return cbuf;
|
||||
}
|
||||
|
||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len)
|
||||
{
|
||||
int nudge;
|
||||
int c;
|
||||
|
||||
/* count the padding characters for the remainder */
|
||||
nudge = -1;
|
||||
c = _base64_invcharmap[(int)buffer[len-1]];
|
||||
if (c < 64) nudge = 0;
|
||||
else if (c == 64) {
|
||||
c = _base64_invcharmap[(int)buffer[len-2]];
|
||||
if (c < 64) nudge = 2;
|
||||
else if (c == 64) {
|
||||
c = _base64_invcharmap[(int)buffer[len-3]];
|
||||
if (c < 64) nudge = 1;
|
||||
}
|
||||
}
|
||||
if (nudge < 0) return 0; /* reject bad coding */
|
||||
|
||||
/* decoded steam is 3 bytes for every four */
|
||||
return 3 * ((len + 3) >> 2) + nudge;
|
||||
}
|
||||
|
||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len)
|
||||
{
|
||||
int dlen;
|
||||
unsigned char *dbuf, *d;
|
||||
uint32_t word, hextet;
|
||||
int i;
|
||||
|
||||
/* len must be a multiple of 4 */
|
||||
if (len & 0x03) return NULL;
|
||||
|
||||
dlen = base64_decoded_len(ctx, buffer, len);
|
||||
dbuf = xmpp_alloc(ctx, dlen + 1);
|
||||
if (dbuf != NULL) {
|
||||
d = dbuf;
|
||||
/* loop over each set of 4 characters, decoding 3 bytes */
|
||||
for (i = 0; i < len - 3; i += 4) {
|
||||
hextet = _base64_invcharmap[(int)buffer[i]];
|
||||
if (hextet & 0xC0) break;
|
||||
word = hextet << 18;
|
||||
hextet = _base64_invcharmap[(int)buffer[i+1]];
|
||||
if (hextet & 0xC0) break;
|
||||
word |= hextet << 12;
|
||||
hextet = _base64_invcharmap[(int)buffer[i+2]];
|
||||
if (hextet & 0xC0) break;
|
||||
word |= hextet << 6;
|
||||
hextet = _base64_invcharmap[(int)buffer[i+3]];
|
||||
if (hextet & 0xC0) break;
|
||||
word |= hextet;
|
||||
*d++ = (word & 0x00FF0000) >> 16;
|
||||
*d++ = (word & 0x0000FF00) >> 8;
|
||||
*d++ = (word & 0x000000FF);
|
||||
}
|
||||
if (hextet > 64) goto _base64_decode_error;
|
||||
/* handle the remainder */
|
||||
switch (dlen % 3) {
|
||||
case 0:
|
||||
/* nothing to do */
|
||||
break;
|
||||
case 1:
|
||||
/* redo the last quartet, checking for correctness */
|
||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word = hextet << 2;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word |= hextet >> 4;
|
||||
*d++ = word & 0xFF;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
||||
if (hextet != 64) goto _base64_decode_error;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
||||
if (hextet != 64) goto _base64_decode_error;
|
||||
break;
|
||||
case 2:
|
||||
/* redo the last quartet, checking for correctness */
|
||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word = hextet << 10;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word |= hextet << 4;
|
||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
||||
if (hextet & 0xC0) goto _base64_decode_error;
|
||||
word |= hextet >> 2;
|
||||
*d++ = (word & 0xFF00) >> 8;
|
||||
*d++ = (word & 0x00FF);
|
||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
||||
if (hextet != 64) goto _base64_decode_error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*d = '\0';
|
||||
return dbuf;
|
||||
|
||||
_base64_decode_error:
|
||||
/* invalid character; abort decoding! */
|
||||
xmpp_free(ctx, dbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*** self tests ***/
|
||||
#ifdef TEST
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int test_charmap_identity(void)
|
||||
{
|
||||
int i, v, u;
|
||||
|
||||
for (i = 0; i < 65; i++) {
|
||||
v = _base64_charmap[i];
|
||||
if (v > 255) return 1;
|
||||
u = _base64_invcharmap[v];
|
||||
/* printf("map: %d -> %d -> %d\n", i, v, u); */
|
||||
if (u != i) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_charmap_range(void)
|
||||
{
|
||||
int i, v;
|
||||
|
||||
for (i = 64; i < 256; i++) {
|
||||
v = _base64_invcharmap[i];
|
||||
if (i < 64) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
printf("testing charmap identity...");
|
||||
ret = test_charmap_identity();
|
||||
if (ret) return ret;
|
||||
printf(" ok.\n");
|
||||
|
||||
printf("testing charmap range...");
|
||||
ret = test_charmap_range();
|
||||
if (ret) return ret;
|
||||
printf(" ok.\n");
|
||||
|
||||
printf("no error\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST */
|
||||
40
src/sasl.h
Normal file
40
src/sasl.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* sasl.h
|
||||
** XMPP client library -- SASL authentication helpers
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_SASL_H__
|
||||
#define __LIBXMPP_SASL_H__
|
||||
|
||||
#include "xmpp.h"
|
||||
|
||||
/** low-level sasl routines */
|
||||
|
||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
|
||||
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
||||
const char *jid, const char *password);
|
||||
|
||||
|
||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
||||
|
||||
int base64_encoded_len(xmpp_ctx_t *ctx, const int len);
|
||||
|
||||
char *base64_encode(xmpp_ctx_t *ctx,
|
||||
const unsigned char * const buffer, const int len);
|
||||
|
||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len);
|
||||
|
||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
||||
const char * const buffer, const int len);
|
||||
|
||||
#endif /* _LIBXMPP_SASL_H__ */
|
||||
385
src/sha1.c
Normal file
385
src/sha1.c
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
100% Public Domain
|
||||
|
||||
-----------------
|
||||
Modified 7/98
|
||||
By James H. Brown <jbrown@burgoyne.com>
|
||||
Still 100% Public Domain
|
||||
|
||||
Corrected a problem which generated improper hash values on 16 bit machines
|
||||
Routine SHA1Update changed from
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||
len)
|
||||
to
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||
long len)
|
||||
|
||||
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||
However, on 16 bit machines an int is too small for the shifts being done
|
||||
against
|
||||
it. This caused the hash function to generate incorrect values if len was
|
||||
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||
|
||||
Since the file IO in main() reads 16K at a time, any file 8K or larger would
|
||||
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
|
||||
"a"s).
|
||||
|
||||
I also changed the declaration of variables i & j in SHA1Update to
|
||||
unsigned long from unsigned int for the same reason.
|
||||
|
||||
These changes should make no difference to any 32 bit implementations since
|
||||
an
|
||||
int and a long are the same size in those environments.
|
||||
|
||||
--
|
||||
I also corrected a few compiler warnings generated by Borland C.
|
||||
1. Added #include <process.h> for exit() prototype
|
||||
2. Removed unused variable 'j' in SHA1Final
|
||||
3. Changed exit(0) to return(0) at end of main.
|
||||
|
||||
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||
-----------------
|
||||
Modified 8/98
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
Still 100% public domain
|
||||
|
||||
1- Removed #include <process.h> and used return() instead of exit()
|
||||
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||
|
||||
-----------------
|
||||
Modified 4/01
|
||||
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||
Still 100% PD
|
||||
Modified to run on Compaq Alpha hardware.
|
||||
|
||||
-----------------
|
||||
Modified 07/2002
|
||||
By Ralph Giles <giles@artofcode.com>
|
||||
Still 100% public domain
|
||||
modified for use with stdint types, autoconf
|
||||
code cleanup, removed attribution comments
|
||||
switched SHA1Final() argument order for consistency
|
||||
use SHA1_ prefix for public api
|
||||
move public api to sha1.h
|
||||
*/
|
||||
|
||||
/*
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define SHA1HANDSOFF */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* make sure the stdint.h types are available */
|
||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
typedef signed char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef __int64 int64_t;
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short int uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
/* no uint64_t */
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
/* FIXME: can we do this in an endian-proof way? */
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
#ifdef VERBOSE /* SAK */
|
||||
void SHAPrintContext(SHA1_CTX *context, char *msg){
|
||||
printf("%s (%d,%d) %x %x %x %x %x\n",
|
||||
msg,
|
||||
context->count[0], context->count[1],
|
||||
context->state[0],
|
||||
context->state[1],
|
||||
context->state[2],
|
||||
context->state[3],
|
||||
context->state[4]);
|
||||
}
|
||||
#endif /* VERBOSE */
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
CHAR64LONG16* block;
|
||||
|
||||
#ifdef SHA1HANDSOFF
|
||||
static uint8_t workspace[64];
|
||||
block = (CHAR64LONG16*)workspace;
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
block = (CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
void SHA1_Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "before");
|
||||
#endif
|
||||
|
||||
j = (context->count[0] >> 3) & 63;
|
||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1_Transform(context->state, data + i);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "after ");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t finalcount[8];
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
SHA1_Update(context, (uint8_t *)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
SHA1_Update(context, (uint8_t *)"\0", 1);
|
||||
}
|
||||
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
|
||||
digest[i] = (uint8_t)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
|
||||
/* Wipe variables */
|
||||
i = 0;
|
||||
memset(context->buffer, 0, 64);
|
||||
memset(context->state, 0, 20);
|
||||
memset(context->count, 0, 8);
|
||||
memset(finalcount, 0, 8); /* SWR */
|
||||
|
||||
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
#if 0
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int i, j;
|
||||
SHA1_CTX context;
|
||||
unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
|
||||
FILE* file;
|
||||
|
||||
if (argc > 2) {
|
||||
puts("Public domain SHA-1 implementation - by Steve Reid <sreid@sea-to-sky.net>");
|
||||
puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* JHB */
|
||||
puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
|
||||
return(0);
|
||||
}
|
||||
if (argc < 2) {
|
||||
file = stdin;
|
||||
}
|
||||
else {
|
||||
if (!(file = fopen(argv[1], "rb"))) {
|
||||
fputs("Unable to open file.", stderr);
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
SHA1_Init(&context);
|
||||
while (!feof(file)) { /* note: what if ferror(file) */
|
||||
i = fread(buffer, 1, 16384, file);
|
||||
SHA1_Update(&context, buffer, i);
|
||||
}
|
||||
SHA1_Final(&context, digest);
|
||||
fclose(file);
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
printf("%02X", digest[i*4+j]);
|
||||
}
|
||||
putchar(' ');
|
||||
}
|
||||
putchar('\n');
|
||||
return(0); /* JHB */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* self test */
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
static char *test_data[] = {
|
||||
"abc",
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||
"A million repetitions of 'a'"};
|
||||
static char *test_results[] = {
|
||||
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
|
||||
"84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
|
||||
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"};
|
||||
|
||||
|
||||
void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
|
||||
{
|
||||
int i,j;
|
||||
char *c = output;
|
||||
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
sprintf(c,"%02X", digest[i*4+j]);
|
||||
c += 2;
|
||||
}
|
||||
sprintf(c, " ");
|
||||
c += 1;
|
||||
}
|
||||
*(c - 1) = '\0';
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int k;
|
||||
SHA1_CTX context;
|
||||
uint8_t digest[20];
|
||||
char output[80];
|
||||
|
||||
fprintf(stdout, "verifying SHA-1 implementation... ");
|
||||
|
||||
for (k = 0; k < 2; k++){
|
||||
SHA1_Init(&context);
|
||||
SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k]));
|
||||
SHA1_Final(&context, digest);
|
||||
digest_to_hex(digest, output);
|
||||
|
||||
if (strcmp(output, test_results[k])) {
|
||||
fprintf(stdout, "FAIL\n");
|
||||
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]);
|
||||
fprintf(stderr,"\t%s returned\n", output);
|
||||
fprintf(stderr,"\t%s is correct\n", test_results[k]);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
/* million 'a' vector we feed separately */
|
||||
SHA1_Init(&context);
|
||||
for (k = 0; k < 1000000; k++)
|
||||
SHA1_Update(&context, (uint8_t*)"a", 1);
|
||||
SHA1_Final(&context, digest);
|
||||
digest_to_hex(digest, output);
|
||||
if (strcmp(output, test_results[2])) {
|
||||
fprintf(stdout, "FAIL\n");
|
||||
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]);
|
||||
fprintf(stderr,"\t%s returned\n", output);
|
||||
fprintf(stderr,"\t%s is correct\n", test_results[2]);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* success */
|
||||
fprintf(stdout, "ok\n");
|
||||
return(0);
|
||||
}
|
||||
#endif /* TEST */
|
||||
27
src/sha1.h
Normal file
27
src/sha1.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* public api for steve reid's public domain SHA-1 implementation */
|
||||
/* this file is in the public domain */
|
||||
|
||||
#ifndef __SHA1_H
|
||||
#define __SHA1_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
void SHA1_Init(SHA1_CTX* context);
|
||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
|
||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SHA1_H */
|
||||
164
src/sock.c
Normal file
164
src/sock.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/* sock.c
|
||||
** XMPP client library -- socket abstraction implementation
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#define snprintf _snprintf
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "sock.h"
|
||||
|
||||
void sock_initialize(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsad;
|
||||
WSAStartup(0x0101, &wsad);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sock_shutdown(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_error(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _in_progress(int error)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
|
||||
#else
|
||||
return (errno == EINPROGRESS);
|
||||
#endif
|
||||
}
|
||||
|
||||
sock_t sock_connect(const char * const host, const unsigned int port)
|
||||
{
|
||||
sock_t sock;
|
||||
char service[6];
|
||||
struct addrinfo *res, *ainfo, hints;
|
||||
int err;
|
||||
|
||||
sock = -1;
|
||||
|
||||
snprintf(service, 6, "%u", port);
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((err = getaddrinfo(host, service, &hints, &res)) != 0)
|
||||
return -1;
|
||||
|
||||
ainfo = res;
|
||||
while (ainfo) {
|
||||
if ((sock = socket(ainfo->ai_family, ainfo->ai_socktype,
|
||||
ainfo->ai_protocol)) >= 0) {
|
||||
sock_set_nonblocking(sock);
|
||||
|
||||
err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
||||
|
||||
if ((err == 0) || (err < 0 && _in_progress(sock_error())))
|
||||
break;
|
||||
}
|
||||
|
||||
ainfo = ainfo->ai_next;
|
||||
}
|
||||
|
||||
if (res) freeaddrinfo(res);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int sock_close(const sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return closesocket(sock);
|
||||
#else
|
||||
return close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_set_blocking(const sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long block = 0;
|
||||
return ioctlsocket(sock, FIONBIO, &block);
|
||||
#else
|
||||
return fcntl(sock, F_SETFL, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_set_nonblocking(const sock_t sock)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
u_long nonblock = 1;
|
||||
return ioctlsocket(sock, FIONBIO, &nonblock);
|
||||
#else
|
||||
return fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_read(const sock_t sock, void * const buff, const size_t len)
|
||||
{
|
||||
return recv(sock, buff, len, 0);
|
||||
}
|
||||
|
||||
int sock_write(const sock_t sock, const void * const buff, const size_t len)
|
||||
{
|
||||
return send(sock, buff, len, 0);
|
||||
}
|
||||
|
||||
int sock_is_recoverable(const int error)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (error == WSAEINTR || error == WSAEWOULDBLOCK ||
|
||||
error == WSAEINPROGRESS);
|
||||
#else
|
||||
return (error == EAGAIN || error == EINTR);
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_connect_error(const sock_t sock)
|
||||
{
|
||||
socklen_t len;
|
||||
int error, ret;
|
||||
|
||||
len = sizeof(int);
|
||||
|
||||
ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (ret < 0) return ret;
|
||||
return error;
|
||||
}
|
||||
42
src/sock.h
Normal file
42
src/sock.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* sock.h
|
||||
** XMPP client library -- socket abstraction header
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_SOCK_H__
|
||||
#define __LIBXMPP_SOCK_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
typedef int sock_t;
|
||||
#else
|
||||
typedef SOCKET sock_t;
|
||||
#endif
|
||||
|
||||
void sock_initialize(void);
|
||||
void sock_shutdown(void);
|
||||
|
||||
int sock_error(void);
|
||||
|
||||
sock_t sock_connect(const char * const host, const unsigned int port);
|
||||
int sock_close(const sock_t sock);
|
||||
|
||||
int sock_set_blocking(const sock_t sock);
|
||||
int sock_set_nonblocking(const sock_t sock);
|
||||
int sock_read(const sock_t sock, void * const buff, const size_t len);
|
||||
int sock_write(const sock_t sock, const void * const buff, const size_t len);
|
||||
int sock_is_recoverable(const int error);
|
||||
/* checks for an error after connect, return 0 if connect successful */
|
||||
int sock_connect_error(const sock_t sock);
|
||||
|
||||
#endif /* __LIBXMPP_SOCK_H__ */
|
||||
505
src/stanza.c
Normal file
505
src/stanza.c
Normal file
@@ -0,0 +1,505 @@
|
||||
/* stanza.c
|
||||
** XMPP client library -- XMPP stanza object and utilities
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* allocate an initialize a blank stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
|
||||
{
|
||||
xmpp_stanza_t *stanza;
|
||||
|
||||
stanza = xmpp_alloc(ctx, sizeof(xmpp_stanza_t));
|
||||
if (stanza != NULL) {
|
||||
stanza->ref = 1;
|
||||
stanza->ctx = ctx;
|
||||
stanza->type = XMPP_STANZA_UNKNOWN;
|
||||
stanza->prev = NULL;
|
||||
stanza->next = NULL;
|
||||
stanza->children = NULL;
|
||||
stanza->parent = NULL;
|
||||
stanza->data = NULL;
|
||||
stanza->attributes = NULL;
|
||||
}
|
||||
|
||||
return stanza;
|
||||
}
|
||||
|
||||
/* clone a stanza */
|
||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
stanza->ref++;
|
||||
|
||||
/* clone all children */
|
||||
for (child = stanza->children; child; child = child->next)
|
||||
xmpp_stanza_clone(child);
|
||||
|
||||
return stanza;
|
||||
}
|
||||
|
||||
/* copies and stanza and all children
|
||||
* this function returns a new stanza copied from stanza. the new
|
||||
* stanza will have no parent and no siblings. the caller is given
|
||||
* a reference to this new stanza. this function is used to extract
|
||||
* a child from one stanza for inclusion in another. */
|
||||
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_stanza_t *copy, *child, *copychild, *tail;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
void *val;
|
||||
|
||||
copy = xmpp_stanza_new(stanza->ctx);
|
||||
if (!copy) goto copy_error;
|
||||
|
||||
copy->type = stanza->type;
|
||||
|
||||
if (stanza->data) {
|
||||
copy->data = xmpp_strdup(stanza->ctx, stanza->data);
|
||||
if (!copy->data) goto copy_error;
|
||||
}
|
||||
|
||||
if (stanza->attributes) {
|
||||
copy->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
if (!copy->attributes) goto copy_error;
|
||||
iter = hash_iter_new(stanza->attributes);
|
||||
if (!iter) { printf("DEBUG HERE\n"); goto copy_error; }
|
||||
while ((key = hash_iter_next(iter))) {
|
||||
val = xmpp_strdup(stanza->ctx,
|
||||
(char *)hash_get(stanza->attributes, key));
|
||||
if (!val) goto copy_error;
|
||||
|
||||
if (hash_add(copy->attributes, key, val))
|
||||
goto copy_error;
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
}
|
||||
|
||||
tail = copy->children;
|
||||
for (child = stanza->children; child; child = child->next) {
|
||||
copychild = xmpp_stanza_copy(child);
|
||||
if (!copychild) goto copy_error;
|
||||
copychild->parent = copy;
|
||||
|
||||
if (tail) {
|
||||
copychild->prev = tail;
|
||||
tail->next = copychild;
|
||||
} else
|
||||
copy->children = copychild;
|
||||
tail = copychild;
|
||||
}
|
||||
|
||||
return copy;
|
||||
|
||||
copy_error:
|
||||
/* release all the hitherto allocated memory */
|
||||
if (copy) xmpp_stanza_release(copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* free a stanza object and it's contents */
|
||||
void xmpp_stanza_release(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
xmpp_stanza_t *child, *tchild;
|
||||
|
||||
/* release all children */
|
||||
child = stanza->children;
|
||||
while (child) {
|
||||
tchild = child;
|
||||
child = child->next;
|
||||
xmpp_stanza_release(tchild);
|
||||
}
|
||||
|
||||
/* release stanza */
|
||||
if (stanza->ref > 1)
|
||||
stanza->ref--;
|
||||
else {
|
||||
if (stanza->attributes) hash_release(stanza->attributes);
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
xmpp_free(stanza->ctx, stanza);
|
||||
}
|
||||
}
|
||||
|
||||
/* small helper function */
|
||||
static inline void _render_update(int *written, const int length,
|
||||
const int lastwrite,
|
||||
size_t *left, char **ptr)
|
||||
{
|
||||
*written += lastwrite;
|
||||
|
||||
if (*written > length) {
|
||||
*left = 0;
|
||||
*ptr = NULL;
|
||||
} else {
|
||||
*left -= lastwrite;
|
||||
*ptr = &(*ptr)[lastwrite];
|
||||
}
|
||||
}
|
||||
|
||||
/* always returns number of bytes written or that would have been
|
||||
* written if the buffer was large enough
|
||||
* return values < 0 indicate some error occured,
|
||||
* and return values > buflen indicate buffer was not large enough
|
||||
*/
|
||||
static int _render_stanza_recursive(xmpp_stanza_t *stanza,
|
||||
char * const buf, size_t const buflen)
|
||||
{
|
||||
char *ptr = buf;
|
||||
size_t left = buflen;
|
||||
int ret, written;
|
||||
xmpp_stanza_t *child;
|
||||
hash_iterator_t *iter;
|
||||
const char *key;
|
||||
|
||||
written = 0;
|
||||
|
||||
if (stanza->type == XMPP_STANZA_UNKNOWN) return -1;
|
||||
|
||||
if (stanza->type == XMPP_STANZA_TEXT) {
|
||||
if (!stanza->data) return -2;
|
||||
|
||||
ret = snprintf(ptr, left, "%s", stanza->data);
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
} else { /* stanza->type == XMPP_STANZA_TAG */
|
||||
if (!stanza->data) return -2;
|
||||
|
||||
/* write begining of tag and attributes */
|
||||
ret = snprintf(ptr, left, "<%s", stanza->data);
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
|
||||
if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) {
|
||||
iter = hash_iter_new(stanza->attributes);
|
||||
while ((key = hash_iter_next(iter))) {
|
||||
ret = snprintf(ptr, left, " %s=\"%s\"", key,
|
||||
(char *)hash_get(stanza->attributes, key));
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
}
|
||||
hash_iter_release(iter);
|
||||
}
|
||||
|
||||
if (!stanza->children) {
|
||||
/* write end if singleton tag */
|
||||
ret = snprintf(ptr, left, "/>");
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
} else {
|
||||
/* this stanza has child stanzas */
|
||||
|
||||
/* write end of start tag */
|
||||
ret = snprintf(ptr, left, ">");
|
||||
if (ret < 0) return -4;
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
|
||||
/* iterate and recurse over child stanzas */
|
||||
child = stanza->children;
|
||||
while (child) {
|
||||
ret = _render_stanza_recursive(child, ptr, left);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
/* write end tag */
|
||||
ret = snprintf(ptr, left, "</%s>", stanza->data);
|
||||
if (ret < 0) return -4;
|
||||
|
||||
_render_update(&written, buflen, ret, &left, &ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
/* render a stanza to text
|
||||
* a buffer is allocated big enough to hold the stanza
|
||||
* and *buf = buffer. the size of buffer filled with data
|
||||
* is returned in *buflen (does not include trailing \0).
|
||||
* the returned buffer contains a trailing \0 so the result is
|
||||
* a valid string.
|
||||
*/
|
||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
||||
char ** const buf,
|
||||
size_t * const buflen)
|
||||
{
|
||||
char *buffer, *tmp;
|
||||
size_t length;
|
||||
int ret;
|
||||
|
||||
/* allocate a default sized buffer and attempt to render */
|
||||
length = 1024;
|
||||
buffer = xmpp_alloc(stanza->ctx, length);
|
||||
if (!buffer) {
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return -5;
|
||||
}
|
||||
|
||||
ret = _render_stanza_recursive(stanza, buffer, length);
|
||||
if (ret < 0) return ret;
|
||||
|
||||
if (ret > length - 1) {
|
||||
tmp = xmpp_realloc(stanza->ctx, buffer, ret + 1);
|
||||
if (!tmp) {
|
||||
xmpp_free(stanza->ctx, buffer);
|
||||
*buf = NULL;
|
||||
*buflen = 0;
|
||||
return -5;
|
||||
}
|
||||
length = ret;
|
||||
buffer = tmp;
|
||||
buffer[length-1] = 0;
|
||||
|
||||
ret = _render_stanza_recursive(stanza, buffer, length);
|
||||
if (ret > length) return -6;
|
||||
}
|
||||
|
||||
length = ret + 1;
|
||||
buffer[length - 1] = 0;
|
||||
|
||||
*buf = buffer;
|
||||
*buflen = length - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_name(xmpp_stanza_t *stanza,
|
||||
const char * const name)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT) return;
|
||||
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
|
||||
stanza->type = XMPP_STANZA_TAG;
|
||||
stanza->data = xmpp_strdup(stanza->ctx, name);
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TEXT) return NULL;
|
||||
return stanza->data;
|
||||
}
|
||||
|
||||
/* convinience function to copy attributes from the xml parser
|
||||
* callback into a stanza. this replaces all previous attributes */
|
||||
void xmpp_stanza_set_attributes(xmpp_stanza_t *stanza,
|
||||
const char * const * const attr)
|
||||
{
|
||||
int i;
|
||||
char *value;
|
||||
|
||||
if (stanza->attributes != NULL)
|
||||
hash_release(stanza->attributes);
|
||||
|
||||
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
if (!stanza->attributes) return;
|
||||
|
||||
for (i = 0; attr[i]; i += 2) {
|
||||
value = xmpp_strdup(stanza->ctx, attr[i + 1]);
|
||||
if (!value) {
|
||||
/* FIXME: memory allocation error */
|
||||
continue;
|
||||
}
|
||||
hash_add(stanza->attributes, attr[i], value);
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
|
||||
const char * const key,
|
||||
const char * const value)
|
||||
{
|
||||
char *val;
|
||||
|
||||
if (stanza->type != XMPP_STANZA_TAG) return;
|
||||
|
||||
if (!stanza->attributes) {
|
||||
stanza->attributes = hash_new(stanza->ctx, 8, xmpp_free);
|
||||
if (!stanza->attributes) return;
|
||||
}
|
||||
|
||||
val = xmpp_strdup(stanza->ctx, value);
|
||||
if (!val) return;
|
||||
|
||||
hash_add(stanza->attributes, key, val);
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_ns(xmpp_stanza_t * const stanza,
|
||||
const char * const ns)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stanza, "xmlns", ns);
|
||||
}
|
||||
|
||||
void xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
|
||||
{
|
||||
xmpp_stanza_t *s;
|
||||
|
||||
/* get a reference to the child */
|
||||
xmpp_stanza_clone(child);
|
||||
|
||||
child->parent = stanza;
|
||||
|
||||
if (!stanza->children)
|
||||
stanza->children = child;
|
||||
else {
|
||||
s = stanza->children;
|
||||
while (s->next) s = s->next;
|
||||
s->next = child;
|
||||
child->prev = s;
|
||||
}
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_text(xmpp_stanza_t *stanza,
|
||||
const char * const text)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TAG) return;
|
||||
|
||||
stanza->type = XMPP_STANZA_TEXT;
|
||||
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
stanza->data = xmpp_strdup(stanza->ctx, text);
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||
const char * const text,
|
||||
const size_t size)
|
||||
{
|
||||
if (stanza->type == XMPP_STANZA_TAG) return;
|
||||
|
||||
stanza->type = XMPP_STANZA_TEXT;
|
||||
|
||||
if (stanza->data) xmpp_free(stanza->ctx, stanza->data);
|
||||
stanza->data = xmpp_alloc(stanza->ctx, size + 1);
|
||||
if (!stanza->data) return;
|
||||
|
||||
memcpy(stanza->data, text, size);
|
||||
stanza->data[size] = 0;
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_id(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return (char *)hash_get(stanza->attributes, "id");
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_ns(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return (char *)hash_get(stanza->attributes, "xmlns");
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_type(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return (char *)hash_get(stanza->attributes, "type");
|
||||
}
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza,
|
||||
const char * const name)
|
||||
{
|
||||
xmpp_stanza_t *child;
|
||||
|
||||
for (child = stanza->children; child; child = child->next) {
|
||||
if (child->type == XMPP_STANZA_TAG &&
|
||||
(strcmp(name, xmpp_stanza_get_name(child)) == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
return stanza->children;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
return stanza->next;
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza)
|
||||
{
|
||||
size_t len, clen;
|
||||
xmpp_stanza_t *child;
|
||||
char *text;
|
||||
|
||||
len = 0;
|
||||
for (child = stanza->children; child; child = child->next)
|
||||
if (child->type == XMPP_STANZA_TEXT)
|
||||
len += strlen(child->data);
|
||||
|
||||
text = (char *)xmpp_alloc(stanza->ctx, len + 1);
|
||||
if (!text) return NULL;
|
||||
|
||||
len = 0;
|
||||
for (child = stanza->children; child; child = child->next)
|
||||
if (child->type == XMPP_STANZA_TEXT) {
|
||||
clen = strlen(child->data);
|
||||
memcpy(&text[len], child->data, clen);
|
||||
len += clen;
|
||||
}
|
||||
|
||||
text[len] = 0;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_id(xmpp_stanza_t * const stanza,
|
||||
const char * const id)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stanza, "id", id);
|
||||
}
|
||||
|
||||
void xmpp_stanza_set_type(xmpp_stanza_t * const stanza,
|
||||
const char * const type)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stanza, "type", type);
|
||||
}
|
||||
|
||||
char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
|
||||
const char * const name)
|
||||
{
|
||||
if (stanza->type != XMPP_STANZA_TAG)
|
||||
return NULL;
|
||||
|
||||
if (!stanza->attributes)
|
||||
return NULL;
|
||||
|
||||
return hash_get(stanza->attributes, name);
|
||||
}
|
||||
70
src/util.c
Normal file
70
src/util.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* util.c
|
||||
** XMPP client library -- various utility functions
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "xmpp.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
|
||||
/** implement our own strdup that uses the ctx allocator */
|
||||
char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s)
|
||||
{
|
||||
size_t len;
|
||||
char *copy;
|
||||
|
||||
len = strlen(s);
|
||||
copy = xmpp_alloc(ctx, len + 1);
|
||||
if (!copy) {
|
||||
xmpp_error(ctx, "xmpp", "failed to allocate required memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(copy, s, len + 1);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
uint64_t time_stamp(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return timeGetTime();
|
||||
#else
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t time_elapsed(uint64_t t1, uint64_t t2)
|
||||
{
|
||||
return (uint64_t)(t2 - t1);
|
||||
}
|
||||
|
||||
void disconnect_mem_error(xmpp_conn_t * const conn)
|
||||
{
|
||||
xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
|
||||
xmpp_disconnect(conn);
|
||||
}
|
||||
26
src/util.h
Normal file
26
src/util.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* util.h
|
||||
** XMPP client library -- various utility functions
|
||||
**
|
||||
** Copyright (C) 2005 OGG, LCC. All rights reserved.
|
||||
**
|
||||
** This software is provided AS-IS with no warranty, either express
|
||||
** or implied.
|
||||
**
|
||||
** This software is distributed under license and may not be copied,
|
||||
** modified or distributed except as expressly authorized under the
|
||||
** terms of the license contained in the file LICENSE.txt in this
|
||||
** distribution.
|
||||
*/
|
||||
|
||||
#ifndef __LIBXMPP_UTIL_H__
|
||||
#define __LIBXMPP_UTIL_H__
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* timing functions */
|
||||
uint64_t time_stamp(void);
|
||||
uint64_t time_elapsed(uint64_t t1, uint64_t t2);
|
||||
|
||||
#endif /* __LIBXMPP_UTIL_H__ */
|
||||
Reference in New Issue
Block a user