bundled libstrophe updated to latest version
authorVictor Kirhenshtein <victor@netxms.org>
Sun, 11 Jun 2017 20:50:50 +0000 (23:50 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Sun, 11 Jun 2017 20:50:50 +0000 (23:50 +0300)
40 files changed:
Makefile.inc.w32
include/strophe.h
src/libstrophe/Makefile.am
src/libstrophe/Makefile.w32
src/libstrophe/auth.c
src/libstrophe/common.h
src/libstrophe/conn.c
src/libstrophe/crypto.c [new file with mode: 0644]
src/libstrophe/ctx.c
src/libstrophe/event.c
src/libstrophe/handler.c
src/libstrophe/hash.c
src/libstrophe/hash.h
src/libstrophe/jid.c
src/libstrophe/md5.c
src/libstrophe/md5.h
src/libstrophe/ostypes.h
src/libstrophe/parser.h
src/libstrophe/parser_expat.c
src/libstrophe/rand.c [new file with mode: 0644]
src/libstrophe/rand.h [new file with mode: 0644]
src/libstrophe/resolver.c [new file with mode: 0644]
src/libstrophe/resolver.h [new file with mode: 0644]
src/libstrophe/sasl.c
src/libstrophe/sasl.h
src/libstrophe/scram.c [new file with mode: 0644]
src/libstrophe/scram.h [new file with mode: 0644]
src/libstrophe/sha1.c [new file with mode: 0644]
src/libstrophe/sha1.h [new file with mode: 0644]
src/libstrophe/snprintf.c [deleted file]
src/libstrophe/snprintf.h [new file with mode: 0644]
src/libstrophe/sock.c
src/libstrophe/sock.h
src/libstrophe/stanza.c
src/libstrophe/tls.h
src/libstrophe/tls_openssl.c
src/libstrophe/util.c
src/libstrophe/util.h
src/libstrophe/uuid.c [new file with mode: 0644]
src/server/core/xmpp.cpp

index 455bd64..10fcd82 100644 (file)
@@ -90,6 +90,28 @@ LDFLAGS = $(LDFLAGS) /SUBSYSTEM:CONSOLE /MACHINE:$(ARCH) /MANIFESTFILE:$(MANIFES
        /LIBPATH:"$(NETXMS_LIBDIR)" \
        /LIBPATH:"$(CURL_BASE)\$(ARCH)\lib" \
        /LIBPATH:"$(OPENSSL_BASE)\$(ARCH)\lib"
+       
+# Common headers
+HEADERS = $(HEADERS) \
+       $(NETXMS_BASE)\include\build.h \
+       $(NETXMS_BASE)\include\geolocation.h \
+       $(NETXMS_BASE)\include\jansson.h \
+       $(NETXMS_BASE)\include\netxms-regex.h \
+       $(NETXMS_BASE)\include\netxms-version.h \
+       $(NETXMS_BASE)\include\netxms_getopt.h \
+       $(NETXMS_BASE)\include\nms_common.h \
+       $(NETXMS_BASE)\include\nms_cscp.h \
+       $(NETXMS_BASE)\include\nms_threads.h \
+       $(NETXMS_BASE)\include\nms_util.h \
+       $(NETXMS_BASE)\include\nxconfig.h \
+       $(NETXMS_BASE)\include\nxcpapi.h \
+       $(NETXMS_BASE)\include\nxcrypto.h \
+       $(NETXMS_BASE)\include\nxqueue.h \
+       $(NETXMS_BASE)\include\nxstat.h \
+       $(NETXMS_BASE)\include\rwlock.h \
+       $(NETXMS_BASE)\include\unicode.h \
+       $(NETXMS_BASE)\include\uthash.h \
+       $(NETXMS_BASE)\include\uuid.h
 
 # Common libraries
 LIBS = $(LIBS) ssleay32.lib libeay32.lib uuid.lib kernel32.lib user32.lib advapi32.lib shell32.lib
@@ -129,7 +151,7 @@ $(BINFILE): objdir $(OBJ) $(RESOBJ)
    @$(LD) /NOLOGO $(LDFLAGS) /out:$(BINFILE) $(OBJ) $(RESOBJ) $(LIBS)
        @rm -f $(MANIFEST)
 
-$(OBJ): $(NETXMS_BASE)\build\netxms-build-tag.h
+$(OBJ): $(NETXMS_BASE)\build\netxms-build-tag.h $(HEADERS)
 
 .PHONY: objdir
 
index e20bd68..e5eddc1 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express or
 **  implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This software is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -131,6 +128,10 @@ xmpp_ctx_t LIBSTROPHE_EXPORTABLE *xmpp_ctx_new(const xmpp_mem_t * const mem,
                             const xmpp_log_t * const log);
 void LIBSTROPHE_EXPORTABLE xmpp_ctx_free(xmpp_ctx_t * const ctx);
 
+/* free some blocks returned by other APIs, for example the
+   buffer you get from xmpp_stanza_to_text */
+void LIBSTROPHE_EXPORTABLE xmpp_free(const xmpp_ctx_t * const ctx, void *p);
+
 struct _xmpp_mem_t {
     void *(*alloc)(const size_t size, void * const userdata);
     void (*free)(void *p, void * const userdata);
@@ -166,13 +167,25 @@ xmpp_log_t LIBSTROPHE_EXPORTABLE *xmpp_get_default_logger(xmpp_log_level_t level
 
 /* connection */
 
+typedef enum {
+    XMPP_LOOP_NOTSTARTED,
+    XMPP_LOOP_RUNNING,
+    XMPP_LOOP_QUIT
+} xmpp_loop_status_t;
+
 /* opaque connection object */
 typedef struct _xmpp_conn_t xmpp_conn_t;
 typedef struct _xmpp_stanza_t xmpp_stanza_t;
 
+/* connection flags */
+#define XMPP_CONN_FLAG_DISABLE_TLS   (1UL << 0)
+#define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1)
+#define XMPP_CONN_FLAG_LEGACY_SSL    (1UL << 2)
+
 /* connect callback */
 typedef enum {
     XMPP_CONN_CONNECT,
+    XMPP_CONN_RAW_CONNECT,
     XMPP_CONN_DISCONNECT,
     XMPP_CONN_FAIL
 } xmpp_conn_event_t;
@@ -220,6 +233,8 @@ xmpp_conn_t LIBSTROPHE_EXPORTABLE *xmpp_conn_new(xmpp_ctx_t * const ctx);
 xmpp_conn_t LIBSTROPHE_EXPORTABLE *xmpp_conn_clone(xmpp_conn_t * const conn);
 int LIBSTROPHE_EXPORTABLE xmpp_conn_release(xmpp_conn_t * const conn);
 
+long LIBSTROPHE_EXPORTABLE xmpp_conn_get_flags(const xmpp_conn_t * const conn);
+int LIBSTROPHE_EXPORTABLE xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags);
 const char LIBSTROPHE_EXPORTABLE *xmpp_conn_get_jid(const xmpp_conn_t * const conn);
 const char LIBSTROPHE_EXPORTABLE *xmpp_conn_get_bound_jid(const xmpp_conn_t * const conn);
 void LIBSTROPHE_EXPORTABLE xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid);
@@ -227,6 +242,8 @@ const char LIBSTROPHE_EXPORTABLE *xmpp_conn_get_pass(const xmpp_conn_t * const c
 void LIBSTROPHE_EXPORTABLE xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass);
 xmpp_ctx_t LIBSTROPHE_EXPORTABLE *xmpp_conn_get_context(xmpp_conn_t * const conn);
 void LIBSTROPHE_EXPORTABLE xmpp_conn_disable_tls(xmpp_conn_t * const conn);
+int LIBSTROPHE_EXPORTABLE xmpp_conn_is_secured(xmpp_conn_t * const conn);
+void LIBSTROPHE_EXPORTABLE xmpp_conn_set_keepalive(xmpp_conn_t * const conn, int timeout, int interval);
 
 int LIBSTROPHE_EXPORTABLE xmpp_connect_client(xmpp_conn_t * const conn, 
                          const char * const altdomain,
@@ -234,9 +251,20 @@ int LIBSTROPHE_EXPORTABLE xmpp_connect_client(xmpp_conn_t * const conn,
                          xmpp_conn_handler callback,
                          void * const userdata);
 
-/*
-int xmpp_connect_component(conn, name)
-*/
+int LIBSTROPHE_EXPORTABLE xmpp_connect_component(xmpp_conn_t * const conn, const char * const server,
+                           unsigned short port, xmpp_conn_handler callback,
+                           void * const userdata);
+
+int LIBSTROPHE_EXPORTABLE xmpp_connect_raw(xmpp_conn_t * const conn,
+                     const char * const altdomain,
+                     unsigned short altport,
+                     xmpp_conn_handler callback,
+                     void * const userdata);
+int LIBSTROPHE_EXPORTABLE xmpp_conn_open_stream_default(xmpp_conn_t * const conn);
+int LIBSTROPHE_EXPORTABLE xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes,
+                          size_t attributes_len);
+int LIBSTROPHE_EXPORTABLE xmpp_conn_tls_start(xmpp_conn_t * const conn);
+
 void LIBSTROPHE_EXPORTABLE xmpp_disconnect(xmpp_conn_t * const conn);
 
 void LIBSTROPHE_EXPORTABLE xmpp_send(xmpp_conn_t * const conn,
@@ -288,28 +316,24 @@ void LIBSTROPHE_EXPORTABLE xmpp_id_handler_delete(xmpp_conn_t * const conn,
 void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
 */
 
-/** stanzas **/
+/* stanzas */
 
-/** allocate an initialize a blank stanza */
+/* allocate and initialize a blank stanza */
 xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_new(xmpp_ctx_t *ctx);
 
-/** clone a stanza */
+/* clone a stanza */
 xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_clone(xmpp_stanza_t * const stanza);
 
-/** copies a stanza and all children */
+/* copies a stanza and all children */
 xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_copy(const xmpp_stanza_t * const stanza);
 
-/** free a stanza object and it's contents */
+/* free a stanza object and it's contents */
 int LIBSTROPHE_EXPORTABLE xmpp_stanza_release(xmpp_stanza_t * const stanza);
 
-/** free some blocks returned by other APIs, for example the
-    buffer you get from xmpp_stanza_to_text **/
-void LIBSTROPHE_EXPORTABLE xmpp_free(const xmpp_ctx_t * const ctx, void *p);
-
 int LIBSTROPHE_EXPORTABLE xmpp_stanza_is_text(xmpp_stanza_t * const stanza);
 int LIBSTROPHE_EXPORTABLE xmpp_stanza_is_tag(xmpp_stanza_t * const stanza);
 
-/** marshall a stanza into text for transmission or display **/
+/* marshall a stanza into text for transmission or display */
 int LIBSTROPHE_EXPORTABLE xmpp_stanza_to_text(xmpp_stanza_t *stanza, 
                        char ** const buf, size_t * const buflen);
 
@@ -319,19 +343,19 @@ xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_child_by_name(xmpp_stanza_t
 xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_child_by_ns(xmpp_stanza_t * const stanza,
                                           const char * const ns);
 xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_next(xmpp_stanza_t * const stanza);
-char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
-                               const char * const name);
-char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_ns(xmpp_stanza_t * const stanza);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza, xmpp_stanza_t *child, int do_clone);
+
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
+                                     const char * const name);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza,
+                              const char **attr, int attrlen);
 /* concatenate all child text nodes.  this function
  * returns a string that must be freed by the caller */
-
 char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_text(xmpp_stanza_t * const stanza);
-char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza);
-char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
-
-int LIBSTROPHE_EXPORTABLE xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
-int LIBSTROPHE_EXPORTABLE xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza, xmpp_stanza_t *child, int do_clone);
-int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza);
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
 /* set_attribute adds/replaces attributes */
 int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza, 
                              const char * const key,
@@ -343,48 +367,84 @@ int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_text(xmpp_stanza_t *stanza,
 int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
                                   const char * const text, 
                                   const size_t size);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_del_attribute(xmpp_stanza_t * const stanza,
+                              const char * const name);
 
 /* common stanza helpers */
-char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_type(xmpp_stanza_t * const stanza);
-char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_id(xmpp_stanza_t * const stanza);
-int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_id(xmpp_stanza_t * const stanza, 
-                      const char * const id);
-int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_type(xmpp_stanza_t * const stanza, 
-                        const char * const type);
-
-/* unimplemented
-int xmpp_stanza_set_to();
-int xmpp_stanza_set_from();
-*/
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_ns(xmpp_stanza_t * const stanza);
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_type(xmpp_stanza_t * const stanza);
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_id(xmpp_stanza_t * const stanza);
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_to(xmpp_stanza_t * const stanza);
+const char LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_from(xmpp_stanza_t * const stanza);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_id(xmpp_stanza_t * const stanza, const char * const id);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_type(xmpp_stanza_t * const stanza, const char * const type);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_to(xmpp_stanza_t * const stanza, const char * const to);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_from(xmpp_stanza_t * const stanza, const char * const from);
 
 /* allocate and initialize a stanza in reply to another */
-/* unimplemented
-xmpp_stanza_t *xmpp_stanza_reply(const xmpp_stanza_t *stanza);
-*/
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_reply(xmpp_stanza_t * const stanza);
 
 /* stanza subclasses */
-/* unimplemented
-void xmpp_message_new();
-void xmpp_message_get_body();
-void xmpp_message_set_body();
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_message_new(xmpp_ctx_t *ctx, const char * const type,
+                                const char * const to, const char * const id);
+char LIBSTROPHE_EXPORTABLE *xmpp_message_get_body(xmpp_stanza_t *msg);
+int LIBSTROPHE_EXPORTABLE xmpp_message_set_body(xmpp_stanza_t *msg, const char * const text);
 
-void xmpp_iq_new();
-void xmpp_presence_new();
-*/
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_iq_new(xmpp_ctx_t *ctx, const char * const type,
+                           const char * const id);
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_presence_new(xmpp_ctx_t *ctx);
 
-/** event loop **/
-typedef enum {
-    XMPP_LOOP_NOTSTARTED,
-    XMPP_LOOP_RUNNING,
-    XMPP_LOOP_QUIT
-} xmpp_loop_status_t;
+/* jid */
+
+/* these return new strings that must be xmpp_free()'d */
+char LIBSTROPHE_EXPORTABLE *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
+                                    const char *domain,
+                                    const char *resource);
+char LIBSTROPHE_EXPORTABLE *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
+char LIBSTROPHE_EXPORTABLE *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
+char LIBSTROPHE_EXPORTABLE *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
+char LIBSTROPHE_EXPORTABLE *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
+
+/* event loop */
 
 void LIBSTROPHE_EXPORTABLE xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long  timeout);
 void LIBSTROPHE_EXPORTABLE xmpp_run(xmpp_ctx_t *ctx);
 void LIBSTROPHE_EXPORTABLE xmpp_stop(xmpp_ctx_t *ctx);
+
+/* UUID */
+
+char LIBSTROPHE_EXPORTABLE *xmpp_uuid_gen(xmpp_ctx_t *ctx);
+
+/* SHA1 */
+
+/** @def XMPP_SHA1_DIGEST_SIZE
+ *  Size of the SHA1 message digest.
+ */
+#define XMPP_SHA1_DIGEST_SIZE 20
+
+typedef struct _xmpp_sha1_t xmpp_sha1_t;
+
+char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
+
+xmpp_sha1_t LIBSTROPHE_EXPORTABLE *xmpp_sha1_new(xmpp_ctx_t *ctx);
+void LIBSTROPHE_EXPORTABLE xmpp_sha1_free(xmpp_sha1_t *sha1);
+void LIBSTROPHE_EXPORTABLE xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len);
+void LIBSTROPHE_EXPORTABLE xmpp_sha1_final(xmpp_sha1_t *sha1);
+char LIBSTROPHE_EXPORTABLE *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen);
+char LIBSTROPHE_EXPORTABLE *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1);
+void LIBSTROPHE_EXPORTABLE xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);
+
+/* Base64 */
+
+char LIBSTROPHE_EXPORTABLE *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
+char LIBSTROPHE_EXPORTABLE *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len);
+void LIBSTROPHE_EXPORTABLE xmpp_base64_decode_bin(xmpp_ctx_t *ctx, const char *base64, size_t len,
+                            unsigned char **out, size_t *outlen);
+
 void LIBSTROPHE_EXPORTABLE xmpp_set_loop_status(xmpp_ctx_t *ctx, xmpp_loop_status_t status);
 xmpp_loop_status_t LIBSTROPHE_EXPORTABLE xmpp_get_loop_status(xmpp_ctx_t *ctx);
-
+                                                       
 #ifdef __cplusplus
 }
 #endif
index 087c5ce..7bba5a3 100644 (file)
@@ -1,7 +1,7 @@
 lib_LTLIBRARIES = libstrophe.la
-libstrophe_la_SOURCES = auth.c conn.c ctx.c event.c handler.c hash.c jid.c \
-                        md5.c parser_expat.c sasl.c snprintf.c sock.c \
-                        stanza.c tls_openssl.c util.c
+libstrophe_la_SOURCES = auth.c conn.c crypto.c ctx.c event.c handler.c hash.c jid.c \
+                        md5.c parser_expat.c rand.c resolver.c sasl.c scram.c sha1.c \
+                        sock.c stanza.c tls_openssl.c util.c uuid.c
 libstrophe_la_CPPFLAGS = -I@top_srcdir@/include
 libstrophe_la_LDFLAGS = -version-info $(NETXMS_LIBRARY_VERSION)
 libstrophe_la_LIBADD =
@@ -19,7 +19,12 @@ EXTRA_DIST = \
        md5.h \
        ostypes.h \
        parser.h \
+       rand.h \
+       resolver.h \
        sasl.h \
+       scram.h \
+       sha1.h \
+       snprintf.h \
        sock.h \
        tls.h \
        util.h
index a404f46..4b35d3d 100644 (file)
@@ -1,8 +1,13 @@
 TARGET = libstrophe.dll
 TYPE = dll
-SOURCES = auth.c conn.c ctx.c event.c handler.c hash.c jid.c \
-       md5.c parser_expat.c sasl.c snprintf.c sock.c \
-       stanza.c tls_openssl.c util.c
+SOURCES = auth.c conn.c crypto.c ctx.c event.c handler.c hash.c jid.c \
+          md5.c parser_expat.c rand.c resolver.c sasl.c scram.c sha1.c \
+          sock.c stanza.c tls_openssl.c util.c uuid.c
+HEADERS = $(MAKEDIR)\common.h $(MAKEDIR)\hash.h $(MAKEDIR)\md5.h \
+          $(MAKEDIR)\ostypes.h $(MAKEDIR)\parser.h $(MAKEDIR)\rand.h \
+          $(MAKEDIR)\resolver.h $(MAKEDIR)\sasl.h $(MAKEDIR)\scram.h \
+          $(MAKEDIR)\sha1.h $(MAKEDIR)\snprintf.h $(MAKEDIR)\sock.h \
+          $(MAKEDIR)\tls.h $(MAKEDIR)\util.h
 
 CPPFLAGS = /I$(NETXMS_BASE)\src\libexpat\libexpat /DLIBSTROPHE_EXPORTS
 LIBS = libexpat.lib ws2_32.lib
index 7b0ab1a..6582311 100644 (file)
@@ -1,18 +1,15 @@
 /* auth.c
 ** strophe XMPP client library -- auth functions and handlers
 **
-** Copyright (C) 2005-2009 Collecta, Inc. 
+** Copyright (C) 2005-2009 Collecta, Inc.
 **
 **  This software is provided AS-IS with no warranty, either express or
 **  implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+** This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
-/** @file 
+/** @file
  *  Authentication function and handlers.
  */
 
  */
 #define LEGACY_TIMEOUT 15000 /* 15 seconds */
 #endif
+#ifndef HANDSHAKE_TIMEOUT
+/** @def HANDSHAKE_TIMEOUT
+ *  Time to wait for component authentication to complete
+ */
+#define HANDSHAKE_TIMEOUT 15000 /* 15 seconds */
+#endif
 
 static void _auth(xmpp_conn_t * const conn);
 static void _handle_open_sasl(xmpp_conn_t * const conn);
+
+static int _handle_component_auth(xmpp_conn_t * const conn);
+static int _handle_component_hs_response(xmpp_conn_t * const conn,
+            xmpp_stanza_t * const stanza,
+            void * const userdata);
+
 static int _handle_missing_legacy(xmpp_conn_t * const conn,
                                  void * const userdata);
 static int _handle_legacy(xmpp_conn_t * const conn,
@@ -71,6 +80,10 @@ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
 static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
                        xmpp_stanza_t * const stanza,
                        void * const userdata);
+static int _handle_scram_sha1_challenge(xmpp_conn_t * const conn,
+                       xmpp_stanza_t * const stanza,
+                       void * const userdata);
+static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn);
 
 static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
                                         void * const userdata);
@@ -84,6 +97,8 @@ static int _handle_session(xmpp_conn_t * const conn,
                           void * const userdata);
 static int _handle_missing_session(xmpp_conn_t * const conn,
                                   void * const userdata);
+static int _handle_missing_handshake(xmpp_conn_t * const conn,
+                                     void * const userdata);
 
 /* stream:error handler */
 static int _handle_error(xmpp_conn_t * const conn,
@@ -91,12 +106,12 @@ static int _handle_error(xmpp_conn_t * const conn,
                         void * const userdata)
 {
     xmpp_stanza_t *child;
-    char *name;
+    const char *name;
 
     /* free old stream error if it's still there */
     if (conn->stream_error) {
        xmpp_stanza_release(conn->stream_error->stanza);
-       if (conn->stream_error->text) 
+       if (conn->stream_error->text)
            xmpp_free(conn->ctx, conn->stream_error->text);
        xmpp_free(conn->ctx, conn->stream_error);
     }
@@ -110,7 +125,7 @@ static int _handle_error(xmpp_conn_t * const conn,
     if (conn->stream_error) {
        child = xmpp_stanza_get_children(stanza);
        do {
-           char *ns = NULL;
+           const char *ns = NULL;
 
            if (child) {
                ns = xmpp_stanza_get_ns(child);
@@ -217,14 +232,16 @@ static int _handle_features(xmpp_conn_t * const conn,
     /* 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; 
+       for (mech = xmpp_stanza_get_children(child); mech;
             mech = xmpp_stanza_get_next(mech)) {
-           if (strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
+           if (xmpp_stanza_get_name(mech) && 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;
+                else if (strcasecmp(text, "SCRAM-SHA-1") == 0)
+                    conn->sasl_support |= SASL_MASK_SCRAMSHA1;
                else if (strcasecmp(text, "ANONYMOUS") == 0)
                    conn->sasl_support |= SASL_MASK_ANONYMOUS;
 
@@ -234,7 +251,7 @@ static int _handle_features(xmpp_conn_t * const conn,
     }
 
     _auth(conn);
+
     return 0;
 }
 
@@ -257,33 +274,21 @@ static int _handle_proceedtls_default(xmpp_conn_t * const conn,
                              xmpp_stanza_t * const stanza,
                              void * const userdata)
 {
-    char *name;
+    const char *name;
+
     name = xmpp_stanza_get_name(stanza);
-    xmpp_debug(conn->ctx, "xmpp", 
-       "handle proceedtls called for %s", name);
+    xmpp_debug(conn->ctx, "xmpp", "handle proceedtls called for %s", name);
 
     if (strcmp(name, "proceed") == 0) {
         xmpp_debug(conn->ctx, "xmpp", "proceeding with TLS");
 
-       conn->tls = tls_new(conn->ctx, conn->sock);
-
-       if (!tls_start(conn->tls))
-       {
-           xmpp_debug(conn->ctx, "xmpp", "Couldn't start TLS! error %d", tls_error(conn->tls));
-           tls_free(conn->tls);
-           conn->tls = NULL;
-           conn->tls_failed = 1;
-       
-           /* failed tls spoils the connection, so disconnect */
-           xmpp_disconnect(conn);
-       }
-       else
-       {
-            conn->secured = 1;
+        if (conn_tls_start(conn) == 0) {
             conn_prepare_reset(conn, auth_handle_open);
-
-           conn_open_stream(conn);
-       }
+            conn_open_stream(conn);
+        } else {
+            /* failed tls spoils the connection, so disconnect */
+            xmpp_disconnect(conn);
+        }
     }
 
     return 0;
@@ -293,20 +298,20 @@ static int _handle_sasl_result(xmpp_conn_t * const conn,
                               xmpp_stanza_t * const stanza,
                               void * const userdata)
 {
-    char *name;
+    const char *name;
 
     name = xmpp_stanza_get_name(stanza);
 
     /* the server should send a <success> or <failure> stanza */
     if (strcmp(name, "failure") == 0) {
-       xmpp_debug(conn->ctx, "xmpp", "SASL %s auth failed", 
+       xmpp_debug(conn->ctx, "xmpp", "SASL %s auth failed",
                   (char *)userdata);
-       
+
        /* 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 %s auth successful", 
+       xmpp_debug(conn->ctx, "xmpp", "SASL %s auth successful",
                   (char *)userdata);
 
        /* reset parser */
@@ -332,7 +337,7 @@ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
     char *text;
     char *response;
     xmpp_stanza_t *auth, *authdata;
-    char *name;
+    const char *name;
 
     name = xmpp_stanza_get_name(stanza);
     xmpp_debug(conn->ctx, "xmpp",\
@@ -351,10 +356,10 @@ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
        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);
@@ -367,7 +372,7 @@ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
        xmpp_stanza_add_child(auth, authdata);
        xmpp_stanza_release(authdata);
 
-       handler_add(conn, _handle_digestmd5_rspauth, 
+       handler_add(conn, _handle_digestmd5_rspauth,
                    XMPP_NS_SASL, NULL, NULL, NULL);
 
        xmpp_send(conn, auth);
@@ -387,7 +392,7 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
                              void * const userdata)
 {
     xmpp_stanza_t *auth;
-    char *name;
+    const char *name;
 
     name = xmpp_stanza_get_name(stanza);
     xmpp_debug(conn->ctx, "xmpp",
@@ -400,7 +405,7 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
        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);
@@ -412,6 +417,96 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
     return 1;
 }
 
+/* handle the challenge phase of SCRAM-SHA-1 auth */
+static int _handle_scram_sha1_challenge(xmpp_conn_t * const conn,
+                                       xmpp_stanza_t * const stanza,
+                                       void * const userdata)
+{
+    char *text;
+    char *response;
+    xmpp_stanza_t *auth, *authdata;
+    const char *name;
+    char *challenge;
+    char *scram_init = (char *)userdata;
+
+    name = xmpp_stanza_get_name(stanza);
+    xmpp_debug(conn->ctx, "xmpp",
+               "handle SCRAM-SHA-1 (challenge) called for %s", name);
+
+    if (strcmp(name, "challenge") == 0) {
+        text = xmpp_stanza_get_text(stanza);
+        if (!text)
+            goto err;
+
+        challenge = xmpp_base64_decode_str(conn->ctx, text, strlen(text));
+        xmpp_free(conn->ctx, text);
+        if (!challenge)
+            goto err;
+
+        response = sasl_scram_sha1(conn->ctx, challenge, scram_init,
+                                   conn->jid, conn->pass);
+        xmpp_free(conn->ctx, challenge);
+        if (!response)
+            goto err;
+
+        auth = xmpp_stanza_new(conn->ctx);
+        if (!auth)
+            goto err_free_response;
+        xmpp_stanza_set_name(auth, "response");
+        xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
+
+        authdata = xmpp_stanza_new(conn->ctx);
+        if (!authdata)
+            goto err_release_auth;
+        xmpp_stanza_set_text(authdata, response);
+        xmpp_free(conn->ctx, response);
+
+        xmpp_stanza_add_child(auth, authdata);
+        xmpp_stanza_release(authdata);
+
+        xmpp_send(conn, auth);
+        xmpp_stanza_release(auth);
+
+    } else {
+        xmpp_free(conn->ctx, scram_init);
+        return _handle_sasl_result(conn, stanza, "SCRAM-SHA-1");
+    }
+
+    return 1;
+
+err_release_auth:
+    xmpp_stanza_release(auth);
+err_free_response:
+    xmpp_free(conn->ctx, response);
+err:
+    xmpp_free(conn->ctx, scram_init);
+    disconnect_mem_error(conn);
+    return 0;
+}
+
+static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn)
+{
+    xmpp_ctx_t *ctx = conn->ctx;
+    size_t message_len;
+    char *node;
+    char *message;
+    char nonce[32];
+
+    node = xmpp_jid_node(ctx, conn->jid);
+    if (!node) {
+        return NULL;
+    }
+    xmpp_rand_nonce(ctx->rand, nonce, sizeof(nonce));
+    message_len = strlen(node) + strlen(nonce) + 8 + 1;
+    message = xmpp_alloc(ctx, message_len);
+    if (message) {
+        xmpp_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce);
+    }
+    xmpp_free(ctx, node);
+
+    return message;
+}
+
 static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn)
 {
     xmpp_stanza_t *starttls;
@@ -422,7 +517,7 @@ static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn)
        xmpp_stanza_set_name(starttls, "starttls");
        xmpp_stanza_set_ns(starttls, XMPP_NS_TLS);
     }
-    
+
     return starttls;
 }
 
@@ -438,19 +533,20 @@ static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
        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, 
+/* 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 
+ * method fails
  */
 static void _auth(xmpp_conn_t * const conn)
 {
     xmpp_stanza_t *auth, *authdata, *query, *child, *iq;
     char *str, *authid;
+    char *scram_init;
     int anonjid;
 
     /* if there is no node in conn->jid, we assume anonymous connect */
@@ -462,19 +558,15 @@ static void _auth(xmpp_conn_t * const conn)
        anonjid = 0;
     }
 
-    if (conn->tls_support)
-    {
+    if (conn->tls_support) {
        tls_t *tls = tls_new(conn->ctx, conn->sock);
 
        /* If we couldn't init tls, it isn't there, so go on */
-       if (!tls)
-       {
+       if (!tls) {
            conn->tls_support = 0;
            _auth(conn);
            return;
-       }
-       else
-       {
+       } else {
            tls_free(tls);
        }
 
@@ -485,7 +577,7 @@ static void _auth(xmpp_conn_t * const conn)
            return;
        }
 
-       handler_add(conn, _handle_proceedtls_default, 
+       handler_add(conn, _handle_proceedtls_default,
                    XMPP_NS_TLS, NULL, NULL, NULL);
 
        xmpp_send(conn, auth);
@@ -493,7 +585,18 @@ static void _auth(xmpp_conn_t * const conn)
 
        /* TLS was tried, unset flag */
        conn->tls_support = 0;
-    } else if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
+       /* _auth() will be called later */
+       return;
+    }
+
+    if (conn->tls_mandatory && !xmpp_conn_is_secured(conn)) {
+        xmpp_error(conn->ctx, "xmpp", "TLS is not supported, but set as "
+                                      "mandatory for this connection");
+        conn_disconnect(conn);
+        return;
+    }
+
+    if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
        /* some crap here */
        auth = _make_sasl_auth(conn, "ANONYMOUS");
        if (!auth) {
@@ -510,9 +613,54 @@ static void _auth(xmpp_conn_t * const conn)
        /* SASL ANONYMOUS was tried, unset flag */
        conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
     } else if (anonjid) {
-       xmpp_error(conn->ctx, "auth", 
+       xmpp_error(conn->ctx, "auth",
                   "No node in JID, and SASL ANONYMOUS unsupported.");
        xmpp_disconnect(conn);
+    } else if (conn->sasl_support & SASL_MASK_SCRAMSHA1) {
+        auth = _make_sasl_auth(conn, "SCRAM-SHA-1");
+        if (!auth) {
+            disconnect_mem_error(conn);
+            return;
+        }
+
+        /* don't free scram_init on success */
+        scram_init = _make_scram_sha1_init_msg(conn);
+        if (!scram_init) {
+            xmpp_stanza_release(auth);
+            disconnect_mem_error(conn);
+            return;
+        }
+
+        str = xmpp_base64_encode(conn->ctx, (unsigned char *)scram_init,
+                                 strlen(scram_init));
+        if (!str) {
+            xmpp_free(conn->ctx, scram_init);
+            xmpp_stanza_release(auth);
+            disconnect_mem_error(conn);
+            return;
+        }
+
+        authdata = xmpp_stanza_new(conn->ctx);
+        if (!authdata) {
+            xmpp_free(conn->ctx, str);
+            xmpp_free(conn->ctx, scram_init);
+            xmpp_stanza_release(auth);
+            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_scram_sha1_challenge,
+                    XMPP_NS_SASL, NULL, NULL, (void *)scram_init);
+
+        xmpp_send(conn, auth);
+        xmpp_stanza_release(auth);
+
+        /* SASL SCRAM-SHA-1 was tried, unset flag */
+        conn->sasl_support &= ~SASL_MASK_SCRAMSHA1;
     } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
        auth = _make_sasl_auth(conn, "DIGEST-MD5");
        if (!auth) {
@@ -521,7 +669,7 @@ static void _auth(xmpp_conn_t * const conn)
 
        }
 
-       handler_add(conn, _handle_digestmd5_challenge, 
+       handler_add(conn, _handle_digestmd5_challenge,
                    XMPP_NS_SASL, NULL, NULL, NULL);
 
        xmpp_send(conn, auth);
@@ -539,7 +687,7 @@ static void _auth(xmpp_conn_t * const conn)
        if (!authdata) {
            disconnect_mem_error(conn);
            return;
-       }       
+       }
        authid = _get_authid(conn);
        if (!authid) {
            disconnect_mem_error(conn);
@@ -567,7 +715,7 @@ static void _auth(xmpp_conn_t * const conn)
        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);
@@ -653,7 +801,7 @@ static void _auth(xmpp_conn_t * const conn)
        } else {
            xmpp_stanza_release(authdata);
            xmpp_stanza_release(iq);
-           xmpp_error(conn->ctx, "auth", 
+           xmpp_error(conn->ctx, "auth",
                       "Cannot authenticate without resource");
            xmpp_disconnect(conn);
            return;
@@ -662,7 +810,7 @@ static void _auth(xmpp_conn_t * const conn)
        xmpp_stanza_release(authdata);
 
        handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
-       handler_add_timed(conn, _handle_missing_legacy, 
+       handler_add_timed(conn, _handle_missing_legacy,
                          LEGACY_TIMEOUT, NULL);
 
        xmpp_send(conn, iq);
@@ -674,7 +822,7 @@ static void _auth(xmpp_conn_t * const conn)
 /** Set up handlers at stream start.
  *  This function is called internally to Strophe for handling the opening
  *  of an XMPP stream.  It's called by the parser when a stream is opened
- *  or reset, and adds the initial handlers for <stream:error/> and 
+ *  or reset, and adds the initial handlers for <stream:error/> and
  *  <stream:features/>.  This function is not intended for use outside
  *  of Strophe.
  *
@@ -736,7 +884,7 @@ static int _handle_features_sasl(xmpp_conn_t * const conn,
     /* if bind is required, go ahead and start it */
     if (conn->bind_required) {
        /* bind resource */
-       
+
        /* setup response handlers */
        handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL);
        handler_add_timed(conn, _handle_missing_bind,
@@ -768,7 +916,7 @@ static int _handle_features_sasl(xmpp_conn_t * const conn,
            resource = NULL;
        }
 
-       /* if we have a resource to request, do it. otherwise the 
+       /* if we have a resource to request, do it. otherwise the
           server will assign us one */
        if (resource) {
            res = xmpp_stanza_new(conn->ctx);
@@ -819,12 +967,12 @@ static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
     xmpp_disconnect(conn);
     return 0;
 }
-                                         
+
 static int _handle_bind(xmpp_conn_t * const conn,
                        xmpp_stanza_t * const stanza,
                        void * const userdata)
 {
-    char *type;
+    const char *type;
     xmpp_stanza_t *iq, *session;
 
     /* delete missing bind handler */
@@ -851,7 +999,7 @@ static int _handle_bind(xmpp_conn_t * const conn,
        if (conn->session_required) {
            /* setup response handlers */
            handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
-           handler_add_timed(conn, _handle_missing_session, 
+           handler_add_timed(conn, _handle_missing_session,
                              SESSION_TIMEOUT, NULL);
 
            /* send session request */
@@ -882,9 +1030,9 @@ static int _handle_bind(xmpp_conn_t * const conn,
            xmpp_stanza_release(iq);
        } else {
            conn->authenticated = 1;
-          
+
            /* call connection handler */
-           conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, 
+           conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL,
                               conn->userdata);
        }
     } else {
@@ -907,7 +1055,7 @@ static int _handle_session(xmpp_conn_t * const conn,
                           xmpp_stanza_t * const stanza,
                           void * const userdata)
 {
-    char *type;
+    const char *type;
 
     /* delete missing session handler */
     xmpp_timed_handler_delete(conn, _handle_missing_session);
@@ -921,7 +1069,7 @@ static int _handle_session(xmpp_conn_t * const conn,
        xmpp_debug(conn->ctx, "xmpp", "Session establishment successful.");
 
        conn->authenticated = 1;
-       
+
        /* call connection handler */
        conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
     } else {
@@ -944,7 +1092,8 @@ static int _handle_legacy(xmpp_conn_t * const conn,
                          xmpp_stanza_t * const stanza,
                          void * const userdata)
 {
-    char *type, *name;
+    const char *type;
+    const char *name;
 
     /* delete missing handler */
     xmpp_timed_handler_delete(conn, _handle_missing_legacy);
@@ -984,3 +1133,120 @@ static int _handle_missing_legacy(xmpp_conn_t * const conn,
     return 0;
 }
 
+void auth_handle_component_open(xmpp_conn_t * const conn)
+{
+    int rc;
+
+    /* reset all timed handlers */
+    handler_reset_timed(conn, 0);
+
+    handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL);
+    handler_add(conn, _handle_component_hs_response, NULL,
+                "handshake", NULL, NULL);
+    handler_add_timed(conn, _handle_missing_handshake, HANDSHAKE_TIMEOUT, NULL);
+
+    rc = _handle_component_auth(conn);
+    if (rc != 0) {
+        xmpp_error(conn->ctx, "auth", "Component authentication failed.");
+        xmpp_disconnect(conn);
+    }
+}
+
+/* Will compute SHA1 and authenticate the component to the server */
+int _handle_component_auth(xmpp_conn_t * const conn)
+{
+    uint8_t md_value[SHA1_DIGEST_SIZE];
+    SHA1_CTX mdctx;
+    char *digest;
+    size_t i;
+
+    if (conn->stream_id == NULL) {
+        xmpp_error(conn->ctx, "auth", "Received no stream id from the server.");
+        return XMPP_EINT;
+    }
+
+    /*¬†Feed the session id and passphrase to the algorithm.
+     * We need to compute SHA1(session_id + passphrase)
+     */
+    crypto_SHA1_Init(&mdctx);
+    crypto_SHA1_Update(&mdctx, (uint8_t*)conn->stream_id,
+                       strlen(conn->stream_id));
+    crypto_SHA1_Update(&mdctx, (uint8_t*)conn->pass, strlen(conn->pass));
+    crypto_SHA1_Final(&mdctx, md_value);
+
+    digest = xmpp_alloc(conn->ctx, 2*sizeof(md_value)+1);
+    if (digest) {
+        /* convert the digest into string representation */
+        for (i = 0; i < sizeof(md_value); i++)
+            xmpp_snprintf(digest+i*2, 3, "%02x", md_value[i]);
+        digest[2*sizeof(md_value)] = '\0';
+
+        xmpp_debug(conn->ctx, "auth", "Digest: %s, len: %d",
+                   digest, strlen(digest));
+
+        /* Send the digest to the server */
+        xmpp_send_raw_string(conn, "<handshake xmlns='%s'>%s</handshake>",
+                             XMPP_NS_COMPONENT, digest);
+        xmpp_debug(conn->ctx, "auth", "Sent component handshake to the server.");
+        xmpp_free(conn->ctx, digest);
+    } else {
+        xmpp_debug(conn->ctx, "auth", "Couldn't allocate memory for component "\
+                                      "handshake digest.");
+        return XMPP_EMEM;
+    }
+
+    return 0;
+}
+
+/* Check if the received stanza is <handshake/> and set auth to true
+ * and fire connection handler.
+ */
+int _handle_component_hs_response(xmpp_conn_t * const conn,
+            xmpp_stanza_t * const stanza,
+            void * const userdata)
+{
+    const char *name;
+
+    xmpp_timed_handler_delete(conn, _handle_missing_handshake);
+
+    name = xmpp_stanza_get_name(stanza);
+    if (strcmp(name, "handshake") != 0) {
+        char *msg;
+        size_t msg_size;
+        xmpp_stanza_to_text(stanza, &msg, &msg_size);
+        if (msg) {
+            xmpp_debug(conn->ctx, "auth", "Handshake failed: %s", msg);
+            xmpp_free(conn->ctx, msg);
+        }
+        xmpp_disconnect(conn);
+        return XMPP_EINT;
+    } else {
+        conn->authenticated = 1;
+        conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
+    }
+
+    /* We don't need this handler anymore, return 0 so it can be deleted
+     * from the list of handlers.
+     */
+    return 0;
+}
+
+int _handle_missing_handshake(xmpp_conn_t * const conn, void * const userdata)
+{
+    xmpp_error(conn->ctx, "xmpp", "Server did not reply to handshake request.");
+    xmpp_disconnect(conn);
+    return 0;
+}
+
+void auth_handle_open_raw(xmpp_conn_t * const conn)
+{
+    handler_reset_timed(conn, 0);
+    /* user handlers are not called before authentication is completed. */
+    conn->authenticated = 1;
+    conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
+}
+
+void auth_handle_open_stub(xmpp_conn_t * const conn)
+{
+    xmpp_warn(conn->ctx, "auth", "Stub callback is called.");
+}
index 5d63aae..76a15a3 100644 (file)
@@ -1,15 +1,12 @@
 /* common.h
 ** strophe XMPP client library -- internal common structures
 **
-** Copyright (C) 2005-2009 Collecta, Inc. 
+** Copyright (C) 2005-2009 Collecta, Inc.
 **
 **  This software is provided AS-IS with no warranty, either express or
 **  implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
 #ifndef __LIBSTROPHE_COMMON_H__
 #define __LIBSTROPHE_COMMON_H__
 
-#include <stdio.h>
-#include <stdarg.h>
 #ifndef _WIN32
 #include <stdint.h>
 #endif
 
-
-#include "strophe.h"
+#include <strophe.h>
 #include "sock.h"
 #include "tls.h"
 #include "hash.h"
 #include "util.h"
 #include "parser.h"
+#include "rand.h"
+#include "sha1.h"
+#include "snprintf.h"
 
 /** run-time context **/
 
@@ -44,6 +41,7 @@ struct _xmpp_ctx_t {
     const xmpp_mem_t *mem;
     const xmpp_log_t *log;
 
+    xmpp_rand_t *rand;
     xmpp_loop_status_t loop_status;
     xmpp_connlist_t *connlist;
 };
@@ -52,43 +50,32 @@ struct _xmpp_ctx_t {
 /* convenience functions for accessing the context */
 void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size);
 void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p, 
-                  const size_t size);
+                   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);
+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,
-               ...);
+                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,
-               ...);
+                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,
-               ...);
+                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);
-
+                const char * const area,
+                const char * const fmt,
+                ...);
 
 /** connection **/
 
@@ -115,32 +102,41 @@ struct _xmpp_handlist_t {
     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 */
+                  * 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;
-       } timed;
-       /* id handlers */
-       struct {
-           char *id;
-       } id;
-       /* normal handlers */
-       struct {
-           char *ns;
-           char *name;
-           char *type;
-       } normal;
-    } data;
+        /* 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
+#define MAX_DOMAIN_LEN 256
+
+#define SASL_MASK_PLAIN     (1 << 0)
+#define SASL_MASK_DIGESTMD5 (1 << 1)
+#define SASL_MASK_ANONYMOUS (1 << 2)
+#define SASL_MASK_SCRAMSHA1 (1 << 3)
+
+enum {
+    XMPP_PORT_CLIENT = 5222,
+    XMPP_PORT_CLIENT_LEGACY_SSL = 5223,
+    XMPP_PORT_COMPONENT = 5347,
+};
 
 typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn);
 
@@ -148,19 +144,25 @@ struct _xmpp_conn_t {
     unsigned int ref;
     xmpp_ctx_t *ctx;
     xmpp_conn_type_t type;
+    int is_raw;
 
     xmpp_conn_state_t state;
     uint64_t timeout_stamp;
     int error;
     xmpp_stream_error_t *stream_error;
+
     sock_t sock;
-    tls_t *tls;
+    int ka_timeout; /* TCP keepalive timeout */
+    int ka_interval; /* TCP keepalive interval */
 
+    tls_t *tls;
     int tls_support;
     int tls_disabled;
+    int tls_mandatory;
+    int tls_legacy_ssl;
     int tls_failed; /* set when tls fails, so we don't try again */
     int sasl_support; /* if true, field is a bitfield of supported 
-                        mechanisms */ 
+                         mechanisms */
     int secured; /* set when stream is secured with TLS */
 
     /* if server returns <bind/> or <session/> we must do them */
@@ -169,8 +171,6 @@ struct _xmpp_conn_t {
 
     char *lang;
     char *domain;
-    char *connectdomain;
-    char *connectport;
     char *jid;
     char *pass;
     char *bound_jid;
@@ -190,7 +190,7 @@ struct _xmpp_conn_t {
     /* timeouts */
     unsigned int connect_timeout;
 
-    /* event handlers */    
+    /* event handlers */
 
     /* stream open handler */
     xmpp_open_handler open_handler;
@@ -210,7 +210,9 @@ struct _xmpp_conn_t {
 
 void conn_disconnect(xmpp_conn_t * const conn);
 void conn_disconnect_clean(xmpp_conn_t * const conn);
+void conn_established(xmpp_conn_t * const conn);
 void conn_open_stream(xmpp_conn_t * const conn);
+int conn_tls_start(xmpp_conn_t * const conn);
 void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler);
 void conn_parser_reset(xmpp_conn_t * const conn);
 
@@ -239,32 +241,31 @@ struct _xmpp_stanza_t {
 
 /* handler management */
 void handler_fire_stanza(xmpp_conn_t * const conn,
-                        xmpp_stanza_t * const stanza);
+                         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);
+                       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);
+                    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);
+                 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);
-
-/* replacement snprintf and vsnprintf */
-int xmpp_snprintf(char *str, size_t count, const char *fmt, ...);
-int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
+void auth_handle_component_open(xmpp_conn_t * const conn);
+void auth_handle_open_raw(xmpp_conn_t * const conn);
+void auth_handle_open_stub(xmpp_conn_t * const conn);
 
 #endif /* __LIBSTROPHE_COMMON_H__ */
index d41383a..7e36fe0 100644 (file)
@@ -1,18 +1,15 @@
 /* conn.c
 ** strophe XMPP client library -- connection object functions
 **
-** Copyright (C) 2005-2009 Collecta, Inc. 
+** Copyright (C) 2005-2009 Collecta, Inc.
 **
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
-/** @file 
+/** @file
  *  Connection management.
  */
 
@@ -24,6 +21,7 @@
 #include "common.h"
 #include "util.h"
 #include "parser.h"
+#include "resolver.h"
 
 #ifndef DEFAULT_SEND_QUEUE_MAX
 /** @def DEFAULT_SEND_QUEUE_MAX
 #endif
 #ifndef CONNECT_TIMEOUT
 /** @def CONNECT_TIMEOUT
- *  The time to wait (in milliseconds) for a connection attempt to succeed 
+ *  The time to wait (in milliseconds) for a connection attempt to succeed
  * or error.  The default is 5 seconds.
  */
 #define CONNECT_TIMEOUT 5000 /* 5 seconds */
 #endif
 
-static int _disconnect_cleanup(xmpp_conn_t * const conn, 
-                              void * const userdata);
-
-static void _handle_stream_start(char *name, char **attrs, 
+static int _disconnect_cleanup(xmpp_conn_t * const conn,
+                               void * const userdata);
+static char *_conn_build_stream_tag(xmpp_conn_t * const conn,
+                                    char **attributes, size_t attributes_len);
+static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs,
+                                 char ***attributes, size_t *attributes_len);
+static void _conn_attributes_destroy(xmpp_conn_t *conn, char **attributes,
+                                     size_t attributes_len);
+static void _handle_stream_start(char *name, char **attrs,
                                  void * const userdata);
 static void _handle_stream_end(char *name,
                                void * const userdata);
 static void _handle_stream_stanza(xmpp_stanza_t *stanza,
                                   void * const userdata);
+static unsigned short _conn_default_port(xmpp_conn_t * const conn,
+                                         xmpp_conn_type_t type);
+static void _conn_reset(xmpp_conn_t * const conn);
+static int _conn_connect(xmpp_conn_t * const conn,
+                         const char * const domain,
+                         const char * const host,
+                         unsigned short port,
+                         xmpp_conn_type_t type,
+                         xmpp_conn_handler callback,
+                         void * const userdata);
 
 /** Create a new Strophe connection object.
  *
@@ -70,86 +83,90 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
     xmpp_connlist_t *tail, *item;
 
     if (ctx == NULL) return NULL;
-       conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
-    
+
+    conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
     if (conn != NULL) {
-       conn->ctx = ctx;
+        conn->ctx = ctx;
 
-       conn->type = XMPP_UNKNOWN;
+        conn->type = XMPP_UNKNOWN;
         conn->state = XMPP_STATE_DISCONNECTED;
-       conn->sock = -1;
-       conn->tls = NULL;
-       conn->timeout_stamp = 0;
-       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;
-
-       /* default timeouts */
-       conn->connect_timeout = CONNECT_TIMEOUT;
-
-       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->sock = -1;
+        conn->ka_timeout = 0;
+        conn->ka_interval = 0;
+        conn->tls = NULL;
+        conn->timeout_stamp = 0;
+        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;
+
+        /* default timeouts */
+        conn->connect_timeout = CONNECT_TIMEOUT;
+
+        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->bound_jid = NULL;
 
-       conn->tls_support = 0;
-       conn->tls_disabled = 0;
-       conn->tls_failed = 0;
-       conn->sasl_support = 0;
+        conn->is_raw = 0;
+        conn->tls_support = 0;
+        conn->tls_disabled = 0;
+        conn->tls_mandatory = 0;
+        conn->tls_legacy_ssl = 0;
+        conn->tls_failed = 0;
+        conn->sasl_support = 0;
         conn->secured = 0;
 
-       conn->bind_required = 0;
-       conn->session_required = 0;
+        conn->bind_required = 0;
+        conn->session_required = 0;
 
-       conn->parser = parser_new(conn->ctx, 
+        conn->parser = parser_new(conn->ctx,
                                   _handle_stream_start,
                                   _handle_stream_end,
                                   _handle_stream_stanza,
                                   conn);
         conn->reset_parser = 0;
-        conn_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);
+
+        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);
             parser_free(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;
-       }
+            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;
@@ -160,7 +177,7 @@ xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
  *  @param conn a Strophe connection object
  *
  *  @return the same conn object passed in with its reference count
- *      incremented by 1
+ *          incremented by 1
  *
  *  @ingroup Connections
  */
@@ -170,8 +187,36 @@ xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t * const conn)
     return conn;
 }
 
+/** Set TCP keepalive parameters
+ *  Turn on TCP keepalive and set timeout and interval. Zero timeout
+ *  disables TCP keepalives. The parameters are applied immediately for
+ *  a non disconnected object. Also, they are applied when the connection
+ *  object connects successfully.
+ *
+ *  @param conn a Strophe connection object
+ *  @param timeout TCP keepalive timeout in seconds
+ *  @param interval TCP keepalive interval in seconds
+ *
+ *  @ingroup Connections
+ */
+void xmpp_conn_set_keepalive(xmpp_conn_t * const conn, int timeout, int interval)
+{
+    int ret = 0;
+
+    conn->ka_timeout = timeout;
+    conn->ka_interval = interval;
+
+    if (conn->state != XMPP_STATE_DISCONNECTED)
+        ret = sock_set_keepalive(conn->sock, timeout, interval);
+
+    if (ret < 0) {
+        xmpp_error(conn->ctx, "xmpp", "Setting TCP keepalive (%d,%d) error: %d",
+                   timeout, interval, sock_error());
+    }
+}
+
 /** Release a Strophe connection object.
- *  Decrement the reference count by one for a connection, freeing the 
+ *  Decrement the reference count by one for a connection, freeing the
  *  connection object if the count reaches 0.
  *
  *  @param conn a Strophe connection object
@@ -189,89 +234,80 @@ int xmpp_conn_release(xmpp_conn_t * const conn)
     const char *key;
     int released = 0;
 
-    if (conn->ref > 1) 
-       conn->ref--;
+    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->data.id.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 (thli->data.normal.ns) xmpp_free(ctx, thli->data.normal.ns);
-           if (thli->data.normal.name) xmpp_free(ctx, thli->data.normal.name);
-           if (thli->data.normal.type) xmpp_free(ctx, thli->data.normal.type);
-           xmpp_free(ctx, thli);
-       }
-
-       if (conn->stream_error) {
-           xmpp_stanza_release(conn->stream_error->stanza);
-           if (conn->stream_error->text)
-               xmpp_free(ctx, conn->stream_error->text);
-           xmpp_free(ctx, conn->stream_error);
-       }
+        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 (thli->ns) xmpp_free(ctx, thli->ns);
+            if (thli->name) xmpp_free(ctx, thli->name);
+            if (thli->type) xmpp_free(ctx, thli->type);
+            xmpp_free(ctx, thli);
+        }
 
         parser_free(conn->parser);
-       
-       if (conn->domain) xmpp_free(ctx, conn->domain);
-       if (conn->jid) xmpp_free(ctx, conn->jid);
-    if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid);
-       if (conn->pass) xmpp_free(ctx, conn->pass);
-       if (conn->stream_id) xmpp_free(ctx, conn->stream_id);
-       if (conn->lang) xmpp_free(ctx, conn->lang);
-       xmpp_free(ctx, conn);
-       released = 1;
+        _conn_reset(conn);
+
+        if (conn->jid) xmpp_free(ctx, conn->jid);
+        if (conn->pass) xmpp_free(ctx, conn->pass);
+        if (conn->lang) xmpp_free(ctx, conn->lang);
+        xmpp_free(ctx, conn);
+        released = 1;
     }
 
     return released;
@@ -309,9 +345,9 @@ const char *xmpp_conn_get_bound_jid(const xmpp_conn_t * const conn)
 }
 
 /** Set the JID of the user that will be bound to the connection.
- *  If any JID was previously set, it will be discarded.  This should not be 
+ *  If any JID was previously set, it will be discarded.  This should not be
  *  be used after a connection is created.  The function will make a copy of
- *  the JID string.  If the supllied JID is missing the node, SASL
+ *  the JID string.  If the supplied JID is missing the node, SASL
  *  ANONYMOUS authentication will be used.
  *
  *  @param conn a Strophe connection object
@@ -362,12 +398,12 @@ void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass)
 */
 xmpp_ctx_t* xmpp_conn_get_context(xmpp_conn_t * const conn)
 {
-       return conn->ctx;
+        return conn->ctx;
 }
 
 /** Initiate a connection to the XMPP server.
  *  This function returns immediately after starting the connection
- *  process to the XMPP server, and notifiations of connection state changes
+ *  process to the XMPP server, and notifications of connection state changes
  *  will be sent to the callback function.  The domain and port to connect to
  *  are usually determined by an SRV lookup for the xmpp-client service at
  *  the domain specified in the JID.  If SRV lookup fails, altdomain and 
@@ -377,65 +413,235 @@ xmpp_ctx_t* xmpp_conn_get_context(xmpp_conn_t * const conn)
  *  @param altdomain a string with domain to use if SRV lookup fails.  If this
  *      is NULL, the domain from the JID will be used.
  *  @param altport an integer port number to use if SRV lookup fails.  If this
- *      is 0, the default port (5222) will be assumed.
+ *      is 0, the default port will be assumed.
  *  @param callback a xmpp_conn_handler callback function that will receive
  *      notifications of connection status
  *  @param userdata an opaque data pointer that will be passed to the callback
  *
- *  @return 0 on success and -1 on an error
+ *  @return XMPP_EOK (0) on success or a number less than 0 on failure
  *
  *  @ingroup Connections
  */
-int xmpp_connect_client(xmpp_conn_t * const conn, 
-                         const char * const altdomain,
-                         unsigned short altport,
-                         xmpp_conn_handler callback,
-                         void * const userdata)
+int xmpp_connect_client(xmpp_conn_t * const conn,
+                        const char * const altdomain,
+                        unsigned short altport,
+                        xmpp_conn_handler callback,
+                        void * const userdata)
 {
-    char connectdomain[2048];
-    int connectport;
-    const char * domain;
+    resolver_srv_rr_t *srv_rr_list = NULL;
+    resolver_srv_rr_t *rr;
+    char *domain;
+    const char *host = NULL;
+    unsigned short port = 0;
+    int found = XMPP_DOMAIN_NOT_FOUND;
+    int rc;
+
+    domain = xmpp_jid_domain(conn->ctx, conn->jid);
+    if (!domain) return XMPP_EMEM;
+
+    if (altdomain != NULL) {
+        xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain.");
+        host = altdomain;
+        port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
+        found = XMPP_DOMAIN_ALTDOMAIN;
+
+    /* SSL tunneled connection on 5223 port is legacy and doesn't
+     * have an SRV record. */
+    } else if (!conn->tls_legacy_ssl) {
+        found = resolver_srv_lookup(conn->ctx, "xmpp-client", "tcp", domain,
+                                    &srv_rr_list);
+    }
+
+    if (XMPP_DOMAIN_NOT_FOUND == found) {
+        xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed, "
+                                      "connecting via domain.");
+        host = domain;
+        port = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
+        found = XMPP_DOMAIN_ALTDOMAIN;
+    }
 
-    conn->type = XMPP_CLIENT;
+    rr = srv_rr_list;
+    do {
+        if (XMPP_DOMAIN_FOUND == found && rr != NULL) {
+            host = rr->target;
+            port = rr->port;
+            rr = rr->next;
+        }
+        rc = _conn_connect(conn, domain, host, port, XMPP_CLIENT,
+                           callback, userdata);
+    } while (rc != 0 && rr != NULL);
 
-    conn->domain = xmpp_jid_domain(conn->ctx, conn->jid);
-    if (!conn->domain) return -1;
+    xmpp_free(conn->ctx, domain);
+    resolver_srv_free(conn->ctx, srv_rr_list);
 
-    if (altdomain) {
-        xmpp_debug(conn->ctx, "xmpp", "Connecting via altdomain.");
-        strcpy(connectdomain, altdomain);
-        connectport = altport ? altport : 5222;
-    } else if (!sock_srv_lookup("xmpp-client", "tcp", conn->domain,
-                                connectdomain, 2048, &connectport)) {
-           xmpp_debug(conn->ctx, "xmpp", "SRV lookup failed.");
-           if (!altdomain)
-                   domain = conn->domain;
-           else
-                   domain = altdomain;
-           xmpp_debug(conn->ctx, "xmpp", "Using alternate domain %s, port %d",
-                   altdomain, altport);
-           strcpy(connectdomain, domain);
-           connectport = altport ? altport : 5222;
+    return rc;
+}
+
+/** Initiate a component connection to server.
+ *  This function returns immediately after starting the connection
+ *  process to the XMPP server, and notifications of connection state changes
+ *  will be sent to the internal callback function that will set up handler
+ *  for the component handshake as defined in XEP-0114.
+ *  The domain and port to connect to must be provided in this case as the JID
+ *  provided to the call serves as component identifier to the server and is
+ *  not subject to DNS resolution.
+ *
+ *  @param conn a Strophe connection object
+ *  @param server a string with domain to use directly as the domain can't be
+ *      extracted from the component name/JID. If this is not set, the call
+ *      will fail.
+ *  @param port an integer port number to use to connect to server expecting
+ *      an external component.  If this is 0, the port 5347 will be assumed.
+ *  @param callback a xmpp_conn_handler callback function that will receive
+ *      notifications of connection status
+ *  @param userdata an opaque data pointer that will be passed to the callback
+ *
+ *  @return XMPP_EOK (0) on success or a number less than 0 on failure
+ *
+ *  @ingroup Connections
+ */
+int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server,
+                           unsigned short port, xmpp_conn_handler callback,
+                           void * const userdata)
+{
+    /*  The server domain, jid and password MUST be specified. */
+    if (!(server && conn->jid && conn->pass)) return XMPP_EINVOP;
+
+    /* XEP-0114 does not support TLS */
+    xmpp_conn_disable_tls(conn);
+    if (!conn->tls_disabled) {
+        xmpp_error(conn->ctx, "conn", "Failed to disable TLS. "
+                                      "XEP-0114 does not support TLS");
+        return XMPP_EINT;
     }
-    conn->sock = sock_connect(connectdomain, connectport);
-    xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d",
-               connectdomain, connectport, conn->sock);
-    if (conn->sock == -1) return -1;
 
-    /* setup handler */
-    conn->conn_handler = callback;
-    conn->userdata = userdata;
+    port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
+    /* JID serves as an identifier here and will be used as "to" attribute
+       of the stream */
+    return _conn_connect(conn, conn->jid, server, port, XMPP_COMPONENT,
+                         callback, 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 */
+/** Initiate a raw connection to the XMPP server.
+ *  Arguments and behaviour of the function are similar to
+ *  xmpp_connect_client(), but it skips authentication process. In opposite to
+ *  xmpp_connect_client() during connection process two events are generated
+ *  instead of one. User's callback is called with event XMPP_CONN_RAW_CONNECT
+ *  when the TCP connection with the server is established. At this point user
+ *  might want to open an XMPP stream with xmpp_conn_open_stream() or establish
+ *  TLS session with xmpp_conn_tls_start(). Event XMPP_CONN_CONNECT is generated
+ *  when the XMPP stream is opened successfully and user may send stanzas over
+ *  the connection.
+ *
+ *  This function doesn't use password nor node part of a jid. Therefore,
+ *  the only required configuration is a domain (or full jid) passed via
+ *  xmpp_conn_set_jid().
+ *
+ *  @see xmpp_connect_client()
+ *
+ *  @return XMPP_EOK (0) on success a number less than 0 on failure
+ *
+ *  @ingroup Connections
+ */
+int xmpp_connect_raw(xmpp_conn_t * const conn,
+                     const char * const altdomain,
+                     unsigned short altport,
+                     xmpp_conn_handler callback,
+                     void * const userdata)
+{
+    conn->is_raw = 1;
+    return xmpp_connect_client(conn, altdomain, altport, callback, userdata);
+}
 
-    conn->state = XMPP_STATE_CONNECTING;
-    conn->timeout_stamp = time_stamp();
-    xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", connectdomain);
+/* Called when tcp connection is established. */
+void conn_established(xmpp_conn_t * const conn)
+{
+    if (conn->tls_legacy_ssl && !conn->is_raw) {
+        xmpp_debug(conn->ctx, "xmpp", "using legacy SSL connection");
+        if (conn_tls_start(conn) != 0) {
+            conn_disconnect(conn);
+            return;
+        }
+    }
 
-    return 0;
+    if (conn->is_raw) {
+        handler_reset_timed(conn, 0);
+        /* we skip authentication for a "raw" connection, but the event loop
+           ignores user's handlers when conn->authenticated is not set. */
+        conn->authenticated = 1;
+        conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL, conn->userdata);
+    } else {
+        /* send stream init */
+        conn_open_stream(conn);
+    }
+}
+
+/** Send the default opening stream tag.
+ *  The default tag is the one sent by xmpp_connect_client().
+ *  User's connection handler is called with event XMPP_CONN_CONNECT when
+ *  server replies with its opening tag.
+ *
+ *  @return XMPP_EOK (0) on success a number less than 0 on failure
+ *
+ *  @note The connection must be connected with xmpp_connect_raw().
+ *
+ *  @ingroup Connections
+ */
+int xmpp_conn_open_stream_default(xmpp_conn_t * const conn)
+{
+    if (!conn->is_raw)
+        return XMPP_EINVOP;
+
+    conn_prepare_reset(conn, auth_handle_open_raw);
+    conn_open_stream(conn);
+
+    return XMPP_EOK;
+}
+
+/** Send an opening stream tag.
+ *  User's connection handler is called with event XMPP_CONN_CONNECT when
+ *  server replies with its opening tag.
+ *
+ *  @param conn a Strophe connection object
+ *  @param attributes Array of strings in format: even index points to
+ *      an attribute name and odd index points to its value
+ *  @param attributes_len Number of elements in the attributes array, it
+ *      should be number of attributes multiplied by 2
+ *
+ *  @return XMPP_EOK (0) on success a number less than 0 on failure
+ *
+ *  @note The connection must be connected with xmpp_connect_raw().
+ *
+ *  @ingroup Connections
+ */
+int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes,
+                          size_t attributes_len)
+{
+    char *tag;
+
+    if (!conn->is_raw)
+        return XMPP_EINVOP;
+
+    tag = _conn_build_stream_tag(conn, attributes, attributes_len);
+    if (!tag)
+        return XMPP_EMEM;
+
+    conn_prepare_reset(conn, auth_handle_open_raw);
+    xmpp_send_raw_string(conn, "<?xml version=\"1.0\"?>%s", tag);
+    xmpp_free(conn->ctx, tag);
+
+    return XMPP_EOK;
+}
+
+/** Start synchronous TLS handshake with the server.
+ *
+ *  @return XMPP_EOK (0) on success a number less than 0 on failure
+ *
+ *  @ingroup Connections
+ */
+int xmpp_conn_tls_start(xmpp_conn_t * const conn)
+{
+    return conn_tls_start(conn);
 }
 
 /** Cleanly disconnect the connection.
@@ -460,18 +666,18 @@ void conn_disconnect_clean(xmpp_conn_t * const conn)
  */
 void conn_disconnect(xmpp_conn_t * const conn) 
 {
-   xmpp_debug(conn->ctx, "xmpp", "Closing socket.");
-   conn->state = XMPP_STATE_DISCONNECTED;
-   if (conn->tls) 
-   {
-      tls_stop(conn->tls);
-      tls_free(conn->tls);
-      conn->tls = NULL;
-   }
-   sock_close(conn->sock);
+    xmpp_debug(conn->ctx, "xmpp", "Closing socket.");
+    conn->state = XMPP_STATE_DISCONNECTED;
+    if (conn->tls) {
+        tls_stop(conn->tls);
+        tls_free(conn->tls);
+        conn->tls = NULL;
+    }
+    sock_close(conn->sock);
 
-   /* fire off connection handler */
-   conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error, conn->stream_error, conn->userdata);
+    /* fire off connection handler */
+    conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
+                       conn->stream_error, conn->userdata);
 }
 
 /* prepares a parser reset.  this is called from handlers. we can't
@@ -489,18 +695,6 @@ void conn_parser_reset(xmpp_conn_t * const conn)
     parser_reset(conn->parser);
 }
 
-/* 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;
-}
-
 /** Initiate termination of the connection to the XMPP server.
  *  This function starts the disconnection sequence by sending
  *  </stream:stream> to the XMPP server.  This function will do nothing
@@ -513,19 +707,19 @@ static int _disconnect_cleanup(xmpp_conn_t * const conn,
 void xmpp_disconnect(xmpp_conn_t * const conn)
 {
     if (conn->state != XMPP_STATE_CONNECTING &&
-       conn->state != XMPP_STATE_CONNECTED)
-       return;
+        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);
+                      DISCONNECT_TIMEOUT, NULL);
 }
 
 /** Send a raw string to the XMPP server.
- *  This function is a convenience function to send raw string data to the 
+ *  This function is a convenience function to send raw string data to the
  *  XMPP server.  It is used by Strophe to send short messages instead of
  *  building up an XML stanza with DOM methods.  This should be used with care
  *  as it does not validate the data; invalid data may result in immediate
@@ -534,9 +728,11 @@ void xmpp_disconnect(xmpp_conn_t * const conn)
  *  @param conn a Strophe connection object
  *  @param fmt a printf-style format string followed by a variable list of
  *      arguments to format
+ *
+ *  @ingroup Connections
  */
-void xmpp_send_raw_string(xmpp_conn_t * const conn, 
-                         const char * const fmt, ...)
+void xmpp_send_raw_string(xmpp_conn_t * const conn,
+                          const char * const fmt, ...)
 {
     va_list ap;
     size_t len;
@@ -548,43 +744,45 @@ void xmpp_send_raw_string(xmpp_conn_t * const conn,
     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);
-       xmpp_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);
+        /* 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);
+        xmpp_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_debug(conn->ctx, "conn", "SENT: %s", buf);
 
-       xmpp_send_raw(conn, buf, len);
+        xmpp_send_raw(conn, buf, len);
     }
 }
 
 /** Send raw bytes to the XMPP server.
- *  This function is a convenience function to send raw bytes to the 
- *  XMPP server.  It is usedly primarly by xmpp_send_raw_string.  This 
+ *  This function is a convenience function to send raw bytes to the
+ *  XMPP server.  It is used primarily by xmpp_send_raw_string().  This
  *  function should be used with care as it does not validate the bytes and
  *  invalid data may result in stream termination by the XMPP server.
  *
  *  @param conn a Strophe connection object
  *  @param data a buffer of raw bytes
  *  @param len the length of the data in the buffer
+ *
+ *  @ingroup Connections
  */
 void xmpp_send_raw(xmpp_conn_t * const conn,
-                  const char * const data, const size_t len)
+                   const char * const data, const size_t len)
 {
     xmpp_send_queue_t *item;
 
@@ -596,8 +794,8 @@ void xmpp_send_raw(xmpp_conn_t * const conn,
 
     item->data = xmpp_alloc(conn->ctx, len);
     if (!item->data) {
-       xmpp_free(conn->ctx, item);
-       return;
+        xmpp_free(conn->ctx, item);
+        return;
     }
     memcpy(item->data, data, len);
     item->len = len;
@@ -606,13 +804,13 @@ void xmpp_send_raw(xmpp_conn_t * const conn,
 
     /* 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;
+        /* 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;
+        /* add to the tail */
+        conn->send_queue_tail->next = item;
+        conn->send_queue_tail = item;
     }
     conn->send_queue_len++;
 }
@@ -626,26 +824,20 @@ void xmpp_send_raw(xmpp_conn_t * const conn,
  *
  *  @ingroup Connections
  */
-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)
-   {
-      ret = xmpp_stanza_to_text(stanza, &buf, &len);
-      if (ret == 0)
-      {
-         xmpp_send_raw(conn, buf, len);
-         xmpp_debug(conn->ctx, "conn", "SENT: %s", buf);
-         xmpp_free(conn->ctx, buf);
-      }
-      else
-      {
-         xmpp_debug(conn->ctx, "conn", "xmpp_stanza_to_text() failed: %d", ret);
-      }
-   }
+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, "conn", "SENT: %s", buf);
+            xmpp_free(conn->ctx, buf);
+        }
+    }
 }
 
 /** Send the opening &lt;stream:stream&gt; tag to the server.
@@ -656,56 +848,244 @@ void xmpp_send(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza)
  */
 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);
+    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);
+}
+
+int conn_tls_start(xmpp_conn_t * const conn)
+{
+    int rc;
+
+    if (conn->tls_disabled) {
+        conn->tls = NULL;
+        rc = XMPP_EINVOP;
+    } else {
+        conn->tls = tls_new(conn->ctx, conn->sock);
+        rc = conn->tls == NULL ? XMPP_EMEM : 0;
+    }
+
+    if (conn->tls != NULL) {
+        if (tls_start(conn->tls)) {
+            conn->secured = 1;
+        } else {
+            rc = XMPP_EINT;
+            conn->error = tls_error(conn->tls);
+            tls_free(conn->tls);
+            conn->tls = NULL;
+            conn->tls_failed = 1;
+        }
+    }
+    if (rc != 0) {
+        xmpp_debug(conn->ctx, "conn", "Couldn't start TLS! "
+                   "error %d tls_error %d", rc, conn->error);
+    }
+    return rc;
+}
+
+/** Return applied flags for the connection.
+ *
+ *  @param conn a Strophe connection object
+ *
+ *  @return ORed connection flags that are applied for the connection.
+ *
+ *  @ingroup Connections
+ */
+long xmpp_conn_get_flags(const xmpp_conn_t * const conn)
+{
+    long flags;
+
+    flags = XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
+            XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
+            XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl;
+
+    return flags;
+}
+
+/** Set flags for the connection.
+ *  This function applies set flags and resets unset ones. Default connection
+ *  configuration is all flags unset. Flags can be applied only for a connection
+ *  in disconnected state.
+ *  All unsupported flags are ignored. If a flag is unset after successful set
+ *  operation then the flag is not supported by current version.
+ *
+ *  Supported flags are:
+ *
+ *    - XMPP_CONN_FLAG_DISABLE_TLS
+ *    - XMPP_CONN_FLAG_MANDATORY_TLS
+ *    - XMPP_CONN_FLAG_LEGACY_SSL
+ *
+ *  @param conn a Strophe connection object
+ *  @param flags ORed connection flags
+ *
+ *  @return XMPP_EOK (0) on success or a number less than 0 on failure
+ *
+ *  @ingroup Connections
+ */
+int xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags)
+{
+    if (conn->state != XMPP_STATE_DISCONNECTED) {
+        xmpp_error(conn->ctx, "conn", "Flags can be set only "
+                                      "for disconnected connection");
+        return XMPP_EINVOP;
+    }
+    if (flags & XMPP_CONN_FLAG_DISABLE_TLS &&
+        flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL)) {
+        xmpp_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
+        return XMPP_EINVOP;
+    }
+
+    conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
+    conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
+    conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
+
+    return 0;
 }
 
 /** Disable TLS for this connection, called by users of the library.
  *  Occasionally a server will be misconfigured to send the starttls
- *  feature, but wil not support the handshake.
+ *  feature, but will not support the handshake.
  *
  *  @param conn a Strophe connection object
+ *
+ *  @note this function is deprecated
+ *  @see xmpp_conn_set_flags()
+ *
+ *  @ingroup Connections
  */
 void xmpp_conn_disable_tls(xmpp_conn_t * const conn)
 {
-    conn->tls_disabled = 1;
+    long flags = xmpp_conn_get_flags(conn);
+
+    flags |= XMPP_CONN_FLAG_DISABLE_TLS;
+    (void)xmpp_conn_set_flags(conn, flags);
 }
 
-static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
+/** Return whether TLS session is established or not.
+ *
+ *  @return TRUE if TLS session is established and FALSE otherwise
+ *
+ *  @ingroup Connections
+ */
+int xmpp_conn_is_secured(xmpp_conn_t * const conn)
 {
-    char buf[4096];
-    size_t pos;
-    int len;
-    int i;
-    
-    if (!attrs) return;
+    return conn->secured && !conn->tls_failed && conn->tls != NULL ? 1 : 0;
+}
 
-    pos = 0;
-    len = xmpp_snprintf(buf, 4096, "<stream:stream");
-    if (len < 0) return;
-    
-    pos += len;
-    
-    for (i = 0; attrs[i]; i += 2) {
-        len = xmpp_snprintf(&buf[pos], 4096 - pos, " %s='%s'",
-                            attrs[i], attrs[i+1]);
-        if (len < 0) return;
-        pos += len;
+/* 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;
+}
+
+static char *_conn_build_stream_tag(xmpp_conn_t * const conn,
+                                    char **attributes, size_t attributes_len)
+{
+    char *tag;
+    size_t len;
+    size_t i;
+
+    static const char *tag_head = "<stream:stream";
+    static const char *tag_tail = ">";
+
+    /* ignore the last element unless number is even */
+    attributes_len &= ~(size_t)1;
+
+    len = strlen(tag_head) + strlen(tag_tail);
+    for (i = 0; i < attributes_len; ++i)
+        len += strlen(attributes[i]) + 2;
+    tag = xmpp_alloc(conn->ctx, len + 1);
+    if (!tag) return NULL;
+
+    strcpy(tag, tag_head);
+    for (i = 0; i < attributes_len; ++i) {
+        if ((i & 1) == 0) {
+            strcat(tag, " ");
+            strcat(tag, attributes[i]);
+            strcat(tag, "=\"");
+        } else {
+            strcat(tag, attributes[i]);
+            strcat(tag, "\"");
+        }
     }
+    strcat(tag, tag_tail);
 
-    len = xmpp_snprintf(&buf[pos], 4096 - pos, ">");
-    if (len < 0) return;
+    if (strlen(tag) != len) {
+        xmpp_error(conn->ctx, "xmpp", "Internal error in "
+                              "_conn_build_stream_tag().");
+        xmpp_free(conn->ctx, tag);
+        tag = NULL;
+    }
+
+    return tag;
+}
+
+static void _conn_attributes_new(xmpp_conn_t *conn, char **attrs,
+                                 char ***attributes, size_t *attributes_len)
+{
+    char **array = NULL;
+    size_t nr = 0;
+    size_t i;
+
+    if (attrs) {
+        for (; attrs[nr]; ++nr);
+        array = xmpp_alloc(conn->ctx, sizeof(*array) * nr);
+        for (i = 0; array && i < nr; ++i) {
+            array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i])
+                                    : xmpp_strdup(conn->ctx, attrs[i]);
+            if (array[i] == NULL) break;
+        }
+        if (!array || i < nr) {
+            xmpp_error(conn->ctx, "xmpp", "Memory allocation error.");
+            _conn_attributes_destroy(conn, array, i);
+            array = NULL;
+            nr = 0;
+        }
+    }
+    *attributes = array;
+    *attributes_len = nr;
+}
+
+static void _conn_attributes_destroy(xmpp_conn_t *conn, char **attributes,
+                                     size_t attributes_len)
+{
+    size_t i;
+
+    if (attributes) {
+        for (i = 0; i < attributes_len; ++i)
+            xmpp_free(conn->ctx, attributes[i]);
+        xmpp_free(conn->ctx, attributes);
+    }
+}
 
-    xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
+static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
+{
+    char **attributes;
+    char *tag;
+    size_t nr;
+
+    _conn_attributes_new(conn, attrs, &attributes, &nr);
+    tag = _conn_build_stream_tag(conn, attributes, nr);
+    if (tag) {
+        xmpp_debug(conn->ctx, "xmpp", "RECV: %s", tag);
+        xmpp_free(conn->ctx, tag);
+    }
+    _conn_attributes_destroy(conn, attributes, nr);
 }
 
 static char *_get_stream_attribute(char **attrs, char *name)
@@ -721,33 +1101,38 @@ static char *_get_stream_attribute(char **attrs, char *name)
     return NULL;
 }
 
-static void _handle_stream_start(char *name, char **attrs, 
+static void _handle_stream_start(char *name, char **attrs,
                                  void * const userdata)
 {
     xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
     char *id;
+    int failed = 0;
 
-    if (strcmp(name, "stream")) {
-        printf("name = %s\n", name);
-        xmpp_error(conn->ctx, "conn", "Server did not open valid stream.");
-        conn_disconnect(conn);
-    } else {
-        _log_open_tag(conn, attrs);
-        
-        if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id);
+    if (conn->stream_id) xmpp_free(conn->ctx, conn->stream_id);
+    conn->stream_id = NULL;
 
+    if (strcmp(name, "stream") == 0) {
+        _log_open_tag(conn, attrs);
         id = _get_stream_attribute(attrs, "id");
         if (id)
             conn->stream_id = xmpp_strdup(conn->ctx, id);
 
-        if (!conn->stream_id) {
+        if (id && !conn->stream_id) {
             xmpp_error(conn->ctx, "conn", "Memory allocation failed.");
-            conn_disconnect(conn);
+            failed = 1;
         }
+    } else {
+        xmpp_error(conn->ctx, "conn", "Server did not open valid stream."
+                                      " name = %s.", name);
+        failed = 1;
+    }
+
+    if (!failed) {
+        /* call stream open handler */
+        conn->open_handler(conn);
+    } else {
+        conn_disconnect(conn);
     }
-    
-    /* call stream open handler */
-    conn->open_handler(conn);
 }
 
 static void _handle_stream_end(char *name,
@@ -774,3 +1159,104 @@ static void _handle_stream_stanza(xmpp_stanza_t *stanza,
 
     handler_fire_stanza(conn, stanza);
 }
+
+static unsigned short _conn_default_port(xmpp_conn_t * const conn,
+                                         xmpp_conn_type_t type)
+{
+    switch (type) {
+    case XMPP_CLIENT:
+        return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL :
+                                      XMPP_PORT_CLIENT;
+    case XMPP_COMPONENT:
+        return XMPP_PORT_COMPONENT;
+    default:
+        return 0;
+    };
+}
+
+static void _conn_reset(xmpp_conn_t * const conn)
+{
+    xmpp_ctx_t *ctx = conn->ctx;
+    xmpp_send_queue_t *sq, *tsq;
+
+    if (conn->state != XMPP_STATE_DISCONNECTED) {
+        xmpp_debug(ctx, "conn", "Can't reset connected object.");
+        return;
+    }
+
+    /* free queued */
+    sq = conn->send_queue_head;
+    while (sq) {
+        tsq = sq;
+        sq = sq->next;
+        xmpp_free(ctx, tsq->data);
+        xmpp_free(ctx, tsq);
+    }
+
+    if (conn->stream_error) {
+        xmpp_stanza_release(conn->stream_error->stanza);
+        if (conn->stream_error->text)
+            xmpp_free(ctx, conn->stream_error->text);
+        xmpp_free(ctx, conn->stream_error);
+        conn->stream_error = NULL;
+    }
+
+    if (conn->domain) xmpp_free(ctx, conn->domain);
+    if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid);
+    if (conn->stream_id) xmpp_free(ctx, conn->stream_id);
+    conn->domain = NULL;
+    conn->bound_jid = NULL;
+    conn->stream_id = NULL;
+    conn->authenticated = 0;
+    conn->secured = 0;
+    conn->tls_failed = 0;
+    conn->error = 0;
+}
+
+static int _conn_connect(xmpp_conn_t * const conn,
+                         const char * const domain,
+                         const char * const host,
+                         unsigned short port,
+                         xmpp_conn_type_t type,
+                         xmpp_conn_handler callback,
+                         void * const userdata)
+{
+    xmpp_open_handler open_handler;
+
+    if (conn->state != XMPP_STATE_DISCONNECTED) return XMPP_EINVOP;
+    if (type != XMPP_CLIENT && type != XMPP_COMPONENT) return XMPP_EINVOP;
+    if (host == NULL || port == 0) return XMPP_EINT;
+
+    _conn_reset(conn);
+
+    conn->type = type;
+    conn->domain = xmpp_strdup(conn->ctx, domain);
+    if (!conn->domain) return XMPP_EMEM;
+
+    conn->sock = sock_connect(host, port);
+    xmpp_debug(conn->ctx, "xmpp", "sock_connect() to %s:%u returned %d",
+               host, port, conn->sock);
+    if (conn->sock == -1) return XMPP_EINT;
+    if (conn->ka_timeout || conn->ka_interval)
+        sock_set_keepalive(conn->sock, conn->ka_timeout, conn->ka_interval);
+
+    /* setup handler */
+    conn->conn_handler = callback;
+    conn->userdata = userdata;
+
+    open_handler = conn->is_raw ? auth_handle_open_stub :
+                   type == XMPP_CLIENT ? auth_handle_open :
+                                         auth_handle_component_open;
+    conn_prepare_reset(conn, open_handler);
+
+    /* 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;
+    conn->timeout_stamp = time_stamp();
+    xmpp_debug(conn->ctx, "xmpp", "Attempting to connect to %s", host);
+
+    return 0;
+}
diff --git a/src/libstrophe/crypto.c b/src/libstrophe/crypto.c
new file mode 100644 (file)
index 0000000..8ccb23e
--- /dev/null
@@ -0,0 +1,463 @@
+/* crypto.c
+ * strophe XMPP client library -- public interface for digests, encodings
+ *
+ * Copyright (C) 2016 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  Public interface for digests and encodings used in XEPs.
+ */
+
+/** @defgroup Digests Message digests
+ */
+
+/** @defgroup Encodings Encodings
+ */
+
+#include <nms_common.h>
+
+#include <assert.h>
+
+#include "common.h"     /* xmpp_alloc */
+#include "ostypes.h"    /* uint8_t, size_t */
+#include "sha1.h"
+#include "strophe.h"    /* xmpp_ctx_t, xmpp_free */
+
+struct _xmpp_sha1_t {
+    xmpp_ctx_t *xmpp_ctx;
+    SHA1_CTX ctx;
+    uint8_t digest[SHA1_DIGEST_SIZE];
+};
+
+static char *digest_to_string(const uint8_t *digest, char *s, size_t len)
+{
+    int i;
+
+    if (len < SHA1_DIGEST_SIZE * 2 + 1)
+        return NULL;
+
+    for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
+        xmpp_snprintf(s + i * 2, 3, "%02x", digest[i]);
+
+    return s;
+}
+
+static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest)
+{
+    char *s;
+    size_t slen;
+
+    slen = SHA1_DIGEST_SIZE * 2 + 1;
+    s = xmpp_alloc(ctx, slen);
+    if (s) {
+        s = digest_to_string(digest, s, slen);
+        assert(s != NULL);
+    }
+    return s;
+}
+
+/** Compute SHA1 message digest
+ *  Returns an allocated string which represents SHA1 message digest in
+ *  hexadecimal notation. The string must be freed with xmpp_free().
+ *
+ *  @param ctx a Strophe context object
+ *  @param data buffer for digest computation
+ *  @param len size of the data buffer
+ *
+ *  @return an allocated string or NULL on allocation error
+ *
+ *  @ingroup Digests
+ */
+char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
+{
+    uint8_t digest[SHA1_DIGEST_SIZE];
+
+    crypto_SHA1((const uint8_t *)data, len, digest);
+    return digest_to_string_alloc(ctx, digest);
+}
+
+/** Create new SHA1 object
+ *
+ *  @param ctx a Strophe context onject
+ *
+ *  @return new SHA1 object
+ *
+ *  @ingroup Digests
+ */
+xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
+{
+    xmpp_sha1_t *sha1;
+
+    sha1 = xmpp_alloc(ctx, sizeof(*sha1));
+    if (sha1) {
+        memset(sha1, 0, sizeof(*sha1));
+        crypto_SHA1_Init(&sha1->ctx);
+        sha1->xmpp_ctx = ctx;
+    }
+    return sha1;
+}
+
+/** Destroy SHA1 object
+ *
+ *  @param sha1 a SHA1 object
+ *
+ *  @ingroup Digests
+ */
+void xmpp_sha1_free(xmpp_sha1_t *sha1)
+{
+    xmpp_free(sha1->xmpp_ctx, sha1);
+}
+
+/** Update SHA1 context with the next portion of data
+ *  Can be called repeatedly.
+ *
+ *  @param sha1 a SHA1 object
+ *  @param data pointer to a buffer to be hashed
+ *  @param len size of the data buffer
+ *
+ *  @ingroup Digests
+ */
+void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len)
+{
+    crypto_SHA1_Update(&sha1->ctx, data, len);
+}
+
+/** Finish SHA1 computation
+ *  Don't call xmpp_sha1_update() after this function. Retrieve resulting
+ *  message digest with xmpp_sha1_to_string() or xmpp_sha1_to_digest().
+ *
+ *  @param sha1 a SHA1 object
+ *
+ *  @ingroup Digests
+ */
+void xmpp_sha1_final(xmpp_sha1_t *sha1)
+{
+    crypto_SHA1_Final(&sha1->ctx, sha1->digest);
+}
+
+/** Return message digest rendered as a string
+ *  Stores the string to a user's buffer and returns the buffer. Call this
+ *  function after xmpp_sha1_final().
+ *
+ *  @param sha1 a SHA1 object
+ *  @param s output string
+ *  @param slen size reserved for the string including '\0'
+ *
+ *  @return pointer s or NULL if resulting string is bigger than slen bytes
+ *
+ *  @ingroup Digests
+ */
+char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen)
+{
+    return digest_to_string(sha1->digest, s, slen);
+}
+
+/** Return message digest rendered as a string
+ *  Returns an allocated string. Free the string using the Strophe context
+ *  which is passed to xmpp_sha1_new(). Call this function after
+ *  xmpp_sha1_final().
+ *
+ *  @param sha1 a SHA1 object
+ *
+ *  @return an allocated string
+ *
+ *  @ingroup Digests
+ */
+char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1)
+{
+    return digest_to_string_alloc(sha1->xmpp_ctx, sha1->digest);
+}
+
+/** Stores message digest to a user's buffer
+ *
+ *  @param sha1 a SHA1 object
+ *  @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes
+ *
+ *  @ingroup Digests
+ */
+void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest)
+{
+    assert(SHA1_DIGEST_SIZE == XMPP_SHA1_DIGEST_SIZE);
+    memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE);
+}
+
+
+/* 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 unsigned 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','+','/',
+    '='
+};
+
+static size_t base64_encoded_len(const size_t len)
+{
+    /* encoded steam is 4 bytes for every three, rounded up */
+    return ((len + 2)/3) << 2;
+}
+
+static char *base64_encode(xmpp_ctx_t *ctx,
+                           const unsigned char * const buffer, const size_t len)
+{
+    size_t clen;
+    char *cbuf, *c;
+    uint32_t word, hextet;
+    size_t i;
+
+    clen = base64_encoded_len(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 + 2 < len; 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] & 0x03) << 4;
+                *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;
+}
+
+static size_t base64_decoded_len(const char * const buffer, const size_t len)
+{
+    size_t nudge = 0;
+    unsigned char c;
+    size_t i;
+
+    if (len < 4) return 0;
+
+    /* count the padding characters for the remainder */
+    for (i = len; i > 0; --i) {
+        c = _base64_invcharmap[(unsigned char)buffer[i-1]];
+        if (c < 64) break;
+        if (c == 64) ++nudge;
+        if (c > 64) return 0;
+    }
+    if (nudge > 2) return 0;
+
+    /* decoded steam is 3 bytes for every four */
+    return 3 * (len >> 2) - nudge;
+}
+
+static void base64_decode(xmpp_ctx_t *ctx,
+                          const char * const buffer, const size_t len,
+                          unsigned char **out, size_t *outlen)
+{
+    size_t dlen;
+    unsigned char *dbuf, *d;
+    uint32_t word, hextet = 0;
+    size_t i;
+
+    /* len must be a multiple of 4 */
+    if (len & 0x03) goto _base64_error;
+
+    dlen = base64_decoded_len(buffer, len);
+    if (dlen == 0) goto _base64_error;
+
+    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 + 3 < len; i += 4) {
+            hextet = _base64_invcharmap[(unsigned char)buffer[i]];
+            if (hextet & 0xC0) break;
+            word = hextet << 18;
+            hextet = _base64_invcharmap[(unsigned char)buffer[i+1]];
+            if (hextet & 0xC0) break;
+            word |= hextet << 12;
+            hextet = _base64_invcharmap[(unsigned char)buffer[i+2]];
+            if (hextet & 0xC0) break;
+            word |= hextet << 6;
+            hextet = _base64_invcharmap[(unsigned char)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[(unsigned char)buffer[len-4]];
+                if (hextet & 0xC0) goto _base64_decode_error;
+                word = hextet << 2;
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-3]];
+                if (hextet & 0xC0) goto _base64_decode_error;
+                word |= hextet >> 4;
+                *d++ = word & 0xFF;
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-2]];
+                if (hextet != 64) goto _base64_decode_error;
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-1]];
+                if (hextet != 64) goto _base64_decode_error;
+                break;
+            case 2:
+                /* redo the last quartet, checking for correctness */
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-4]];
+                if (hextet & 0xC0) goto _base64_decode_error;
+                word = hextet << 10;
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-3]];
+                if (hextet & 0xC0) goto _base64_decode_error;
+                word |= hextet << 4;
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-2]];
+                if (hextet & 0xC0) goto _base64_decode_error;
+                word |= hextet >> 2;
+                *d++ = (word & 0xFF00) >> 8;
+                *d++ = (word & 0x00FF);
+                hextet = _base64_invcharmap[(unsigned char)buffer[len-1]];
+                if (hextet != 64) goto _base64_decode_error;
+                break;
+        }
+        *d = '\0';
+    }
+    *out = dbuf;
+    *outlen = dbuf == NULL ? 0 : dlen;
+    return;
+
+_base64_decode_error:
+    /* invalid character; abort decoding! */
+    xmpp_free(ctx, dbuf);
+_base64_error:
+    *out = NULL;
+    *outlen = 0;
+}
+
+/** Base64 encoding routine
+ *  Returns an allocated string which must be freed with xmpp_free().
+ *
+ *  @param ctx a Strophe context
+ *  @param data buffer to encode
+ *  @param len size of the data buffer
+ *
+ *  @return an allocated null-terminated string or NULL on error
+ *
+ *  @ingroup Encodings
+ */
+char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
+{
+    return base64_encode(ctx, data, len);
+}
+
+/** Base64 decoding routine
+ *  Returns an allocated string which must be freed with xmpp_free(). User
+ *  calls this function when the result must be a string. When decoded buffer
+ *  contains '\0' NULL is returned.
+ *
+ *  @param ctx a Strophe context
+ *  @param base64 encoded buffer
+ *  @param len size of the buffer
+ *
+ *  @return an allocated null-terminated string or NULL on error
+ *
+ *  @ingroup Encodings
+ */
+char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
+{
+    unsigned char *buf = NULL;
+    size_t buflen;
+
+    if (len == 0) {
+        /* handle empty string */
+        buf = xmpp_alloc(ctx, 1);
+        if (buf)
+            buf[0] = '\0';
+        buflen = 0;
+    } else {
+        base64_decode(ctx, base64, len, &buf, &buflen);
+    }
+    if (buf) {
+        if (buflen != strlen((char *)buf)) {
+            xmpp_free(ctx, buf);
+            buf = NULL;
+        }
+    }
+    return (char *)buf;
+}
+
+/** Base64 decoding routine
+ *  Returns an allocated buffer which must be freed with xmpp_free().
+ *
+ *  @param ctx a Strophe context
+ *  @param base64 encoded buffer
+ *  @param len size of the encoded buffer
+ *  @param out allocated buffer is stored here
+ *  @param outlen size of the allocated buffer
+ *
+ *  @note on an error the `*out` will be NULL
+ *
+ *  @ingroup Encodings
+ */
+void xmpp_base64_decode_bin(xmpp_ctx_t *ctx, const char *base64, size_t len,
+                            unsigned char **out, size_t *outlen)
+{
+    base64_decode(ctx, base64, len, out, outlen);
+}
index c30c041..bbda733 100644 (file)
@@ -1,15 +1,12 @@
 /* ctx.c
 ** strophe XMPP client library -- run-time context implementation
 **
-** Copyright (C) 2005-2009 Collecta, Inc. 
+** Copyright (C) 2005-2009 Collecta, Inc.
 **
-**  This software is provided AS-IS with no warranty, either express 
+**  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -39,7 +36,7 @@
  *  result in strange (and platform dependent) behavior.
  *
  *  Specifically, the socket library on Win32 platforms must be initialized
- *  before use (although this is not the case on POSIX systems).  The TLS 
+ *  before use (although this is not the case on POSIX systems).  The TLS
  *  subsystem must also seed the random number generator.
  */
 
@@ -97,12 +94,12 @@ void xmpp_shutdown(void)
 int xmpp_version_check(int major, int minor)
 {
     return (major == LIBXMPP_VERSION_MAJOR) &&
-          (minor >= LIBXMPP_VERSION_MINOR);
+           (minor >= LIBXMPP_VERSION_MINOR);
 }
 
 /* We define the global default allocator, logger, and context here. */
 
-/* Wrap stdlib routines malloc, free, and realloc for default memory 
+/* Wrap stdlib routines malloc, free, and realloc for default memory
  * management. 
  */
 static void *_malloc(const size_t size, void * const userdata)
@@ -131,9 +128,9 @@ static xmpp_mem_t xmpp_default_mem = {
 /* log levels and names */
 static const char * const _xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
 static const xmpp_log_level_t _xmpp_default_logger_levels[] = {XMPP_LEVEL_DEBUG,
-                                                              XMPP_LEVEL_INFO,
-                                                              XMPP_LEVEL_WARN,
-                                                              XMPP_LEVEL_ERROR};
+                                                               XMPP_LEVEL_INFO,
+                                                               XMPP_LEVEL_WARN,
+                                                               XMPP_LEVEL_ERROR};
 
 /** Log a message.
  *  The default logger writes to stderr.
@@ -144,21 +141,21 @@ static const xmpp_log_level_t _xmpp_default_logger_levels[] = {XMPP_LEVEL_DEBUG,
  *  @param area the area the log message is for
  *  @param msg the log message
  */
-void xmpp_default_logger(void * const userdata,
-                        const xmpp_log_level_t level,
-                        const char * const area,
-                        const char * const msg)
+static void xmpp_default_logger(void * const userdata,
+                         const xmpp_log_level_t level,
+                         const char * const area,
+                         const char * const msg)
 {
     xmpp_log_level_t filter_level = * (xmpp_log_level_t*)userdata;
     if (level >= filter_level)
-       fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
+        fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
 }
 
 static const xmpp_log_t _xmpp_default_loggers[] = {
-       {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
-       {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
-       {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
-       {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}
+        {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
+        {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
+        {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
+        {&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}
 };
 
 /** Get a default logger with filtering.
@@ -186,7 +183,7 @@ static xmpp_log_t xmpp_default_log = { NULL, NULL };
 /* convenience functions for accessing the context */
 
 /** Allocate memory in a Strophe context.
- *  All Strophe functions will use this to allocate memory. 
+ *  All Strophe functions will use this to allocate memory.
  *
  *  @param ctx a Strophe context object
  *  @param size the number of bytes to allocate
@@ -219,7 +216,7 @@ void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
  *  @return a pointer to the reallocated memory or NULL on an error
  */
 void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
-                  const size_t size)
+                   const size_t size)
 {
     return ctx->mem->realloc(p, size, ctx->mem->userdata);
 }
@@ -228,7 +225,7 @@ void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
  *  Write a log message to the logger for the context for the specified
  *  level and area.  This function takes a printf-style format string and a
  *  variable argument list (in va_list) format.  This function is not meant
- *  to be called directly, but is used via xmpp_error, xmpp_warn, xmpp_info, 
+ *  to be called directly, but is used via xmpp_error, xmpp_warn, xmpp_info,
  *  and xmpp_debug.
  *
  *  @param ctx a Strophe context object
@@ -238,10 +235,10 @@ void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
  *  @param ap variable argument list supplied for the format string
  */
 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)
+              const xmpp_log_level_t level,
+              const char * const area,
+              const char * const fmt,
+              va_list ap)
 {
     int oldret, ret;
 #if HAVE_DECL_VA_COPY
@@ -283,12 +280,12 @@ void xmpp_log(const xmpp_ctx_t * const ctx,
 
     ctx->log->handler(ctx->log->userdata, level, area, buf);
     if (buf != smbuf)
-       xmpp_free(ctx, buf);
+        xmpp_free(ctx, buf);
 }
 
 /** Write to the log at the ERROR level.
  *  This is a convenience function for writing to the log at the
- *  ERROR level.  It takes a printf-style format string followed by a 
+ *  ERROR level.  It takes a printf-style format string followed by a
  *  variable list of arguments for formatting.
  *
  *  @param ctx a Strophe context object
@@ -388,29 +385,34 @@ void xmpp_debug(const xmpp_ctx_t * const ctx,
  *
  *  @ingroup Context
  */
-xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem, 
-                        const xmpp_log_t * const log)
+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), NULL);
+        ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
     else
-       ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
+        ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
 
     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;
+        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;
+        ctx->rand = xmpp_rand_new(ctx);
+        if (ctx->rand == NULL) {
+            xmpp_free(ctx, ctx);
+            ctx = NULL;
+        }
     }
 
     return ctx;
@@ -425,6 +427,7 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
 void xmpp_ctx_free(xmpp_ctx_t * const ctx)
 {
     /* mem and log are owned by their suppliers */
+    xmpp_rand_free(ctx, ctx->rand);
     xmpp_free(ctx, ctx); /* pull the hole in after us */
 }
 
index e43287f..5a3bced 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+** This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
  */
 
 /** @defgroup EventLoop Event loop
- *  These functions manage the Strophe event loop.  
- *  
+ *  These functions manage the Strophe event loop.
+ *
  *  Simple tools can use xmpp_run() and xmpp_stop() to manage the life
  *  cycle of the program.  A common idiom is to set up a few initial
  *  event handers, call xmpp_run(), and then respond and react to
  *  events as they come in.  At some point, one of the handlers will
  *  call xmpp_stop() to quit the event loop which leads to the program
  *  terminating.
- * 
+ *
  *  More complex programs will have their own event loops, and should
  *  ensure that xmpp_run_once() is called regularly from there.  For
  *  example, a GUI program will already include an event loop to
@@ -103,8 +100,8 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
            ret = tls_clear_pending_write(conn->tls);
 
            if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
-               /* an error occured */
-               xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
+               /* an error occurred */
+               xmpp_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
                conn->error = ECONNABORTED;
                conn_disconnect(conn);
            }
@@ -119,7 +116,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
                ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
 
                if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
-                   /* an error occured */
+                   /* an error occurred */
                    conn->error = tls_error(conn->tls);
                    break;
                } else if (ret < towrite) {
@@ -132,7 +129,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
                ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
 
                if (ret < 0 && !sock_is_recoverable(sock_error())) {
-                   /* an error occured */
+                   /* an error occurred */
                    conn->error = sock_error();
                    break;
                } else if (ret < towrite) {
@@ -146,35 +143,36 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
            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, disconnecting.");
-           conn->error = ECONNABORTED;
-           conn_disconnect(conn);
-       }
-       
-       connitem = connitem->next;
+            conn->send_queue_len--;
+            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 occurred, disconnecting.");
+            conn->error = ECONNABORTED;
+            conn_disconnect(conn);
+        }
+
+        connitem = connitem->next;
     }
 
     /* reset parsers if needed */
     for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
-       if (connitem->conn->reset_parser)
-           conn_parser_reset(connitem->conn);
+        if (connitem->conn->reset_parser)
+            conn_parser_reset(connitem->conn);
     }
 
 
     /* fire any ready timed handlers, then
-       make sure we don't wait past the time when timed handlers need 
+       make sure we don't wait past the time when timed handlers need
        to be called */
     next = handler_fire_timed(ctx);
 
@@ -182,47 +180,47 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
     tv.tv_sec = (long)(usec / 1000000);
     tv.tv_usec = (long)(usec % 1000000);
 
-    FD_ZERO(&rfds); 
+    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 */
-           
-           /* make sure the timeout hasn't expired */
-           if (time_elapsed(conn->timeout_stamp, time_stamp()) <= 
-               conn->connect_timeout)
-               FD_SET(conn->sock, &wfds);
-           else {
-               conn->error = ETIMEDOUT;
-               xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
-               conn_disconnect(conn);
-           }
-           break;
-       case XMPP_STATE_CONNECTED:
-           FD_SET(conn->sock, &rfds);
-           break;
-       case XMPP_STATE_DISCONNECTED:
-           /* do nothing */
-       default:
-           break;
-       }
-       
-       /* Check if there is something in the SSL buffer. */
-       if (conn->tls) {
-           tls_read_bytes += tls_pending(conn->tls);
-       }
-       
-       if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
-           max = conn->sock;
-
-       connitem = connitem->next;
+        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 */
+
+            /* make sure the timeout hasn't expired */
+            if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
+                conn->connect_timeout)
+                FD_SET(conn->sock, &wfds);
+            else {
+                conn->error = ETIMEDOUT;
+                xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
+                conn_disconnect(conn);
+            }
+            break;
+        case XMPP_STATE_CONNECTED:
+            FD_SET(conn->sock, &rfds);
+            break;
+        case XMPP_STATE_DISCONNECTED:
+            /* do nothing */
+        default:
+            break;
+        }
+
+        /* Check if there is something in the SSL buffer. */
+        if (conn->tls) {
+            tls_read_bytes += tls_pending(conn->tls);
+        }
+
+        if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
+            max = conn->sock;
+
+        connitem = connitem->next;
     }
 
     /* check for events */
@@ -232,7 +230,7 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
     {
         if (timeout > 0)
             _sleep(timeout);
-        return;      
+        return;
     }
 
     /* select errored */
@@ -249,78 +247,76 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
       }
           return;
     }
-    
+
     /* no events happened */
     if (ret == 0 && tls_read_bytes == 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_disconnect(conn);
-                   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) || (conn->tls && tls_pending(conn->tls))) {
-               if (conn->tls) {
-                   ret = tls_read(conn->tls, buf, 4096);
-               } else {
-                   ret = sock_read(conn->sock, buf, 4096);
-               }
-
-               if (ret > 0) {
-                   ret = parser_feed(conn->parser, buf, ret);
-                   if (!ret) {
-                       /* parse error, we need to shut down */
-                       /* FIXME */
-                       xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
-                       conn_disconnect(conn);
-                   }
-               } else {
-                   if (conn->tls) {
-                       if (!tls_is_recoverable(tls_error(conn->tls)))
-                       {
-                           xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls));
-                           conn->error = tls_error(conn->tls);
-                           conn_disconnect(conn);
-                       }
-                   } else {
-                       /* return of 0 means socket closed by server */
-                       xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
-                       conn->error = ECONNRESET;
-                       conn_disconnect(conn);
-                   }
-               }
-           }
-
-           break;
-       case XMPP_STATE_DISCONNECTED:
-           /* do nothing */
-       default:
-           break;
-       }
-
-       connitem = connitem->next;
+        conn = connitem->conn;
+
+        switch (conn->state) {
+        case XMPP_STATE_CONNECTING:
+            if (FD_ISSET(conn->sock, &wfds)) {
+                /* connection complete */
+
+                /* check for error */
+                ret = sock_connect_error(conn->sock);
+                if (ret != 0) {
+                    /* connection failed */
+                    xmpp_debug(ctx, "xmpp", "connection failed, error %d", ret);
+                    conn_disconnect(conn);
+                    break;
+                }
+
+                conn->state = XMPP_STATE_CONNECTED;
+                xmpp_debug(ctx, "xmpp", "connection successful");
+                conn_established(conn);
+            }
+
+            break;
+        case XMPP_STATE_CONNECTED:
+            if (FD_ISSET(conn->sock, &rfds) || (conn->tls && tls_pending(conn->tls))) {
+                if (conn->tls) {
+                    ret = tls_read(conn->tls, buf, 4096);
+                } else {
+                    ret = sock_read(conn->sock, buf, 4096);
+                }
+
+                if (ret > 0) {
+                    ret = parser_feed(conn->parser, buf, ret);
+                    if (!ret) {
+                        /* parse error, we need to shut down */
+                        /* FIXME */
+                        xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
+                        conn_disconnect(conn);
+                    }
+                } else {
+                    if (conn->tls) {
+                        if (!tls_is_recoverable(tls_error(conn->tls)))
+                        {
+                            xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls));
+                            conn->error = tls_error(conn->tls);
+                            conn_disconnect(conn);
+                        }
+                    } else {
+                        /* return of 0 means socket closed by server */
+                        xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
+                        conn->error = ECONNRESET;
+                        conn_disconnect(conn);
+                    }
+                }
+            }
+
+            break;
+        case XMPP_STATE_DISCONNECTED:
+            /* do nothing */
+        default:
+            break;
+        }
+
+        connitem = connitem->next;
     }
 
     /* fire any ready handlers */
@@ -341,9 +337,12 @@ void xmpp_run(xmpp_ctx_t *ctx)
 
     ctx->loop_status = XMPP_LOOP_RUNNING;
     while (ctx->loop_status == XMPP_LOOP_RUNNING) {
-       xmpp_run_once(ctx, DEFAULT_TIMEOUT);
+        xmpp_run_once(ctx, DEFAULT_TIMEOUT);
     }
 
+    /* make it possible to start event loop again */
+    ctx->loop_status = XMPP_LOOP_NOTSTARTED;
+
     xmpp_debug(ctx, "event", "Event loop completed.");
 }
 
index 619ecb2..952410a 100644 (file)
@@ -1,15 +1,12 @@
 /* handler.c
 ** strophe XMPP client library -- event handler management
 **
-** Copyright (C) 2005-2009 Collecta, Inc. 
+** Copyright (C) 2005-2009 Collecta, Inc.
 **
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
  *  @param stanza a Strophe stanza object
  */
 void handler_fire_stanza(xmpp_conn_t * const conn,
-                        xmpp_stanza_t * const stanza)
+                         xmpp_stanza_t * const stanza)
 {
     xmpp_handlist_t *item, *prev;
-    char *id, *ns, *name, *type;
-    
+    const char *id, *ns, *name, *type;
+
     /* call id handlers */
     id = xmpp_stanza_get_id(stanza);
     if (id) {
-       prev = NULL;
-       item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
-       while (item) {
-           xmpp_handlist_t *next = item->next;
-
-           if (item->user_handler && !conn->authenticated) {
-               item = next;
-               continue;
-           }
-
-           if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
-               /* handler is one-shot, so delete it */
-               if (prev)
-                   prev->next = next;
-               else {
-                   hash_drop(conn->id_handlers, id);
-                   hash_add(conn->id_handlers, id, next);
-               }
-                xmpp_free(conn->ctx, item->data.id.id);
-               xmpp_free(conn->ctx, item);
-               item = NULL;
-           }
-           if (item)
-               prev = item;
-           item = next;
-       }
+        /* enable all added handlers */
+        item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
+        for (; item; item = item->next)
+            item->enabled = 1;
+
+        prev = NULL;
+        item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
+        while (item) {
+            xmpp_handlist_t *next = item->next;
+
+            /* don't fire user handlers until authentication succeeds and
+               and skip newly added handlers */
+            if ((item->user_handler && !conn->authenticated) || !item->enabled) {
+                prev = item;
+                item = next;
+                continue;
+            }
+
+            if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
+                /* handler is one-shot, so delete it */
+                if (prev)
+                    prev->next = next;
+                else {
+                    hash_drop(conn->id_handlers, id);
+                    hash_add(conn->id_handlers, id, next);
+                }
+                xmpp_free(conn->ctx, item->id);
+                xmpp_free(conn->ctx, item);
+                item = NULL;
+            }
+            if (item)
+                prev = item;
+            item = next;
+        }
     }
-    
+
     /* 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;
+        item->enabled = 1;
 
     prev = NULL;
     item = conn->handlers;
     while (item) {
-       /* skip newly added handlers */
-       if (!item->enabled) {
-           prev = item;
-           item = item->next;
-           continue;
-       }
-
-       /* don't call user handlers until authentication succeeds */
-       if (item->user_handler && !conn->authenticated) {
-           prev = item;
-           item = item->next;
-           continue;
-       }
-
-       if ((!item->data.normal.ns || (ns && strcmp(ns, item->data.normal.ns) == 0) ||
-            xmpp_stanza_get_child_by_ns(stanza, item->data.normal.ns)) &&
-           (!item->data.normal.name || (name && strcmp(name, item->data.normal.name) == 0)) &&
-           (!item->data.normal.type || (type && strcmp(type, item->data.normal.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;
-                if (item->data.normal.ns) xmpp_free(conn->ctx, item->data.normal.ns);
-                if (item->data.normal.name) xmpp_free(conn->ctx, item->data.normal.name);
-                if (item->data.normal.type) xmpp_free(conn->ctx, item->data.normal.type);
-               xmpp_free(conn->ctx, item);
-               item = NULL;
-           }
-       
-       if (item) {
-           prev = item;
-           item = item->next;
-       } else if (prev)
-           item = prev->next;
-       else
-           item = conn->handlers;
+        /* don't fire user handlers until authentication succeeds and
+           skip newly added handlers */
+        if ((item->user_handler && !conn->authenticated) || !item->enabled) {
+            prev = item;
+            item = item->next;
+            continue;
+        }
+
+        if ((!item->ns || (ns && strcmp(ns, item->ns) == 0) ||
+             xmpp_stanza_get_child_by_ns(stanza, item->ns)) &&
+            (!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;
+                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);
+                item = NULL;
+            }
+
+        if (item) {
+            prev = item;
+            item = item->next;
+        } else
+            item = prev ? prev->next : conn->handlers;
     }
 }
 
@@ -137,57 +134,61 @@ void handler_fire_stanza(xmpp_conn_t * const conn,
 uint64_t handler_fire_timed(xmpp_ctx_t * const ctx)
 {
     xmpp_connlist_t *connitem;
-    xmpp_handlist_t *handitem, *temp;
-    int ret, fired;
+    xmpp_handlist_t *item, *prev;
+    xmpp_conn_t *conn;
     uint64_t elapsed, min;
+    int ret;
 
     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->data.timed.last_stamp, time_stamp());
-           if (elapsed >= handitem->data.timed.period) {
-               /* fire! */
-               fired = 1;
-               handitem->data.timed.last_stamp = time_stamp();
-               ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
-           } else if (min > (handitem->data.timed.period - elapsed))
-               min = handitem->data.timed.period - elapsed;
-               
-           temp = handitem;
-           handitem = handitem->next;
-
-           /* delete handler if it returned false */
-           if (fired && !ret)
-               xmpp_timed_handler_delete(connitem->conn, (xmpp_timed_handler)temp->handler);
-       }
-
-       connitem = connitem->next;
+        conn = connitem->conn;
+        if (conn->state != XMPP_STATE_CONNECTED) {
+            connitem = connitem->next;
+            continue;
+        }
+
+        /* enable all handlers that were added */
+        for (item = conn->timed_handlers; item; item = item->next)
+            item->enabled = 1;
+
+        prev = NULL;
+        item = conn->timed_handlers;
+        while (item) {
+            /* don't fire user handlers until authentication succeeds and
+               skip newly added handlers */
+            if ((item->user_handler && !conn->authenticated) || !item->enabled) {
+                prev = item;
+                item = item->next;
+                continue;
+            }
+
+            elapsed = time_elapsed(item->last_stamp, time_stamp());
+            if (elapsed >= item->period) {
+                /* fire! */
+                item->last_stamp = time_stamp();
+                ret = ((xmpp_timed_handler)item->handler)(conn, item->userdata);
+                if (!ret) {
+                    /* delete handler if it returned false */
+                    if (prev)
+                        prev->next = item->next;
+                    else
+                        conn->timed_handlers = item->next;
+                    xmpp_free(conn->ctx, item);
+                    item = NULL;
+                }
+            } else if (min > (item->period - elapsed))
+                min = item->period - elapsed;
+
+            if (item) {
+                prev = item;
+                item = item->next;
+            } else
+                item = prev ? prev->next : conn->timed_handlers;
+        }
+
+        connitem = connitem->next;
     }
 
     return min;
@@ -205,25 +206,27 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
 
     handitem = conn->timed_handlers;
     while (handitem) {
-       if ((user_only && handitem->user_handler) || !user_only)
-           handitem->data.timed.last_stamp = time_stamp();
-       
-       handitem = handitem->next;
+        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_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->handler == (void *)handler && item->userdata == userdata) {
+            xmpp_warn(conn->ctx, "xmpp", "Timed handler already exists.");
+            break;
+        }
     }
     if (item) return;
 
@@ -237,17 +240,17 @@ static void _timed_handler_add(xmpp_conn_t * const conn,
     item->enabled = 0;
     item->next = NULL;
 
-    item->data.timed.period = period;
-    item->data.timed.last_stamp = time_stamp();
+    item->period = period;
+    item->last_stamp = time_stamp();
 
     /* append item to list */
     if (!conn->timed_handlers)
-       conn->timed_handlers = item;
+        conn->timed_handlers = item;
     else {
-       tail = conn->timed_handlers;
-       while (tail->next) 
-           tail = tail->next;
-       tail->next = item;
+        tail = conn->timed_handlers;
+        while (tail->next)
+            tail = tail->next;
+        tail->next = item;
     }
 }
 
@@ -259,7 +262,7 @@ static void _timed_handler_add(xmpp_conn_t * const conn,
  *  @ingroup Handlers
  */
 void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
-                              xmpp_timed_handler handler)
+                               xmpp_timed_handler handler)
 {
     xmpp_handlist_t *item, *prev;
 
@@ -268,35 +271,36 @@ void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
     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);
+        if (item->handler == (void *)handler) {
+            if (prev)
+                prev->next = item->next;
+            else
+                conn->timed_handlers = item->next;
+
+            xmpp_free(conn->ctx, item);
+            item = prev ? prev->next : conn->timed_handlers;
+        } else {
+            prev = item;
+            item = item->next;
+        }
     }
 }
 
 static void _id_handler_add(xmpp_conn_t * const conn,
-                        xmpp_handler handler,
-                        const char * const id,
-                        void * const userdata, int user_handler)
+                         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->handler == (void *)handler && item->userdata == userdata) {
+            xmpp_warn(conn->ctx, "xmpp", "Id handler already exists.");
+            break;
+        }
+        item = item->next;
     }
     if (item) return;
 
@@ -310,20 +314,20 @@ static void _id_handler_add(xmpp_conn_t * const conn,
     item->enabled = 0;
     item->next = NULL;
 
-    item->data.id.id = xmpp_strdup(conn->ctx, id);
-    if (!item->data.id.id) {
-       xmpp_free(conn->ctx, item);
-       return;
+    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);
+        hash_add(conn->id_handlers, id, item);
     else {
-       while (tail->next) 
-           tail = tail->next;
-       tail->next = item;
+        while (tail->next)
+            tail = tail->next;
+        tail->next = item;
     }
 }
 
@@ -336,49 +340,54 @@ static void _id_handler_add(xmpp_conn_t * const conn,
  *  @ingroup Handlers
  */
 void xmpp_id_handler_delete(xmpp_conn_t * const conn,
-                           xmpp_handler handler,
-                           const char * const id)
+                            xmpp_handler handler,
+                            const char * const id)
 {
-    xmpp_handlist_t *item, *prev;
+    xmpp_handlist_t *item, *prev, *next;
 
     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->data.id.id);
-       xmpp_free(conn->ctx, item);
+        next = item->next;
+
+        if (item->handler == (void *)handler) {
+            if (prev)
+                prev->next = next;
+            else {
+                hash_drop(conn->id_handlers, id);
+                hash_add(conn->id_handlers, id, next);
+            }
+
+            xmpp_free(conn->ctx, item->id);
+            xmpp_free(conn->ctx, item);
+            item = next;
+        } else {
+            prev = item;
+            item = next;
+        }
     }
 }
 
 /* add a stanza handler */
 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_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;
+        /* same handler function can process different stanzas and
+           distinguish them according to userdata. */
+        if (item->handler == (void *)handler && item->userdata == userdata) {
+            xmpp_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
+            break;
+        }
     }
     if (item) return;
 
@@ -391,42 +400,42 @@ static void _handler_add(xmpp_conn_t * const conn,
     item->userdata = userdata;
     item->enabled = 0;
     item->next = NULL;
-    
+
     if (ns) {
-       item->data.normal.ns = xmpp_strdup(conn->ctx, ns);
-       if (!item->data.normal.ns) {
-           xmpp_free(conn->ctx, item);
-           return;
-       }
+        item->ns = xmpp_strdup(conn->ctx, ns);
+        if (!item->ns) {
+            xmpp_free(conn->ctx, item);
+            return;
+        }
     } else
-       item->data.normal.ns = NULL;
+        item->ns = NULL;
     if (name) {
-       item->data.normal.name = xmpp_strdup(conn->ctx, name);
-       if (!item->data.normal.name) {
-           if (item->data.normal.ns) xmpp_free(conn->ctx, item->data.normal.ns);
-           xmpp_free(conn->ctx, item);
-           return;
-       }
+        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->data.normal.name = NULL;
+        item->name = NULL;
     if (type) {
-       item->data.normal.type = xmpp_strdup(conn->ctx, type);
-       if (!item->data.normal.type) {
-           if (item->data.normal.ns) xmpp_free(conn->ctx, item->data.normal.ns);
-           if (item->data.normal.name) xmpp_free(conn->ctx, item->data.normal.name);
-           xmpp_free(conn->ctx, item);
-       }
+        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->data.normal.type = NULL;
+        item->type = NULL;
 
     /* append to list */
     if (!conn->handlers)
-       conn->handlers = item;
+        conn->handlers = item;
     else {
-       tail = conn->handlers;
-       while (tail->next) 
-           tail = tail->next;
-       tail->next = item;
+        tail = conn->handlers;
+        while (tail->next)
+            tail = tail->next;
+        tail->next = item;
     }
 }
 
@@ -438,7 +447,7 @@ static void _handler_add(xmpp_conn_t * const conn,
  *  @ingroup Handlers
  */
 void xmpp_handler_delete(xmpp_conn_t * const conn,
-                        xmpp_handler handler)
+                         xmpp_handler handler)
 {
     xmpp_handlist_t *prev, *item;
 
@@ -447,23 +456,21 @@ void xmpp_handler_delete(xmpp_conn_t * const conn,
     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->data.normal.ns) xmpp_free(conn->ctx, item->data.normal.ns);
-       if (item->data.normal.name) xmpp_free(conn->ctx, item->data.normal.name);
-       if (item->data.normal.type) xmpp_free(conn->ctx, item->data.normal.type);
-       xmpp_free(conn->ctx, item);
+        if (item->handler == (void *)handler) {
+            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);
+            item = prev ? prev->next : conn->handlers;
+        } else {
+            prev = item;
+            item = item->next;
+        }
     }
 }
 
@@ -472,7 +479,7 @@ void xmpp_handler_delete(xmpp_conn_t * const conn,
  *  and continue firing regularly after that.  Strophe will try its best
  *  to fire handlers as close to the period times as it can, but accuracy
  *  will vary depending on the resolution of the event loop.
- *   
+ *
  *  If the handler function returns true, it will be kept, and if it
  *  returns false, it will be deleted from the list of handlers.
  *
@@ -484,9 +491,9 @@ void xmpp_handler_delete(xmpp_conn_t * const conn,
  *  @ingroup Handlers
  */
 void xmpp_timed_handler_add(xmpp_conn_t * const conn,
-                           xmpp_timed_handler handler,
-                           const unsigned long period,
-                           void * const userdata)
+                            xmpp_timed_handler handler,
+                            const unsigned long period,
+                            void * const userdata)
 {
     _timed_handler_add(conn, handler, period, userdata, 1);
 }
@@ -501,9 +508,9 @@ void xmpp_timed_handler_add(xmpp_conn_t * const conn,
  *  @param userdata an opaque data pointer that will be passed to the handler
  */
 void handler_add_timed(xmpp_conn_t * const conn,
-                      xmpp_timed_handler handler,
-                      const unsigned long period,
-                      void * const userdata)
+                       xmpp_timed_handler handler,
+                       const unsigned long period,
+                       void * const userdata)
 {
     _timed_handler_add(conn, handler, period, userdata, 0);
 }
@@ -525,9 +532,9 @@ void handler_add_timed(xmpp_conn_t * const conn,
  *  @ingroup Handlers
  */
 void xmpp_id_handler_add(xmpp_conn_t * const conn,
-                        xmpp_handler handler,
-                        const char * const id,
-                        void * const userdata)
+                         xmpp_handler handler,
+                         const char * const id,
+                         void * const userdata)
 {
     _id_handler_add(conn, handler, id, userdata, 1);
 }
@@ -542,9 +549,9 @@ void xmpp_id_handler_add(xmpp_conn_t * const conn,
  *  @param userdata an opaque data pointer that will be passed to the handler
  */
 void handler_add_id(xmpp_conn_t * const conn,
-                   xmpp_handler handler,
-                   const char * const id,
-                   void * const userdata)
+                    xmpp_handler handler,
+                    const char * const id,
+                    void * const userdata)
 {
     _id_handler_add(conn, handler, id, userdata, 0);
 }
@@ -572,11 +579,11 @@ void handler_add_id(xmpp_conn_t * const conn,
  *  @ingroup Handlers
  */
 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)
+                      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);
 }
@@ -593,11 +600,11 @@ void xmpp_handler_add(xmpp_conn_t * const conn,
  *  @param userdata an opaque data pointer that will be passed to the handler
  */
 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)
+                 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);
 }
index 1b09b52..faebd65 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -48,7 +45,7 @@ struct _hash_iterator_t {
    
 /** allocate and initialize a new hash table */
 hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
-                hash_free_func free)
+                hash_free_func free_func)
 {
     hash_t *result = NULL;
 
@@ -63,7 +60,7 @@ hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
        result->length = size;
 
        result->ctx = ctx;
-       result->free = free;
+       result->free = free_func;
        result->num_keys = 0;
        /* give the caller a reference */
        result->ref = 1;
@@ -129,7 +126,7 @@ 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);
+   int table_index = _hash_key(table, key);
 
    /* drop existing entry, if any */
    hash_drop(table, key);
@@ -145,8 +142,8 @@ int hash_add(hash_t *table, const char * const key, void *data)
    entry->value = data;
    /* insert ourselves in the linked list */
    /* TODO: this leaks duplicate keys */
-   entry->next = table->entries[index];
-   table->entries[index] = entry;
+   entry->next = table->entries[table_index];
+   table->entries[table_index] = entry;
    table->num_keys++;
 
    return 0;
@@ -156,11 +153,11 @@ int hash_add(hash_t *table, const char * const key, void *data)
 void *hash_get(hash_t *table, const char *key)
 {
    hashentry_t *entry;
-   int index = _hash_key(table, key);
+   int table_index = _hash_key(table, key);
    void *result = NULL;
 
    /* look up the hash entry */
-   entry = table->entries[index];
+   entry = table->entries[table_index];
    while (entry != NULL) {
        /* traverse the linked list looking for the key */
        if (!strcmp(key, entry->key)) {
@@ -179,10 +176,10 @@ 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);
+   int table_index = _hash_key(table, key);
 
    /* look up the hash entry */
-   entry = table->entries[index];
+   entry = table->entries[table_index];
    prev = NULL;
    while (entry != NULL) {
        /* traverse the linked list looking for the key */
@@ -191,7 +188,7 @@ int hash_drop(hash_t *table, const char *key)
          xmpp_free(ctx, entry->key);
          if (table->free) table->free(ctx, entry->value);
          if (prev == NULL) {
-           table->entries[index] = entry->next;
+           table->entries[table_index] = entry->next;
          } else {
            prev->next = entry->next;
          }
@@ -248,12 +245,13 @@ const char * hash_iter_next(hash_iterator_t *iter)
 {
     hash_t *table = iter->table;
     hashentry_t *entry = iter->entry;
-    int i = iter->index + 1;
+    int i;
 
     /* 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 */
+       i = iter->index + 1;
        while (i < iter->table->length) {
            entry = table->entries[i];
            if (entry != NULL) {
@@ -264,7 +262,7 @@ const char * hash_iter_next(hash_iterator_t *iter)
        }
     }
 
-    if ((entry == NULL) || (i >= table->length)) {
+    if (entry == NULL) {
        /* no more keys! */
        return NULL;
     }
index 94c0b14..55892d5 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -25,7 +22,7 @@ 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);
+                hash_free_func free_func);
 
 /** allocate a new reference to an existing hash table */
 hash_t *hash_clone(hash_t * const table);
index bb2f5aa..767661c 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -35,7 +32,7 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
                                    const char *resource)
 {
     char *result;
-    size_t len,nlen,dlen,rlen;
+    size_t len, nlen, dlen, rlen;
 
     /* jid must at least have a domain */
     if (domain == NULL) return NULL;
@@ -58,7 +55,7 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
            result[nlen+dlen] = '/';
            memcpy(result+nlen+dlen+1, resource, rlen - 1);
        }
-       result[nlen+dlen+rlen] = '\0';
+       result[len] = '\0';
     }
 
     return result;
index 01231b1..4e890be 100644 (file)
 
 #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; \
+        (cp)[0] = (value) & 0xFF; \
+        (cp)[1] = ((value) >> 8)  & 0xFF; \
+        (cp)[2] = ((value) >> 16) & 0xFF; \
+        (cp)[3] = ((value) >> 24) & 0xFF; \
     } while(0)
 
+static void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
+                         struct MD5Context *ctx);
+
 /*
  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
  * initialization constants.
@@ -57,7 +60,7 @@ void MD5Init(struct MD5Context *ctx)
     ctx->bits[0] = 0;
     ctx->bits[1] = 0;
 
-       memset(ctx->in, 0, 64);
+    memset(ctx->in, 0, 64);
 }
 
 /*
@@ -72,33 +75,33 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
 
     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]++;         /* Carry from low to high */
     ctx->bits[1] += len >> 29;
 
-    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+    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;
+        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;
+        memcpy(ctx->in, buf, 64);
+        MD5Transform(ctx->buf, ctx->in, ctx);
+        buf += 64;
+        len -= 64;
     }
 
     /* Handle any remaining bytes of data. */
@@ -128,15 +131,15 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
 
     /* 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);
+        /* 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);
+        /* Now fill the next block with 56 bytes */
+        memset(ctx->in, 0, 56);
     } else {
-       /* Pad block to 56 bytes */
-       memset(p, 0, count - 8);
+        /* Pad block to 56 bytes */
+        memset(p, 0, count - 8);
     }
 
     /* Append length in bits and transform */
@@ -148,11 +151,9 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
     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 */
+    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) */
@@ -165,22 +166,22 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
 /* 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 )
+        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 )
+        ( 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)
+static 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];
@@ -267,23 +268,3 @@ void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
     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 */
index 3095f95..235d4fa 100644 (file)
 #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
+#include "ostypes.h"
 
 struct MD5Context {
-       uint32_t buf[4];
-       uint32_t bits[2];
-       unsigned char in[64];
+    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);
+               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 */
index 0794b2b..6e9a5ae 100644 (file)
@@ -7,10 +7,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
 #ifndef __LIBSTROPHE_OSTYPES_H__
 #define __LIBSTROPHE_OSTYPES_H__
 
-#ifdef _WIN32
+#include <stddef.h>     /* size_t */
+
+#if defined (_MSC_VER) && _MSC_VER < 1600
+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;
 typedef unsigned __int64 uint64_t; 
+
+#ifndef UINT16_MAX
+#define UINT16_MAX ((uint16_t)0xffff)
+#endif /* UINT16_MAX */
+#ifndef UINT32_MAX
+#define UINT32_MAX ((uint32_t)0xffffffff)
+#endif /* UINT32_MAX */
+#ifndef SIZE_MAX
+#define SIZE_MAX UINT32_MAX
+#endif /* SIZE_MAX */
+
+#else
+#include <stdint.h>
 #endif
 
 #endif /* __LIBSTROPHE_OSTYPES_H__ */
index 32329f6..1bbb71e 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express or
 **  implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -37,6 +34,7 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
                      parser_stanza_callback stanzacb,
                      void *userdata);
 void parser_free(parser_t *parser);
+char* parser_attr_name(xmpp_ctx_t *ctx, char *nsname);
 int parser_reset(parser_t *parser);
 int parser_feed(parser_t *parser, char *chunk, int len);
 
index 789fb6b..9bd0e8f 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -18,6 +15,7 @@
 
 #include <nms_common.h>
 #include <expat.h>
+
 #include <strophe.h>
 #include "common.h"
 #include "parser.h"
@@ -108,41 +106,27 @@ static void _start_element(void *userdata,
             parser->startcb((char *)name, (char **)attrs, 
                             parser->userdata);
     } else {
-       /* build stanzas at depth 1 */
-       if (!parser->stanza && parser->depth != 1) {
-           /* something terrible happened */
-           /* FIXME: shutdown disconnect */
-           xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
-       } else if (!parser->stanza) {
-           /* starting a new toplevel stanza */
-           parser->stanza = xmpp_stanza_new(parser->ctx);
-           if (!parser->stanza) {
-               /* FIXME: can't allocate, disconnect */
-           }
-           xmpp_stanza_set_name(parser->stanza, name);
-           _set_attributes(parser->stanza, attrs);
-           if (ns)
-               xmpp_stanza_set_ns(parser->stanza, ns);
-       } else {
-           /* starting a child of parser->stanza */
-           child = xmpp_stanza_new(parser->ctx);
-           if (!child) {
-               /* FIXME: can't allocate, disconnect */
-           }
-           xmpp_stanza_set_name(child, name);
-           _set_attributes(child, attrs);
-           if (ns)
-               xmpp_stanza_set_ns(child, ns);
-
-           /* add child to parent */
-           xmpp_stanza_add_child(parser->stanza, child);
-           
-           /* the child is owned by the toplevel stanza now */
-           xmpp_stanza_release(child);
-
-           /* make child the current stanza */
-           parser->stanza = child;
-       }
+        /* build stanzas at depth 1 */
+        if (!parser->stanza && parser->depth != 1) {
+            /* something terrible happened */
+            /* FIXME: shutdown disconnect */
+            xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
+        } else {
+            child = xmpp_stanza_new(parser->ctx);
+            if (!child) {
+                /* FIXME: can't allocate, disconnect */
+            }
+            xmpp_stanza_set_name(child, name);
+            _set_attributes(child, attrs);
+            if (ns)
+                xmpp_stanza_set_ns(child, ns);
+
+            if (parser->stanza != NULL) {
+                xmpp_stanza_add_child(parser->stanza, child);
+                xmpp_stanza_release(child);
+            }
+            parser->stanza = child;
+        }
     }
 
     if (ns) xmpp_free(parser->ctx, ns);
@@ -219,6 +203,11 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
     return parser;
 }
 
+char* parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
+{
+    return _xml_name(ctx, nsname);
+}
+
 /* free a parser */
 void parser_free(parser_t *parser)
 {
diff --git a/src/libstrophe/rand.c b/src/libstrophe/rand.c
new file mode 100644 (file)
index 0000000..61fa10d
--- /dev/null
@@ -0,0 +1,311 @@
+/* rand.c
+ * strophe XMPP client library -- pseudo-random number generator
+ *
+ * Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  Pseudo-random number generator.
+ *
+ *  Implemented Hash_DRBG mechanism according to NIST SP 800-90A.
+ *  Hash function is SHA1.
+ */
+
+/** @defgroup Random Pseudo-random number generator
+ */
+
+#include <nms_common.h>
+#include <time.h>
+#include <assert.h>
+#include "common.h"     /* xmpp_alloc, xmpp_free */
+#include "ostypes.h"    /* uint8_t, uint32_t, size_t */
+#include "sha1.h"
+#include "snprintf.h"   /* xmpp_snprintf */
+
+#include "rand.h"       /* xmpp_rand_t */
+
+#define outlen SHA1_DIGEST_SIZE
+#define seedlen (440 / 8)
+#define reseed_interval 0x7fffffff
+
+/* maximum number of bytes that can be generated per call */
+#define GENERATE_MAX (outlen * 10)
+#define ENTROPY_MAX 128
+#define NONCE_MAX 8
+
+#define RESEED_NEEDED (-1)
+
+struct Hash_DRBG_CTX_struc {
+    uint8_t V[seedlen];
+    uint8_t C[seedlen];
+    uint32_t reseed_counter;
+};
+typedef struct Hash_DRBG_CTX_struc Hash_DRBG_CTX;
+
+struct _xmpp_rand_t {
+    int inited;
+    unsigned reseed_count;
+    Hash_DRBG_CTX ctx;
+};
+
+/* returns smallest number mupliple of y that not less than x */
+#define round_up(x, y) (((x) + (y) - 1) / (y) * (y))
+/* returns smallest integer number that not less than x/y */
+#define div_round_up(x, y) (((x) + (y) - 1) / (y))
+
+/* adds two arrays as numbers in big-endian representation and stores
+ * result in the first one.
+ */
+static void arr_add(uint8_t *arr1, size_t arr1_len,
+                    uint8_t *arr2, size_t arr2_len)
+{
+    size_t i;
+    uint32_t acc;
+    uint32_t carry = 0;
+
+    assert(arr1_len >= arr2_len);
+
+    for (i = 1; (i <= arr2_len) || (carry != 0 && i <= arr1_len); ++i) {
+        acc = (uint32_t)arr1[arr1_len - i] + carry;
+        if (i <= arr2_len)
+            acc += (uint32_t)arr2[arr2_len - i];
+        carry = acc >> 8;
+        arr1[arr1_len - i] = (uint8_t)(acc & 0xff);
+    }
+}
+
+/* stores 32-bit number in big-endian representation */
+static void store_be32(uint32_t val, uint8_t be[4])
+{
+    be[0] = (uint8_t)((val >> 24) & 0xff);
+    be[1] = (uint8_t)((val >> 16) & 0xff);
+    be[2] = (uint8_t)((val >> 8) & 0xff);
+    be[3] = (uint8_t)(val & 0xff);
+}
+
+static void Hash_df(uint8_t *input_string, size_t input_string_len,
+                    uint8_t *output_string, size_t no_of_bytes_to_return)
+{
+    uint8_t counter;
+    uint8_t temp[round_up(seedlen, outlen)];
+    uint8_t conj[ENTROPY_MAX + NONCE_MAX + seedlen + 6];
+    size_t len;
+    size_t i;
+    size_t offset;
+
+    assert(no_of_bytes_to_return <= sizeof(temp));
+    assert(input_string_len + 5 <= sizeof(conj));
+
+    len = div_round_up(no_of_bytes_to_return, outlen);
+    for (i = 1; i <= len; ++i) {
+        offset = (i - 1) * outlen;
+        counter = (uint8_t)i;
+        conj[0] = counter;
+        store_be32((uint32_t)no_of_bytes_to_return * 8, conj + 1);
+        memcpy(conj + 5, input_string, input_string_len);
+        crypto_SHA1(conj, input_string_len + 5, temp + offset);
+    }
+
+    memcpy(output_string, temp, no_of_bytes_to_return);
+}
+
+/* assume personalization_string is zero length string */
+static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
+                                  uint8_t *entropy_input,
+                                  size_t entropy_input_len,
+                                  uint8_t *nonce, size_t nonce_len)
+{
+    uint8_t seed_material[ENTROPY_MAX + NONCE_MAX];
+    uint8_t seed0[seedlen + 1];
+    uint8_t *seed = seed0 + 1;
+
+    assert(entropy_input_len <= ENTROPY_MAX);
+    assert(nonce_len <= NONCE_MAX);
+
+    memcpy(seed_material, entropy_input, entropy_input_len);
+    memcpy(seed_material + entropy_input_len, nonce, nonce_len);
+    Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen);
+    seed0[0] = 0;
+
+    memcpy(ctx->V, seed, seedlen);
+    Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
+    ctx->reseed_counter = 1;
+}
+
+/* assume additional_input is zero length string */
+static void Hash_DRBG_Reseed(Hash_DRBG_CTX *ctx,
+                             uint8_t *entropy_input,
+                             size_t entropy_input_len)
+{
+    uint8_t seed_material[1 + seedlen + ENTROPY_MAX];
+    uint8_t seed0[seedlen + 1];
+    uint8_t *seed = seed0 + 1;
+
+    assert(entropy_input_len <= ENTROPY_MAX);
+
+    seed_material[0] = 1;
+    memcpy(seed_material + 1, ctx->V, seedlen);
+    memcpy(seed_material + 1 + seedlen, entropy_input, entropy_input_len);
+    Hash_df(seed_material, entropy_input_len + seedlen + 1, seed, seedlen);
+    seed0[0] = 0;
+
+    memcpy(ctx->V, seed, seedlen);
+    Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
+    ctx->reseed_counter = 1;
+}
+
+static void Hashgen(uint8_t *V, uint8_t *output,
+                    size_t requested_number_of_bytes)
+{
+    uint8_t data[seedlen];
+    uint8_t W[GENERATE_MAX];
+    uint8_t i1 = 1;
+    size_t m;
+    size_t i;
+    size_t offset;
+
+    assert(requested_number_of_bytes <= sizeof(W));
+
+    m = div_round_up(requested_number_of_bytes, outlen);
+    memcpy(data, V, seedlen);
+    for (i = 1; i <= m; ++i) {
+        offset = (i - 1) * outlen;
+        crypto_SHA1(data, seedlen, W + offset);
+        /* increase data by 1 */
+        arr_add(data, sizeof(data), &i1, 1);
+    }
+
+    memcpy(output, W, requested_number_of_bytes);
+}
+
+/* assume additional_input is zero length string */
+static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx, uint8_t *output,
+                              size_t requested_number_of_bytes)
+{
+    uint8_t H[outlen];
+    uint8_t V3[seedlen + 1];
+    uint8_t reseed_counter[4];
+
+    if (ctx->reseed_counter > reseed_interval || ctx->reseed_counter == 0)
+        return RESEED_NEEDED;
+
+    Hashgen(ctx->V, output, requested_number_of_bytes);
+
+    V3[0] = 3;
+    memcpy(V3 + 1, ctx->V, seedlen);
+    crypto_SHA1(V3, sizeof(V3), H);
+    arr_add(ctx->V, sizeof(ctx->V), ctx->C, sizeof(ctx->C));
+    arr_add(ctx->V, sizeof(ctx->V), H, sizeof(H));
+    store_be32(ctx->reseed_counter, reseed_counter);
+    arr_add(ctx->V, sizeof(ctx->V), reseed_counter, sizeof(reseed_counter));
+
+    ++ctx->reseed_counter;
+    return 0;
+}
+
+#define ENTROPY_ACCUMULATE(ptr, last, type, arg)    \
+do {                                                \
+    type __arg = (type)(arg);                       \
+    if ((char*)ptr + sizeof(__arg) < (char*)last) { \
+        *(type*)ptr = __arg;                        \
+        ptr = (void*)((char*)ptr + sizeof(__arg));  \
+    }                                               \
+} while (0)
+
+static void xmpp_rand_reseed(xmpp_rand_t *rand)
+{
+    uint8_t entropy[ENTROPY_MAX];
+    uint8_t *ptr = entropy;
+    const uint8_t *last = entropy + sizeof(entropy);
+    size_t len;
+
+    /* entropy:
+     *  1. time_stamp()
+     *  2. clock(3)
+     *  3. xmpp_rand_t address to make unique seed within one process
+     *  4. counter to make unique seed within one context
+     *  5. stack address
+     *  6. local ports of every connection in list (getsockname)
+     *  7. other non-constant info that can be retieved from socket
+     *
+     *  rand(3) can't be used as it isn't thread-safe.
+     *  XXX 6 and 7 are not implemented yet.
+     */
+
+    ENTROPY_ACCUMULATE(ptr, last, uint64_t, time_stamp());
+    ENTROPY_ACCUMULATE(ptr, last, clock_t, clock());
+    ENTROPY_ACCUMULATE(ptr, last, void *, rand);
+    ENTROPY_ACCUMULATE(ptr, last, unsigned, ++rand->reseed_count);
+    ENTROPY_ACCUMULATE(ptr, last, void *, &entropy);
+    len = ptr - entropy;
+
+    if (rand->inited) {
+        Hash_DRBG_Reseed(&rand->ctx, entropy, len);
+    } else {
+        Hash_DRBG_Instantiate(&rand->ctx, entropy, len, NULL, 0);
+        rand->inited = 1;
+    }
+}
+
+xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
+{
+    xmpp_rand_t *out = xmpp_alloc(ctx, sizeof(*out));
+    if (out != NULL) {
+        memset(out, 0, sizeof(*out));
+    }
+    return out;
+}
+
+void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
+{
+    xmpp_free(ctx, rand);
+}
+
+void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
+{
+    int rc;
+
+    rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output, len);
+    if (rc == RESEED_NEEDED) {
+        xmpp_rand_reseed(rand);
+        rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output, len);
+        assert(rc == 0);
+    }
+}
+
+int xmpp_rand(xmpp_rand_t *rand)
+{
+    int result;
+
+    xmpp_rand_bytes(rand, (unsigned char *)&result, sizeof(result));
+    return result;
+}
+
+void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len)
+{
+    size_t i;
+    size_t rand_len = len / 2;
+#ifndef _MSC_VER
+    unsigned char rand_buf[rand_len];
+#else
+    unsigned char *rand_buf = (unsigned char *)_alloca(rand_len);
+#endif
+
+    /* current implementation returns printable HEX representation of
+     * a random buffer, however base64 encoding can be used instead;
+     * the only problem is that base64_encode() allocates memory and
+     * as result can fail.
+     */
+
+    xmpp_rand_bytes(rand, rand_buf, rand_len);
+    for (i = 0; i < rand_len; ++i) {
+        xmpp_snprintf(output + i * 2, len, "%02x", rand_buf[i]);
+        len -= 2;
+    }
+}
diff --git a/src/libstrophe/rand.h b/src/libstrophe/rand.h
new file mode 100644 (file)
index 0000000..1e4880b
--- /dev/null
@@ -0,0 +1,65 @@
+/* rand.h
+ * strophe XMPP client library -- pseudo-random number generator
+ *
+ * Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  Pseudo-random number generator.
+ */
+
+#ifndef __LIBSTROPHE_RAND_H__
+#define __LIBSTROPHE_RAND_H__
+
+#include <stddef.h>     /* size_t */
+#include "strophe.h"    /* xmpp_ctx_t */
+
+typedef struct _xmpp_rand_t xmpp_rand_t;
+
+/** Create new xmpp_rand_t object.
+ *
+ *  @param ctx A Strophe context object
+ *
+ *  @ingroup Random
+ */
+xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
+/** Destroy an xmpp_rand_t object.
+ *
+ *  @param ctx A Strophe context object
+ *
+ *  @ingroup Random
+ */
+void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
+
+/** Generate random integer
+ *  Analogue of rand(3).
+ *
+ *  @ingroup Random
+ */
+int xmpp_rand(xmpp_rand_t *rand);
+
+/** Generate random bytes.
+ *  Generates len bytes and stores them to the output buffer.
+ *
+ *  @ingroup Random
+ */
+void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
+
+/** Generate a nonce that is printable randomized string.
+ *  This function doesn't allocate memory and doesn't fail.
+ *
+ *  @param output A buffer where a NULL-terminated string will be placed.
+ *                The string will contain len-1 printable symbols.
+ *  @param len Number of bytes reserved for the output string, including
+ *             end of line '\0'.
+ *
+ *  @ingroup Random
+ */
+void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
+
+#endif /* __LIBSTROPHE_RAND_H__ */
diff --git a/src/libstrophe/resolver.c b/src/libstrophe/resolver.c
new file mode 100644 (file)
index 0000000..1436a18
--- /dev/null
@@ -0,0 +1,769 @@
+/* resolver.h
+ * strophe XMPP client library -- DNS resolver
+ *
+ * Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  DNS resolver.
+ */
+
+#include <nms_common.h>
+#include <time.h>
+#include <strophe.h>
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>             /* res_query */
+#endif /* _WIN32 */
+
+#include "ostypes.h"
+#include "snprintf.h"
+#include "util.h"               /* xmpp_min */
+#include "resolver.h"
+
+#define MESSAGE_HEADER_LEN 12
+#define MESSAGE_RESPONSE 1
+#define MESSAGE_T_SRV 33
+#define MESSAGE_C_IN 1
+
+struct message_header {
+    uint16_t id;
+    uint8_t octet2;
+    uint8_t octet3;
+    uint16_t qdcount;
+    uint16_t ancount;
+    uint16_t nscount;
+    uint16_t arcount;
+};
+
+#ifdef _WIN32
+static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
+                                     resolver_srv_rr_t **srv_rr_list);
+static int resolver_win32_srv_query(const char *fulldomain,
+                                    unsigned char *buf, size_t len);
+#endif /* _WIN32 */
+
+/* the same as ntohs(), but receives pointer to the value */
+static uint16_t xmpp_ntohs_ptr(const void *ptr)
+{
+    const uint8_t *p = (const uint8_t *)ptr;
+
+    return (uint16_t)((p[0] << 8U) + p[1]);
+}
+
+static uint8_t message_header_qr(const struct message_header *header)
+{
+    return (header->octet2 >> 7) & 1;
+}
+
+static uint8_t message_header_rcode(const struct message_header *header)
+{
+    return header->octet3 & 0x0f;
+}
+
+/*
+ * Append a label or a dot to the target name with buffer overflow checks.
+ * Returns length of the non-truncated resulting string, may be bigger than
+ * name_max.
+ */
+static size_t message_name_append_safe(char *name, size_t name_len,
+                                       size_t name_max,
+                                       const char *tail, size_t tail_len)
+{
+    size_t copy_len;
+
+    copy_len = name_max > name_len ? name_max - name_len : 0;
+    copy_len = xmpp_min(tail_len, copy_len);
+    if (copy_len > 0)
+        strncpy(&name[name_len], tail, copy_len);
+
+    return name_len + tail_len;
+}
+
+/* Returns length of the compressed name. This is NOT the same as strlen(). */
+static unsigned message_name_get(const unsigned char *buf, size_t buf_len,
+                                 unsigned buf_offset,
+                                 char *name, size_t name_max)
+{
+    size_t name_len = 0;
+    unsigned i = buf_offset;
+    unsigned pointer;
+    unsigned rc;
+    unsigned char label_len;
+
+
+    while (1) {
+        if (i >= buf_len) return 0;
+        label_len = buf[i++];
+        if (label_len == 0) break;
+
+        /* Label */
+        if ((label_len & 0xc0) == 0) {
+            if (i + label_len - 1 >= buf_len) return 0;
+            if (name != NULL) {
+                name_len = message_name_append_safe(name, name_len, name_max,
+                                                    (char *)&buf[i], label_len);
+                name_len = message_name_append_safe(name, name_len, name_max,
+                                                    ".", 1);
+            }
+            i += label_len;
+
+        /* Pointer */
+        } else if ((label_len & 0xc0) == 0xc0) {
+            if (i >= buf_len) return 0;
+            pointer = (label_len & 0x3f) << 8 | buf[i++];
+            if (name != NULL && name_len >= name_max && name_max > 0) {
+                /* We have filled the name buffer. Don't pass it recursively. */
+                name[name_max - 1] = '\0';
+                name = NULL;
+                name_max = 0;
+            }
+            rc = message_name_get(buf, buf_len, pointer,
+                                  name != NULL ? &name[name_len] : NULL,
+                                  name_max > name_len ? name_max - name_len : 0);
+            if (rc == 0) return 0;
+            /* Pointer is always the last. */
+            break;
+
+        /* The 10 and 01 combinations are reserved for future use. */
+        } else {
+            return 0;
+        }
+    }
+    if (label_len == 0) {
+        if (name_len == 0) name_len = 1;
+        /*
+         * At this point name_len is length of the resulting name,
+         * including '\0'. This value can be exported to allocate buffer
+         * of precise size.
+         */
+        if (name != NULL && name_max > 0) {
+            /*
+             * Overwrite leading '.' with a '\0'. If the resulting name is
+             * bigger than name_max it is truncated.
+             */
+            name[xmpp_min(name_len, name_max) - 1] = '\0';
+        }
+    }
+
+    return i - buf_offset;
+}
+
+static unsigned message_name_len(const unsigned char *buf, size_t buf_len,
+                                 unsigned buf_offset)
+{
+    return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX);
+}
+
+static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
+{
+    resolver_srv_rr_t * rr_head;
+    resolver_srv_rr_t * rr_current;
+    resolver_srv_rr_t * rr_next;
+    resolver_srv_rr_t * rr_prev;
+    int swap;
+
+    rr_head = *srv_rr_list;
+
+    if ((rr_head == NULL) || (rr_head->next == NULL)) {
+        /* Empty or single record list */
+        return;
+    }
+
+    do {
+        rr_prev = NULL;
+        rr_current = rr_head;
+        rr_next = rr_head->next;
+        swap = 0;
+        while (rr_next != NULL) {
+            /*
+             * RFC2052: A client MUST attempt to contact the target host
+             * with the lowest-numbered priority it can reach.
+             * RFC2052: When selecting a target host among the
+             * those that have the same priority, the chance of trying
+             * this one first SHOULD be proportional to its weight.
+             */
+            if ((rr_current->priority > rr_next->priority) ||
+                (rr_current->priority == rr_next->priority &&
+                 rr_current->weight < rr_next->weight))
+            {
+                /* Swap node */
+                swap = 1;
+                if (rr_prev != NULL) {
+                    rr_prev->next = rr_next;
+                } else {
+                    /* Swap head node */
+                    rr_head = rr_next;
+                }
+                rr_current->next = rr_next->next;
+                rr_next->next = rr_current;
+
+                rr_prev = rr_next;
+                rr_next = rr_current->next;
+            } else {
+                /* Next node */
+                rr_prev = rr_current;
+                rr_current = rr_next;
+                rr_next = rr_next->next;
+            }
+        }
+    } while (swap != 0);
+
+    *srv_rr_list = rr_head;
+}
+
+#define BUF_OVERFLOW_CHECK(ptr, len) do {         \
+    if ((ptr) >= (len)) {                         \
+        if (*srv_rr_list != NULL)                 \
+            resolver_srv_free(ctx, *srv_rr_list); \
+        *srv_rr_list = NULL;                      \
+        return XMPP_DOMAIN_NOT_FOUND;             \
+    }                                             \
+} while (0)
+
+int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf,
+                            size_t len, resolver_srv_rr_t **srv_rr_list)
+{
+    unsigned i;
+    unsigned j;
+    unsigned name_len;
+    unsigned rdlength;
+    uint16_t type;
+    uint16_t class;
+    struct message_header header;
+    resolver_srv_rr_t *rr;
+
+    *srv_rr_list = NULL;
+
+    if (len < MESSAGE_HEADER_LEN)
+        return XMPP_DOMAIN_NOT_FOUND;
+
+    header.id = xmpp_ntohs_ptr(&buf[0]);
+    header.octet2 = buf[2];
+    header.octet3 = buf[3];
+    header.qdcount = xmpp_ntohs_ptr(&buf[4]);
+    header.ancount = xmpp_ntohs_ptr(&buf[6]);
+    header.nscount = xmpp_ntohs_ptr(&buf[8]);
+    header.arcount = xmpp_ntohs_ptr(&buf[10]);
+    if (message_header_qr(&header) != MESSAGE_RESPONSE ||
+        message_header_rcode(&header) != 0)
+    {
+        return XMPP_DOMAIN_NOT_FOUND;
+    }
+    j = MESSAGE_HEADER_LEN;
+
+    /* skip question section */
+    for (i = 0; i < header.qdcount; ++i) {
+        BUF_OVERFLOW_CHECK(j, len);
+        name_len = message_name_len(buf, len, j);
+        /* error in name format */
+        if (name_len == 0) return XMPP_DOMAIN_NOT_FOUND;
+        j += name_len + 4;
+    }
+
+    for (i = 0; i < header.ancount; ++i) {
+        BUF_OVERFLOW_CHECK(j, len);
+        name_len = message_name_len(buf, len, j);
+        /* error in name format */
+        if (name_len == 0) return XMPP_DOMAIN_NOT_FOUND;
+        j += name_len;
+        BUF_OVERFLOW_CHECK(j + 16, len);
+        type = xmpp_ntohs_ptr(&buf[j]);
+        class = xmpp_ntohs_ptr(&buf[j + 2]);
+        rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
+        j += 10;
+        if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
+            rr = xmpp_alloc(ctx, sizeof(*rr));
+            rr->next = *srv_rr_list;
+            rr->priority = xmpp_ntohs_ptr(&buf[j]);
+            rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
+            rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
+            name_len = message_name_get(buf, len, j + 6, rr->target,
+                                        sizeof(rr->target));
+            if (name_len > 0)
+                *srv_rr_list = rr;
+            else
+                xmpp_free(ctx, rr); /* skip broken record */
+        }
+        j += rdlength;
+    }
+    resolver_srv_list_sort(srv_rr_list);
+
+    return *srv_rr_list != NULL ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND;
+}
+
+int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto,
+                        const char *domain, resolver_srv_rr_t **srv_rr_list)
+{
+    char fulldomain[2048];
+    unsigned char buf[65535];
+    int len;
+    int set = XMPP_DOMAIN_NOT_FOUND;
+
+    xmpp_snprintf(fulldomain, sizeof(fulldomain),
+                  "_%s._%s.%s", service, proto, domain);
+
+    *srv_rr_list = NULL;
+
+#ifdef _WIN32
+    set = resolver_win32_srv_lookup(ctx, fulldomain, srv_rr_list);
+    if (set == XMPP_DOMAIN_FOUND)
+        return set;
+    len = resolver_win32_srv_query(fulldomain, buf, sizeof(buf));
+#else /* _WIN32 */
+    len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf, sizeof(buf));
+#endif /* _WIN32 */
+
+    if (len > 0)
+        set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list);
+
+    return set;
+}
+
+void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list)
+{
+    resolver_srv_rr_t *rr;
+
+    while (srv_rr_list != NULL) {
+        rr = srv_rr_list->next;
+        xmpp_free(ctx, srv_rr_list);
+        srv_rr_list = rr;
+    }
+}
+
+#ifdef _WIN32
+
+/*******************************************************************************
+ * Next part was copied from sock.c and contains old win32 code.
+ *
+ * The idea is to get raw response from a name server and pass it to
+ * resolver_srv_lookup_buf(). In fact, resolver_win32_srv_query() replaces
+ * the call of res_query().
+ * Dnsapi code is moved to a separated function resolver_srv_win32_lookup() and
+ * changed to meet new API.
+ *
+ * XXX If the code is compiled it should work like before.
+ ******************************************************************************/
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windns.h>
+#include <Iphlpapi.h>
+
+struct dnsquery_header
+{
+       unsigned short id;
+       unsigned char qr;
+       unsigned char opcode;
+       unsigned char aa;
+       unsigned char tc;
+       unsigned char rd;
+       unsigned char ra;
+       unsigned char z;
+       unsigned char rcode;
+       unsigned short qdcount;
+       unsigned short ancount;
+       unsigned short nscount;
+       unsigned short arcount;
+};
+
+struct dnsquery_question
+{
+       char qname[1024];
+       unsigned short qtype;
+       unsigned short qclass;
+};
+
+static void netbuf_add_16bitnum(unsigned char *buf, int buflen, int *offset, unsigned short num)
+{
+       unsigned char *start = buf + *offset;
+       unsigned char *p = start;
+
+       /* assuming big endian */
+       *p++ = (num >> 8) & 0xff;
+       *p++ = (num)      & 0xff;
+
+       *offset += 2;
+}
+
+static void netbuf_add_domain_name(unsigned char *buf, int buflen, int *offset,
+                           char *name)
+{
+       unsigned char *start = buf + *offset;
+       unsigned char *p = start;
+       unsigned char *wordstart, *wordend;
+
+       wordstart = (unsigned char *)name;
+
+       while (*wordstart)
+       {
+               int len;
+               wordend = wordstart;
+               while (*wordend && *wordend != '.')
+               {
+                       wordend++;
+               }
+
+               len = (int)(wordend - wordstart);
+
+               if (len > 0x3F)
+               {
+                       len = 0x3F;
+               }
+
+               *p++ = len;
+
+               while (wordstart != wordend)
+               {
+                       *p++ = *wordstart++;
+               }
+
+               if (*wordstart == '.')
+               {
+                       wordstart++;
+               }
+       }
+
+       *p++ = '\0';
+
+       *offset += p - start;
+}
+
+static void netbuf_add_dnsquery_header(unsigned char *buf, int buflen, int *offset, struct dnsquery_header *header)
+{
+       unsigned char *p;
+
+       netbuf_add_16bitnum(buf, buflen, offset, header->id);
+
+       p = buf + *offset;
+       *p++ =    ((header->qr     & 0x01) << 7)
+               | ((header->opcode & 0x0F) << 3)
+               | ((header->aa     & 0x01) << 2)
+               | ((header->tc     & 0x01) << 1)
+               | ((header->rd     & 0x01));
+       *p++ =    ((header->ra     & 0x01) << 7)
+               | ((header->z      & 0x07) << 4)
+               | ((header->rcode  & 0x0F));
+       *offset += 2;
+
+       netbuf_add_16bitnum(buf, buflen, offset, header->qdcount);
+       netbuf_add_16bitnum(buf, buflen, offset, header->ancount);
+       netbuf_add_16bitnum(buf, buflen, offset, header->nscount);
+       netbuf_add_16bitnum(buf, buflen, offset, header->arcount);
+}
+
+static void netbuf_add_dnsquery_question(unsigned char *buf, int buflen, int *offset, struct dnsquery_question *question)
+{
+       netbuf_add_domain_name(buf, buflen, offset, question->qname);
+       netbuf_add_16bitnum(buf, buflen, offset, question->qtype);
+       netbuf_add_16bitnum(buf, buflen, offset, question->qclass);
+}
+
+static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx, const char *fulldomain,
+                                     resolver_srv_rr_t **srv_rr_list)
+{
+    resolver_srv_rr_t *rr;
+    HINSTANCE hdnsapi = NULL;
+
+    DNS_STATUS (WINAPI * pDnsQuery_A)(PCSTR, WORD, DWORD, PIP4_ARRAY, PDNS_RECORD*, PVOID*);
+    void (WINAPI * pDnsRecordListFree)(PDNS_RECORD, DNS_FREE_TYPE);
+
+    if (hdnsapi = LoadLibraryA("dnsapi.dll")) {
+        pDnsQuery_A = (void *)GetProcAddress(hdnsapi, "DnsQuery_A");
+        pDnsRecordListFree = (void *)GetProcAddress(hdnsapi, "DnsRecordListFree");
+
+        if (pDnsQuery_A && pDnsRecordListFree) {
+            PDNS_RECORD dnsrecords = NULL;
+            DNS_STATUS error;
+
+            error = pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &dnsrecords, NULL);
+
+            if (error == 0) {
+                PDNS_RECORD current = dnsrecords;
+
+                while (current) {
+                    if (current->wType == DNS_TYPE_SRV) {
+                        rr = xmpp_alloc(ctx, sizeof(*rr));
+                        if (rr == NULL)
+                            break;
+                        rr->next = *srv_rr_list;
+                        rr->port = current->Data.Srv.wPort;
+                        rr->priority = current->Data.Srv.wPriority;
+                        rr->weight = current->Data.Srv.wWeight;
+                        xmpp_snprintf(rr->target, sizeof(rr->target), "%s",
+                                      current->Data.Srv.pNameTarget);
+                        *srv_rr_list = rr;
+                    }
+                    current = current->pNext;
+                }
+            }
+
+            pDnsRecordListFree(dnsrecords, DnsFreeRecordList);
+        }
+
+        FreeLibrary(hdnsapi);
+    }
+    resolver_srv_list_sort(srv_rr_list);
+
+    return *srv_rr_list != NULL ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND;
+}
+
+static int resolver_win32_srv_query(const char *fulldomain,
+                                    unsigned char *buf, size_t len)
+{
+    int set = 0;
+    int insize;
+
+    /* if dnsapi didn't work/isn't there, try querying the dns server manually */
+    if (!set)
+    {
+       struct dnsquery_header header;
+       struct dnsquery_question question;
+       int offset = 0;
+       int addrlen;
+       sock_t sock;
+       struct sockaddr_in dnsaddr;
+       char dnsserverips[16][256];
+       int numdnsservers = 0;
+       int j;
+
+       /* Try getting the DNS server ips from GetNetworkParams() in iphlpapi first */
+       if (!numdnsservers)
+       {
+               HINSTANCE hiphlpapi = NULL;
+               DWORD (WINAPI * pGetNetworkParams)(PFIXED_INFO, PULONG);
+
+               if (hiphlpapi = LoadLibraryA("Iphlpapi.dll"))
+               {
+                       pGetNetworkParams = (void *)GetProcAddress(hiphlpapi, "GetNetworkParams");
+
+                       if (pGetNetworkParams)
+                       {
+                               FIXED_INFO *fi;
+                               ULONG len;
+                               DWORD error;
+                               char buffer[65535];
+
+                               len = 65535;
+                               fi = (FIXED_INFO *)buffer;
+
+                               if ((error = pGetNetworkParams(fi, &len)) == ERROR_SUCCESS)
+                               {
+                                       IP_ADDR_STRING *pias = &(fi->DnsServerList);
+
+                                       while (pias && numdnsservers < 16)
+                                       {
+                                                strcpy(dnsserverips[numdnsservers++], pias->IpAddress.String);
+                                               pias = pias->Next;
+                                       }
+                               }
+                       }
+               }
+               FreeLibrary(hiphlpapi);
+       }
+
+       /* Next, try getting the DNS server ips from the registry */
+       if (!numdnsservers)
+       {
+               HKEY search;
+               LONG error;
+
+               error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0, KEY_READ, &search);
+
+               if (error != ERROR_SUCCESS)
+               {
+                       error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &search);
+               }
+
+               if (error == ERROR_SUCCESS)
+               {
+                       char name[512];
+                       DWORD len = 512;
+
+                       error = RegQueryValueExA(search, "NameServer", NULL, NULL, (LPBYTE)name, &len);
+
+                       if (error != ERROR_SUCCESS)
+                       {
+                               error = RegQueryValueExA(search, "DhcpNameServer", NULL, NULL, (LPBYTE)name, &len);
+                       }
+
+                       if (error == ERROR_SUCCESS)
+                       {
+                               char *parse = "0123456789.", *start, *end;
+                               start = name;
+                               end = name;
+                               name[len] = '\0';
+
+                               while (*start && numdnsservers < 16)
+                               {
+                                       while (strchr(parse, *end))
+                                       {
+                                               end++;
+                                       }
+
+                                       strncpy(dnsserverips[numdnsservers++], start, end - start);
+
+                                       while (*end && !strchr(parse, *end))
+                                       {
+                                               end++;
+                                       }
+
+                                       start = end;
+                               }
+                       }
+               }
+
+               RegCloseKey(search);
+       }
+
+       if (!numdnsservers)
+       {
+               HKEY searchlist;
+               LONG error;
+
+               error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", 0, KEY_READ, &searchlist);
+
+               if (error == ERROR_SUCCESS)
+               {
+                       unsigned int i;
+                       DWORD numinterfaces = 0;
+
+                       RegQueryInfoKey(searchlist, NULL, NULL, NULL, &numinterfaces, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+                       for (i = 0; i < numinterfaces; i++)
+                       {
+                               char name[512];
+                               DWORD len = 512;
+                               HKEY searchentry;
+
+                               RegEnumKeyEx(searchlist, i, (LPTSTR)name, &len, NULL, NULL, NULL, NULL);
+
+                               if (RegOpenKeyExA(searchlist, name, 0, KEY_READ, &searchentry) == ERROR_SUCCESS)
+                               {
+                                       if (RegQueryValueExA(searchentry, "DhcpNameServer", NULL, NULL, (LPBYTE)name, &len) == ERROR_SUCCESS)
+                                       {
+                                               char *parse = "0123456789.", *start, *end;
+                                               start = name;
+                                               end = name;
+                                               name[len] = '\0';
+
+                                               while (*start && numdnsservers < 16)
+                                               {
+                                                       while (strchr(parse, *end))
+                                                       {
+                                                               end++;
+                                                       }
+
+                                                       strncpy(dnsserverips[numdnsservers++], start, end - start);
+
+                                                       while (*end && !strchr(parse, *end))
+                                                       {
+                                                               end++;
+                                                       }
+
+                                                       start = end;
+                                               }
+                                       }
+                                       else if (RegQueryValueExA(searchentry, "NameServer", NULL, NULL, (LPBYTE)name, &len) == ERROR_SUCCESS)
+                                       {
+                                               char *parse = "0123456789.", *start, *end;
+                                               start = name;
+                                               end = name;
+                                               name[len] = '\0';
+
+                                               while (*start && numdnsservers < 16)
+                                               {
+                                                       while (strchr(parse, *end))
+                                                       {
+                                                               end++;
+                                                       }
+
+                                                       strncpy(dnsserverips[numdnsservers++], start, end - start);
+
+                                                       while (*end && !strchr(parse, *end))
+                                                       {
+                                                               end++;
+                                                       }
+
+                                                       start = end;
+                                               }
+                                       }
+                                       RegCloseKey(searchentry);
+                               }
+                       }
+                       RegCloseKey(searchlist);
+               }
+       }
+
+       /* If we have a DNS server, use it */
+       if (numdnsservers)
+       {
+               ULONG nonblocking = 1;
+               int i;
+
+               memset(&header, 0, sizeof(header));
+               header.id = 12345; /* FIXME: Get a better id here */
+               header.rd = 1;
+               header.qdcount = 1;
+
+               netbuf_add_dnsquery_header(buf, len, &offset, &header);
+
+               memset(&question, 0, sizeof(question));
+               strncpy(question.qname, fulldomain, 1024);
+               question.qtype = 33; /* SRV */
+               question.qclass = 1; /* INTERNET! */
+
+               netbuf_add_dnsquery_question(buf, len, &offset, &question);
+
+               insize = 0;
+               for (i = 0; i < numdnsservers && insize <= 0; i++)
+               {
+                       sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+                       ioctlsocket(sock, FIONBIO, &nonblocking);
+
+                       memset(&dnsaddr, 0, sizeof(dnsaddr));
+
+                       dnsaddr.sin_family      = AF_INET;
+                       dnsaddr.sin_port        = htons(53);
+                       dnsaddr.sin_addr.s_addr = inet_addr(dnsserverips[i]);
+
+                       addrlen = sizeof(dnsaddr);
+                       sendto(sock, (char *)buf, offset, 0, (struct sockaddr *)&dnsaddr, addrlen);
+                       for (j = 0; j < 50; j++)
+                       {
+                               insize = recvfrom(sock, (char *)buf, len, 0, (struct sockaddr *)&dnsaddr, &addrlen);
+                               if (insize == SOCKET_ERROR)
+                               {
+                                       if (sock_error() == WSAEWOULDBLOCK)
+                                       {
+                                               Sleep(100);
+                                       }
+                                       else
+                                       {
+                                               break;
+                                       }
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+
+                       closesocket(sock);
+               }
+                set = insize > 0;
+       }
+
+    }
+
+    return set ? insize : -1;
+}
+
+#endif /* _WIN32 */
diff --git a/src/libstrophe/resolver.h b/src/libstrophe/resolver.h
new file mode 100644 (file)
index 0000000..ac4653e
--- /dev/null
@@ -0,0 +1,69 @@
+/* resolver.h
+ * strophe XMPP client library -- DNS resolver
+ *
+ * Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  DNS resolver.
+ */
+
+#ifndef __LIBSTROPHE_RESOLVER_H__
+#define __LIBSTROPHE_RESOLVER_H__
+
+#include "ostypes.h"
+#include "common.h"
+
+typedef enum {
+    XMPP_DOMAIN_NOT_FOUND,
+    XMPP_DOMAIN_FOUND,
+    XMPP_DOMAIN_ALTDOMAIN
+} xmpp_domain_state_t;
+
+typedef struct resolver_srv_rr_struc {
+    uint16_t priority;
+    uint16_t weight;
+    uint16_t port;
+    char target[MAX_DOMAIN_LEN];
+    struct resolver_srv_rr_struc *next;
+} resolver_srv_rr_t;
+
+/** Perform lookup for RFC1035 message format.
+ *  This function allocates all elements.
+ *
+ *  @param ctx a Strophe context object
+ *  @param buf message in RFC1035 format
+ *  @param len length of the message
+ *  @param srv_rr_list is the result
+ *
+ *  @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
+ */
+int resolver_srv_lookup_buf(xmpp_ctx_t *ctx, const unsigned char *buf,
+                            size_t len, resolver_srv_rr_t **srv_rr_list);
+/** Resolve SRV record.
+ *
+ *  @param ctx a Strophe context object
+ *  @param service service of the SRV record
+ *  @param proto protocol of the SRV record
+ *  @param domain resolving domain
+ *  @param srv_rr_list is the result
+ *
+ *  @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
+ */
+int resolver_srv_lookup(xmpp_ctx_t *ctx, const char *service, const char *proto,
+                        const char *domain, resolver_srv_rr_t **srv_rr_list);
+
+/** Release a list returned by resolver_srv_lookup() or
+ *  resolver_srv_lookup_buf().
+ *
+ *  @param ctx a Strophe context object
+ *  @param srv_rr_list a list allocated by lookup functions
+ */
+void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list);
+
+#endif /* __LIBSTROPHE_RESOLVER_H__ */
index eba2d9e..5c34dad 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
 #include "common.h"
 #include "sasl.h"
 #include "md5.h"
-
-
-static int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len);
-
-static char *base64_encode(xmpp_ctx_t *ctx,
-                    const unsigned char * const buffer, const unsigned len);
-
-static int base64_decoded_len(xmpp_ctx_t *ctx,
-                       const char * const buffer, const unsigned len);
-
-static unsigned char *base64_decode(xmpp_ctx_t *ctx,
-                             const char * const buffer, const unsigned  len);
-
-/* 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"
+#include "scram.h"
+#include "rand.h"
+#include "util.h"
+
+/* strtok_s() has appeared in visual studio 2005.
+   Use own implementation for older versions. */
+#ifdef _MSC_VER
+# if (_MSC_VER >= 1400)
+# define strtok_r strtok_s
+# else
+# define strtok_r xmpp_strtok_r
+# endif
+#endif /* _MSC_VER */
 
 
 /** generate authentication string for the SASL PLAIN mechanism */
 char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
     size_t idlen, passlen;
+    size_t msglen;
     char *result = NULL;
     char *msg;
     
@@ -61,13 +46,14 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
 
     idlen = strlen(authid);
     passlen = strlen(password);
-    msg = xmpp_alloc(ctx, 2 + idlen + passlen);
+    msglen = 2 + idlen + passlen;
+    msg = xmpp_alloc(ctx, msglen);
     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, (unsigned char *)msg, 2 + idlen + passlen);
+       result = xmpp_base64_encode(ctx, (unsigned char *)msg, msglen);
        xmpp_free(ctx, msg);
     }
 
@@ -113,7 +99,7 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
     char *key, *value;
     unsigned char *s, *t;
 
-    text = base64_decode(ctx, msg, strlen(msg));
+    text = (unsigned char *)xmpp_base64_decode_str(ctx, msg, strlen(msg));
     if (text == NULL) {
        xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
        return NULL;
@@ -239,6 +225,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
     struct MD5Context MD5;
     unsigned char digest[16], HA1[16], HA2[16];
     char hex[32];
+    char cnonce[13];
 
     /* our digest response is 
        Hex( KD( HEX(MD5(A1)),
@@ -271,8 +258,8 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
 
     /* 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"));
+    xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce));
+    hash_add(table, "cnonce", xmpp_strdup(ctx, cnonce));
     hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
     hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
     value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
@@ -365,266 +352,114 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
     hash_release(table); /* also frees value strings */
 
     /* reuse response for the base64 encode of our result */
-    response = base64_encode(ctx, (unsigned char *)result, strlen(result));
+    response = xmpp_base64_encode(ctx, (unsigned char *)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 unsigned len)
-{
-    /* encoded steam is 4 bytes for every three, rounded up */
-    return ((len + 2)/3) << 2;
-}
-
-static char *base64_encode(xmpp_ctx_t *ctx, 
-                   const unsigned char * const buffer, const unsigned len)
+/** generate auth response string for the SASL SCRAM-SHA-1 mechanism */
+char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
+                      const char *first_bare, const char *jid,
+                      const char *password)
 {
-    int clen;
-    char *cbuf, *c;
-    uint32_t word, hextet;
-    int i;
+    uint8_t key[SHA1_DIGEST_SIZE];
+    uint8_t sign[SHA1_DIGEST_SIZE];
+    char *r = NULL;
+    char *s = NULL;
+    char *i = NULL;
+    unsigned char *sval;
+    size_t sval_len;
+    long ival;
+    char *tmp;
+    char *ptr;
+    char *saveptr = NULL;
+    char *response;
+    char *auth;
+    char *response_b64;
+    char *sign_b64;
+    char *result = NULL;
+    size_t response_len;
+    size_t auth_len;
+    int j;
 
-    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] & 0x03) << 4;
-               *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';
+    tmp = xmpp_strdup(ctx, challenge);
+    if (!tmp) {
+        return NULL;
     }
 
-    return cbuf;
-}
-
-static int base64_decoded_len(xmpp_ctx_t *ctx, 
-                      const char * const buffer, const unsigned 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 = 1;
-       else if (c == 64) {
-           c = _base64_invcharmap[(int)buffer[len-3]];
-           if (c < 64) nudge = 2;
-       } 
+    ptr = strtok_r(tmp, ",", &saveptr);
+    while (ptr) {
+        if (strncmp(ptr, "r=", 2) == 0) {
+            r = ptr;
+        } else if (strncmp(ptr, "s=", 2) == 0) {
+            s = ptr + 2;
+        } else if (strncmp(ptr, "i=", 2) == 0) {
+            i = ptr + 2;
+        }
+        ptr = strtok_r(NULL, ",", &saveptr);
     }
-    if (nudge < 0) return 0; /* reject bad coding */
 
-    /* decoded steam is 3 bytes for every four */ 
-    return 3 * (len >> 2) - nudge;
-}
-
-static unsigned char *base64_decode(xmpp_ctx_t *ctx,
-                            const char * const buffer, const unsigned 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;
+    if (!r || !s || !i) {
+        goto out;
     }
 
-    return 0; 
-}
-
-int test_charmap_range(void)
-{
-    int i, v;
+    xmpp_base64_decode_bin(ctx, s, strlen(s), &sval, &sval_len);
+    if (!sval) {
+        goto out;
+    }
+    ival = strtol(i, &saveptr, 10);
 
-    for (i = 64; i < 256; i++) {
-       v = _base64_invcharmap[i];
-       if (i < 64) return 1;
+    auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge);
+    auth = xmpp_alloc(ctx, auth_len);
+    if (!auth) {
+        goto out_sval;
     }
 
-    return 0;
-}
+    response_len = 39 + strlen(r);
+    response = xmpp_alloc(ctx, response_len);
+    if (!response) {
+        goto out_auth;
+    }
 
-int main(int argc, char *argv[])
-{
-    int ret = 0;
+    xmpp_snprintf(response, response_len, "c=biws,%s", r);
+    xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge,
+                  response);
 
-    printf("testing charmap identity...");
-    ret = test_charmap_identity();
-    if (ret) return ret;
-    printf(" ok.\n");
+    SCRAM_SHA1_ClientKey((uint8_t *)password, strlen(password),
+                         (uint8_t *)sval, sval_len, (uint32_t)ival, key);
+    SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign);
+    for (j = 0; j < SHA1_DIGEST_SIZE; j++) {
+        sign[j] ^= key[j];
+    }
 
-    printf("testing charmap range...");
-    ret = test_charmap_range();
-    if (ret) return ret;
-    printf(" ok.\n");
+    sign_b64 = xmpp_base64_encode(ctx, sign, sizeof(sign));
+    if (!sign_b64) {
+        goto out_response;
+    }
 
-    printf("no error\n");
-    return 0;
+    if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) {
+        xmpp_free(ctx, sign_b64);
+        goto out_response;
+    }
+    strcat(response, ",p=");
+    strcat(response, sign_b64);
+    xmpp_free(ctx, sign_b64);
+
+    response_b64 = xmpp_base64_encode(ctx, (unsigned char *)response,
+                                      strlen(response));
+    if (!response_b64) {
+        goto out_response;
+    }
+    result = response_b64;
+
+out_response:
+    xmpp_free(ctx, response);
+out_auth:
+    xmpp_free(ctx, auth);
+out_sval:
+    xmpp_free(ctx, sval);
+out:
+    xmpp_free(ctx, tmp);
+    return result;
 }
-
-#endif /* TEST */
index bf1bbfe..07f0f14 100644 (file)
@@ -6,10 +6,7 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+**  This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
@@ -26,5 +23,8 @@
 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);
+char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge,
+                      const char *first_bare, const char *jid,
+                      const char *password);
 
 #endif /* _LIBXMPP_SASL_H__ */
diff --git a/src/libstrophe/scram.c b/src/libstrophe/scram.c
new file mode 100644 (file)
index 0000000..97d0784
--- /dev/null
@@ -0,0 +1,131 @@
+/* scram.c
+ * strophe XMPP client library
+ *
+ * SCRAM-SHA1 helper functions according to RFC5802
+ * HMAC-SHA1 implementation according to RFC2104
+ *
+ * Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  SCRAM-SHA1 helper functions.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "sha1.h"
+#include "ostypes.h"
+
+#include "scram.h"
+
+#define HMAC_BLOCK_SIZE 64
+
+static const uint8_t ipad = 0x36;
+static const uint8_t opad = 0x5C;
+
+static void crypto_HMAC_SHA1(const uint8_t *key, size_t key_len,
+                             const uint8_t *text, size_t len,
+                             uint8_t *digest)
+{
+    uint8_t key_pad[HMAC_BLOCK_SIZE];
+    uint8_t key_ipad[HMAC_BLOCK_SIZE];
+    uint8_t key_opad[HMAC_BLOCK_SIZE];
+    uint8_t sha_digest[SHA1_DIGEST_SIZE];
+    int i;
+    SHA1_CTX ctx;
+
+    memset(key_pad, 0, sizeof(key_pad));
+    if (key_len <= HMAC_BLOCK_SIZE) {
+        memcpy(key_pad, key, key_len);
+    } else {
+        /* according to RFC2104 */
+        crypto_SHA1(key, key_len, key_pad);
+    }
+
+    for (i = 0; i < HMAC_BLOCK_SIZE; i++) {
+        key_ipad[i] = key_pad[i] ^ ipad;
+        key_opad[i] = key_pad[i] ^ opad;
+    }
+
+    crypto_SHA1_Init(&ctx);
+    crypto_SHA1_Update(&ctx, key_ipad, HMAC_BLOCK_SIZE);
+    crypto_SHA1_Update(&ctx, text, len);
+    crypto_SHA1_Final(&ctx, sha_digest);
+
+    crypto_SHA1_Init(&ctx);
+    crypto_SHA1_Update(&ctx, key_opad, HMAC_BLOCK_SIZE);
+    crypto_SHA1_Update(&ctx, sha_digest, SHA1_DIGEST_SIZE);
+    crypto_SHA1_Final(&ctx, digest);
+}
+
+static void SCRAM_SHA1_Hi(const uint8_t *text, size_t len,
+                          const uint8_t *salt, size_t salt_len, uint32_t i,
+                          uint8_t *digest)
+{
+    int  k;
+    uint32_t j;
+    uint8_t tmp[128];
+
+    static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1};
+
+    /* assume salt + INT(1) isn't longer than sizeof(tmp) */
+    assert(salt_len <= sizeof(tmp) - sizeof(int1));
+
+    memset(digest, 0, SHA1_DIGEST_SIZE);
+    if (i == 0) {
+        return;
+    }
+
+    memcpy(tmp, salt, salt_len);
+    memcpy(&tmp[salt_len], int1, sizeof(int1));
+
+    /* 'text' for Hi is a 'key' for HMAC */
+    crypto_HMAC_SHA1(text, len, tmp, salt_len + sizeof(int1), digest);
+    memcpy(tmp, digest, SHA1_DIGEST_SIZE);
+
+    for (j = 1; j < i; j++) {
+        crypto_HMAC_SHA1(text, len, tmp, SHA1_DIGEST_SIZE, tmp);
+        for (k = 0; k < SHA1_DIGEST_SIZE; k++) {
+            digest[k] ^= tmp[k];
+        }
+    }
+}
+
+void SCRAM_SHA1_ClientKey(const uint8_t *password, size_t len,
+                          const uint8_t *salt, size_t salt_len, uint32_t i,
+                          uint8_t *key)
+{
+    uint8_t salted[SHA1_DIGEST_SIZE];
+
+    /* XXX: Normalize(password) is omitted */
+
+    SCRAM_SHA1_Hi(password, len, salt, salt_len, i, salted);
+    crypto_HMAC_SHA1(salted, SHA1_DIGEST_SIZE, (uint8_t *)"Client Key",
+                     strlen("Client Key"), key);
+}
+
+void SCRAM_SHA1_ClientSignature(const uint8_t *ClientKey,
+                                const uint8_t *AuthMessage, size_t len,
+                                uint8_t *sign)
+{
+    uint8_t stored[SHA1_DIGEST_SIZE];
+
+    crypto_SHA1(ClientKey, SHA1_DIGEST_SIZE, stored);
+    crypto_HMAC_SHA1(stored, SHA1_DIGEST_SIZE, AuthMessage, len, sign);
+}
+
+void SCRAM_SHA1_ClientProof(const uint8_t *ClientKey,
+                            const uint8_t *ClientSignature,
+                            uint8_t *proof)
+{
+    int i;
+    for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+        proof[i] = ClientKey[i] ^ ClientSignature[i];
+    }
+}
diff --git a/src/libstrophe/scram.h b/src/libstrophe/scram.h
new file mode 100644 (file)
index 0000000..e6b8cc8
--- /dev/null
@@ -0,0 +1,36 @@
+/* scram.h
+ * strophe XMPP client library -- SCRAM-SHA1 helper functions
+ *
+ * Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
+ *
+ *  This software is provided AS-IS with no warranty, either express
+ *  or implied.
+ *
+ *  This program is dual licensed under the MIT and GPLv3 licenses.
+ */
+
+/** @file
+ *  SCRAM-SHA1 helper functions.
+ */
+
+#ifndef __LIBSTROPHE_SCRAM_H__
+#define __LIBSTROPHE_SCRAM_H__
+
+/* make sure the stdint.h types are available */
+#include "ostypes.h"
+
+#include "sha1.h"
+
+void SCRAM_SHA1_ClientKey(const uint8_t *password, size_t len,
+                          const uint8_t *salt, size_t salt_len, uint32_t i,
+                          uint8_t *key);
+
+void SCRAM_SHA1_ClientSignature(const uint8_t *ClientKey,
+                                const uint8_t *AuthMessage, size_t len,
+                                uint8_t *sign);
+
+void SCRAM_SHA1_ClientProof(const uint8_t *ClientKey,
+                            const uint8_t *ClientSignature,
+                            uint8_t *proof);
+
+#endif /* __LIBSTROPHE_SCRAM_H__ */
diff --git a/src/libstrophe/sha1.c b/src/libstrophe/sha1.c
new file mode 100644 (file)
index 0000000..429e22f
--- /dev/null
@@ -0,0 +1,254 @@
+/** @file
+ *  SHA-1 hash.
+ */
+
+/*
+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
+*/
+
+#include <string.h>
+
+#include "ostypes.h"
+#include "sha1.h"
+
+/* Don't change user's data */
+#define SHA1HANDSOFF
+
+static uint32_t host_to_be(uint32_t i);
+static 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 */
+#define blk0(i) (block->l[i] = host_to_be(block->l[i]))
+#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);
+
+
+static uint32_t host_to_be(uint32_t i)
+{
+#define le_to_be(i) ((rol((i),24) & 0xFF00FF00) | (rol((i),8) & 0x00FF00FF))
+#if defined(__BIG_ENDIAN__) || \
+    (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+    __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+    return i;
+#elif defined(__LITTLE_ENDIAN__) || \
+      (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+      __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+    return le_to_be(i);
+#else /* fallback to run-time check */
+    static const union {
+        uint32_t u;
+        unsigned char c;
+    } check = {1};
+    return check.c ? le_to_be(i) : i;
+#endif
+#undef le_to_be
+}
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+static 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 crypto_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 crypto_SHA1_Update(SHA1_CTX* context, const uint8_t* data,
+                        const size_t len)
+{
+    size_t i, j;
+
+    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);
+}
+
+
+/* Add padding and return the message digest. */
+void crypto_SHA1_Final(SHA1_CTX* context, uint8_t* digest)
+{
+    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 */
+    }
+    crypto_SHA1_Update(context, (uint8_t *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        crypto_SHA1_Update(context, (uint8_t *)"\0", 1);
+    }
+    crypto_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
+}
+
+
+void crypto_SHA1(const uint8_t* data, size_t len, uint8_t* digest)
+{
+    SHA1_CTX ctx;
+    crypto_SHA1_Init(&ctx);
+    crypto_SHA1_Update(&ctx, data, len);
+    crypto_SHA1_Final(&ctx, digest);
+}
diff --git a/src/libstrophe/sha1.h b/src/libstrophe/sha1.h
new file mode 100644 (file)
index 0000000..8dd7700
--- /dev/null
@@ -0,0 +1,36 @@
+/* public api for steve reid's public domain SHA-1 implementation */
+/* this file is in the public domain */
+
+/** @file
+ *  SHA-1 hash API.
+ */
+
+#ifndef __LIBSTROPHE_SHA1_H__
+#define __LIBSTROPHE_SHA1_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* make sure the stdint.h types are available */
+#include "ostypes.h"
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    uint8_t  buffer[64];
+} SHA1_CTX;
+
+#define SHA1_DIGEST_SIZE 20
+
+void crypto_SHA1_Init(SHA1_CTX* context);
+void crypto_SHA1_Update(SHA1_CTX* context, const uint8_t* data,
+                        const size_t len);
+void crypto_SHA1_Final(SHA1_CTX* context, uint8_t* digest);
+void crypto_SHA1(const uint8_t* data, size_t len, uint8_t* digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBSTROPHE_SHA1_H__ */
diff --git a/src/libstrophe/snprintf.c b/src/libstrophe/snprintf.c
deleted file mode 100644 (file)
index 055cdf7..0000000
+++ /dev/null
@@ -1,828 +0,0 @@
-/*
- * Copyright Patrick Powell 1995
- * This code is based on code written by Patrick Powell (papowell@astart.com)
- * It may be used for any purpose as long as this notice remains intact
- * on all source code distributions
- */
-
-/**************************************************************
- * Original:
- * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
- * A bombproof version of doprnt (dopr) included.
- * Sigh.  This sort of thing is always nasty do deal with.  Note that
- * the version here does not include floating point...
- *
- * snprintf() is used instead of sprintf() as it does limit checks
- * for string length.  This covers a nasty loophole.
- *
- * The other functions are there to prevent NULL pointers from
- * causing nast effects.
- *
- * More Recently:
- *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
- *  This was ugly.  It is still ugly.  I opted out of floating point
- *  numbers, but the formatter understands just about everything
- *  from the normal C string format, at least as far as I can tell from
- *  the Solaris 2.5 printf(3S) man page.
- *
- *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
- *    Ok, added some minimal floating point support, which means this
- *    probably requires libm on most operating systems.  Don't yet
- *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
- *    was pretty badly broken, it just wasn't being exercised in ways
- *    which showed it, so that's been fixed.  Also, formated the code
- *    to mutt conventions, and removed dead code left over from the
- *    original.  Also, there is now a builtin-test, just compile with:
- *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
- *    and run snprintf for results.
- * 
- *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
- *    The PGP code was using unsigned hexadecimal formats. 
- *    Unfortunately, unsigned formats simply didn't work.
- *
- *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
- *    The original code assumed that both snprintf() and vsnprintf() were
- *    missing.  Some systems only have snprintf() but not vsnprintf(), so
- *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
- *
- *  Andrew Tridgell (tridge@samba.org) Oct 1998
- *    fixed handling of %.0f
- *    added test for HAVE_LONG_DOUBLE
- *
- *  Russ Allbery <rra@stanford.edu> 2000-08-26
- *    fixed return value to comply with C99
- *    fixed handling of snprintf(NULL, ...)
- *
- **************************************************************/
-
-/** @file
- *  A snprintf implementation.
- */
-
-#include <nms_common.h>
-
-/* JAM: we don't need this - #include "config.h" */
-
-/* JAM: changed declarations to xmpp_snprintf and xmpp_vsnprintf to
-   avoid namespace collision. */
-
-#include <string.h>
-#include <ctype.h>
-#include <sys/types.h>
-
-/* Define this as a fall through, HAVE_STDARG_H is probably already set */
-
-/* varargs declarations: */
-
-#if defined(HAVE_STDARG_H)
-# include <stdarg.h>
-# define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
-# define VA_LOCAL_DECL   va_list ap
-# define VA_START(f)     va_start(ap, f)
-# define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
-# define VA_END          va_end(ap)
-#else
-# if defined(HAVE_VARARGS_H)
-#  include <varargs.h>
-#  undef HAVE_STDARGS
-#  define VA_LOCAL_DECL   va_list ap
-#  define VA_START(f)     va_start(ap)      /* f is ignored! */
-#  define VA_SHIFT(v,t) v = va_arg(ap,t)
-#  define VA_END        va_end(ap)
-# else
-/*XX ** NO VARARGS ** XX*/
-# endif
-#endif
-
-#ifdef HAVE_LONG_DOUBLE
-#define LDOUBLE long double
-#else
-#define LDOUBLE double
-#endif
-
-int xmpp_snprintf (char *str, size_t count, const char *fmt, ...);
-int xmpp_vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
-
-static int dopr (char *buffer, size_t maxlen, const char *format, 
-                 va_list args);
-static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
-                  char *value, int flags, int min, int max);
-static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
-                  long value, int base, int min, int max, int flags);
-static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
-                 LDOUBLE fvalue, int min, int max, int flags);
-static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
-
-/*
- * dopr(): poor man's version of doprintf
- */
-
-/* format read states */
-#define DP_S_DEFAULT 0
-#define DP_S_FLAGS   1
-#define DP_S_MIN     2
-#define DP_S_DOT     3
-#define DP_S_MAX     4
-#define DP_S_MOD     5
-#define DP_S_CONV    6
-#define DP_S_DONE    7
-
-/* format flags - Bits */
-#define DP_F_MINUS     (1 << 0)
-#define DP_F_PLUS      (1 << 1)
-#define DP_F_SPACE     (1 << 2)
-#define DP_F_NUM       (1 << 3)
-#define DP_F_ZERO      (1 << 4)
-#define DP_F_UP        (1 << 5)
-#define DP_F_UNSIGNED  (1 << 6)
-
-/* Conversion Flags */
-#define DP_C_SHORT   1
-#define DP_C_LONG    2
-#define DP_C_LDOUBLE 3
-
-#define char_to_int(p) (p - '0')
-#define MAX(p,q) ((p >= q) ? p : q)
-#define MIN(p,q) ((p <= q) ? p : q)
-
-static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
-{
-  char ch;
-  long value;
-  LDOUBLE fvalue;
-  char *strvalue;
-  int min;
-  int max;
-  int state;
-  int flags;
-  int cflags;
-  int total;
-  size_t currlen;
-  
-  state = DP_S_DEFAULT;
-  currlen = flags = cflags = min = 0;
-  max = -1;
-  ch = *format++;
-  total = 0;
-
-  while (state != DP_S_DONE)
-  {
-    if (ch == '\0')
-      state = DP_S_DONE;
-
-    switch(state) 
-    {
-    case DP_S_DEFAULT:
-      if (ch == '%') 
-       state = DP_S_FLAGS;
-      else 
-       total += dopr_outch (buffer, &currlen, maxlen, ch);
-      ch = *format++;
-      break;
-    case DP_S_FLAGS:
-      switch (ch) 
-      {
-      case '-':
-       flags |= DP_F_MINUS;
-        ch = *format++;
-       break;
-      case '+':
-       flags |= DP_F_PLUS;
-        ch = *format++;
-       break;
-      case ' ':
-       flags |= DP_F_SPACE;
-        ch = *format++;
-       break;
-      case '#':
-       flags |= DP_F_NUM;
-        ch = *format++;
-       break;
-      case '0':
-       flags |= DP_F_ZERO;
-        ch = *format++;
-       break;
-      default:
-       state = DP_S_MIN;
-       break;
-      }
-      break;
-    case DP_S_MIN:
-      if (isdigit(ch)) 
-      {
-       min = 10*min + char_to_int (ch);
-       ch = *format++;
-      } 
-      else if (ch == '*') 
-      {
-       min = va_arg (args, int);
-       ch = *format++;
-       state = DP_S_DOT;
-      } 
-      else 
-       state = DP_S_DOT;
-      break;
-    case DP_S_DOT:
-      if (ch == '.') 
-      {
-       state = DP_S_MAX;
-       ch = *format++;
-      } 
-      else 
-       state = DP_S_MOD;
-      break;
-    case DP_S_MAX:
-      if (isdigit(ch)) 
-      {
-       if (max < 0)
-         max = 0;
-       max = 10*max + char_to_int (ch);
-       ch = *format++;
-      } 
-      else if (ch == '*') 
-      {
-       max = va_arg (args, int);
-       ch = *format++;
-       state = DP_S_MOD;
-      } 
-      else 
-       state = DP_S_MOD;
-      break;
-    case DP_S_MOD:
-      /* Currently, we don't support Long Long, bummer */
-      switch (ch) 
-      {
-      case 'h':
-       cflags = DP_C_SHORT;
-       ch = *format++;
-       break;
-      case 'l':
-       cflags = DP_C_LONG;
-       ch = *format++;
-       break;
-      case 'L':
-       cflags = DP_C_LDOUBLE;
-       ch = *format++;
-       break;
-      default:
-       break;
-      }
-      state = DP_S_CONV;
-      break;
-    case DP_S_CONV:
-      switch (ch) 
-      {
-      case 'd':
-      case 'i':
-       if (cflags == DP_C_SHORT) 
-         value = va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, long int);
-       else
-         value = va_arg (args, int);
-       total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
-       break;
-      case 'o':
-       flags |= DP_F_UNSIGNED;
-       if (cflags == DP_C_SHORT)
-         value = va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, unsigned long int);
-       else
-         value = va_arg (args, unsigned int);
-       total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
-       break;
-      case 'u':
-       flags |= DP_F_UNSIGNED;
-       if (cflags == DP_C_SHORT)
-         value = va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, unsigned long int);
-       else
-         value = va_arg (args, unsigned int);
-       total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
-       break;
-      case 'X':
-       flags |= DP_F_UP;
-      case 'x':
-       flags |= DP_F_UNSIGNED;
-       if (cflags == DP_C_SHORT)
-         value = va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, unsigned long int);
-       else
-         value = va_arg (args, unsigned int);
-       total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
-       break;
-      case 'f':
-       if (cflags == DP_C_LDOUBLE)
-         fvalue = va_arg (args, LDOUBLE);
-       else
-         fvalue = va_arg (args, double);
-       /* um, floating point? */
-       total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
-       break;
-      case 'E':
-       flags |= DP_F_UP;
-      case 'e':
-       if (cflags == DP_C_LDOUBLE)
-         fvalue = va_arg (args, LDOUBLE);
-       else
-         fvalue = va_arg (args, double);
-       break;
-      case 'G':
-       flags |= DP_F_UP;
-      case 'g':
-       if (cflags == DP_C_LDOUBLE)
-         fvalue = va_arg (args, LDOUBLE);
-       else
-         fvalue = va_arg (args, double);
-       break;
-      case 'c':
-       total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
-       break;
-      case 's':
-       strvalue = va_arg (args, char *);
-       total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
-       break;
-      case 'p':
-       strvalue = va_arg (args, void *);
-       total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
-                         max, flags);
-       break;
-      case 'n':
-       if (cflags == DP_C_SHORT) 
-       {
-         short int *num;
-         num = va_arg (args, short int *);
-         *num = currlen;
-        } 
-       else if (cflags == DP_C_LONG) 
-       {
-         long int *num;
-         num = va_arg (args, long int *);
-         *num = currlen;
-        } 
-       else 
-       {
-         int *num;
-         num = va_arg (args, int *);
-         *num = currlen;
-        }
-       break;
-      case '%':
-       total += dopr_outch (buffer, &currlen, maxlen, ch);
-       break;
-      case 'w':
-       /* not supported yet, treat as next char */
-       ch = *format++;
-       break;
-      default:
-       /* Unknown, skip */
-       break;
-      }
-      ch = *format++;
-      state = DP_S_DEFAULT;
-      flags = cflags = min = 0;
-      max = -1;
-      break;
-    case DP_S_DONE:
-      break;
-    default:
-      /* hmm? */
-      break; /* some picky compilers need this */
-    }
-  }
-  if (buffer != NULL)
-  {
-    if (currlen < maxlen - 1) 
-      buffer[currlen] = '\0';
-    else 
-      buffer[maxlen - 1] = '\0';
-  }
-  return total;
-}
-
-static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
-                   char *value, int flags, int min, int max)
-{
-  int padlen, strln;     /* amount to pad */
-  int cnt = 0;
-  int total = 0;
-  
-  if (value == 0)
-  {
-    value = "<NULL>";
-  }
-
-  for (strln = 0; value[strln]; ++strln); /* strlen */
-  if (max >= 0 && max < strln)
-    strln = max;
-  padlen = min - strln;
-  if (padlen < 0) 
-    padlen = 0;
-  if (flags & DP_F_MINUS) 
-    padlen = -padlen; /* Left Justify */
-
-  while (padlen > 0)
-  {
-    total += dopr_outch (buffer, currlen, maxlen, ' ');
-    --padlen;
-  }
-  while (*value && ((max < 0) || (cnt < max)))
-  {
-    total += dopr_outch (buffer, currlen, maxlen, *value++);
-    ++cnt;
-  }
-  while (padlen < 0)
-  {
-    total += dopr_outch (buffer, currlen, maxlen, ' ');
-    ++padlen;
-  }
-  return total;
-}
-
-/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
-static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
-                  long value, int base, int min, int max, int flags)
-{
-  int signvalue = 0;
-  unsigned long uvalue;
-  char convert[20];
-  int place = 0;
-  int spadlen = 0; /* amount to space pad */
-  int zpadlen = 0; /* amount to zero pad */
-  int caps = 0;
-  int total = 0;
-  
-  if (max < 0)
-    max = 0;
-
-  uvalue = value;
-
-  if(!(flags & DP_F_UNSIGNED))
-  {
-    if( value < 0 ) {
-      signvalue = '-';
-      uvalue = -value;
-    }
-    else
-      if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
-       signvalue = '+';
-    else
-      if (flags & DP_F_SPACE)
-       signvalue = ' ';
-  }
-  
-  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
-
-  do {
-    convert[place++] =
-      (caps? "0123456789ABCDEF":"0123456789abcdef")
-      [uvalue % (unsigned)base  ];
-    uvalue = (uvalue / (unsigned)base );
-  } while(uvalue && (place < 20));
-  if (place == 20) place--;
-  convert[place] = 0;
-
-  zpadlen = max - place;
-  spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
-  if (zpadlen < 0) zpadlen = 0;
-  if (spadlen < 0) spadlen = 0;
-  if (flags & DP_F_ZERO)
-  {
-    zpadlen = MAX(zpadlen, spadlen);
-    spadlen = 0;
-  }
-  if (flags & DP_F_MINUS) 
-    spadlen = -spadlen; /* Left Justifty */
-
-#ifdef DEBUG_SNPRINTF
-  dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
-      zpadlen, spadlen, min, max, place));
-#endif
-
-  /* Spaces */
-  while (spadlen > 0) 
-  {
-    total += dopr_outch (buffer, currlen, maxlen, ' ');
-    --spadlen;
-  }
-
-  /* Sign */
-  if (signvalue) 
-    total += dopr_outch (buffer, currlen, maxlen, signvalue);
-
-  /* Zeros */
-  if (zpadlen > 0) 
-  {
-    while (zpadlen > 0)
-    {
-      total += dopr_outch (buffer, currlen, maxlen, '0');
-      --zpadlen;
-    }
-  }
-
-  /* Digits */
-  while (place > 0) 
-    total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
-  
-  /* Left Justified spaces */
-  while (spadlen < 0) {
-    total += dopr_outch (buffer, currlen, maxlen, ' ');
-    ++spadlen;
-  }
-
-  return total;
-}
-
-static LDOUBLE abs_val (LDOUBLE value)
-{
-  LDOUBLE result = value;
-
-  if (value < 0)
-    result = -value;
-
-  return result;
-}
-
-static LDOUBLE _snp_pow10 (int exp)
-{
-  LDOUBLE result = 1;
-
-  while (exp)
-  {
-    result *= 10;
-    exp--;
-  }
-  
-  return result;
-}
-
-static long _snp_round (LDOUBLE value)
-{
-  long intpart;
-
-  intpart = value;
-  value = value - intpart;
-  if (value >= 0.5)
-    intpart++;
-
-  return intpart;
-}
-
-static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
-                 LDOUBLE fvalue, int min, int max, int flags)
-{
-  int signvalue = 0;
-  LDOUBLE ufvalue;
-  char iconvert[20];
-  char fconvert[20];
-  int iplace = 0;
-  int fplace = 0;
-  int padlen = 0; /* amount to pad */
-  int zpadlen = 0; 
-  int caps = 0;
-  int total = 0;
-  long intpart;
-  long fracpart;
-  
-  /* 
-   * AIX manpage says the default is 0, but Solaris says the default
-   * is 6, and sprintf on AIX defaults to 6
-   */
-  if (max < 0)
-    max = 6;
-
-  ufvalue = abs_val (fvalue);
-
-  if (fvalue < 0)
-    signvalue = '-';
-  else
-    if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
-      signvalue = '+';
-    else
-      if (flags & DP_F_SPACE)
-       signvalue = ' ';
-
-#if 0
-  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
-#endif
-
-  intpart = ufvalue;
-
-  /* 
-   * Sorry, we only support 9 digits past the decimal because of our 
-   * conversion method
-   */
-  if (max > 9)
-    max = 9;
-
-  /* We "cheat" by converting the fractional part to integer by
-   * multiplying by a factor of 10
-   */
-  fracpart = _snp_round ((_snp_pow10 (max)) * (ufvalue - intpart));
-
-  if (fracpart >= _snp_pow10 (max))
-  {
-    intpart++;
-    fracpart -= _snp_pow10 (max);
-  }
-
-#ifdef DEBUG_SNPRINTF
-  dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
-#endif
-
-  /* Convert integer part */
-  do {
-    iconvert[iplace++] =
-      (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
-    intpart = (intpart / 10);
-  } while(intpart && (iplace < 20));
-  if (iplace == 20) iplace--;
-  iconvert[iplace] = 0;
-
-  /* Convert fractional part */
-  do {
-    fconvert[fplace++] =
-      (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
-    fracpart = (fracpart / 10);
-  } while(fracpart && (fplace < 20));
-  if (fplace == 20) fplace--;
-  fconvert[fplace] = 0;
-
-  /* -1 for decimal point, another -1 if we are printing a sign */
-  padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
-  zpadlen = max - fplace;
-  if (zpadlen < 0)
-    zpadlen = 0;
-  if (padlen < 0) 
-    padlen = 0;
-  if (flags & DP_F_MINUS) 
-    padlen = -padlen; /* Left Justifty */
-
-  if ((flags & DP_F_ZERO) && (padlen > 0)) 
-  {
-    if (signvalue) 
-    {
-      total += dopr_outch (buffer, currlen, maxlen, signvalue);
-      --padlen;
-      signvalue = 0;
-    }
-    while (padlen > 0)
-    {
-      total += dopr_outch (buffer, currlen, maxlen, '0');
-      --padlen;
-    }
-  }
-  while (padlen > 0)
-  {
-    total += dopr_outch (buffer, currlen, maxlen, ' ');
-    --padlen;
-  }
-  if (signvalue) 
-    total += dopr_outch (buffer, currlen, maxlen, signvalue);
-
-  while (iplace > 0) 
-    total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
-
-  /*
-   * Decimal point.  This should probably use locale to find the correct
-   * char to print out.
-   */
-  if (max > 0)
-  {
-    total += dopr_outch (buffer, currlen, maxlen, '.');
-
-    while (fplace > 0) 
-      total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
-  }
-
-  while (zpadlen > 0)
-  {
-    total += dopr_outch (buffer, currlen, maxlen, '0');
-    --zpadlen;
-  }
-
-  while (padlen < 0) 
-  {
-    total += dopr_outch (buffer, currlen, maxlen, ' ');
-    ++padlen;
-  }
-
-  return total;
-}
-
-static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
-{
-  if (*currlen + 1 < maxlen)
-    buffer[(*currlen)++] = c;
-  return 1;
-}
-
-int xmpp_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
-{
-  if (str != NULL)
-    str[0] = 0;
-  return dopr(str, count, fmt, args);
-}
-
-/* VARARGS3 */
-#ifdef HAVE_STDARGS
-int xmpp_snprintf (char *str,size_t count,const char *fmt,...)
-#else
-int xmpp_snprintf (va_alist) va_dcl
-#endif
-{
-#ifndef HAVE_STDARGS
-  char *str;
-  size_t count;
-  char *fmt;
-#endif
-  VA_LOCAL_DECL;
-  int total;
-    
-  VA_START (fmt);
-  VA_SHIFT (str, char *);
-  VA_SHIFT (count, size_t );
-  VA_SHIFT (fmt, char *);
-  total = xmpp_vsnprintf(str, count, fmt, ap);
-  VA_END;
-  return total;
-}
-
-#ifdef TEST_SNPRINTF
-#ifndef LONG_STRING
-#define LONG_STRING 1024
-#endif
-int main (void)
-{
-  char buf1[LONG_STRING];
-  char buf2[LONG_STRING];
-  char *fp_fmt[] = {
-    "%-1.5f",
-    "%1.5f",
-    "%123.9f",
-    "%10.5f",
-    "% 10.5f",
-    "%+22.9f",
-    "%+4.9f",
-    "%01.3f",
-    "%4f",
-    "%3.1f",
-    "%3.2f",
-    "%.0f",
-    "%.1f",
-    NULL
-  };
-  double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
-    0.9996, 1.996, 4.136, 0};
-  char *int_fmt[] = {
-    "%-1.5d",
-    "%1.5d",
-    "%123.9d",
-    "%5.5d",
-    "%10.5d",
-    "% 10.5d",
-    "%+22.33d",
-    "%01.3d",
-    "%4d",
-    NULL
-  };
-  long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
-  int x, y;
-  int fail = 0;
-  int num = 0;
-
-  printf ("Testing xmpp_snprintf format codes against system sprintf...\n");
-
-  for (x = 0; fp_fmt[x] != NULL ; x++)
-    for (y = 0; fp_nums[y] != 0 ; y++)
-    {
-      xmpp_snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
-      sprintf (buf2, fp_fmt[x], fp_nums[y]);
-      if (strcmp (buf1, buf2))
-      {
-       printf("xmpp_snprintf doesn't match Format: %s\n\txmpp_snprintf = %s\n\tsprintf  = %s\n", 
-           fp_fmt[x], buf1, buf2);
-       fail++;
-      }
-      num++;
-    }
-
-  for (x = 0; int_fmt[x] != NULL ; x++)
-    for (y = 0; int_nums[y] != 0 ; y++)
-    {
-      xmpp_snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
-      sprintf (buf2, int_fmt[x], int_nums[y]);
-      if (strcmp (buf1, buf2))
-      {
-       printf("xmpp_snprintf doesn't match Format: %s\n\txmpp_snprintf = %s\n\tsprintf  = %s\n", 
-           int_fmt[x], buf1, buf2);
-       fail++;
-      }
-      num++;
-    }
-  printf ("%d tests failed out of %d.\n", fail, num);
-}
-#endif /* SNPRINTF_TEST */
diff --git a/src/libstrophe/snprintf.h b/src/libstrophe/snprintf.h
new file mode 100644 (file)
index 0000000..18eedcf
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/** @file
+ *  Compatibility wrappers for OSes lacking snprintf(3) and/or vsnprintf(3).
+ */
+
+#ifndef __LIBSTROPHE_SNPRINTF_H__
+#define __LIBSTROPHE_SNPRINTF_H__
+
+#define xmpp_snprintf snprintf
+#define xmpp_vsnprintf vsnprintf
+
+#endif /* __LIBSTROPHE_SNPRINTF_H__ */
index 6061135..4921f04 100644 (file)
@@ -6,37 +6,24 @@
 **  This software is provided AS-IS with no warranty, either express
 **  or implied.
 **
-**  This software is distributed under license and may not be copied,
-**  modified or distributed except as expressly authorized under the
-**  terms of the license contained in the file LICENSE.txt in this
-**  distribution.
+** This program is dual licensed under the MIT and GPLv3 licenses.
 */
 
 /** @file
  *  Socket abstraction.
  */
 
-#define _CRT_SECURE_NO_WARNINGS
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
+#include <nms_common.h>
 
 #ifdef _WIN32
-#include <winsock2.h>
+
 #include <ws2tcpip.h>
 #include <windns.h>
 #include <Iphlpapi.h>
-#include <tchar.h>
-#define snprintf _snprintf
-#else
-#include <config.h>
+#include <Mstcpip.h>
+
+#else /* _WIN32 */
 
-#include <errno.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
 #include <netdb.h>
 #include <fcntl.h>
 #include <arpa/nameser.h>
 #include <arpa/nameser_compat.h>
 #endif
 #include <resolv.h>
-#endif
 
-#include "sock.h"
+#endif /* _WIN32 */
+
+#include "common.h"
 
 #ifndef T_SRV
 #define T_SRV (33)
@@ -81,47 +69,88 @@ static int _in_progress(int error)
 #ifdef _WIN32
     return (error == WSAEWOULDBLOCK || error == WSAEINPROGRESS);
 #else
-    return (errno == EINPROGRESS);
+    return (error == EINPROGRESS);
 #endif
 }
 
-sock_t sock_connect(const char * const host, const unsigned int port)
+sock_t sock_connect(const char * const host, const unsigned short port)
 {
     sock_t sock;
     char service[6];
     struct addrinfo *res, *ainfo, hints;
     int err;
-    
-    sock = -1;
 
-    snprintf(service, 6, "%u", port);
+    xmpp_snprintf(service, 6, "%u", port);
 
     memset(&hints, 0, sizeof(struct addrinfo));
-    hints.ai_family = AF_INET;
+    hints.ai_family = AF_UNSPEC;
+#ifdef AI_ADDRCONFIG
+    hints.ai_flags = AI_ADDRCONFIG;
+#endif /* AI_ADDRCONFIG */
     hints.ai_protocol = IPPROTO_TCP;
     hints.ai_socktype = SOCK_STREAM;
 
-    if ((err = getaddrinfo(host, service, &hints, &res)) != 0)
-       return -1;
+    err = getaddrinfo(host, service, &hints, &res);
+    if (err != 0)
+        return -1;
+
+    for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
+        sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
+        if (sock < 0)
+            continue;
+
+        err = sock_set_nonblocking(sock);
+        if (err == 0) {
+            err = connect(sock, ainfo->ai_addr, ainfo->ai_addrlen);
+            if (err == 0 || _in_progress(sock_error()))
+                break;
+        }
+        sock_close(sock);
+    }
+    freeaddrinfo(res);
+    sock = ainfo == NULL ? -1 : sock;
 
-    ainfo = res;
-    while (ainfo) {
-       if ((sock = socket(ainfo->ai_family, ainfo->ai_socktype, 
-                  ainfo->ai_protocol)) >= 0) {
-           sock_set_nonblocking(sock);
+    return sock;
+}
 
-           err = connect(sock, ainfo->ai_addr, (int)ainfo->ai_addrlen);
+int sock_set_keepalive(const sock_t sock, int timeout, int interval)
+{
+    int ret;
+    int optval = (timeout && interval) ? 1 : 0;
 
-           if ((err == 0) || (err < 0 && _in_progress(sock_error())))
-               break;
-       }
+    /* This function doesn't change maximum number of keepalive probes */
 
-       ainfo = ainfo->ai_next;
-    }
+#ifdef _WIN32
+    struct tcp_keepalive ka;
+    DWORD dw = 0;
 
-    if (res) freeaddrinfo(res);
+    ka.onoff = optval;
+    ka.keepalivetime = timeout * 1000;
+    ka.keepaliveinterval = interval * 1000;
+    ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw, NULL, NULL);
+#else
+    ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
+    if (ret < 0)
+        return ret;
+
+    if (optval) {
+#ifdef TCP_KEEPIDLE
+        ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout, sizeof(timeout));
+#elif defined(TCP_KEEPALIVE)
+        /* QNX receives `struct timeval' as argument, but it seems OSX does int */
+        ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout, sizeof(timeout));
+#endif /* TCP_KEEPIDLE */
+        if (ret < 0)
+            return ret;
+#ifdef TCP_KEEPINTVL
+        ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
+        if (ret < 0)
+            return ret;
+#endif /* TCP_KEEPINTVL */
+    }
+#endif /* _WIN32 */
 
-    return sock;
+    return ret;
 }
 
 int sock_close(const sock_t sock)
@@ -139,8 +168,13 @@ int sock_set_blocking(const sock_t sock)
     u_long block = 0;
     return ioctlsocket(sock, FIONBIO, &block);
 #else
-   int f = fcntl(sock, F_GETFL);
-   return (f != -1) ? fcntl(sock, F_SETFL, f & ~O_NONBLOCK) : -1;
+    int rc;
+
+    rc = fcntl(sock, F_GETFL, NULL);
+    if (rc >= 0) {
+        rc = fcntl(sock, F_SETFL, rc & (~O_NONBLOCK));
+    }
+    return rc;
 #endif
 }
 
@@ -150,8 +184,13 @@ int sock_set_nonblocking(const sock_t sock)
     u_long nonblock = 1;
     return ioctlsocket(sock, FIONBIO, &nonblock);
 #else
-   int f = fcntl(sock, F_GETFL);
-   return (f != -1) ? fcntl(sock, F_SETFL, f | O_NONBLOCK) : -1;
+    int rc;
+
+    rc = fcntl(sock, F_GETFL, NULL);
+    if (rc >= 0) {
+        rc = fcntl(sock, F_SETFL, rc | O_NONBLOCK);
+    }
+    return rc;
 #endif
 }
 
@@ -169,7 +208,7 @@ int sock_is_recoverable(const int error)
 {
 #ifdef _WIN32
     return (error == WSAEINTR || error == WSAEWOULDBLOCK || 
-           error == WSAEINPROGRESS);
+            error == WSAEINPROGRESS);
 #else
     return (error == EAGAIN || error == EINTR);
 #endif
@@ -181,15 +220,15 @@ int sock_connect_error(const sock_t sock)
     socklen_t len;
     char temp;
 
-    sa.sa_family = AF_INET;
-
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_family = AF_UNSPEC;
     len = sizeof(sa);
 
     /* we don't actually care about the peer name, we're just checking if
      * we're connected or not */
     if (getpeername(sock, &sa, &len) == 0)
     {
-       return 0;
+        return 0;
     }
 
     /* it's possible that the error wasn't ENOTCONN, so if it wasn't,
@@ -205,720 +244,3 @@ int sock_connect_error(const sock_t sock)
 
     return sock_error();
 }
-
-struct dnsquery_header
-{
-       unsigned short id;
-       unsigned char qr;
-       unsigned char opcode;
-       unsigned char aa;
-       unsigned char tc;
-       unsigned char rd;
-       unsigned char ra;
-       unsigned char z;
-       unsigned char rcode;
-       unsigned short qdcount;
-       unsigned short ancount;
-       unsigned short nscount;
-       unsigned short arcount;
-};
-
-struct dnsquery_question
-{
-       char qname[1024];
-       unsigned short qtype;
-       unsigned short qclass;
-};
-
-struct dnsquery_srvrdata
-{
-       unsigned short priority;
-       unsigned short weight;
-       unsigned short port;
-       char target[1024];
-};
-
-struct dnsquery_resourcerecord
-{
-       char name[1024];
-       unsigned short type;
-       unsigned short _class;
-       unsigned int ttl;
-       unsigned short rdlength;
-       struct dnsquery_srvrdata rdata;
-};
-
-
-void netbuf_add_32bitnum(unsigned char *buf, int buflen, int *offset, unsigned int num)
-{
-       unsigned char *start = buf + *offset;
-       unsigned char *p = start;
-
-       /* assuming big endian */
-       *p++ = (num >> 24) & 0xff;
-       *p++ = (num >> 16) & 0xff;
-       *p++ = (num >> 8)  & 0xff;
-       *p++ = (num)       & 0xff;
-
-       *offset += 4;
-}
-
-void netbuf_get_32bitnum(unsigned char *buf, int buflen, int *offset, unsigned int *num)
-{
-       unsigned char *start = buf + *offset;
-       unsigned char *p = start;
-       *num = 0;
-
-       /* assuming big endian */
-       *num |= (*p++) << 24;
-       *num |= (*p++) << 16;
-       *num |= (*p++) << 8;
-       *num |= (*p++);
-
-       *offset += 4;
-}
-
-void netbuf_add_16bitnum(unsigned char *buf, int buflen, int *offset, unsigned short num)
-{
-       unsigned char *start = buf + *offset;
-       unsigned char *p = start;
-
-       /* assuming big endian */
-       *p++ = (num >> 8) & 0xff;
-       *p++ = (num)      & 0xff;
-
-       *offset += 2;
-}
-
-void netbuf_get_16bitnum(unsigned char *buf, int buflen, int *offset, unsigned short *num)
-{
-       unsigned char *start = buf + *offset;
-       unsigned char *p = start;
-       *num = 0;
-
-       /* assuming big endian */
-       *num |= (*p++) << 8;
-       *num |= (*p++);
-
-       *offset += 2;
-}
-
-void netbuf_add_domain_name(unsigned char *buf, int buflen, int *offset, 
-                           char *name)
-{
-       unsigned char *start = buf + *offset;
-       unsigned char *p = start;
-       unsigned char *wordstart, *wordend;
-
-       wordstart = (unsigned char *)name;
-       
-       while (*wordstart)
-       {
-               int len;
-               wordend = wordstart;
-               while (*wordend && *wordend != '.')
-               {
-                       wordend++;
-               }
-
-               len = (int)(wordend - wordstart);
-
-               if (len > 0x3F)
-               {
-                       len = 0x3F;
-               }
-
-               *p++ = len;
-
-               while (wordstart != wordend)
-               {
-                       *p++ = *wordstart++;
-               }
-
-               if (*wordstart == '.')
-               {
-                       wordstart++;
-               }
-       }
-
-       *p++ = '\0';
-
-       *offset += (int)(p - start);
-}
-
-int calc_domain_name_size(unsigned char *buf, int buflen, int offset)
-{
-       unsigned char *p = buf + offset;
-       int len = 0;
-
-       while (*p)
-       {
-               if ((*p & 0xC0) == 0xC0)
-               {
-                       int newoffset = 0;
-                       newoffset |= (*p++ & 0x3F) << 8;
-                       newoffset |= *p;
-
-                       p = buf + newoffset;
-               }
-               else
-               {
-                       if (len)
-                       {
-                               len += 1;
-                       }
-                       len += *p;
-                       p += *p + 1;
-               }
-       }
-
-       return len;
-}
-
-int netbuf_get_domain_name(unsigned char *buf, int buflen, int *offset, char *namebuf, int namebuflen)
-{
-       unsigned char *start = buf + *offset;
-       unsigned char *p, *p2;
-       int *curroffset = offset;
-       int len = 0;
-
-       *namebuf = '\0';
-
-       /* measure length */
-       p = start;
-       while (*p)
-       {
-               if ((*p & 0xC0) == 0xC0)
-               {
-                       int newoffset = 0;
-                       newoffset |= (*p++ & 0x3F) << 8;
-                       newoffset |= *p++;
-
-                       p = buf + newoffset;
-               }
-               else
-               {
-                       len += *p;
-                       p += *p + 1;
-               }
-       }
-
-       if (namebuflen < len)
-       {
-               return len;
-       }
-
-       /* actually copy in name */
-       p = start;
-       p2 = (unsigned char *)namebuf;
-       while (*p)
-       {
-               if ((*p & 0xC0) == 0xC0)
-               {
-                       int newoffset = 0;
-                       newoffset |= (*p++ & 0x3F) << 8;
-                       newoffset |= *p++;
-
-                       if (curroffset)
-                       {
-                               *curroffset += (int)(p - start);
-                               curroffset = NULL;
-                       }
-
-                       p = buf + newoffset;
-               }
-               else
-               {
-                       int i, partlen;
-
-                       if (*namebuf != '\0')
-                       {
-                               *p2++ = '.';
-                       }
-
-                       partlen = *p++;
-                       
-                       for (i=0; i < partlen; i++)
-                       {
-                                *p2++ = *p++;
-                       }
-               }
-       }
-
-       if (curroffset)
-       {
-               p++;
-               *curroffset += (int)(p - start);
-               curroffset = NULL;
-       }
-
-       *p2 = '\0';
-
-       return 0;
-}
-
-void netbuf_add_dnsquery_header(unsigned char *buf, int buflen, int *offset, struct dnsquery_header *header)
-{
-       unsigned char *p;
-
-       netbuf_add_16bitnum(buf, buflen, offset, header->id);
-       
-       p = buf + *offset;
-       *p++ =    ((header->qr     & 0x01) << 7)
-               | ((header->opcode & 0x0F) << 3)
-               | ((header->aa     & 0x01) << 2)
-               | ((header->tc     & 0x01) << 1)
-               | ((header->rd     & 0x01));
-       *p++ =    ((header->ra     & 0x01) << 7)
-               | ((header->z      & 0x07) << 4)
-               | ((header->rcode  & 0x0F));
-       *off