Added XMPP messaging support
authorVictor Kirhenshtein <victor@netxms.org>
Fri, 20 Dec 2013 16:55:03 +0000 (18:55 +0200)
committerVictor Kirhenshtein <victor@netxms.org>
Fri, 20 Dec 2013 16:55:03 +0000 (18:55 +0200)
80 files changed:
include/netxmsdb.h
include/nms_cscp.h
include/nms_util.h
include/nxclapi.h
include/strophe.h [new file with mode: 0644]
include/strophepp.h [new file with mode: 0644]
netxms.sln
sql/schema.in
sql/setup.in
src/java/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/java/netxms-client-api/src/main/java/org/netxms/api/client/constants/UserAccessRights.java
src/java/netxms-client-api/src/main/java/org/netxms/api/client/users/User.java
src/java/netxms-client-api/src/main/java/org/netxms/api/client/users/UserManager.java
src/java/netxms-client/src/main/java/org/netxms/client/ServerAction.java
src/java/netxms-eclipse/ActionManager/OSGI-INF/l10n/bundle_cs.properties
src/java/netxms-eclipse/ActionManager/icons/xmpp.png [new file with mode: 0644]
src/java/netxms-eclipse/ActionManager/src/org/netxms/ui/eclipse/actionmanager/ServerActionAdapterFactory.java
src/java/netxms-eclipse/ActionManager/src/org/netxms/ui/eclipse/actionmanager/dialogs/EditActionDlg.java
src/java/netxms-eclipse/UserManager/OSGI-INF/l10n/bundle_cs.properties
src/java/netxms-eclipse/UserManager/src/org/netxms/ui/eclipse/usermanager/propertypages/General.java
src/java/netxms-eclipse/UserManager/src/org/netxms/ui/eclipse/usermanager/propertypages/SystemRights.java
src/libstrophe/GPL-LICENSE.txt [new file with mode: 0644]
src/libstrophe/LICENSE.txt [new file with mode: 0644]
src/libstrophe/MIT-LICENSE.txt [new file with mode: 0644]
src/libstrophe/auth.c [new file with mode: 0644]
src/libstrophe/common.h [new file with mode: 0644]
src/libstrophe/conn.c [new file with mode: 0644]
src/libstrophe/ctx.c [new file with mode: 0644]
src/libstrophe/event.c [new file with mode: 0644]
src/libstrophe/handler.c [new file with mode: 0644]
src/libstrophe/hash.c [new file with mode: 0644]
src/libstrophe/hash.h [new file with mode: 0644]
src/libstrophe/jid.c [new file with mode: 0644]
src/libstrophe/libstrophe.vcproj [new file with mode: 0644]
src/libstrophe/md5.c [new file with mode: 0644]
src/libstrophe/md5.h [new file with mode: 0644]
src/libstrophe/oocontext.cpp [new file with mode: 0644]
src/libstrophe/oostanza.cpp [new file with mode: 0644]
src/libstrophe/ostypes.h [new file with mode: 0644]
src/libstrophe/parser.h [new file with mode: 0644]
src/libstrophe/parser_expat.c [new file with mode: 0644]
src/libstrophe/sasl.c [new file with mode: 0644]
src/libstrophe/sasl.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 [new file with mode: 0644]
src/libstrophe/sock.c [new file with mode: 0644]
src/libstrophe/sock.h [new file with mode: 0644]
src/libstrophe/stanza.c [new file with mode: 0644]
src/libstrophe/thread.c [new file with mode: 0644]
src/libstrophe/thread.h [new file with mode: 0644]
src/libstrophe/tls.h [new file with mode: 0644]
src/libstrophe/tls_openssl.c [new file with mode: 0644]
src/libstrophe/util.c [new file with mode: 0644]
src/libstrophe/util.h [new file with mode: 0644]
src/server/core/actions.cpp
src/server/core/admin.cpp
src/server/core/cert.cpp
src/server/core/debug.cpp
src/server/core/email.cpp
src/server/core/main.cpp
src/server/core/nxcore.vcproj
src/server/core/session.cpp
src/server/core/userdb.cpp
src/server/core/userdb_objects.cpp
src/server/core/xmpp.cpp [new file with mode: 0644]
src/server/include/nms_core.h
src/server/include/nms_users.h
src/server/libnxsrv/messages.mc
src/server/tools/nxdbmgr/upgrade.cpp
webui/webapp/ActionManager/OSGI-INF/l10n/bundle_cs.properties
webui/webapp/ActionManager/build.properties
webui/webapp/ActionManager/icons/xmpp.png [new file with mode: 0644]
webui/webapp/ActionManager/src/org/netxms/ui/eclipse/actionmanager/ServerActionAdapterFactory.java
webui/webapp/ActionManager/src/org/netxms/ui/eclipse/actionmanager/dialogs/EditActionDlg.java
webui/webapp/ActionManager/src/org/netxms/ui/eclipse/actionmanager/messages_cs.properties
webui/webapp/UserManager/OSGI-INF/l10n/bundle_cs.properties
webui/webapp/UserManager/src/org/netxms/ui/eclipse/usermanager/messages_cs.properties
webui/webapp/UserManager/src/org/netxms/ui/eclipse/usermanager/propertypages/General.java
webui/webapp/UserManager/src/org/netxms/ui/eclipse/usermanager/propertypages/SystemRights.java

index a55d4f7..6ea140d 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   299
+#define DB_FORMAT_VERSION   300
 
 #endif
index 46a9b7a..1b3b8a6 100644 (file)
@@ -947,6 +947,7 @@ typedef struct
 #define VID_BOOT_TIME               ((UINT32)454)
 #define VID_REQUEST_ID              ((UINT32)455)
 #define VID_ADDRESS_MAP             ((UINT32)456)
+#define VID_XMPP_ID                 ((UINT32)457)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((UINT32)0x00800000)
index 6a6c3df..92b0913 100644 (file)
@@ -875,7 +875,7 @@ extern "C"
        void LIBNETXMS_EXPORTABLE QSortEx(void *base, size_t nmemb, size_t size, void *arg,
                                                                                                 int (*compare)(const void *, const void *, void *));
 
-   INT64 LIBNETXMS_EXPORTABLE GetCurrentTimeMs(void);
+   INT64 LIBNETXMS_EXPORTABLE GetCurrentTimeMs();
 
        UINT64 LIBNETXMS_EXPORTABLE FileSizeW(const WCHAR *pszFileName);
        UINT64 LIBNETXMS_EXPORTABLE FileSizeA(const char *pszFileName);
index 7cf0fc6..4ee98c5 100644 (file)
@@ -621,8 +621,9 @@ enum
 #define SYSTEM_ACCESS_MANAGE_MAPPING_TBLS 0x00800000
 #define SYSTEM_ACCESS_MANAGE_SUMMARY_TBLS 0x01000000
 #define SYSTEM_ACCESS_REPORTING_SERVER    0x02000000
+#define SYSTEM_ACCESS_XMPP_COMMANDS       0x04000000
 
-#define SYSTEM_ACCESS_FULL                0x03FFFFFF
+#define SYSTEM_ACCESS_FULL                0x07FFFFFF
 
 #endif /* LIBNXCL_CUSTOM_USER_RIGHTS */
 
@@ -664,11 +665,9 @@ enum
 #define UF_INTRUDER_LOCKOUT         0x0020
 #define UF_PASSWORD_NEVER_EXPIRES   0x0040
 
-
-//
-// Fields for NXCModifyUserEx
-//
-
+/**
+ * Fields for NXCModifyUserEx
+ */
 #define USER_MODIFY_LOGIN_NAME         0x00000001
 #define USER_MODIFY_DESCRIPTION        0x00000002
 #define USER_MODIFY_FULL_NAME          0x00000004
@@ -680,20 +679,17 @@ enum
 #define USER_MODIFY_PASSWD_LENGTH      0x00000100
 #define USER_MODIFY_TEMP_DISABLE       0x00000200
 #define USER_MODIFY_CUSTOM_ATTRIBUTES  0x00000400
+#define USER_MODIFY_XMPP_ID            0x00000800
 
-
-//
-// User certificate mapping methods
-//
-
+/**
+ * User certificate mapping methods
+ */
 #define USER_MAP_CERT_BY_SUBJECT               0
 #define USER_MAP_CERT_BY_PUBKEY                1
 
-
-//
-// User database change notification types
-//
-
+/**
+ * User database change notification types
+ */
 #define USER_DB_CREATE              0
 #define USER_DB_DELETE              1
 #define USER_DB_MODIFY              2
@@ -861,17 +857,16 @@ enum
 #define RF_SEVERITY_MAJOR        0x0800
 #define RF_SEVERITY_CRITICAL     0x1000
 
-
-//
-// Action types
-//
-
+/**
+ * Action types
+ */
 #define ACTION_EXEC           0
 #define ACTION_REMOTE         1
 #define ACTION_SEND_EMAIL     2
 #define ACTION_SEND_SMS       3
 #define ACTION_FORWARD_EVENT  4
 #define ACTION_NXSL_SCRIPT    5
+#define ACTION_XMPP_MESSAGE   6
 
 /**
  * Network map types
diff --git a/include/strophe.h b/include/strophe.h
new file mode 100644 (file)
index 0000000..6370d9e
--- /dev/null
@@ -0,0 +1,384 @@
+/* strophe.h
+** strophe XMPP client library C API
+**
+** 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.
+*/
+
+/** @file
+ *  Strophe public C API definitions.
+ */
+
+#ifndef __LIBSTROPHE_STROPHE_H__
+#define __LIBSTROPHE_STROPHE_H__
+
+#ifdef _WIN32
+#ifdef LIBSTROPHE_EXPORTS
+#define LIBSTROPHE_EXPORTABLE __declspec(dllexport)
+#else
+#define LIBSTROPHE_EXPORTABLE __declspec(dllimport)
+#endif
+#else
+#define LIBSTROPHE_EXPORTABLE
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/* namespace defines */
+/** @def XMPP_NS_CLIENT
+ *  Namespace definition for 'jabber:client'.
+ */
+#define XMPP_NS_CLIENT "jabber:client"
+/** @def XMPP_NS_COMPONENT
+ *  Namespace definition for 'jabber:component:accept'.
+ */
+#define XMPP_NS_COMPONENT "jabber:component:accept"
+/** @def XMPP_NS_STREAMS
+ *  Namespace definition for 'http://etherx.jabber.org/streams'.
+ */
+#define XMPP_NS_STREAMS "http://etherx.jabber.org/streams"
+/** @def XMPP_NS_STREAMS_IETF
+ *  Namespace definition for 'urn:ietf:params:xml:ns:xmpp-streams'.
+ */
+#define XMPP_NS_STREAMS_IETF "urn:ietf:params:xml:ns:xmpp-streams"
+/** @def XMPP_NS_TLS
+ *  Namespace definition for 'url:ietf:params:xml:ns:xmpp-tls'.
+ */
+#define XMPP_NS_TLS "urn:ietf:params:xml:ns:xmpp-tls"
+/** @def XMPP_NS_SASL
+ *  Namespace definition for 'urn:ietf:params:xml:ns:xmpp-sasl'.
+ */
+#define XMPP_NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
+/** @def XMPP_NS_BIND
+ *  Namespace definition for 'urn:ietf:params:xml:ns:xmpp-bind'.
+ */
+#define XMPP_NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
+/** @def XMPP_NS_SESSION
+ *  Namespace definition for 'urn:ietf:params:xml:ns:xmpp-session'.
+ */
+#define XMPP_NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
+/** @def XMPP_NS_AUTH
+ *  Namespace definition for 'jabber:iq:auth'.
+ */
+#define XMPP_NS_AUTH "jabber:iq:auth"
+/** @def XMPP_NS_DISCO_INFO
+ *  Namespace definition for 'http://jabber.org/protocol/disco#info'.
+ */
+#define XMPP_NS_DISCO_INFO "http://jabber.org/protocol/disco#info"
+/** @def XMPP_NS_DISCO_ITEMS
+ *  Namespace definition for 'http://jabber.org/protocol/disco#items'.
+ */
+#define XMPP_NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
+/** @def XMPP_NS_ROSTER
+ *  Namespace definition for 'jabber:iq:roster'.
+ */
+#define XMPP_NS_ROSTER "jabber:iq:roster"
+
+/* error defines */
+/** @def XMPP_EOK
+ *  Success error code.
+ */
+#define XMPP_EOK 0
+/** @def XMPP_EMEM
+ *  Memory related failure error code.
+ *  
+ *  This is returned on allocation errors and signals that the host may
+ *  be out of memory.
+ */
+#define XMPP_EMEM -1
+/** @def XMPP_EINVOP
+ *  Invalid operation error code.
+ *
+ *  This error code is returned when the operation was invalid and signals
+ *  that the Strophe API is being used incorrectly.
+ */
+#define XMPP_EINVOP -2
+/** @def XMPP_EINT
+ *  Internal failure error code.
+ */
+#define XMPP_EINT -3
+
+/* initialization and shutdown */
+void LIBSTROPHE_EXPORTABLE xmpp_initialize();
+void LIBSTROPHE_EXPORTABLE xmpp_shutdown();
+
+/* version */
+int LIBSTROPHE_EXPORTABLE xmpp_version_check(int major, int minor);
+
+/* run-time contexts */
+
+/* user-replaceable memory allocator */
+typedef struct _xmpp_mem_t xmpp_mem_t;
+
+/* user-replaceable log object */
+typedef struct _xmpp_log_t xmpp_log_t;
+
+/* opaque run time context containing the above hooks */
+typedef struct _xmpp_ctx_t xmpp_ctx_t;
+
+xmpp_ctx_t 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);
+
+struct _xmpp_mem_t {
+    void *(*alloc)(const size_t size, void * const userdata);
+    void (*free)(void *p, void * const userdata);
+    void *(*realloc)(void *p, const size_t size, void * const userdata);
+    void *userdata;
+};
+
+typedef enum {
+    XMPP_LEVEL_DEBUG,
+    XMPP_LEVEL_INFO,
+    XMPP_LEVEL_WARN,
+    XMPP_LEVEL_ERROR
+} xmpp_log_level_t;
+
+typedef enum {
+    XMPP_UNKNOWN,
+    XMPP_CLIENT,
+    XMPP_COMPONENT
+} xmpp_conn_type_t;
+
+typedef void (*xmpp_log_handler)(void * const userdata, 
+                                const xmpp_log_level_t level,
+                                const char * const area,
+                                const char * const msg);
+
+struct _xmpp_log_t {
+    xmpp_log_handler handler;
+    void *userdata;
+    /* mutex_t lock; */
+};
+
+/* return a default logger filtering at a given level */
+xmpp_log_t LIBSTROPHE_EXPORTABLE *xmpp_get_default_logger(xmpp_log_level_t level);
+
+/* connection */
+
+/* opaque connection object */
+typedef struct _xmpp_conn_t xmpp_conn_t;
+typedef struct _xmpp_stanza_t xmpp_stanza_t;
+
+/* connect callback */
+typedef enum {
+    XMPP_CONN_CONNECT,
+    XMPP_CONN_DISCONNECT,
+    XMPP_CONN_FAIL
+} xmpp_conn_event_t;
+
+typedef enum {
+    XMPP_SE_BAD_FORMAT,
+    XMPP_SE_BAD_NS_PREFIX,
+    XMPP_SE_CONFLICT,
+    XMPP_SE_CONN_TIMEOUT,
+    XMPP_SE_HOST_GONE,
+    XMPP_SE_HOST_UNKNOWN,
+    XMPP_SE_IMPROPER_ADDR,
+    XMPP_SE_INTERNAL_SERVER_ERROR,
+    XMPP_SE_INVALID_FROM,
+    XMPP_SE_INVALID_ID,
+    XMPP_SE_INVALID_NS,
+    XMPP_SE_INVALID_XML,
+    XMPP_SE_NOT_AUTHORIZED,
+    XMPP_SE_POLICY_VIOLATION,
+    XMPP_SE_REMOTE_CONN_FAILED,
+    XMPP_SE_RESOURCE_CONSTRAINT,
+    XMPP_SE_RESTRICTED_XML,
+    XMPP_SE_SEE_OTHER_HOST,
+    XMPP_SE_SYSTEM_SHUTDOWN,
+    XMPP_SE_UNDEFINED_CONDITION,
+    XMPP_SE_UNSUPPORTED_ENCODING,
+    XMPP_SE_UNSUPPORTED_STANZA_TYPE,
+    XMPP_SE_UNSUPPORTED_VERSION,
+    XMPP_SE_XML_NOT_WELL_FORMED
+} xmpp_error_type_t;
+
+typedef struct {
+    xmpp_error_type_t type;
+    char *text;
+    xmpp_stanza_t *stanza;
+} xmpp_stream_error_t;
+
+typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn, 
+                                 const xmpp_conn_event_t event,
+                                 const int error,
+                                 xmpp_stream_error_t * const stream_error,
+                                 void * const userdata);
+
+xmpp_conn_t 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);
+
+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);
+const char LIBSTROPHE_EXPORTABLE *xmpp_conn_get_pass(const xmpp_conn_t * const conn);
+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_connect_client(xmpp_conn_t * const conn, 
+                         const char * const altdomain,
+                         unsigned short altport,
+                         xmpp_conn_handler callback,
+                         void * const userdata);
+
+/*
+int xmpp_connect_component(conn, name)
+*/
+void LIBSTROPHE_EXPORTABLE xmpp_disconnect(xmpp_conn_t * const conn);
+
+void LIBSTROPHE_EXPORTABLE xmpp_send(xmpp_conn_t * const conn,
+              xmpp_stanza_t * const stanza);
+
+void LIBSTROPHE_EXPORTABLE xmpp_send_raw_string(xmpp_conn_t * const conn, 
+                         const char * const fmt, ...);
+void LIBSTROPHE_EXPORTABLE xmpp_send_raw(xmpp_conn_t * const conn, 
+                  const char * const data, const size_t len);
+
+
+/* handlers */
+
+/* if the handle returns false it is removed */
+typedef int (*xmpp_timed_handler)(xmpp_conn_t * const conn, 
+                                 void * const userdata);
+
+void LIBSTROPHE_EXPORTABLE xmpp_timed_handler_add(xmpp_conn_t * const conn,
+                           xmpp_timed_handler handler,
+                           const unsigned long period,
+                           void * const userdata);
+void LIBSTROPHE_EXPORTABLE xmpp_timed_handler_delete(xmpp_conn_t * const conn,
+                              xmpp_timed_handler handler);
+
+
+/* if the handler returns false it is removed */
+typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
+                            xmpp_stanza_t * const stanza,
+                            void * const userdata);
+
+void LIBSTROPHE_EXPORTABLE xmpp_handler_add(xmpp_conn_t * const conn,
+                     xmpp_handler handler,
+                     const char * const ns,
+                     const char * const name,
+                     const char * const type,
+                     void * const userdata);
+void LIBSTROPHE_EXPORTABLE xmpp_handler_delete(xmpp_conn_t * const conn,
+                        xmpp_handler handler);
+
+void LIBSTROPHE_EXPORTABLE xmpp_id_handler_add(xmpp_conn_t * const conn,
+                        xmpp_handler handler,
+                        const char * const id,
+                        void * const userdata);
+void LIBSTROPHE_EXPORTABLE xmpp_id_handler_delete(xmpp_conn_t * const conn,
+                           xmpp_handler handler,
+                           const char * const id);
+
+/*
+void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
+*/
+
+/** stanzas **/
+
+/** allocate an initialize a blank stanza */
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_new(xmpp_ctx_t *ctx);
+
+/** clone a stanza */
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_clone(xmpp_stanza_t * const stanza);
+
+/** 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 */
+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 **/
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_to_text(xmpp_stanza_t *stanza, 
+                       char ** const buf, size_t * const buflen);
+
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_children(xmpp_stanza_t * const stanza);
+xmpp_stanza_t LIBSTROPHE_EXPORTABLE *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza, 
+                                            const char * const name);
+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);
+/* 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_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
+/* set_attribute adds/replaces attributes */
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza, 
+                             const char * const key,
+                             const char * const value);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_name(xmpp_stanza_t *stanza,
+                        const char * const name);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_text(xmpp_stanza_t *stanza,
+                        const char * const text);
+int LIBSTROPHE_EXPORTABLE xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
+                                  const char * const text, 
+                                  const size_t size);
+
+/* 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();
+*/
+
+/* allocate and initialize a stanza in reply to another */
+/* unimplemented
+xmpp_stanza_t *xmpp_stanza_reply(const xmpp_stanza_t *stanza);
+*/
+
+/* stanza subclasses */
+/* unimplemented
+void xmpp_message_new();
+void xmpp_message_get_body();
+void xmpp_message_set_body();
+
+void xmpp_iq_new();
+void xmpp_presence_new();
+*/
+
+/** event loop **/
+void 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LIBSTROPHE_STROPHE_H__ */
diff --git a/include/strophepp.h b/include/strophepp.h
new file mode 100644 (file)
index 0000000..0043136
--- /dev/null
@@ -0,0 +1,136 @@
+/* strophepp.h
+** strophe XMPP client library C++ API
+**
+** 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.
+*/
+
+/** @file
+ *  Strophe public C++ API definitions.
+ */
+
+#ifndef __LIBSTROPHE_STROPHEPP_H__
+#define __LIBSTROPHE_STROPHEPP_H__
+
+#include "strophe.h"
+
+namespace XMPP {
+    class Context {
+    private:
+       xmpp_mem_t m_mem;
+       xmpp_log_t m_log;
+       xmpp_ctx_t *m_ctx;
+
+    public:
+       Context();
+       virtual ~Context();
+
+       virtual void *alloc(const size_t size);
+       virtual void *realloc(void *p, const size_t size);
+       virtual void free(void *p);
+       virtual void log(const xmpp_log_level_t level,
+                        const char * const area,
+                        const char * const msg);
+
+       xmpp_ctx_t *getContext();
+
+    private:
+       static void *callAlloc(const size_t size, void * const userdata);
+       static void *callRealloc(void *p, const size_t size, 
+                                void * const userdata);
+       static void callFree(void *p, void * const userdata);
+       static void callLog(void * const userdata, 
+                           const xmpp_log_level_t level,
+                           const char * const area,
+                           const char * const msg);
+    };
+
+    class Stanza {
+    private:
+       Context *m_ctx;
+       xmpp_stanza_t *m_stanza;
+
+       void *operator new(size_t size, Context *ctx);
+       void operator delete(void *p);
+       Stanza(Context *ctx);
+       virtual ~Stanza();
+
+    public:
+       static Stanza *create(Context *ctx);
+       void release();
+       Stanza *clone();
+       Stanza *copy();
+       
+       int toText(const char ** const buf, size_t * const buflen);
+       Stanza *getChildren();
+       Stanza *getChildByName(const char * const name);
+       Stanza *getNext();
+        char *getAttribute(const char * const name);
+       char *getNamespace();
+       char *getText();
+       char *getName();
+       void addChild(Stanza *child);
+       void setNamespace(const char * const ns);
+       void setAttribute(const char * const key, const char * const value);
+       void setName(const char * const name);
+       void setText(const char * const text);
+       void setText(const char * const text, const size_t size);
+       char *getType();
+       char *getId();
+       char *getTo();
+       char *getFrom();
+       void setType(const char * const type);
+       void setId(const char * const id);
+       void setTo(const char * const to);
+       void setFrom(const char * const from);
+    };
+
+    class Connection {
+    private:
+       Context *m_ctx;
+       xmpp_conn_t *conn;
+
+       void *operator new(size_t size, Context *ctx);
+       Connection(Context *ctx);
+
+    public:
+       static Connection *create(Context *ctx);
+       virtual ~Connection();
+       Connection *clone();
+       void operator delete(void *p);
+
+       const char *getJID();
+       void setJID(const char * const jid);
+       const char *getPass();
+       void setPass(const char * const pass);
+       bool connectClient(const char * const domain,
+                          xmpp_conn_handler callback,
+                          void * const userdata);
+       void disconnect();
+       void send(Stanza *stanza);
+
+       void addTimedHandler(xmpp_timed_handler handler,
+                            const unsigned long perdio,
+                            void * const userdata);
+       void deleteTimedHandler(xmpp_timed_handler handler);
+       void addHandler(xmpp_handler handler,
+                       const char * const ns,
+                       const char * const name,
+                       const char * const type,
+                       void * const userdata);
+       void deleteHandler(xmpp_handler handler);
+       void addIdHandler(xmpp_handler handler,
+                         const char * const id,
+                         void * const userdata);
+       void deleteIdHandler(xmpp_handler handler);
+    };
+}
+
+#endif /* __LIBSTROPHE_STROPHEPP_H__ */
index c046f7f..48e139b 100644 (file)
@@ -164,6 +164,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnxsrv", "src\server\libn
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nxcore", "src\server\core\nxcore.vcproj", "{3B172035-5EEC-45A3-8471-2C390B7ED683}"
        ProjectSection(ProjectDependencies) = postProject
+               {CB89D905-C8BE-4027-B2D8-F96C245E9160} = {CB89D905-C8BE-4027-B2D8-F96C245E9160}
+               {F4169894-5C48-4E11-A82E-1055F9572EEF} = {F4169894-5C48-4E11-A82E-1055F9572EEF}
                {B2988503-1921-4B9F-BBC1-5E5CF62F335E} = {B2988503-1921-4B9F-BBC1-5E5CF62F335E}
                {7DC90EE4-E31C-4F12-8F1E-81F10E9099FB} = {7DC90EE4-E31C-4F12-8F1E-81F10E9099FB}
                {406AE5C7-343D-4C88-B8F3-84E46255B830} = {406AE5C7-343D-4C88-B8F3-84E46255B830}
@@ -171,7 +173,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nxcore", "src\server\core\n
                {B1745870-F3ED-4ACB-B813-0C4F47EF0793} = {B1745870-F3ED-4ACB-B813-0C4F47EF0793}
                {F3E29541-3A0E-45EC-8BEC-E193F2401622} = {F3E29541-3A0E-45EC-8BEC-E193F2401622}
                {AB386821-B630-49F5-95C3-677B9DCE1270} = {AB386821-B630-49F5-95C3-677B9DCE1270}
-               {CB89D905-C8BE-4027-B2D8-F96C245E9160} = {CB89D905-C8BE-4027-B2D8-F96C245E9160}
        EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nxsl", "nxsl", "{CE0DA89E-CF0C-452C-9C33-75A143355BDC}"
@@ -563,6 +564,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nxcproxy", "src\nxcproxy\nx
                {B1745870-F3ED-4ACB-B813-0C4F47EF0793} = {B1745870-F3ED-4ACB-B813-0C4F47EF0793}
        EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libstrophe", "src\libstrophe\libstrophe.vcproj", "{F4169894-5C48-4E11-A82E-1055F9572EEF}"
+       ProjectSection(ProjectDependencies) = postProject
+               {6A88EF62-C4F2-4295-A833-549DAE4DF5ED} = {6A88EF62-C4F2-4295-A833-549DAE4DF5ED}
+       EndProjectSection
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Win32 = Debug|Win32
@@ -1299,6 +1305,14 @@ Global
                {C6F495E0-71D7-4B7E-AC3A-ABBEFBFFD2DF}.Release|Win32.Build.0 = Release|Win32
                {C6F495E0-71D7-4B7E-AC3A-ABBEFBFFD2DF}.Release|x64.ActiveCfg = Release|x64
                {C6F495E0-71D7-4B7E-AC3A-ABBEFBFFD2DF}.Release|x64.Build.0 = Release|x64
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Debug|Win32.ActiveCfg = Debug|Win32
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Debug|Win32.Build.0 = Debug|Win32
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Debug|x64.ActiveCfg = Debug|x64
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Debug|x64.Build.0 = Debug|x64
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Release|Win32.ActiveCfg = Release|Win32
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Release|Win32.Build.0 = Release|Win32
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Release|x64.ActiveCfg = Release|x64
+               {F4169894-5C48-4E11-A82E-1055F9572EEF}.Release|x64.Build.0 = Release|x64
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -1313,6 +1327,7 @@ Global
                {DAA82F5C-1144-4DC0-A2E8-5253068565F1} = {71683564-472B-4216-BA74-0F34BC843D92}
                {406AE5C7-343D-4C88-B8F3-84E46255B830} = {71683564-472B-4216-BA74-0F34BC843D92}
                {C6F495E0-71D7-4B7E-AC3A-ABBEFBFFD2DF} = {71683564-472B-4216-BA74-0F34BC843D92}
+               {F4169894-5C48-4E11-A82E-1055F9572EEF} = {71683564-472B-4216-BA74-0F34BC843D92}
                {451F583D-C2DB-4414-870C-7FA0189BE7DD} = {8BC9D64D-347C-41BE-A506-D21C8FB72D56}
                {57598B02-3295-4FE8-9322-94CE871CC84D} = {8BC9D64D-347C-41BE-A506-D21C8FB72D56}
                {896A7CDA-423A-460A-83E2-6ED37DAE187C} = {8BC9D64D-347C-41BE-A506-D21C8FB72D56}
index c966212..ffcbc1d 100644 (file)
@@ -64,6 +64,7 @@ CREATE TABLE users
        disabled_until integer not null,
        last_login integer not null,
        password_history SQL_TEXT null,
+   xmpp_id varchar(127) null,
        PRIMARY KEY(id)
 ) TABLE_TYPE;
 
index 24ff3bd..82ab163 100644 (file)
@@ -57,6 +57,7 @@ INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EnableObjectTransactions','0',1,1);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EnableSNMPTraps','1',1,1);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EnableSyslogDaemon','0',1,1);
+INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EnableXMPPConnector','0',1,1);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EnableZoning','0',1,1);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EventLogRetentionTime','90',1,0);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('EventStormDuration','15',1,1);
@@ -137,6 +138,10 @@ INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('UseIfXTable','1',1,0);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('UseInterfaceAliases','0',1,0);
 INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('UseSNMPTrapsForDiscovery','0',1,1);
+INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('XMPPLogin','netxms@localhost',1,1);
+INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('XMPPPassword','netxms',1,1);
+INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('XMPPPort','5222',1,1);
+INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('XMPPServer','localhost',1,1);
 
 /*
 ** Default admin user
index 76c5a42..a433850 100644 (file)
@@ -774,6 +774,7 @@ public class NXCPCodes
    public static final long VID_BOOT_TIME = 454;
    public static final long VID_REQUEST_ID = 455;
    public static final long VID_ADDRESS_MAP = 456;
+   public static final long VID_XMPP_ID = 457;
 
        public static final long VID_ACL_USER_BASE = 0x00001000L;
        public static final long VID_ACL_USER_LAST = 0x00001FFFL;
index 699f05a..c8cc490 100644 (file)
@@ -34,6 +34,9 @@ public class UserAccessRights
        public static final int SYSTEM_ACCESS_SERVER_CONSOLE      = 0x00200000;
        public static final int SYSTEM_ACCESS_MANAGE_FILES        = 0x00400000;
        public static final int SYSTEM_ACCESS_MANAGE_MAPPING_TBLS = 0x00800000;
+   public static final int SYSTEM_ACCESS_MANAGE_SUMMARY_TBLS = 0x01000000;
+   public static final int SYSTEM_ACCESS_REPORTING_SERVER    = 0x02000000;
+   public static final int SYSTEM_ACCESS_XMPP_COMMANDS       = 0x04000000;
        
        public static final int OBJECT_ACCESS_READ          = 0x00000001;
        public static final int OBJECT_ACCESS_MODIFY        = 0x00000002;
index ffc6ddf..c0824ca 100644 (file)
@@ -49,6 +49,7 @@ public class User extends AbstractUserObject
        private int minPasswordLength;
        private Date disabledUntil = null;
        private int authFailures;
+       private String xmppId;
 
        /**
         * Default constructor
@@ -57,6 +58,7 @@ public class User extends AbstractUserObject
        {
                super(name);
                fullName = "";
+               xmppId = "";
        }
        
        /**
@@ -75,6 +77,7 @@ public class User extends AbstractUserObject
                this.minPasswordLength = src.minPasswordLength;
                this.disabledUntil = src.disabledUntil;
                this.authFailures = src.authFailures;
+               this.xmppId = src.xmppId;
        }
        
        /**
@@ -92,6 +95,7 @@ public class User extends AbstractUserObject
                minPasswordLength = msg.getVariableAsInteger(NXCPCodes.VID_MIN_PASSWORD_LENGTH);
                disabledUntil = msg.getVariableAsDate(NXCPCodes.VID_DISABLED_UNTIL);
                authFailures = msg.getVariableAsInteger(NXCPCodes.VID_AUTH_FAILURES);
+               xmppId = msg.getVariableAsString(NXCPCodes.VID_XMPP_ID);
        }
        
        /**
@@ -106,6 +110,7 @@ public class User extends AbstractUserObject
                msg.setVariable(NXCPCodes.VID_CERT_MAPPING_DATA, certMappingData);
                msg.setVariableInt16(NXCPCodes.VID_MIN_PASSWORD_LENGTH, minPasswordLength);
                msg.setVariableInt32(NXCPCodes.VID_DISABLED_UNTIL, (disabledUntil != null) ? (int)(disabledUntil.getTime() / 1000) : 0);
+               msg.setVariable(NXCPCodes.VID_XMPP_ID, xmppId);
        }
 
        /**
@@ -236,4 +241,20 @@ public class User extends AbstractUserObject
        {
                return authFailures;
        }
+
+   /**
+    * @return the xmppId
+    */
+   public String getXmppId()
+   {
+      return xmppId;
+   }
+
+   /**
+    * @param xmppId the xmppId to set
+    */
+   public void setXmppId(String xmppId)
+   {
+      this.xmppId = xmppId;
+   }
 }
index bd55e7a..396909d 100644 (file)
@@ -39,6 +39,7 @@ public interface UserManager
        public static final int USER_MODIFY_PASSWD_LENGTH     = 0x00000100;
        public static final int USER_MODIFY_TEMP_DISABLE      = 0x00000200;
        public static final int USER_MODIFY_CUSTOM_ATTRIBUTES = 0x00000400;
+   public static final int USER_MODIFY_XMPP_ID           = 0x00000800;
 
        /**
         * Synchronize user database
index 524d7ee..59c9c3b 100644 (file)
@@ -33,6 +33,7 @@ public class ServerAction
        public static final int SEND_SMS = 3;
        public static final int FORWARD_EVENT = 4;
        public static final int EXEC_NXSL_SCRIPT = 5;
+   public static final int XMPP_MESSAGE = 6;
        
        private long id;
        private int type;
index c591fc0..029f87d 100644 (file)
@@ -1,6 +1,6 @@
 action.OpenActionsConfiguration=Konfigurace akc\u00ED
 actionDescription.OpenActionsConfiguration=Otev\u0159\u00EDt okno konfigurace akc\u00ED
-actionSet.ActionManager.label = Action Manager
+actionSet.ActionManager.label=Spr\u00E1vce akc\u00ED
 decorator.actions=Dekorace serverov\u00FDch akc\u00ED
 decorator.actions.description=Dekoruj \u0161t\u00EDtek server akc\u00ED "disabled" ikonou, kdy\u017E je akce zak\u00E1z\u00E1na
 view.ActionsConfiguration=Konfigurace akc\u00ED
diff --git a/src/java/netxms-eclipse/ActionManager/icons/xmpp.png b/src/java/netxms-eclipse/ActionManager/icons/xmpp.png
new file mode 100644 (file)
index 0000000..6852b47
Binary files /dev/null and b/src/java/netxms-eclipse/ActionManager/icons/xmpp.png differ
index 8e7eee8..e3c438e 100644 (file)
@@ -69,6 +69,8 @@ public class ServerActionAdapterFactory implements IAdapterFactory
                                                return Activator.getImageDescriptor("icons/fwd_event.png"); //$NON-NLS-1$
                                        case ServerAction.EXEC_NXSL_SCRIPT:
                                                return Activator.getImageDescriptor("icons/exec_script.gif"); //$NON-NLS-1$
+               case ServerAction.XMPP_MESSAGE:
+                  return Activator.getImageDescriptor("icons/xmpp.png"); //$NON-NLS-1$
                                }
                                return null;
                        }
index 2afdf5a..e2e85ba 100644 (file)
@@ -53,6 +53,7 @@ public class EditActionDlg extends Dialog
        private Button typeExecScript;
        private Button typeEMail;
        private Button typeSMS;
+       private Button typeXMPP;
        private Button typeForward;
        private Button markDisabled;
 
@@ -144,6 +145,11 @@ public class EditActionDlg extends Dialog
                typeSMS.setSelection(action.getType() == ServerAction.SEND_SMS);
                typeSMS.addSelectionListener(new TypeButtonSelectionListener());
                
+      typeXMPP = new Button(typeGroup, SWT.RADIO);
+      typeXMPP.setText("Send XMPP message");
+      typeXMPP.setSelection(action.getType() == ServerAction.XMPP_MESSAGE);
+      typeXMPP.addSelectionListener(new TypeButtonSelectionListener());
+      
                typeForward = new Button(typeGroup, SWT.RADIO);
                typeForward.setText(Messages.get().EditActionDlg_ForwardEvent);
                typeForward.setSelection(action.getType() == ServerAction.FORWARD_EVENT);
@@ -205,6 +211,8 @@ public class EditActionDlg extends Dialog
                                return Messages.get().EditActionDlg_RemoteHost;
                        case ServerAction.SEND_SMS:
                                return Messages.get().EditActionDlg_PhoneNumber;
+         case ServerAction.XMPP_MESSAGE:
+            return "Jabber/XMPP ID";
                        case ServerAction.FORWARD_EVENT:
                                return Messages.get().EditActionDlg_RemoteServer;
                        case ServerAction.EXEC_NXSL_SCRIPT:
@@ -247,6 +255,8 @@ public class EditActionDlg extends Dialog
                        action.setType(ServerAction.SEND_EMAIL);
                else if (typeSMS.getSelection())
                        action.setType(ServerAction.SEND_SMS);
+      else if (typeXMPP.getSelection())
+         action.setType(ServerAction.XMPP_MESSAGE);
                else if (typeForward.getSelection())
                        action.setType(ServerAction.FORWARD_EVENT);
                
@@ -276,6 +286,8 @@ public class EditActionDlg extends Dialog
                        type = ServerAction.SEND_EMAIL;
                else if (typeSMS.getSelection())
                        type = ServerAction.SEND_SMS;
+      else if (typeXMPP.getSelection())
+         type = ServerAction.XMPP_MESSAGE;
                else if (typeForward.getSelection())
                        type = ServerAction.FORWARD_EVENT;
                
@@ -287,6 +299,8 @@ public class EditActionDlg extends Dialog
                                data.setEnabled(true);
                                break;
                        case ServerAction.EXEC_REMOTE:
+         case ServerAction.SEND_SMS:
+         case ServerAction.XMPP_MESSAGE:
                                recipient.setEnabled(true);
                                subject.setEnabled(false);
                                data.setEnabled(true);
@@ -296,16 +310,7 @@ public class EditActionDlg extends Dialog
                                subject.setEnabled(true);
                                data.setEnabled(true);
                                break;
-                       case ServerAction.SEND_SMS:
-                               recipient.setEnabled(true);
-                               subject.setEnabled(false);
-                               data.setEnabled(true);
-                               break;
                        case ServerAction.FORWARD_EVENT:
-                               recipient.setEnabled(true);
-                               subject.setEnabled(false);
-                               data.setEnabled(false);
-                               break;
                        case ServerAction.EXEC_NXSL_SCRIPT:
                                recipient.setEnabled(true);
                                subject.setEnabled(false);
index be39966..9b97db1 100644 (file)
@@ -1,16 +1,16 @@
-action.ChangePasswd.label = Change password...
-action.ChangePasswd.tooltip = Change password for current user
-action.UserManager.label = User Manager
-action.UserManager.tooltip = Open user manager
-actionSet.UserManager.label = User Manager
-category.Tools = Tools
-category.Views = Views
-command.ChangePasswd.description = Change password for current user
-command.ChangePasswd.name = Change password...
-command.UserManager.description = Open user manager
-command.UserManager.name = User Manager
-page.Authentication.name = Authentication
-page.General.name = General
-page.Members.name = Members
-page.SystemRights.name = System Rights
-view.UserManager.name = User Manager
+action.ChangePasswd.label=Zm\u011Bnit heslo...
+action.ChangePasswd.tooltip=Zm\u011Bnit heslo sou\u010Dasn\u00E9ho u\u017Eivatele
+action.UserManager.label=Spr\u00E1vce u\u017Eivatel\u016F
+action.UserManager.tooltip=Otev\u0159it spr\u00E1vce u\u017Eivatel\u016F
+actionSet.UserManager.label=Spr\u00E1vce u\u017Eivatel\u016F
+category.Tools=N\u00E1stroje
+category.Views=Pohledy
+command.ChangePasswd.description=Zm\u011Bnit heslo sou\u010Dasn\u00E9ho u\u017Eivatele
+command.ChangePasswd.name=Zm\u011Bnit heslo...
+command.UserManager.description=Otev\u0159\u00EDt spr\u00E1vce u\u017Eivatel\u016F
+command.UserManager.name=Spr\u00E1vce u\u017Eivatel\u016F
+page.Authentication.name=Ov\u011B\u0159en\u00ED pravosti
+page.General.name=Obecn\u00FD
+page.Members.name=\u010Clenov\u00E9
+page.SystemRights.name=Syst\u00E9mov\u00E1 pr\u00E1va
+view.UserManager.name=Spr\u00E1vce u\u017Eivatel\u016F
index 676ca94..b8ccc1b 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * NetXMS - open source network management system
- * Copyright (C) 2003-2011 Victor Kirhenshtein
+ * Copyright (C) 2003-2013 Victor Kirhenshtein
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -37,16 +37,17 @@ import org.netxms.ui.eclipse.tools.WidgetHelper;
 
 /**
  * User's "general" property page
- *
  */
 public class General extends PropertyPage
 {
        private Text textName;
        private Text textFullName;
        private Text textDescription;
+       private Text textXmppId;
        private String initialName;
        private String initialFullName;
        private String initialDescription;
+   private String initialXmppId;
        private AbstractUserObject object;
        private Session session;
        
@@ -88,10 +89,15 @@ public class General extends PropertyPage
              initialFullName = new String(((User)object).getFullName());
              textFullName = WidgetHelper.createLabeledText(dialogArea, SWT.SINGLE | SWT.BORDER, SWT.DEFAULT, Messages.get().General_FullName,
                                                                initialFullName, WidgetHelper.DEFAULT_LAYOUT_DATA);
+
+         initialXmppId = new String(((User)object).getXmppId());
+         textXmppId = WidgetHelper.createLabeledText(dialogArea, SWT.SINGLE | SWT.BORDER, SWT.DEFAULT, "XMPP ID",
+                                                       initialXmppId, WidgetHelper.DEFAULT_LAYOUT_DATA);
       }
       else
       {
        initialFullName = ""; //$NON-NLS-1$
+       initialXmppId = "";
       }
       
                // Description
@@ -111,11 +117,13 @@ public class General extends PropertyPage
        {
                final String newName = new String(textName.getText());
                final String newDescription = new String(textDescription.getText());
-               final String newFullName = (object instanceof User) ? new String(textFullName.getText()) : ""; //$NON-NLS-1$
+               final String newFullName = (object instanceof User) ? textFullName.getText() : ""; //$NON-NLS-1$
+      final String newXmppId = (object instanceof User) ? textXmppId.getText() : ""; //$NON-NLS-1$
                
                if (newName.equals(initialName) && 
                    newDescription.equals(initialDescription) &&
-                   newFullName.equals(initialFullName))
+                   newFullName.equals(initialFullName) &&
+                   newXmppId.equals(initialXmppId))
                        return;         // Nothing to apply
                
                if (isApply)
@@ -128,6 +136,7 @@ public class General extends PropertyPage
                                initialName = newName;
                                initialFullName = newFullName;
                                initialDescription = newDescription;
+                               initialXmppId = newXmppId;
                                
                                int fields = UserManager.USER_MODIFY_LOGIN_NAME | UserManager.USER_MODIFY_DESCRIPTION;
                                object.setName(newName);
@@ -135,7 +144,8 @@ public class General extends PropertyPage
                                if (object instanceof User)
                                {
                                        ((User)object).setFullName(newFullName);
-                                       fields |= UserManager.USER_MODIFY_FULL_NAME;
+               ((User)object).setXmppId(newXmppId);
+                                       fields |= UserManager.USER_MODIFY_FULL_NAME | UserManager.USER_MODIFY_XMPP_ID;
                                }
                                ((UserManager)session).modifyUserDBObject(object, fields);
                        }
index 9db6d27..bb3e10e 100644 (file)
@@ -70,6 +70,7 @@ public class SystemRights extends PropertyPage
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_EPP, Messages.get().SystemRights_EditEPP);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_SCRIPTS, Messages.get().SystemRights_ManageScripts);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_TOOLS, Messages.get().SystemRights_ConfigureObjTools);
+      addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_SUMMARY_TBLS, "Manage DCI summary tables");
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_CONFIGURE_TRAPS, Messages.get().SystemRights_ConfigureTraps);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_AGENT_CFG, Messages.get().SystemRights_ManageAgents);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_PACKAGES, Messages.get().SystemRights_ManagePackages);
@@ -81,6 +82,7 @@ public class SystemRights extends PropertyPage
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_READ_FILES, Messages.get().SystemRights_ReadFiles);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_FILES, Messages.get().SystemRights_ManageFiles);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_SERVER_CONSOLE, Messages.get().SystemRights_AccessConsole);
+      addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_XMPP_COMMANDS, "Execute commands via XMPP");
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_SESSIONS, Messages.get().SystemRights_ControlSessions);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_MANAGE_USERS, Messages.get().SystemRights_ManageUsers);
                addCheckbox(dialogArea, UserAccessRights.SYSTEM_ACCESS_SEND_SMS, Messages.get().SystemRights_SendSMS);
diff --git a/src/libstrophe/GPL-LICENSE.txt b/src/libstrophe/GPL-LICENSE.txt
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/src/libstrophe/LICENSE.txt b/src/libstrophe/LICENSE.txt
new file mode 100644 (file)
index 0000000..fc52e2a
--- /dev/null
@@ -0,0 +1,5 @@
+libstrophe XMPP client library
+Copyright (C) 2005-2009 Collecta, Inc.
+
+This program is dual licensed under the MIT and GPLv3 licenses.
+Please the files MIT-LICENSE.txt and GPL-LICENSE.txt for details.
diff --git a/src/libstrophe/MIT-LICENSE.txt b/src/libstrophe/MIT-LICENSE.txt
new file mode 100644 (file)
index 0000000..f0d90d5
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2005-2009 Collecta, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/libstrophe/auth.c b/src/libstrophe/auth.c
new file mode 100644 (file)
index 0000000..03b64cc
--- /dev/null
@@ -0,0 +1,986 @@
+/* auth.c
+** strophe XMPP client library -- auth functions and handlers
+**
+** 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.
+*/
+
+/** @file 
+ *  Authentication function and handlers.
+ */
+
+#include <nms_common.h>
+#include <strophe.h>
+#include "common.h"
+#include "sasl.h"
+
+#ifdef _WIN32
+#define strcasecmp stricmp
+#endif
+
+/* TODO: these should configurable at runtime on a per connection basis  */
+
+#ifndef FEATURES_TIMEOUT
+/** @def FEATURES_TIMEOUT
+ *  Time to wait for &lt;stream:features/&gt; stanza.
+ */
+#define FEATURES_TIMEOUT 15000 /* 15 seconds */
+#endif
+#ifndef BIND_TIMEOUT
+/** @def BIND_TIMEOUT
+ *  Time to wait for &lt;bind/&gt; stanza reply.
+ */
+#define BIND_TIMEOUT 15000 /* 15 seconds */
+#endif
+#ifndef SESSION_TIMEOUT
+/** @def SESSION_TIMEOUT
+ *  Time to wait for &lt;session/&gt; stanza reply.
+ */
+#define SESSION_TIMEOUT 15000 /* 15 seconds */
+#endif
+#ifndef LEGACY_TIMEOUT
+/** @def LEGACY_TIMEOUT
+ *  Time to wait for legacy authentication to complete.
+ */
+#define LEGACY_TIMEOUT 15000 /* 15 seconds */
+#endif
+
+static void _auth(xmpp_conn_t * const conn);
+static void _handle_open_tls(xmpp_conn_t * const conn);
+static void _handle_open_sasl(xmpp_conn_t * const conn);
+static int _handle_missing_legacy(xmpp_conn_t * const conn,
+                                 void * const userdata);
+static int _handle_legacy(xmpp_conn_t * const conn,
+                         xmpp_stanza_t * const stanza,
+                         void * const userdata);
+static int _handle_features_sasl(xmpp_conn_t * const conn,
+                                xmpp_stanza_t * const stanza,
+                                void * const userdata);
+static int _handle_sasl_result(xmpp_conn_t * const conn,
+                       xmpp_stanza_t * const stanza,
+                       void * const userdata);
+static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
+                       xmpp_stanza_t * const stanza,
+                       void * const userdata);
+static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
+                       xmpp_stanza_t * const stanza,
+                       void * const userdata);
+
+static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
+                                        void * const userdata);
+static int _handle_missing_bind(xmpp_conn_t * const conn,
+                               void * const userdata);
+static int _handle_bind(xmpp_conn_t * const conn,
+                       xmpp_stanza_t * const stanza,
+                       void * const userdata);
+static int _handle_session(xmpp_conn_t * const conn,
+                          xmpp_stanza_t * const stanza,
+                          void * const userdata);
+static int _handle_missing_session(xmpp_conn_t * const conn,
+                                  void * const userdata);
+
+/* stream:error handler */
+static int _handle_error(xmpp_conn_t * const conn,
+                        xmpp_stanza_t * const stanza,
+                        void * const userdata)
+{
+    xmpp_stanza_t *child;
+    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) 
+           xmpp_free(conn->ctx, conn->stream_error->text);
+       xmpp_free(conn->ctx, conn->stream_error);
+    }
+
+    /* create stream error structure */
+    conn->stream_error = (xmpp_stream_error_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_stream_error_t));
+
+       conn->stream_error->text = NULL;
+       conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
+
+    if (conn->stream_error) {
+       child = xmpp_stanza_get_children(stanza);
+       do {
+           char *ns = NULL;
+
+           if (child) {
+               ns = xmpp_stanza_get_ns(child);
+           }
+
+           if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) {
+               name = xmpp_stanza_get_name(child);
+               if (strcmp(name, "text") == 0) {
+                   if (conn->stream_error->text)
+                       xmpp_free(conn->ctx, conn->stream_error->text);
+                   conn->stream_error->text = xmpp_stanza_get_text(child);
+               } else if (strcmp(name, "bad-format") == 0)
+                   conn->stream_error->type = XMPP_SE_BAD_FORMAT;
+               else if (strcmp(name, "bad-namespace-prefix") == 0)
+                   conn->stream_error->type = XMPP_SE_BAD_NS_PREFIX;
+               else if (strcmp(name, "conflict") == 0)
+                   conn->stream_error->type = XMPP_SE_CONFLICT;
+               else if (strcmp(name, "connection-timeout") == 0)
+                   conn->stream_error->type = XMPP_SE_CONN_TIMEOUT;
+               else if (strcmp(name, "host-gone") == 0)
+                   conn->stream_error->type = XMPP_SE_HOST_GONE;
+               else if (strcmp(name, "host-unknown") == 0)
+                   conn->stream_error->type = XMPP_SE_HOST_UNKNOWN;
+               else if (strcmp(name, "improper-addressing") == 0)
+                   conn->stream_error->type = XMPP_SE_IMPROPER_ADDR;
+               else if (strcmp(name, "internal-server-error") == 0)
+                   conn->stream_error->type = XMPP_SE_INTERNAL_SERVER_ERROR;
+               else if (strcmp(name, "invalid-from") == 0)
+                   conn->stream_error->type = XMPP_SE_INVALID_FROM;
+               else if (strcmp(name, "invalid-id") == 0)
+                   conn->stream_error->type = XMPP_SE_INVALID_ID;
+               else if (strcmp(name, "invalid-namespace") == 0)
+                   conn->stream_error->type = XMPP_SE_INVALID_NS;
+               else if (strcmp(name, "invalid-xml") == 0)
+                   conn->stream_error->type = XMPP_SE_INVALID_XML;
+               else if (strcmp(name, "not-authorized") == 0)
+                   conn->stream_error->type = XMPP_SE_NOT_AUTHORIZED;
+               else if (strcmp(name, "policy-violation") == 0)
+                   conn->stream_error->type = XMPP_SE_POLICY_VIOLATION;
+               else if (strcmp(name, "remote-connection-failed") == 0)
+                   conn->stream_error->type = XMPP_SE_REMOTE_CONN_FAILED;
+               else if (strcmp(name, "resource-constraint") == 0)
+                   conn->stream_error->type = XMPP_SE_RESOURCE_CONSTRAINT;
+               else if (strcmp(name, "restricted-xml") == 0)
+                   conn->stream_error->type = XMPP_SE_RESTRICTED_XML;
+               else if (strcmp(name, "see-other-host") == 0)
+                   conn->stream_error->type = XMPP_SE_SEE_OTHER_HOST;
+               else if (strcmp(name, "system-shutdown") == 0)
+                   conn->stream_error->type = XMPP_SE_SYSTEM_SHUTDOWN;
+               else if (strcmp(name, "undefined-condition") == 0)
+                   conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
+               else if (strcmp(name, "unsupported-encoding") == 0)
+                   conn->stream_error->type = XMPP_SE_UNSUPPORTED_ENCODING;
+               else if (strcmp(name, "unsupported-stanza-type") == 0)
+                   conn->stream_error->type = XMPP_SE_UNSUPPORTED_STANZA_TYPE;
+               else if (strcmp(name, "unsupported-version") == 0)
+                   conn->stream_error->type = XMPP_SE_UNSUPPORTED_VERSION;
+               else if (strcmp(name, "xml-not-well-formed") == 0)
+                   conn->stream_error->type = XMPP_SE_XML_NOT_WELL_FORMED;
+           }
+       } while ((child = xmpp_stanza_get_next(child)));
+
+       conn->stream_error->stanza = xmpp_stanza_clone(stanza);
+    }
+
+    return 1;
+}
+
+/* stream:features handlers */
+static int _handle_missing_features(xmpp_conn_t * const conn,
+                                   void * const userdata)
+{
+    xmpp_debug(conn->ctx, "xmpp", "didn't get stream features");
+
+    /* legacy auth will be attempted */
+    _auth(conn);
+
+    return 0;
+}
+
+
+
+static int _handle_features(xmpp_conn_t * const conn,
+                           xmpp_stanza_t * const stanza,
+                           void * const userdata)
+{
+    xmpp_stanza_t *child, *mech;
+    char *text;
+
+    /* remove the handler that detects missing stream:features */
+    xmpp_timed_handler_delete(conn, _handle_missing_features);
+
+    /* check for TLS */
+    if (!conn->secured) {
+        if (!conn->tls_disabled) {
+            child = xmpp_stanza_get_child_by_name(stanza, "starttls");
+            if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0))
+                conn->tls_support = 1;
+        } else {
+            conn->tls_support = 0;
+        }
+    }
+
+    /* check for SASL */
+    child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
+    if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_SASL) == 0)) {
+       for (mech = xmpp_stanza_get_children(child); mech; 
+            mech = xmpp_stanza_get_next(mech)) {
+           if (strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
+               text = xmpp_stanza_get_text(mech);
+               if (strcasecmp(text, "PLAIN") == 0)
+                   conn->sasl_support |= SASL_MASK_PLAIN;
+               else if (strcasecmp(text, "DIGEST-MD5") == 0)
+                   conn->sasl_support |= SASL_MASK_DIGESTMD5;
+               else if (strcasecmp(text, "ANONYMOUS") == 0)
+                   conn->sasl_support |= SASL_MASK_ANONYMOUS;
+
+               xmpp_free(conn->ctx, text);
+           }
+       }
+    }
+
+    _auth(conn);
+    return 0;
+}
+
+/* returns the correct auth id for a component or a client.
+ * returned string must be freed by caller */
+static char *_get_authid(xmpp_conn_t * const conn)
+{
+    char *authid = NULL;
+
+    if (conn->type == XMPP_CLIENT) {
+       /* authid is the node portion of jid */
+       if (!conn->jid) return NULL;
+       authid = xmpp_jid_node(conn->ctx, conn->jid);
+    }
+
+    return authid;
+}
+
+static int _handle_proceedtls_default(xmpp_conn_t * const conn,
+                             xmpp_stanza_t * const stanza,
+                             void * const userdata)
+{
+    char *name;
+    name = xmpp_stanza_get_name(stanza);
+    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;
+            conn_prepare_reset(conn, auth_handle_open);
+
+           conn_open_stream(conn);
+       }
+    }
+
+    return 0;
+}
+
+static int _handle_sasl_result(xmpp_conn_t * const conn,
+                              xmpp_stanza_t * const stanza,
+                              void * const userdata)
+{
+    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", 
+                  (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", 
+                  (char *)userdata);
+
+       /* reset parser */
+       conn_prepare_reset(conn, _handle_open_sasl);
+
+       /* send stream tag */
+       conn_open_stream(conn);
+    } else {
+       /* got unexpected reply */
+       xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL %s"\
+                  "authentication.", (char *)userdata);
+       xmpp_disconnect(conn);
+    }
+
+    return 0;
+}
+
+/* handle the challenge phase of digest auth */
+static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
+                             xmpp_stanza_t * const stanza,
+                             void * const userdata)
+{
+    char *text;
+    char *response;
+    xmpp_stanza_t *auth, *authdata;
+    char *name;
+
+    name = xmpp_stanza_get_name(stanza);
+    xmpp_debug(conn->ctx, "xmpp",\
+       "handle digest-md5 (challenge) called for %s", name);
+
+    if (strcmp(name, "challenge") == 0) {
+       text = xmpp_stanza_get_text(stanza);
+       response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
+       if (!response) {
+           disconnect_mem_error(conn);
+           return 0;
+       }
+       xmpp_free(conn->ctx, text);
+
+       auth = xmpp_stanza_new(conn->ctx);
+       if (!auth) {
+           disconnect_mem_error(conn);
+           return 0;
+       }       
+       xmpp_stanza_set_name(auth, "response");
+       xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
+       
+       authdata = xmpp_stanza_new(conn->ctx);
+       if (!authdata) {
+           disconnect_mem_error(conn);
+           return 0;
+       }
+
+       xmpp_stanza_set_text(authdata, response);
+       xmpp_free(conn->ctx, response);
+
+       xmpp_stanza_add_child(auth, authdata);
+       xmpp_stanza_release(authdata);
+
+       handler_add(conn, _handle_digestmd5_rspauth, 
+                   XMPP_NS_SASL, NULL, NULL, NULL);
+
+       xmpp_send(conn, auth);
+       xmpp_stanza_release(auth);
+
+    } else {
+       return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
+    }
+
+    /* remove ourselves */
+    return 0;
+}
+
+/* handle the rspauth phase of digest auth */
+static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
+                             xmpp_stanza_t * const stanza,
+                             void * const userdata)
+{
+    xmpp_stanza_t *auth;
+    char *name;
+
+    name = xmpp_stanza_get_name(stanza);
+    xmpp_debug(conn->ctx, "xmpp",
+       "handle digest-md5 (rspauth) called for %s", name);
+
+
+    if (strcmp(name, "challenge") == 0) {
+       /* assume it's an rspauth response */
+       auth = xmpp_stanza_new(conn->ctx);
+       if (!auth) {
+           disconnect_mem_error(conn);
+           return 0;
+       }       
+       xmpp_stanza_set_name(auth, "response");
+       xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
+       xmpp_send(conn, auth);
+       xmpp_stanza_release(auth);
+    } else {
+       return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
+    }
+
+    return 1;
+}
+
+static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn)
+{
+    xmpp_stanza_t *starttls;
+
+    /* build start stanza */
+    starttls = xmpp_stanza_new(conn->ctx);
+    if (starttls) {
+       xmpp_stanza_set_name(starttls, "starttls");
+       xmpp_stanza_set_ns(starttls, XMPP_NS_TLS);
+    }
+    
+    return starttls;
+}
+
+static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
+                                const char * const mechanism)
+{
+    xmpp_stanza_t *auth;
+
+    /* build auth stanza */
+    auth = xmpp_stanza_new(conn->ctx);
+    if (auth) {
+       xmpp_stanza_set_name(auth, "auth");
+       xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
+       xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
+    }
+    
+    return auth;
+}
+
+/* authenticate the connection 
+ * this may get called multiple times.  if any auth method fails, 
+ * this will get called again until one auth method succeeds or every
+ * method fails 
+ */
+static void _auth(xmpp_conn_t * const conn)
+{
+    xmpp_stanza_t *auth, *authdata, *query, *child, *iq;
+    char *str, *authid;
+    int anonjid;
+
+    /* if there is no node in conn->jid, we assume anonymous connect */
+    str = xmpp_jid_node(conn->ctx, conn->jid);
+    if (str == NULL) {
+       anonjid = 1;
+    } else {
+       xmpp_free(conn->ctx, str);
+       anonjid = 0;
+    }
+
+    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)
+       {
+           conn->tls_support = 0;
+           _auth(conn);
+           return;
+       }
+       else
+       {
+           tls_free(tls);
+       }
+
+       auth = _make_starttls(conn);
+
+       if (!auth) {
+           disconnect_mem_error(conn);
+           return;
+       }
+
+       handler_add(conn, _handle_proceedtls_default, 
+                   XMPP_NS_TLS, NULL, NULL, NULL);
+
+       xmpp_send(conn, auth);
+       xmpp_stanza_release(auth);
+
+       /* TLS was tried, unset flag */
+       conn->tls_support = 0;
+    } else if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
+       /* some crap here */
+       auth = _make_sasl_auth(conn, "ANONYMOUS");
+       if (!auth) {
+           disconnect_mem_error(conn);
+           return;
+       }
+
+       handler_add(conn, _handle_sasl_result, XMPP_NS_SASL,
+                   NULL, NULL, "ANONYMOUS");
+
+       xmpp_send(conn, auth);
+       xmpp_stanza_release(auth);
+
+       /* SASL ANONYMOUS was tried, unset flag */
+       conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
+    } else if (anonjid) {
+       xmpp_error(conn->ctx, "auth", 
+                  "No node in JID, and SASL ANONYMOUS unsupported.");
+       xmpp_disconnect(conn);
+    } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
+       auth = _make_sasl_auth(conn, "DIGEST-MD5");
+       if (!auth) {
+           disconnect_mem_error(conn);
+           return;
+
+       }
+
+       handler_add(conn, _handle_digestmd5_challenge, 
+                   XMPP_NS_SASL, NULL, NULL, NULL);
+
+       xmpp_send(conn, auth);
+       xmpp_stanza_release(auth);
+
+       /* SASL DIGEST-MD5 was tried, unset flag */
+       conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
+    } else if (conn->sasl_support & SASL_MASK_PLAIN) {
+       auth = _make_sasl_auth(conn, "PLAIN");
+       if (!auth) {
+           disconnect_mem_error(conn);
+           return;
+       }
+       authdata = xmpp_stanza_new(conn->ctx);
+       if (!authdata) {
+           disconnect_mem_error(conn);
+           return;
+       }       
+       authid = _get_authid(conn);
+       if (!authid) {
+           disconnect_mem_error(conn);
+           return;
+       }
+       str = sasl_plain(conn->ctx, authid, conn->pass);
+       if (!str) {
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_text(authdata, str);
+       xmpp_free(conn->ctx, str);
+
+       xmpp_stanza_add_child(auth, authdata);
+       xmpp_stanza_release(authdata);
+
+       handler_add(conn, _handle_sasl_result,
+                   XMPP_NS_SASL, NULL, NULL, "PLAIN");
+
+       xmpp_send(conn, auth);
+       xmpp_stanza_release(auth);
+
+       /* SASL PLAIN was tried */
+       conn->sasl_support &= ~SASL_MASK_PLAIN;
+    } else if (conn->type == XMPP_CLIENT) {
+       /* legacy client authentication */
+       
+       iq = xmpp_stanza_new(conn->ctx);
+       if (!iq) {
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_name(iq, "iq");
+       xmpp_stanza_set_type(iq, "set");
+       xmpp_stanza_set_id(iq, "_xmpp_auth1");
+
+       query = xmpp_stanza_new(conn->ctx);
+       if (!query) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_name(query, "query");
+       xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
+       xmpp_stanza_add_child(iq, query);
+       xmpp_stanza_release(query);
+
+       child = xmpp_stanza_new(conn->ctx);
+       if (!child) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_name(child, "username");
+       xmpp_stanza_add_child(query, child);
+       xmpp_stanza_release(child);
+
+       authdata = xmpp_stanza_new(conn->ctx);
+       if (!authdata) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       str = xmpp_jid_node(conn->ctx, conn->jid);
+       xmpp_stanza_set_text(authdata, str);
+       xmpp_free(conn->ctx, str);
+       xmpp_stanza_add_child(child, authdata);
+       xmpp_stanza_release(authdata);
+
+       child = xmpp_stanza_new(conn->ctx);
+       if (!child) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_name(child, "password");
+       xmpp_stanza_add_child(query, child);
+       xmpp_stanza_release(child);
+
+       authdata = xmpp_stanza_new(conn->ctx);
+       if (!authdata) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_text(authdata, conn->pass);
+       xmpp_stanza_add_child(child, authdata);
+       xmpp_stanza_release(authdata);
+
+       child = xmpp_stanza_new(conn->ctx);
+       if (!child) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       xmpp_stanza_set_name(child, "resource");
+       xmpp_stanza_add_child(query, child);
+       xmpp_stanza_release(child);
+
+       authdata = xmpp_stanza_new(conn->ctx);
+       if (!authdata) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return;
+       }
+       str = xmpp_jid_resource(conn->ctx, conn->jid);
+       if (str) {
+           xmpp_stanza_set_text(authdata, str);
+           xmpp_free(conn->ctx, str);
+       } else {
+           xmpp_stanza_release(authdata);
+           xmpp_stanza_release(iq);
+           xmpp_error(conn->ctx, "auth", 
+                      "Cannot authenticate without resource");
+           xmpp_disconnect(conn);
+           return;
+       }
+       xmpp_stanza_add_child(child, authdata);
+       xmpp_stanza_release(authdata);
+
+       handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
+       handler_add_timed(conn, _handle_missing_legacy, 
+                         LEGACY_TIMEOUT, NULL);
+
+       xmpp_send(conn, iq);
+       xmpp_stanza_release(iq);
+    }
+}
+
+
+/** 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 
+ *  <stream:features/>.  This function is not intended for use outside
+ *  of Strophe.
+ *
+ *  @param conn a Strophe connection object
+ */
+void auth_handle_open(xmpp_conn_t * const conn)
+{
+    /* reset all timed handlers */
+    handler_reset_timed(conn, 0);
+
+    /* setup handler for stream:error */
+    handler_add(conn, _handle_error,
+               NULL, "stream:error", NULL, NULL);
+
+    /* setup handlers for incoming <stream:features> */
+    handler_add(conn, _handle_features,
+               NULL, "stream:features", NULL, NULL);
+    handler_add_timed(conn, _handle_missing_features,
+                     FEATURES_TIMEOUT, NULL);
+}
+
+/* called when stream:stream tag received after SASL auth */
+static void _handle_open_sasl(xmpp_conn_t * const conn)
+{
+    xmpp_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
+
+    /* setup stream:features handlers */
+    handler_add(conn, _handle_features_sasl,
+               NULL, "stream:features", NULL, NULL);
+    handler_add_timed(conn, _handle_missing_features_sasl,
+                     FEATURES_TIMEOUT, NULL);
+}
+
+static int _handle_features_sasl(xmpp_conn_t * const conn,
+                                xmpp_stanza_t * const stanza,
+                                void * const userdata)
+{
+    xmpp_stanza_t *bind, *session, *iq, *res, *text;
+    char *resource;
+
+    /* remove missing features handler */
+    xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
+
+    /* we are expecting <bind/> and <session/> since this is a
+       XMPP style connection */
+
+    bind = xmpp_stanza_get_child_by_name(stanza, "bind");
+    if (bind && strcmp(xmpp_stanza_get_ns(bind), XMPP_NS_BIND) == 0) {
+       /* resource binding is required */
+       conn->bind_required = 1;
+    }
+
+    session = xmpp_stanza_get_child_by_name(stanza, "session");
+    if (session && strcmp(xmpp_stanza_get_ns(session), XMPP_NS_SESSION) == 0) {
+       /* session establishment required */
+       conn->session_required = 1;
+    }
+
+    /* 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,
+                         BIND_TIMEOUT, NULL);
+
+       /* send bind request */
+       iq = xmpp_stanza_new(conn->ctx);
+       if (!iq) {
+           disconnect_mem_error(conn);
+           return 0;
+       }
+
+       xmpp_stanza_set_name(iq, "iq");
+       xmpp_stanza_set_type(iq, "set");
+       xmpp_stanza_set_id(iq, "_xmpp_bind1");
+
+       bind = xmpp_stanza_copy(bind);
+       if (!bind) {
+           xmpp_stanza_release(iq);
+           disconnect_mem_error(conn);
+           return 0;
+       }
+
+       /* request a specific resource if we have one */
+        resource = xmpp_jid_resource(conn->ctx, conn->jid);
+       if ((resource != NULL) && (strlen(resource) == 0)) {
+           /* jabberd2 doesn't handle an empty resource */
+           xmpp_free(conn->ctx, resource);
+           resource = NULL;
+       }
+
+       /* if we have a resource to request, do it. otherwise the 
+          server will assign us one */
+       if (resource) {
+           res = xmpp_stanza_new(conn->ctx);
+           if (!res) {
+               xmpp_stanza_release(bind);
+               xmpp_stanza_release(iq);
+               disconnect_mem_error(conn);
+               return 0;
+           }
+           xmpp_stanza_set_name(res, "resource");
+           text = xmpp_stanza_new(conn->ctx);
+           if (!text) {
+               xmpp_stanza_release(res);
+               xmpp_stanza_release(bind);
+               xmpp_stanza_release(iq);
+               disconnect_mem_error(conn);
+               return 0;
+           }
+           xmpp_stanza_set_text(text, resource);
+           xmpp_stanza_add_child(res, text);
+            xmpp_stanza_release(text);
+           xmpp_stanza_add_child(bind, res);
+            xmpp_stanza_release(res);
+           xmpp_free(conn->ctx, resource);
+       }
+
+       xmpp_stanza_add_child(iq, bind);
+       xmpp_stanza_release(bind);
+
+       /* send bind request */
+       xmpp_send(conn, iq);
+       xmpp_stanza_release(iq);
+    } else {
+       /* can't bind, disconnect */
+       xmpp_error(conn->ctx, "xmpp", "Stream features does not allow "\
+                  "resource bind.");
+       xmpp_disconnect(conn);
+    }
+
+    return 0;
+}
+
+static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
+                                        void * const userdata)
+{
+    xmpp_error(conn->ctx, "xmpp", "Did not receive stream features "\
+              "after SASL authentication.");
+    xmpp_disconnect(conn);
+    return 0;
+}
+                                         
+static int _handle_bind(xmpp_conn_t * const conn,
+                       xmpp_stanza_t * const stanza,
+                       void * const userdata)
+{
+    char *type;
+    xmpp_stanza_t *iq, *session;
+
+    /* delete missing bind handler */
+    xmpp_timed_handler_delete(conn, _handle_missing_bind);
+
+    /* server has replied to bind request */
+    type = xmpp_stanza_get_type(stanza);
+    if (type && strcmp(type, "error") == 0) {
+       xmpp_error(conn->ctx, "xmpp", "Binding failed.");
+       xmpp_disconnect(conn);
+    } else if (type && strcmp(type, "result") == 0) {
+        xmpp_stanza_t *binding = xmpp_stanza_get_child_by_name(stanza, "bind");
+       xmpp_debug(conn->ctx, "xmpp", "Bind successful.");
+
+        if (binding) {
+            xmpp_stanza_t *jid_stanza = xmpp_stanza_get_child_by_name(binding,
+                                                                      "jid");
+            if (jid_stanza) {
+                conn->bound_jid = xmpp_stanza_get_text(jid_stanza);
+            }
+        }
+
+       /* establish a session if required */
+       if (conn->session_required) {
+           /* setup response handlers */
+           handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
+           handler_add_timed(conn, _handle_missing_session, 
+                             SESSION_TIMEOUT, NULL);
+
+           /* send session request */
+           iq = xmpp_stanza_new(conn->ctx);
+           if (!iq) {
+               disconnect_mem_error(conn);
+               return 0;
+           }
+
+           xmpp_stanza_set_name(iq, "iq");
+           xmpp_stanza_set_type(iq, "set");
+           xmpp_stanza_set_id(iq, "_xmpp_session1");
+
+           session = xmpp_stanza_new(conn->ctx);
+           if (!session) {
+               xmpp_stanza_release(iq);
+               disconnect_mem_error(conn);
+           }
+
+           xmpp_stanza_set_name(session, "session");
+           xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
+
+           xmpp_stanza_add_child(iq, session);
+           xmpp_stanza_release(session);
+
+           /* send session establishment request */
+           xmpp_send(conn, iq);
+           xmpp_stanza_release(iq);
+       } else {
+           conn->authenticated = 1;
+          
+           /* call connection handler */
+           conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, 
+                              conn->userdata);
+       }
+    } else {
+       xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
+       xmpp_disconnect(conn);
+    }
+
+    return 0;
+}
+
+static int _handle_missing_bind(xmpp_conn_t * const conn,
+                               void * const userdata)
+{
+    xmpp_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
+    xmpp_disconnect(conn);
+    return 0;
+}
+
+static int _handle_session(xmpp_conn_t * const conn,
+                          xmpp_stanza_t * const stanza,
+                          void * const userdata)
+{
+    char *type;
+
+    /* delete missing session handler */
+    xmpp_timed_handler_delete(conn, _handle_missing_session);
+
+    /* server has replied to the session request */
+    type = xmpp_stanza_get_type(stanza);
+    if (type && strcmp(type, "error") == 0) {
+       xmpp_error(conn->ctx, "xmpp", "Session establishment failed.");
+       xmpp_disconnect(conn);
+    } else if (type && strcmp(type, "result") == 0) {
+       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 {
+       xmpp_error(conn->ctx, "xmpp", "Server sent malformed session reply.");
+       xmpp_disconnect(conn);
+    }
+
+    return 0;
+}
+
+static int _handle_missing_session(xmpp_conn_t * const conn,
+                                  void * const userdata)
+{
+    xmpp_error(conn->ctx, "xmpp", "Server did not reply to session request.");
+    xmpp_disconnect(conn);
+    return 0;
+}
+
+static int _handle_legacy(xmpp_conn_t * const conn,
+                         xmpp_stanza_t * const stanza,
+                         void * const userdata)
+{
+    char *type, *name;
+
+    /* delete missing handler */
+    xmpp_timed_handler_delete(conn, _handle_missing_legacy);
+
+    /* server responded to legacy auth request */
+    type = xmpp_stanza_get_type(stanza);
+    name = xmpp_stanza_get_name(stanza);
+    if (!type || strcmp(name, "iq") != 0) {
+       xmpp_error(conn->ctx, "xmpp", "Server sent us an unexpected response "\
+                  "to legacy authentication request.");
+       xmpp_disconnect(conn);
+    } else if (strcmp(type, "error") == 0) {
+       /* legacy client auth failed, no more fallbacks */
+       xmpp_error(conn->ctx, "xmpp", "Legacy client authentication failed.");
+       xmpp_disconnect(conn);
+    } else if (strcmp(type, "result") == 0) {
+       /* auth succeeded */
+       xmpp_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
+
+       conn->authenticated = 1;
+       conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
+    } else {
+       xmpp_error(conn->ctx, "xmpp", "Server sent us a legacy authentication "\
+                  "response with a bad type.");
+       xmpp_disconnect(conn);
+    }
+
+    return 0;
+}
+
+static int _handle_missing_legacy(xmpp_conn_t * const conn,
+                                 void * const userdata)
+{
+    xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\
+              "authentication request.");
+    xmpp_disconnect(conn);
+    return 0;
+}
+
diff --git a/src/libstrophe/common.h b/src/libstrophe/common.h
new file mode 100644 (file)
index 0000000..9434e6f
--- /dev/null
@@ -0,0 +1,276 @@
+/* common.h
+** strophe XMPP client library -- internal common structures
+**
+** 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.
+*/
+
+/** @file
+ *  Internally used functions and structures.
+ */
+
+#ifndef __LIBSTROPHE_COMMON_H__
+#define __LIBSTROPHE_COMMON_H__
+
+#include <stdio.h>
+#include <stdarg.h>
+#ifndef _WIN32
+#include <stdint.h>
+#endif
+
+
+#include "strophe.h"
+#include "sock.h"
+#include "tls.h"
+#include "hash.h"
+#include "util.h"
+#include "parser.h"
+
+/** run-time context **/
+
+typedef enum {
+    XMPP_LOOP_NOTSTARTED,
+    XMPP_LOOP_RUNNING,
+    XMPP_LOOP_QUIT
+} xmpp_loop_status_t;
+
+typedef struct _xmpp_connlist_t {
+    xmpp_conn_t *conn;
+    struct _xmpp_connlist_t *next;
+} xmpp_connlist_t;
+
+struct _xmpp_ctx_t {
+    const xmpp_mem_t *mem;
+    const xmpp_log_t *log;
+
+    xmpp_loop_status_t loop_status;
+    xmpp_connlist_t *connlist;
+};
+
+
+/* convenience functions for accessing the context */
+void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size);
+void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p, 
+                  const size_t size);
+char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s);
+
+void xmpp_log(const xmpp_ctx_t * const ctx, 
+             const xmpp_log_level_t level,
+             const char * const area,
+             const char * const fmt, 
+             va_list ap);
+
+/* wrappers for xmpp_log at specific levels */
+void xmpp_error(const xmpp_ctx_t * const ctx,
+               const char * const area,
+               const char * const fmt,
+               ...);
+void xmpp_warn(const xmpp_ctx_t * const ctx,
+               const char * const area,
+               const char * const fmt,
+               ...);
+void xmpp_info(const xmpp_ctx_t * const ctx,
+               const char * const area,
+               const char * const fmt,
+               ...);
+void xmpp_debug(const xmpp_ctx_t * const ctx,
+               const char * const area,
+               const char * const fmt,
+               ...);
+
+/** jid */
+/* these return new strings that must be xmpp_free()'d */
+char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
+                                    const char *domain,
+                                    const char *resource);
+char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
+char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
+char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
+char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
+
+
+/** connection **/
+
+/* opaque connection object */
+typedef enum {
+    XMPP_STATE_DISCONNECTED,
+    XMPP_STATE_CONNECTING,
+    XMPP_STATE_CONNECTED
+} xmpp_conn_state_t;
+
+typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
+struct _xmpp_send_queue_t {
+    char *data;
+    size_t len;
+    size_t written;
+
+    xmpp_send_queue_t *next;
+};
+
+typedef struct _xmpp_handlist_t xmpp_handlist_t;
+struct _xmpp_handlist_t {
+    /* common members */
+    int user_handler;
+    void *handler;
+    void *userdata;
+    int enabled; /* handlers are added disabled and enabled after the
+                 * handler chain is processed to prevent stanzas from
+                 * getting processed by newly added handlers */
+    xmpp_handlist_t *next;
+
+    union {
+       /* timed handlers */
+       struct {
+           unsigned long period;
+           uint64_t last_stamp;
+       };
+       /* id handlers */
+       struct {
+           char *id;
+       };
+       /* normal handlers */
+       struct {
+           char *ns;
+           char *name;
+           char *type;
+       };
+    };
+};
+
+#define SASL_MASK_PLAIN 0x01
+#define SASL_MASK_DIGESTMD5 0x02
+#define SASL_MASK_ANONYMOUS 0x04
+
+typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn);
+
+struct _xmpp_conn_t {
+    unsigned int ref;
+    xmpp_ctx_t *ctx;
+    xmpp_conn_type_t type;
+
+    xmpp_conn_state_t state;
+    uint64_t timeout_stamp;
+    int error;
+    xmpp_stream_error_t *stream_error;
+    sock_t sock;
+    tls_t *tls;
+
+    int tls_support;
+    int tls_disabled;
+    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 */ 
+    int secured; /* set when stream is secured with TLS */
+
+    /* if server returns <bind/> or <session/> we must do them */
+    int bind_required;
+    int session_required;
+
+    char *lang;
+    char *domain;
+    char *connectdomain;
+    char *connectport;
+    char *jid;
+    char *pass;
+    char *bound_jid;
+    char *stream_id;
+
+    /* send queue and parameters */
+    int blocking_send;
+    int send_queue_max;
+    int send_queue_len;
+    xmpp_send_queue_t *send_queue_head;
+    xmpp_send_queue_t *send_queue_tail;
+
+    /* xml parser */
+    int reset_parser;
+    parser_t *parser;
+
+    /* timeouts */
+    unsigned int connect_timeout;
+
+    /* event handlers */    
+
+    /* stream open handler */
+    xmpp_open_handler open_handler;
+
+    /* user handlers only get called after authentication */
+    int authenticated;
+    
+    /* connection events handler */
+    xmpp_conn_handler conn_handler;
+    void *userdata;
+
+    /* other handlers */
+    xmpp_handlist_t *timed_handlers;
+    hash_t *id_handlers;
+    xmpp_handlist_t *handlers;
+};
+
+void conn_disconnect(xmpp_conn_t * const conn);
+void conn_disconnect_clean(xmpp_conn_t * const conn);
+void conn_open_stream(xmpp_conn_t * const conn);
+void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler);
+void conn_parser_reset(xmpp_conn_t * const conn);
+
+
+typedef enum {
+    XMPP_STANZA_UNKNOWN,
+    XMPP_STANZA_TEXT,
+    XMPP_STANZA_TAG
+} xmpp_stanza_type_t;
+
+struct _xmpp_stanza_t {
+    int ref;
+    xmpp_ctx_t *ctx;
+
+    xmpp_stanza_type_t type;
+    
+    xmpp_stanza_t *prev;
+    xmpp_stanza_t *next;
+    xmpp_stanza_t *children;
+    xmpp_stanza_t *parent;
+
+    char *data;
+
+    hash_t *attributes;
+};
+
+/* handler management */
+void handler_fire_stanza(xmpp_conn_t * const conn,
+                        xmpp_stanza_t * const stanza);
+uint64_t handler_fire_timed(xmpp_ctx_t * const ctx);
+void handler_reset_timed(xmpp_conn_t *conn, int user_only);
+void handler_add_timed(xmpp_conn_t * const conn,
+                      xmpp_timed_handler handler,
+                      const unsigned long period,
+                      void * const userdata);
+void handler_add_id(xmpp_conn_t * const conn,
+                   xmpp_handler handler,
+                   const char * const id,
+                   void * const userdata);
+void handler_add(xmpp_conn_t * const conn,
+                xmpp_handler handler,
+                const char * const ns,
+                const char * const name,
+                const char * const type,
+                void * const userdata);
+
+/* utility functions */
+void disconnect_mem_error(xmpp_conn_t * const conn);
+
+/* auth functions */
+void auth_handle_open(xmpp_conn_t * const conn);
+
+/* 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);
+
+#endif /* __LIBSTROPHE_COMMON_H__ */
diff --git a/src/libstrophe/conn.c b/src/libstrophe/conn.c
new file mode 100644 (file)
index 0000000..44644e8
--- /dev/null
@@ -0,0 +1,770 @@
+/* conn.c
+** strophe XMPP client library -- connection object functions
+**
+** 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.
+*/
+
+/** @file 
+ *  Connection management.
+ */
+
+/** @defgroup Connections Connection management
+ */
+
+#include <nms_common.h>
+#include <strophe.h>
+#include "common.h"
+#include "util.h"
+#include "parser.h"
+
+#ifndef DEFAULT_SEND_QUEUE_MAX
+/** @def DEFAULT_SEND_QUEUE_MAX
+ *  The default maximum send queue size.  This is currently unused.
+ */
+#define DEFAULT_SEND_QUEUE_MAX 64
+#endif
+#ifndef DISCONNECT_TIMEOUT
+/** @def DISCONNECT_TIMEOUT 
+ *  The time to wait (in milliseconds) for graceful disconnection to
+ *  complete before the connection is reset.  The default is 2 seconds.
+ */
+#define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
+#endif
+#ifndef CONNECT_TIMEOUT
+/** @def CONNECT_TIMEOUT
+ *  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, 
+                                 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);
+
+/** Create a new Strophe connection object.
+ *
+ *  @param ctx a Strophe context object
+ *
+ *  @return a Strophe connection object or NULL on an error
+ *
+ *  @ingroup Connections
+ */
+xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx)
+{
+    xmpp_conn_t *conn = NULL;
+    xmpp_connlist_t *tail, *item;
+
+    if (ctx == NULL) return NULL;
+       conn = xmpp_alloc(ctx, sizeof(xmpp_conn_t));
+    
+    if (conn != NULL) {
+       conn->ctx = ctx;
+
+       conn->type = XMPP_UNKNOWN;
+        conn->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->bound_jid = NULL;
+
+       conn->tls_support = 0;
+       conn->tls_disabled = 0;
+       conn->tls_failed = 0;
+       conn->sasl_support = 0;
+        conn->secured = 0;
+
+       conn->bind_required = 0;
+       conn->session_required = 0;
+
+       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);
+            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;
+       }
+    }
+    
+    return conn;
+}
+
+/** Clone a Strophe connection object.
+ *  
+ *  @param conn a Strophe connection object
+ *
+ *  @return the same conn object passed in with its reference count
+ *      incremented by 1
+ *
+ *  @ingroup Connections
+ */
+xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t * const conn)
+{
+    conn->ref++;
+    return conn;
+}
+
+/** Release a Strophe connection object.
+ *  Decrement the reference count by one for a connection, freeing the 
+ *  connection object if the count reaches 0.
+ *
+ *  @param conn a Strophe connection object
+ *
+ *  @return TRUE if the connection object was freed and FALSE otherwise
+ *
+ *  @ingroup Connections
+ */
+int xmpp_conn_release(xmpp_conn_t * const conn)
+{
+    xmpp_ctx_t *ctx;
+    xmpp_connlist_t *item, *prev;
+    xmpp_handlist_t *hlitem, *thli;
+    hash_iterator_t *iter;
+    const char *key;
+    int released = 0;
+
+    if (conn->ref > 1) 
+       conn->ref--;
+    else {
+       ctx = conn->ctx;
+
+       /* remove connection from context's connlist */
+       if (ctx->connlist->conn == conn) {
+           item = ctx->connlist;
+           ctx->connlist = item->next;
+           xmpp_free(ctx, item);
+       } else {
+           prev = NULL;
+           item = ctx->connlist;
+           while (item && item->conn != conn) {
+               prev = item;
+               item = item->next;
+           }
+
+           if (!item) {
+               xmpp_error(ctx, "xmpp", "Connection not in context's list\n");
+           } else {
+               prev->next = item->next;
+               xmpp_free(ctx, item);
+           }
+       }
+
+       /* free handler stuff
+        * note that userdata is the responsibility of the client
+        * and the handler pointers don't need to be freed since they
+        * are pointers to functions */
+
+       hlitem = conn->timed_handlers;
+       while (hlitem) {
+           thli = hlitem;
+           hlitem = hlitem->next;
+
+           xmpp_free(ctx, thli);
+       }
+
+       /* id handlers
+        * we have to traverse the hash table freeing list elements 
+        * then release the hash table */
+       iter = hash_iter_new(conn->id_handlers);
+       while ((key = hash_iter_next(iter))) {
+           hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
+           while (hlitem) {
+               thli = hlitem;
+               hlitem = hlitem->next;
+               xmpp_free(conn->ctx, thli->id);
+               xmpp_free(conn->ctx, thli);
+           }
+       }
+       hash_iter_release(iter);
+       hash_release(conn->id_handlers);
+
+       hlitem = conn->handlers;
+       while (hlitem) {
+           thli = hlitem;
+           hlitem = hlitem->next;
+
+           if (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);
+       }
+
+       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);
+       }
+
+        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;
+    }
+
+    return released;
+}
+
+/** Get the JID which is or will be bound to the connection.
+ *  
+ *  @param conn a Strophe connection object
+ *
+ *  @return a string containing the full JID or NULL if it has not been set
+ *
+ *  @ingroup Connections
+ */
+const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn)
+{
+    return conn->jid;
+}
+
+/**
+ * Get the JID discovered during binding time.
+ *
+ * This JID will contain the resource used by the current connection.
+ * This is useful in the case where a resource was not specified for
+ * binding.
+ *
+ * @param conn a Strophe connection object.
+ *
+ * @return a string containing the full JID or NULL if it's not been discovered
+ *
+ * @ingroup Connections
+ */
+const char *xmpp_conn_get_bound_jid(const xmpp_conn_t * const conn)
+{
+    return conn->bound_jid;
+}
+
+/** 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 
+ *  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
+ *  ANONYMOUS authentication will be used.
+ *
+ *  @param conn a Strophe connection object
+ *  @param jid a full or bare JID
+ *
+ *  @ingroup Connections
+ */
+void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid)
+{
+    if (conn->jid) xmpp_free(conn->ctx, conn->jid);
+    conn->jid = xmpp_strdup(conn->ctx, jid);
+}
+
+/** Get the password used for authentication of a connection.
+ *
+ *  @param conn a Strophe connection object
+ *
+ *  @return a string containing the password or NULL if it has not been set
+ *
+ *  @ingroup Connections
+ */
+const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn)
+{
+    return conn->pass;
+}
+
+/** Set the password used to authenticate the connection.
+ *  If any password was previously set, it will be discarded.  The function
+ *  will make a copy of the password string.
+ * 
+ *  @param conn a Strophe connection object
+ *  @param pass the password
+ *
+ *  @ingroup Connections
+ */
+void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass)
+{
+    if (conn->pass) xmpp_free(conn->ctx, conn->pass);
+    conn->pass = xmpp_strdup(conn->ctx, pass);
+}
+
+/** Get the strophe context that the connection is associated with.
+*  @param conn a Strophe connection object
+* 
+*  @return a Strophe context
+* 
+*  @ingroup Connections
+*/
+xmpp_ctx_t* xmpp_conn_get_context(xmpp_conn_t * const conn)
+{
+       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
+ *  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 
+ *  altport will be used instead if specified.
+ *
+ *  @param conn a Strophe connection object
+ *  @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.
+ *  @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
+ *
+ *  @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)
+{
+    char connectdomain[2048];
+    int connectport;
+    const char * domain;
+
+    conn->type = XMPP_CLIENT;
+
+    conn->domain = xmpp_jid_domain(conn->ctx, conn->jid);
+    if (!conn->domain) return -1;
+
+    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;
+    }
+    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;
+
+    /* 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", connectdomain);
+
+    return 0;
+}
+
+/** Cleanly disconnect the connection.
+ *  This function is only called by the stream parser when </stream:stream>
+ *  is received, and it not intended to be called by code outside of Strophe.
+ *
+ *  @param conn a Strophe connection object
+ */
+void conn_disconnect_clean(xmpp_conn_t * const conn)
+{
+    /* remove the timed handler */
+    xmpp_timed_handler_delete(conn, _disconnect_cleanup);
+
+    conn_disconnect(conn);
+}
+
+/** Disconnect from the XMPP server.
+ *  This function immediately disconnects from the XMPP server, and should
+ *  not be used outside of the Strophe library.
+ *
+ *  @param conn a Strophe connection object
+ */
+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);
+
+    /* 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
+ * reset the parser immediately as it is not re-entrant. */
+void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler)
+{
+    conn->reset_parser = 1;
+    conn->open_handler = handler;
+}
+
+/* reset the parser */
+void conn_parser_reset(xmpp_conn_t * const conn)
+{
+    conn->reset_parser = 0;
+    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
+ *  if the connection state is CONNECTING or CONNECTED.
+ *
+ *  @param conn a Strophe connection object
+ *
+ *  @ingroup Connections
+ */
+void xmpp_disconnect(xmpp_conn_t * const conn)
+{
+    if (conn->state != XMPP_STATE_CONNECTING &&
+       conn->state != XMPP_STATE_CONNECTED)
+       return;
+
+    /* close the stream */
+    xmpp_send_raw_string(conn, "</stream:stream>");
+
+    /* setup timed handler in case disconnect takes too long */
+    handler_add_timed(conn, _disconnect_cleanup,
+                     DISCONNECT_TIMEOUT, NULL);
+}
+
+/** Send a raw string to the XMPP server.
+ *  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
+ *  stream termination by the XMPP server.
+ *
+ *  @param conn a Strophe connection object
+ *  @param fmt a printf-style format string followed by a variable list of
+ *      arguments to format
+ */
+void xmpp_send_raw_string(xmpp_conn_t * const conn, 
+                         const char * const fmt, ...)
+{
+    va_list ap;
+    size_t len;
+    char buf[1024]; /* small buffer for common case */
+    char *bigbuf;
+
+    va_start(ap, fmt);
+    len = xmpp_vsnprintf(buf, 1024, fmt, ap);
+    va_end(ap);
+
+    if (len >= 1024) {
+       /* we need more space for this data, so we allocate a big 
+        * enough buffer and print to that */
+       len++; /* account for trailing \0 */
+       bigbuf = xmpp_alloc(conn->ctx, len);
+       if (!bigbuf) {
+           xmpp_debug(conn->ctx, "xmpp", "Could not allocate memory for send_raw_string");
+           return;
+       }
+       va_start(ap, fmt);
+       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_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 
+ *  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
+ */
+void xmpp_send_raw(xmpp_conn_t * const conn,
+                  const char * const data, const size_t len)
+{
+    xmpp_send_queue_t *item;
+
+    if (conn->state != XMPP_STATE_CONNECTED) return;
+
+    /* create send queue item for queue */
+    item = xmpp_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
+    if (!item) return;
+
+    item->data = xmpp_alloc(conn->ctx, len);
+    if (!item->data) {
+       xmpp_free(conn->ctx, item);
+       return;
+    }
+    memcpy(item->data, data, len);
+    item->len = len;
+    item->next = NULL;
+    item->written = 0;
+
+    /* add item to the send queue */
+    if (!conn->send_queue_tail) {
+       /* first item, set head and tail */
+       conn->send_queue_head = item;
+       conn->send_queue_tail = item;
+    } else {
+       /* add to the tail */
+       conn->send_queue_tail->next = item;
+       conn->send_queue_tail = item;
+    }
+    conn->send_queue_len++;
+}
+
+/** Send an XML stanza to the XMPP server.
+ *  This is the main way to send data to the XMPP server.  The function will
+ *  terminate without action if the connection state is not CONNECTED.
+ *
+ *  @param conn a Strophe connection object
+ *  @param stanza a Strophe stanza object
+ *
+ *  @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) {
+       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.
+ *  This function is used by Strophe to begin an XMPP stream.  It should
+ *  not be used outside of the library.
+ *
+ *  @param conn a Strophe connection object
+ */
+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);
+}
+
+/** 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.
+ *
+ *  @param conn a Strophe connection object
+ */
+void xmpp_conn_disable_tls(xmpp_conn_t * const conn)
+{
+    conn->tls_disabled = 1;
+}
+
+static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
+{
+    char buf[4096];
+    size_t pos;
+    int len;
+    int i;
+    
+    if (!attrs) return;
+
+    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;
+    }
+
+    len = xmpp_snprintf(&buf[pos], 4096 - pos, ">");
+    if (len < 0) return;
+
+    xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
+}
+
+static char *_get_stream_attribute(char **attrs, char *name)
+{
+    int i;
+
+    if (!attrs) return NULL;
+
+    for (i = 0; attrs[i]; i += 2)
+        if (strcmp(name, attrs[i]) == 0)
+            return attrs[i+1];
+
+    return NULL;
+}
+
+static void _handle_stream_start(char *name, char **attrs, 
+                                 void * const userdata)
+{
+    xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
+    char *id;
+
+    if (strcmp(name, "stream:stream") != 0) {
+        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);
+
+        id = _get_stream_attribute(attrs, "id");
+        if (id)
+            conn->stream_id = xmpp_strdup(conn->ctx, id);
+
+        if (!conn->stream_id) {
+            xmpp_error(conn->ctx, "conn", "Memory allocation failed.");
+            conn_disconnect(conn);
+        }
+    }
+    
+    /* call stream open handler */
+    conn->open_handler(conn);
+}
+
+static void _handle_stream_end(char *name,
+                               void * const userdata)
+{
+    xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
+
+    /* stream is over */
+    xmpp_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
+    conn_disconnect_clean(conn);
+}
+
+static void _handle_stream_stanza(xmpp_stanza_t *stanza,
+                                  void * const userdata)
+{
+    xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
+    char *buf;
+    size_t len;
+
+    if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
+        xmpp_debug(conn->ctx, "xmpp", "RECV: %s", buf);
+        xmpp_free(conn->ctx, buf);
+    }
+
+    handler_fire_stanza(conn, stanza);
+}
diff --git a/src/libstrophe/ctx.c b/src/libstrophe/ctx.c
new file mode 100644 (file)
index 0000000..7dc0668
--- /dev/null
@@ -0,0 +1,417 @@
+/* ctx.c
+** strophe XMPP client library -- run-time context implementation
+**
+** 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.
+*/
+
+/** @file
+ *  Runtime contexts, library initialization and shutdown, and versioning.
+ */
+
+/** @defgroup Context Context objects
+ *  These functions create and manipulate Strophe context objects.
+ *
+ *  In order to support usage in a variety of environments, the
+ *  Strophe library uses a runtime context object.  This object
+ *  contains the information on how to do memory allocation and
+ *  logging.  This allows the user to control how memory is allocated
+ *  and what do to with log messages.
+ *
+ *  These issues do not affect programs in the common case, but many
+ *  environments require special treatment.  Abstracting these into a runtime
+ *  context object makes it easy to use Strophe on embedded platforms.
+ *
+ *  Objects in Strophe are reference counted to ease memory management issues,
+ *  but the context objects are not.
+ */
+
+/** @defgroup Init Initialization, shutdown, and versioning
+ *  These functions initialize and shutdown the library, and also allow
+ *  for API version checking.  Failure to properly call these functions may
+ *  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 
+ *  subsystem must also seed the random number generator.
+ */
+
+#include <nms_common.h>
+#include <strophe.h>
+#include "common.h"
+#include "util.h"
+
+/** Initialize the Strophe library.
+ *  This function initializes subcomponents of the Strophe library and must
+ *  be called for Strophe to operate correctly.
+ *
+ *  @ingroup Init
+ */
+ void xmpp_initialize(void)
+{
+    sock_initialize();
+    tls_initialize();
+}
+
+/** Shutdown the Strophe library.
+ *
+ *  @ingroup Init
+ */
+void xmpp_shutdown(void)
+{
+    tls_shutdown();
+    sock_shutdown();
+}
+
+/* version information */
+
+#ifndef LIBXMPP_VERSION_MAJOR
+/** @def LIBXMPP_VERSION_MAJOR
+ *  The major version number of Strophe.
+ */
+#define LIBXMPP_VERSION_MAJOR (0)
+#endif
+#ifndef LIBXMPP_VERSION_MINOR
+/** @def LIBXMPP_VERSION_MINOR
+ *  The minor version number of Strophe.
+ */
+#define LIBXMPP_VERSION_MINOR (0)
+#endif
+
+/** Check that Strophe supports a specific API version.
+ *
+ *  @param major the major version number
+ *  @param minor the minor version number
+ *
+ *  @return TRUE if the version is supported and FALSE if unsupported
+ *
+ *  @ingroup Init
+ */
+int xmpp_version_check(int major, int minor)
+{
+    return (major == LIBXMPP_VERSION_MAJOR) &&
+          (minor >= LIBXMPP_VERSION_MINOR);
+}
+
+/* We define the global default allocator, logger, and context here. */
+
+/* Wrap stdlib routines malloc, free, and realloc for default memory 
+ * management. 
+ */
+static void *_malloc(const size_t size, void * const userdata)
+{
+    return malloc(size);
+}
+
+static void _free(void *p, void * const userdata)
+{
+    free(p);
+}
+
+static void *_realloc(void *p, const size_t size, void * const userdata)
+{
+    return realloc(p, size);
+}
+
+/* default memory function map */
+static xmpp_mem_t xmpp_default_mem = {
+    _malloc, /* use the thinly wrapped stdlib routines by default */
+    _free,
+    _realloc,
+    NULL
+};
+
+/* 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};
+
+/** Log a message.
+ *  The default logger writes to stderr.
+ *
+ *  @param userdata the opaque data used by the default logger.  This contains
+ *      the filter level in the default logger.
+ *  @param level the level to log at
+ *  @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)
+{
+    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);
+}
+
+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]}
+};
+
+/** Get a default logger with filtering.
+ *  The default logger provides a basic logging setup which writes log
+ *  messages to stderr.  Only messages where level is greater than or
+ *  equal to the filter level will be logged.
+ *
+ *  @param level the highest level the logger will log at
+ *
+ *  @return the log structure for the given level
+ *
+ *  @ingroup Context
+ */
+xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
+{
+    /* clamp to the known range */
+    if (level > XMPP_LEVEL_ERROR) level = XMPP_LEVEL_ERROR;
+    if (level < XMPP_LEVEL_DEBUG) level = XMPP_LEVEL_DEBUG;
+
+    return (xmpp_log_t*)&_xmpp_default_loggers[level];
+}
+
+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. 
+ *
+ *  @param ctx a Strophe context object
+ *  @param size the number of bytes to allocate
+ *
+ *  @return a pointer to the allocated memory or NULL on an error
+ */
+void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size)
+{
+    return ctx->mem->alloc(size, ctx->mem->userdata);
+}
+
+/** Free memory in a Strophe context.
+ *  All Strophe functions will use this to free allocated memory.
+ *
+ *  @param ctx a Strophe context object
+ *  @param p a pointer referencing memory to be freed
+ */
+void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
+{
+    ctx->mem->free(p, ctx->mem->userdata);
+}
+
+/** Reallocate memory in a Strophe context.
+ *  All Strophe functions will use this to reallocate memory.
+ *
+ *  @param ctx a Strophe context object
+ *  @param p a pointer to previously allocated memory
+ *  @param size the new size in bytes to allocate
+ *
+ *  @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)
+{
+    return ctx->mem->realloc(p, size, ctx->mem->userdata);
+}
+
+/** Write a log message to the logger.
+ *  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, 
+ *  and xmpp_debug.
+ *
+ *  @param ctx a Strophe context object
+ *  @param level the level at which to log
+ *  @param area the area to log for
+ *  @param fmt a printf-style format string for the message
+ *  @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)
+{
+    int oldret, ret;
+    char smbuf[1024];
+    char *buf;
+    va_list copy;
+
+    buf = smbuf;
+    va_copy(copy, ap);
+    ret = xmpp_vsnprintf(buf, 1023, fmt, ap);
+    if (ret > 1023) {
+       buf = (char *)xmpp_alloc(ctx, ret + 1);
+       if (!buf) {
+           buf = NULL;
+           xmpp_error(ctx, "log", "Failed allocating memory for log message.");
+            va_end(copy);
+           return;
+       }
+       oldret = ret;
+       ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy);
+       if (ret > oldret) {
+           xmpp_error(ctx, "log", "Unexpected error");
+           return;
+       }
+    } else {
+        va_end(copy);
+    }
+
+    if (ctx->log->handler)
+        ctx->log->handler(ctx->log->userdata, level, area, 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 
+ *  variable list of arguments for formatting.
+ *
+ *  @param ctx a Strophe context object
+ *  @param area the area to log for
+ *  @param fmt a printf-style format string followed by a variable list of
+ *      arguments to format
+ */
+void xmpp_error(const xmpp_ctx_t * const ctx,
+                const char * const area,
+                const char * const fmt,
+                ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
+    va_end(ap);
+}
+
+/** Write to the log at the WARN level.
+ *  This is a convenience function for writing to the log at the WARN level.
+ *  It takes a printf-style format string followed by a variable list of
+ *  arguments for formatting.
+ *
+ *  @param ctx a Strophe context object
+ *  @param area the area to log for
+ *  @param fmt a printf-style format string followed by a variable list of
+ *      arguments to format
+ */
+void xmpp_warn(const xmpp_ctx_t * const ctx,
+                const char * const area,
+                const char * const fmt,
+                ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
+    va_end(ap);
+}
+
+/** Write to the log at the INFO level.
+ *  This is a convenience function for writing to the log at the INFO level.
+ *  It takes a printf-style format string followed by a variable list of
+ *  arguments for formatting.
+ *
+ *  @param ctx a Strophe context object
+ *  @param area the area to log for
+ *  @param fmt a printf-style format string followed by a variable list of
+ *      arguments to format
+ */
+void xmpp_info(const xmpp_ctx_t * const ctx,
+                const char * const area,
+                const char * const fmt,
+                ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
+    va_end(ap);
+}
+
+/** Write to the log at the DEBUG level.
+ *  This is a convenience function for writing to the log at the DEBUG level.
+ *  It takes a printf-style format string followed by a variable list of
+ *  arguments for formatting.
+ *
+ *  @param ctx a Strophe context object
+ *  @param area the area to log for
+ *  @param fmt a printf-style format string followed by a variable list of
+ *      arguments to format
+ */
+void xmpp_debug(const xmpp_ctx_t * const ctx,
+                const char * const area,
+                const char * const fmt,
+                ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
+    va_end(ap);
+}
+
+/** Create and initialize a Strophe context object.
+ *  If mem is NULL, a default allocation setup will be used which
+ *  wraps malloc(), free(), and realloc() from the standard library.
+ *  If log is NULL, a default logger will be used which does no
+ *  logging.  Basic filtered logging to stderr can be done with the
+ *  xmpp_get_default_logger() convenience function.
+ *
+ *  @param mem a pointer to an xmpp_mem_t structure or NULL
+ *  @param log a pointer to an xmpp_log_t structure or NULL
+ *
+ *  @return the allocated Strophe context object or NULL on an error
+ *
+ *  @ingroup Context
+ */
+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);
+    else
+       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;
+    }
+
+    return ctx;
+}
+
+/** Free a Strophe context object that is no longer in use.
+ *
+ *  @param ctx a Strophe context object
+ *
+ *  @ingroup Context
+ */
+void xmpp_ctx_free(xmpp_ctx_t * const ctx)
+{
+    /* mem and log are owned by their suppliers */
+    xmpp_free(ctx, ctx); /* pull the hole in after us */
+}
+
diff --git a/src/libstrophe/event.c b/src/libstrophe/event.c
new file mode 100644 (file)
index 0000000..1524f03
--- /dev/null
@@ -0,0 +1,352 @@
+/* event.c
+** strophe XMPP client library -- event loop and management
+**
+** 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.
+*/
+
+/** @file
+ *  Event loop and management.
+ */
+
+/** @defgroup EventLoop 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
+ *  process UI events from users, and xmpp_run_once() would be called
+ *  from an idle function.
+ */
+
+#include <nms_common.h>
+
+#ifdef _WIN32
+#define ETIMEDOUT WSAETIMEDOUT
+#define ECONNRESET WSAECONNRESET
+#define ECONNABORTED WSAECONNABORTED
+#endif
+
+#include <strophe.h>
+#include "common.h"
+#include "parser.h"
+
+#ifndef DEFAULT_TIMEOUT
+/** @def DEFAULT_TIMEOUT
+ *  The default timeout in milliseconds for the event loop.
+ *  This is set to 1 millisecond.
+ */
+#define DEFAULT_TIMEOUT 1
+#endif
+
+/** Run the event loop once.
+ *  This function will run send any data that has been queued by
+ *  xmpp_send and related functions and run through the Strophe even
+ *  loop a single time, and will not wait more than timeout
+ *  milliseconds for events.  This is provided to support integration
+ *  with event loops outside the library, and if used, should be
+ *  called regularly to achieve low latency event handling.
+ *
+ *  @param ctx a Strophe context object
+ *  @param timeout time to wait for events in milliseconds
+ *
+ *  @ingroup EventLoop
+ */
+void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
+{
+    xmpp_connlist_t *connitem;
+    xmpp_conn_t *conn;
+    fd_set rfds, wfds;
+    sock_t max = 0;
+    int ret;
+    struct timeval tv;
+    xmpp_send_queue_t *sq, *tsq;
+    int towrite;
+    char buf[4096];
+    uint64_t next;
+    uint64_t usec;
+    int tls_read_bytes = 0;
+
+    if (ctx->loop_status == XMPP_LOOP_QUIT) return;
+    ctx->loop_status = XMPP_LOOP_RUNNING;
+
+    /* send queued data */
+    connitem = ctx->connlist;
+    while (connitem) {
+       conn = connitem->conn;
+       if (conn->state != XMPP_STATE_CONNECTED) {
+           connitem = connitem->next;
+           continue;
+       }
+
+       /* if we're running tls, there may be some remaining data waiting to
+        * be sent, so push that out */
+       if (conn->tls) {
+           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.");
+               conn->error = ECONNABORTED;
+               conn_disconnect(conn);
+           }
+       }
+
+       /* write all data from the send queue to the socket */
+       sq = conn->send_queue_head;
+       while (sq) {
+           towrite = (int)(sq->len - sq->written);
+
+           if (conn->tls) {
+               ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
+
+               if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
+                   /* an error occured */
+                   conn->error = tls_error(conn->tls);
+                   break;
+               } else if (ret < towrite) {
+                   /* not all data could be sent now */
+                   if (ret >= 0) sq->written += ret;
+                   break;
+               }
+
+           } else {
+               ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
+
+               if (ret < 0 && !sock_is_recoverable(sock_error())) {
+                   /* an error occured */
+                   conn->error = sock_error();
+                   break;
+               } else if (ret < towrite) {
+                   /* not all data could be sent now */
+                   if (ret >= 0) sq->written += ret;
+                   break;
+               }
+           }
+
+           /* all data for this queue item written, delete and move on */
+           xmpp_free(ctx, sq->data);
+           tsq = sq;
+           sq = sq->next;
+           xmpp_free(ctx, tsq);
+
+           /* pop the top item */
+           conn->send_queue_head = sq;
+           /* if we've sent everything update the tail */
+           if (!sq) conn->send_queue_tail = NULL;
+       }
+
+       /* tear down connection on error */
+       if (conn->error) {
+           /* FIXME: need to tear down send queues and random other things
+            * maybe this should be abstracted */
+           xmpp_debug(ctx, "xmpp", "Send error occured, 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);
+    }
+
+
+    /* fire any ready timed handlers, then
+       make sure we don't wait past the time when timed handlers need 
+       to be called */
+    next = handler_fire_timed(ctx);
+
+    usec = ((next < timeout) ? next : timeout) * 1000;
+    tv.tv_sec = (long)(usec / 1000000);
+    tv.tv_usec = (long)(usec % 1000000);
+
+    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->sock > max) max = conn->sock;
+
+       connitem = connitem->next;
+    }
+
+    /* check for events */
+    ret = select(SELECT_NFDS(max + 1), &rfds,  &wfds, NULL, &tv);
+
+    /* select errored */
+    if (ret < 0) 
+    {
+          if (!sock_is_recoverable(sock_error()))
+      {
+//            xmpp_error(ctx, "xmpp", "unrecoverable socket error %d", sock_error());
+#ifdef _WIN32
+          Sleep(1000);
+#else
+          sleep(1);
+#endif
+      }
+          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;
+    }
+
+    /* fire any ready handlers */
+    handler_fire_timed(ctx);
+}
+
+/** Start the event loop.
+ *  This function continuously calls xmpp_run_once and does not return
+ *  until xmpp_stop has been called.
+ *
+ *  @param ctx a Strophe context object
+ *
+ *  @ingroup EventLoop
+ */
+void xmpp_run(xmpp_ctx_t *ctx)
+{
+    if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
+
+    ctx->loop_status = XMPP_LOOP_RUNNING;
+    while (ctx->loop_status == XMPP_LOOP_RUNNING) {
+       xmpp_run_once(ctx, DEFAULT_TIMEOUT);
+    }
+
+    xmpp_debug(ctx, "event", "Event loop completed.");
+}
+
+/** Stop the event loop.
+ *  This will stop the event loop after the current iteration and cause
+ *  xmpp_run to exit.
+ *
+ *  @param ctx a Strophe context object
+ *
+ *  @ingroup EventLoop
+ */
+void xmpp_stop(xmpp_ctx_t *ctx)
+{
+    xmpp_debug(ctx, "event", "Stopping event loop.");
+
+    if (ctx->loop_status == XMPP_LOOP_RUNNING)
+       ctx->loop_status = XMPP_LOOP_QUIT;
+}
diff --git a/src/libstrophe/handler.c b/src/libstrophe/handler.c
new file mode 100644 (file)
index 0000000..709be84
--- /dev/null
@@ -0,0 +1,603 @@
+/* handler.c
+** strophe XMPP client library -- event handler management
+**
+** 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.
+*/
+
+/** @file
+ *  Event handler management.
+ */
+
+/** @defgroup Handlers Stanza and timed event handlers
+ */
+
+#include <nms_common.h>
+
+#ifndef _WIN32
+#include <stdint.h>
+#else
+#include "ostypes.h"
+#endif
+
+#include <strophe.h>
+#include "common.h"
+
+/** Fire off all stanza handlers that match.
+ *  This function is called internally by the event loop whenever stanzas
+ *  are received from the XMPP server.
+ *
+ *  @param conn a Strophe connection object
+ *  @param stanza a Strophe stanza object
+ */
+void handler_fire_stanza(xmpp_conn_t * const conn,
+                        xmpp_stanza_t * const stanza)
+{
+    xmpp_handlist_t *item, *prev;
+    char *id, *ns, *name, *type;
+    
+    /* call id handlers */
+    id = xmpp_stanza_get_id(stanza);
+    if (id) {
+       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->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;
+
+    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->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 if (prev)
+           item = prev->next;
+       else
+           item = conn->handlers;
+    }
+}
+
+/** Fire off all timed handlers that are ready.
+ *  This function is called internally by the event loop.
+ *
+ *  @param ctx a Strophe context object
+ *
+ *  @return the time in milliseconds until the next handler will be ready
+ */
+uint64_t handler_fire_timed(xmpp_ctx_t * const ctx)
+{
+    xmpp_connlist_t *connitem;
+    xmpp_handlist_t *handitem, *temp;
+    int ret, fired;
+    uint64_t elapsed, min;
+
+    min = (uint64_t)(-1);
+
+    connitem = ctx->connlist;
+    while (connitem) {
+       if (connitem->conn->state != XMPP_STATE_CONNECTED) {
+           connitem = connitem->next;
+           continue;
+       }
+       
+       /* enable all handlers that were added */
+       for (handitem = connitem->conn->timed_handlers; handitem;
+            handitem = handitem->next)
+           handitem->enabled = 1;
+
+       handitem = connitem->conn->timed_handlers;
+       while (handitem) {
+           /* skip newly added handlers */
+           if (!handitem->enabled) {
+               handitem = handitem->next;
+               continue;
+           }
+
+           /* only fire user handlers after authentication */
+           if (handitem->user_handler && !connitem->conn->authenticated) {
+               handitem = handitem->next;
+               continue;
+           }
+
+           fired = 0;
+           elapsed = time_elapsed(handitem->last_stamp, time_stamp());
+           if (elapsed >= handitem->period) {
+               /* fire! */
+               fired = 1;
+               handitem->last_stamp = time_stamp();
+               ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
+           } else if (min > (handitem->period - elapsed))
+               min = handitem->period - elapsed;
+               
+           temp = handitem;
+           handitem = handitem->next;
+
+           /* delete handler if it returned false */
+           if (fired && !ret)
+               xmpp_timed_handler_delete(connitem->conn, temp->handler);
+       }
+
+       connitem = connitem->next;
+    }
+
+    return min;
+}
+
+/** Reset all timed handlers.
+ *  This function is called internally when a connection is successful.
+ *
+ *  @param conn a Strophe connection object
+ *  @param user_only whether to reset all handlers or only user ones
+ */
+void handler_reset_timed(xmpp_conn_t *conn, int user_only)
+{
+    xmpp_handlist_t *handitem;
+
+    handitem = conn->timed_handlers;
+    while (handitem) {
+       if ((user_only && handitem->user_handler) || !user_only)
+           handitem->last_stamp = time_stamp();
+       
+       handitem = handitem->next;
+    }
+}
+
+static void _timed_handler_add(xmpp_conn_t * const conn,
+                              xmpp_timed_handler handler,
+                              const unsigned long period,
+                              void * const userdata, 
+                              const int user_handler)
+{
+    xmpp_handlist_t *item, *tail;
+
+    /* check if handler is already in the list */
+    for (item = conn->timed_handlers; item; item = item->next) {
+       if (item->handler == (void *)handler)
+           break;
+    }
+    if (item) return;
+
+    /* build new item */
+    item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
+    if (!item) return;
+
+    item->user_handler = user_handler;
+    item->handler = (void *)handler;
+    item->userdata = userdata;
+    item->enabled = 0;
+    item->next = NULL;
+
+    item->period = period;
+    item->last_stamp = time_stamp();
+
+    /* append item to list */
+    if (!conn->timed_handlers)
+       conn->timed_handlers = item;
+    else {
+       tail = conn->timed_handlers;
+       while (tail->next) 
+           tail = tail->next;
+       tail->next = item;
+    }
+}
+
+/** Delete a timed handler.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler function pointer to the handler
+ *
+ *  @ingroup Handlers
+ */
+void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
+                              xmpp_timed_handler handler)
+{
+    xmpp_handlist_t *item, *prev;
+
+    if (!conn->timed_handlers) return;
+
+    prev = NULL;
+    item = conn->timed_handlers;
+    while (item) {
+       if (item->handler == (void *)handler)
+           break;
+       prev = item;
+       item = item->next;
+    }
+
+    if (item) {
+       if (prev)
+           prev->next = item->next;
+       else
+           conn->timed_handlers = item->next;
+       
+       xmpp_free(conn->ctx, item);
+    }
+}
+
+static void _id_handler_add(xmpp_conn_t * const conn,
+                        xmpp_handler handler,
+                        const char * const id,
+                        void * const userdata, int user_handler)
+{
+    xmpp_handlist_t *item, *tail;
+
+    /* check if handler is already in the list */
+    item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
+    while (item) {
+       if (item->handler == (void *)handler)
+           break;
+       item = item->next;
+    }
+    if (item) return;
+
+    /* build new item */
+    item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
+    if (!item) return;
+
+    item->user_handler = user_handler;
+    item->handler = (void *)handler;
+    item->userdata = userdata;
+    item->enabled = 0;
+    item->next = NULL;
+
+    item->id = xmpp_strdup(conn->ctx, id);
+    if (!item->id) {
+       xmpp_free(conn->ctx, item);
+       return;
+    }
+
+    /* put on list in hash table */
+    tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
+    if (!tail)
+       hash_add(conn->id_handlers, id, item);
+    else {
+       while (tail->next) 
+           tail = tail->next;
+       tail->next = item;
+    }
+}
+
+/** Delete an id based stanza handler.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a stanza handler
+ *  @param id a string containing the id the handler is for
+ *
+ *  @ingroup Handlers
+ */
+void xmpp_id_handler_delete(xmpp_conn_t * const conn,
+                           xmpp_handler handler,
+                           const char * const id)
+{
+    xmpp_handlist_t *item, *prev;
+
+    prev = NULL;
+    item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
+    if (!item) return;
+
+    while (item) {
+       if (item->handler == (void *)handler)
+           break;
+
+       prev = item;
+       item = item->next;
+    }
+
+    if (item) {
+       if (prev)
+           prev->next = item->next;
+       else {
+           hash_drop(conn->id_handlers, id);
+           hash_add(conn->id_handlers, id, item->next);
+       }
+       xmpp_free(conn->ctx, item->id);
+       xmpp_free(conn->ctx, item);
+    }
+}
+
+/* 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_handlist_t *item, *tail;
+
+    /* check if handler already in list */
+    for (item = conn->handlers; item; item = item->next) {
+       if (item->handler == (void *)handler)
+           break;
+    }
+    if (item) return;
+
+    /* build new item */
+    item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
+    if (!item) return;
+
+    item->user_handler = user_handler;
+    item->handler = (void *)handler;
+    item->userdata = userdata;
+    item->enabled = 0;
+    item->next = NULL;
+    
+    if (ns) {
+       item->ns = xmpp_strdup(conn->ctx, ns);
+       if (!item->ns) {
+           xmpp_free(conn->ctx, item);
+           return;
+       }
+    } else
+       item->ns = NULL;
+    if (name) {
+       item->name = xmpp_strdup(conn->ctx, name);
+       if (!item->name) {
+           if (item->ns) xmpp_free(conn->ctx, item->ns);
+           xmpp_free(conn->ctx, item);
+           return;
+       }
+    } else
+       item->name = NULL;
+    if (type) {
+       item->type = xmpp_strdup(conn->ctx, type);
+       if (!item->type) {
+           if (item->ns) xmpp_free(conn->ctx, item->ns);
+           if (item->name) xmpp_free(conn->ctx, item->name);
+           xmpp_free(conn->ctx, item);
+       }
+    } else
+       item->type = NULL;
+
+    /* append to list */
+    if (!conn->handlers)
+       conn->handlers = item;
+    else {
+       tail = conn->handlers;
+       while (tail->next) 
+           tail = tail->next;
+       tail->next = item;
+    }
+}
+
+/** Delete a stanza handler.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a stanza handler
+ *
+ *  @ingroup Handlers
+ */
+void xmpp_handler_delete(xmpp_conn_t * const conn,
+                        xmpp_handler handler)
+{
+    xmpp_handlist_t *prev, *item;
+
+    if (!conn->handlers) return;
+
+    prev = NULL;
+    item = conn->handlers;
+    while (item) {
+       if (item->handler == (void *)handler)
+           break;
+       
+       prev = item;
+       item = item->next;
+    }
+
+    if (item) {
+       if (prev)
+           prev->next = item->next;
+       else
+           conn->handlers = item->next;
+
+       if (item->ns) xmpp_free(conn->ctx, item->ns);
+       if (item->name) xmpp_free(conn->ctx, item->name);
+       if (item->type) xmpp_free(conn->ctx, item->type);
+       xmpp_free(conn->ctx, item);
+    }
+}
+
+/** Add a timed handler.
+ *  The handler will fire for the first time once the period has elapsed,
+ *  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.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a timed handler
+ *  @param period the time in milliseconds between firings
+ *  @param userdata an opaque data pointer that will be passed to the handler
+ *
+ *  @ingroup Handlers
+ */
+void xmpp_timed_handler_add(xmpp_conn_t * const conn,
+                           xmpp_timed_handler handler,
+                           const unsigned long period,
+                           void * const userdata)
+{
+    _timed_handler_add(conn, handler, period, userdata, 1);
+}
+
+/** Add a timed system handler.
+ *  This function is used to add internal timed handlers and should not be
+ *  used outside of the library.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a timed handler
+ *  @param period the time in milliseconds between firings
+ *  @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)
+{
+    _timed_handler_add(conn, handler, period, userdata, 0);
+}
+
+/** Add an id based stanza handler.
+
+ *  This function adds a stanza handler for an &lt;iq/&gt; stanza of
+ *  type 'result' or 'error' with a specific id attribute.  This can
+ *  be used to handle responses to specific &lt;iq/&gt;s.
+ *
+ *  If the handler function returns true, it will be kept, and if it
+ *  returns false, it will be deleted from the list of handlers.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a stanza handler
+ *  @param id a string with the id
+ *  @param userdata an opaque data pointer that will be passed to the handler
+ *
+ *  @ingroup Handlers
+ */
+void xmpp_id_handler_add(xmpp_conn_t * const conn,
+                        xmpp_handler handler,
+                        const char * const id,
+                        void * const userdata)
+{
+    _id_handler_add(conn, handler, id, userdata, 1);
+}
+
+/** Add an id based system stanza handler.
+ *  This function is used to add internal id based stanza handlers and should
+ *  not be used outside of the library.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a stanza handler
+ *  @param id a string with the id
+ *  @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)
+{
+    _id_handler_add(conn, handler, id, userdata, 0);
+}
+
+/** Add a stanza handler.
+ *  This function is used to add a stanza handler to a connection.
+ *  The handler will be called when the any of the filters match.  The
+ *  name filter matches to the top level stanza name.  The type filter
+ *  matches the 'type' attribute of the top level stanza.  The ns
+ *  filter matches the namespace ('xmlns' attribute) of either the top
+ *  level stanza or any of it's immediate children (this allows you do
+ *  handle specific &lt;iq/&gt; stanzas based on the &lt;query/&gt;
+ *  child namespace.
+ *
+ *  If the handler function returns true, it will be kept, and if it
+ *  returns false, it will be deleted from the list of handlers.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a stanza handler
+ *  @param ns a string with the namespace to match
+ *  @param name a string with the stanza name to match
+ *  @param type a string with the 'type' attribute to match
+ *  @param userdata an opaque data pointer that will be passed to the handler
+ *
+ *  @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)
+{
+    _handler_add(conn, handler, ns, name, type, userdata, 1);
+}
+
+/** Add a system stanza handler.
+ *  This function is used to add internal stanza handlers and should
+ *  not be used outside of the library.
+ *
+ *  @param conn a Strophe connection object
+ *  @param handler a function pointer to a stanza handler
+ *  @param ns a string with the namespace to match
+ *  @param name a string with the stanza name to match
+ *  @param type a string with the 'type' attribute value to match
+ *  @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)
+{
+    _handler_add(conn, handler, ns, name, type, userdata, 0);
+}
diff --git a/src/libstrophe/hash.c b/src/libstrophe/hash.c
new file mode 100644 (file)
index 0000000..1b09b52
--- /dev/null
@@ -0,0 +1,276 @@
+/* hash.c
+** strophe XMPP client library -- hash table implementation
+** 
+** 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.
+*/
+
+/** @file
+ *  Hash tables.
+ */
+
+#include <nms_common.h>
+#include <strophe.h>
+#include "common.h"
+#include "hash.h"
+
+/* private types */
+typedef struct _hashentry_t hashentry_t;
+
+struct _hashentry_t {
+    hashentry_t *next;
+    char *key;
+    void *value;
+};
+
+struct _hash_t {
+    unsigned int ref;
+    xmpp_ctx_t *ctx;
+    hash_free_func free;
+    int length;
+    int num_keys;
+    hashentry_t **entries;
+};
+
+struct _hash_iterator_t {
+    unsigned int ref;
+    hash_t *table;
+    hashentry_t *entry;
+    int index;
+};
+   
+/** allocate and initialize a new hash table */
+hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
+                hash_free_func free)
+{
+    hash_t *result = NULL;
+
+    result = xmpp_alloc(ctx, sizeof(hash_t));
+    if (result != NULL) {
+       result->entries = xmpp_alloc(ctx, size * sizeof(hashentry_t *));
+       if (result->entries == NULL) {
+           xmpp_free(ctx, result);
+           return NULL;
+       }
+       memset(result->entries, 0, size * sizeof(hashentry_t *));
+       result->length = size;
+
+       result->ctx = ctx;
+       result->free = free;
+       result->num_keys = 0;
+       /* give the caller a reference */
+       result->ref = 1;
+    }
+    
+    return result;
+}
+
+/** obtain a new reference to an existing hash table */
+hash_t *hash_clone(hash_t * const table)
+{
+    table->ref++;
+    return table;
+}
+
+/** release a hash table that is no longer needed */
+void hash_release(hash_t * const table)
+{
+    xmpp_ctx_t *ctx = table->ctx;
+    hashentry_t *entry, *next;
+    int i;
+    
+    if (table->ref > 1)
+       table->ref--;
+    else {
+       for (i = 0; i < table->length; i++) {
+           entry = table->entries[i];
+           while (entry != NULL) {
+               next = entry->next;
+               xmpp_free(ctx, entry->key);
+               if (table->free) table->free(ctx, entry->value);
+               xmpp_free(ctx, entry);
+               entry = next;
+           }
+       }
+       xmpp_free(ctx, table->entries);
+       xmpp_free(ctx, table);
+    }
+}
+
+/** hash a key for our table lookup */
+static int _hash_key(hash_t *table, const char *key)
+{
+   int hash = 0;
+   int shift = 0;
+   const char *c = key;
+
+   while (*c != '\0') {
+       /* assume 32 bit ints */
+       hash ^= ((int)*c++ << shift);
+       shift += 8;
+       if (shift > 24) shift = 0;
+   }
+
+   return hash % table->length;
+}
+
+/** add a key, value pair to a hash table.
+ *  each key can appear only once; the value of any
+ *  identical key will be replaced
+ */
+int hash_add(hash_t *table, const char * const key, void *data)
+{
+   xmpp_ctx_t *ctx = table->ctx;
+   hashentry_t *entry = NULL;
+   int index = _hash_key(table, key);
+
+   /* drop existing entry, if any */
+   hash_drop(table, key);
+
+   /* allocate and fill a new entry */
+   entry = xmpp_alloc(ctx, sizeof(hashentry_t));
+   if (!entry) return -1;
+   entry->key = xmpp_strdup(ctx, key);
+   if (!entry->key) {
+       xmpp_free(ctx, entry);
+       return -1;
+   }
+   entry->value = data;
+   /* insert ourselves in the linked list */
+   /* TODO: this leaks duplicate keys */
+   entry->next = table->entries[index];
+   table->entries[index] = entry;
+   table->num_keys++;
+
+   return 0;
+}
+
+/** look up a key in a hash table */
+void *hash_get(hash_t *table, const char *key)
+{
+   hashentry_t *entry;
+   int index = _hash_key(table, key);
+   void *result = NULL;
+
+   /* look up the hash entry */
+   entry = table->entries[index];
+   while (entry != NULL) {
+       /* traverse the linked list looking for the key */
+       if (!strcmp(key, entry->key)) {
+         /* match */
+         result = entry->value;
+         return result;
+       }
+       entry = entry->next;
+   }
+   /* no match */
+   return result;
+}
+
+/** delete a key from a hash table */
+int hash_drop(hash_t *table, const char *key)
+{
+   xmpp_ctx_t *ctx = table->ctx;
+   hashentry_t *entry, *prev;
+   int index = _hash_key(table, key);
+
+   /* look up the hash entry */
+   entry = table->entries[index];
+   prev = NULL;
+   while (entry != NULL) {
+       /* traverse the linked list looking for the key */
+       if (!strcmp(key, entry->key)) {
+         /* match, remove the entry */
+         xmpp_free(ctx, entry->key);
+         if (table->free) table->free(ctx, entry->value);
+         if (prev == NULL) {
+           table->entries[index] = entry->next;
+         } else {
+           prev->next = entry->next;
+         }
+         xmpp_free(ctx, entry);
+         table->num_keys--;
+         return 0;
+       }
+       prev = entry;
+       entry = entry->next;
+   }
+   /* no match */
+   return -1;
+}
+
+int hash_num_keys(hash_t *table)
+{
+    return table->num_keys;
+}
+
+/** allocate and initialize a new iterator */
+hash_iterator_t *hash_iter_new(hash_t *table)
+{
+    xmpp_ctx_t *ctx = table->ctx;
+    hash_iterator_t *iter;
+
+    iter = xmpp_alloc(ctx, sizeof(*iter));
+    if (iter != NULL) {
+       iter->ref = 1;
+       iter->table = hash_clone(table);
+       iter->entry = NULL;
+       iter->index = -1;
+    }
+
+    return iter;
+}
+
+
+/** release an iterator that is no longer needed */
+void hash_iter_release(hash_iterator_t *iter)
+{
+    xmpp_ctx_t *ctx = iter->table->ctx;
+
+    iter->ref--;
+
+    if (iter->ref <= 0) {
+       hash_release(iter->table);
+       xmpp_free(ctx, iter);
+    }
+}
+
+/** return the next hash table key from the iterator.
+    the returned key should not be freed */
+const char * hash_iter_next(hash_iterator_t *iter)
+{
+    hash_t *table = iter->table;
+    hashentry_t *entry = iter->entry;
+    int i = iter->index + 1;
+
+    /* advance until we find the next entry */
+    if (entry != NULL) entry = entry->next;
+    if (entry == NULL) {
+       /* we're off the end of list, search for a new entry */
+       while (i < iter->table->length) {
+           entry = table->entries[i];
+           if (entry != NULL) {
+               iter->index = i;
+               break;
+           }
+           i++;
+       }
+    }
+
+    if ((entry == NULL) || (i >= table->length)) {
+       /* no more keys! */
+       return NULL;
+    }
+
+    /* remember our current match */
+    iter->entry = entry;
+    return entry->key;
+}
+
diff --git a/src/libstrophe/hash.h b/src/libstrophe/hash.h
new file mode 100644 (file)
index 0000000..94c0b14
--- /dev/null
@@ -0,0 +1,64 @@
+/* hash.h
+** strophe XMPP client library -- hash table interface
+** 
+** 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.
+*/
+
+/** @file
+ *  Hash table API.
+ */
+
+#ifndef __LIBSTROPHE_HASH_H__
+#define __LIBSTROPHE_HASH_H__
+
+typedef struct _hash_t hash_t;
+
+typedef void (*hash_free_func)(const xmpp_ctx_t * const ctx, void *p);
+
+/** allocate and initialize a new hash table */
+hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
+                hash_free_func free);
+
+/** allocate a new reference to an existing hash table */
+hash_t *hash_clone(hash_t * const table);
+
+/** release a hash table when no longer needed */
+void hash_release(hash_t * const table);
+
+/** add a key, value pair to a hash table.
+ *  each key can appear only once; the value of any
+ *  identical key will be replaced
+ */
+int hash_add(hash_t *table, const char * const key, void *data);
+
+/** look up a key in a hash table */
+void *hash_get(hash_t *table, const char *key);
+
+/** delete a key from a hash table */
+int hash_drop(hash_t *table, const char *key);
+
+/** return the number of keys in a hash */
+int hash_num_keys(hash_t *table);
+
+/** hash key iterator functions */
+typedef struct _hash_iterator_t hash_iterator_t;
+
+/** allocate and initialize a new iterator */
+hash_iterator_t *hash_iter_new(hash_t *table);
+
+/** release an iterator that is no longer needed */
+void hash_iter_release(hash_iterator_t *iter);
+
+/** return the next hash table key from the iterator.
+    the returned key should not be freed */
+const char * hash_iter_next(hash_iterator_t *iter);
+
+#endif /* __LIBXMPPP_HASH_H__ */
diff --git a/src/libstrophe/jid.c b/src/libstrophe/jid.c
new file mode 100644 (file)
index 0000000..26d5e20
--- /dev/null
@@ -0,0 +1,176 @@
+/* jid.c
+** strophe XMPP client library -- helper functions for parsing JIDs
+**
+** 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.
+*/
+
+/** @file
+ *  JID creation and parsing.
+ */
+
+#include <nms_common.h>
+#include <strophe.h>
+#include "common.h"
+
+/** Create a JID string from component parts node, domain, and resource.
+ *
+ *  @param ctx the Strophe context object
+ *  @param node a string representing the node
+ *  @param domain a string representing the domain.  Required.
+ *  @param resource a string representing the resource
+ *
+ *  @return an allocated string with the full JID or NULL if no domain
+ *      is specified
+ */
+char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
+                                   const char *domain,
+                                   const char *resource)
+{
+    char *result;
+    int len,nlen,dlen,rlen;
+
+    /* jid must at least have a domain */
+    if (domain == NULL) return NULL;
+
+    /* accumulate lengths */
+    dlen = strlen(domain);
+    nlen = (node) ? strlen(node) + 1 : 0;
+    rlen = (resource) ? strlen(resource) + 1 : 0;
+    len = nlen + dlen + rlen;
+
+    /* concat components */
+    result = xmpp_alloc(ctx, len + 1);
+    if (result != NULL) {
+       if (node != NULL) {
+           memcpy(result, node, nlen - 1);
+           result[nlen-1] = '@';
+       }
+        memcpy(result + nlen, domain, dlen);
+       if (resource != NULL) {
+           result[nlen+dlen] = '/';
+           memcpy(result+nlen+dlen+1, resource, rlen - 1);
+       }
+       result[nlen+dlen+rlen] = '\0';
+    }
+
+    return result;
+}
+
+/** Create a bare JID from a JID.
+ *  
+ *  @param ctx the Strophe context object
+ *  @param jid the JID
+ *
+ *  @return an allocated string with the bare JID or NULL on an error
+ */
+char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
+{
+    char *result;
+    const char *c;
+
+    c = strchr(jid, '/');
+    if (c == NULL) return xmpp_strdup(ctx, jid);
+
+    result = xmpp_alloc(ctx, c-jid+1);
+    if (result != NULL) {
+       memcpy(result, jid, c-jid);
+       result[c-jid] = '\0';
+    }
+
+    return result;
+}
+
+/** Create a node string from a JID.
+ *  
+ *  @param ctx a Strophe context object
+ *  @param jid the JID
+ *
+ *  @return an allocated string with the node or NULL if no node is found
+ *      or an error occurs
+ */
+char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
+{
+    char *result = NULL;
+    const char *c;
+
+    c = strchr(jid, '@');
+    if (c != NULL) {
+       result = xmpp_alloc(ctx, (c-jid) + 1);
+       if (result != NULL) {
+           memcpy(result, jid, (c-jid));
+           result[c-jid] = '\0';
+       }
+    }
+
+    return result;
+}
+
+/** Create a domain string from a JID.
+ *
+ *  @param ctx the Strophe context object
+ *  @param jid the JID
+ *
+ *  @return an allocated string with the domain or NULL on an error
+ */
+char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
+{
+    char *result = NULL;
+    const char *c,*s;
+
+    c = strchr(jid, '@');
+    if (c == NULL) {
+       /* no node, assume domain */
+       c = jid;
+    } else {
+       /* advance past the separator */
+       c++;
+    }
+    s = strchr(c, '/');
+    if (s == NULL) {
+       /* no resource */
+       s = c + strlen(c);
+    }
+    result = xmpp_alloc(ctx, (s-c) + 1);
+    if (result != NULL) {
+       memcpy(result, c, (s-c));
+       result[s-c] = '\0';
+    }
+
+    return result;
+}
+
+/** Create a resource string from a JID.
+ *
+ *  @param ctx a Strophe context object
+ *  @param jid the JID
+ *
+ *  @return an allocated string with the resource or NULL if no resource 
+ *      is found or an error occurs
+ */
+char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
+{
+    char *result = NULL;
+    const char *c;
+    int len;
+
+    c = strchr(jid, '/');
+    if (c != NULL)  {
+       c++;
+       len = strlen(c);
+       result = xmpp_alloc(ctx, len + 1);
+       if (result != NULL) {
+           memcpy(result, c, len);
+           result[len] = '\0';
+       }
+    }
+
+    return result;
+}
diff --git a/src/libstrophe/libstrophe.vcproj b/src/libstrophe/libstrophe.vcproj
new file mode 100644 (file)
index 0000000..9a0da63
--- /dev/null
@@ -0,0 +1,480 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8.00"
+       Name="libstrophe"
+       ProjectGUID="{F4169894-5C48-4E11-A82E-1055F9572EEF}"
+       RootNamespace="libstrophe"
+       Keyword="Win32Proj"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+               <Platform
+                       Name="x64"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+                       IntermediateDirectory="$(ConfigurationName)"
+                       ConfigurationType="2"
+                       CharacterSet="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..\..\include;..\libexpat\libexpat"
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSTROPHE_EXPORTS"
+                               MinimalRebuild="true"
+                               ExceptionHandling="0"
+                               BasicRuntimeChecks="3"
+                               RuntimeLibrary="3"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="true"
+                               DebugInformationFormat="4"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="ws2_32.lib libeay32.lib ssleay32.lib"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+                       IntermediateDirectory="$(ConfigurationName)"
+                       ConfigurationType="2"
+                       CharacterSet="1"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalIncludeDirectories="..\..\include;..\libexpat\libexpat"
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSTROPHE_EXPORTS"
+                               ExceptionHandling="0"
+                               RuntimeLibrary="2"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="true"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="ws2_32.lib libeay32.lib ssleay32.lib"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Debug|x64"
+                       OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
+                       IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+                       ConfigurationType="2"
+                       CharacterSet="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                               TargetEnvironment="3"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="..\..\include;..\libexpat\libexpat"
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSTROPHE_EXPORTS"
+                               MinimalRebuild="true"
+                               ExceptionHandling="0"
+                               BasicRuntimeChecks="3"
+                               RuntimeLibrary="3"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="true"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="ws2_32.lib libeay32.lib ssleay32.lib"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                               TargetMachine="17"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|x64"
+                       OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
+                       IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+                       ConfigurationType="2"
+                       CharacterSet="1"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                               TargetEnvironment="3"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalIncludeDirectories="..\..\include;..\libexpat\libexpat"
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSTROPHE_EXPORTS"
+                               ExceptionHandling="0"
+                               RuntimeLibrary="2"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="true"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               AdditionalDependencies="ws2_32.lib libeay32.lib ssleay32.lib"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="true"
+                               SubSystem="2"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="17"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+                       >
+                       <File
+                               RelativePath=".\auth.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\conn.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\ctx.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\event.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\handler.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\hash.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\jid.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\md5.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\parser_expat.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\sasl.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\sha1.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\snprintf.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\sock.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\stanza.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\thread.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\tls_openssl.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\util.c"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+                       >
+                       <File
+                               RelativePath=".\common.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\hash.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\md5.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\include\nms_common.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\ostypes.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\parser.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\sasl.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\sha1.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\sock.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\include\strophe.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\thread.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\tls.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\util.h"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Resource Files"
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+                       >
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/src/libstrophe/md5.c b/src/libstrophe/md5.c
new file mode 100644 (file)
index 0000000..01231b1
--- /dev/null
@@ -0,0 +1,289 @@
+/* md5.c
+** MD5 hash function implemention, adapted for local use
+**
+** This code is in the Public Domain
+*/
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/** @file
+ *  MD5 hash.
+ */
+
+#include <string.h> /* memcpy(), memset() */
+#include "md5.h"
+
+/* little-endian word access macros */
+#define GET_32BIT_LSB_FIRST(cp) \
+    (((uint32_t)(unsigned char)(cp)[0]) | \
+     ((uint32_t)(unsigned char)(cp)[1] << 8 ) | \
+     ((uint32_t)(unsigned char)(cp)[2] << 16) | \
+     ((uint32_t)(unsigned char)(cp)[3] << 24))
+
+#define PUT_32BIT_LSB_FIRST(cp, value) \
+    do { \
+       (cp)[0] = (value) & 0xFF; \
+       (cp)[1] = ((value) >> 8)  & 0xFF; \
+       (cp)[2] = ((value) >> 16) & 0xFF; \
+       (cp)[3] = ((value) >> 24) & 0xFF; \
+    } while(0)
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+
+       memset(ctx->in, 0, 64);
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = (t + ((uint32_t)len << 3)) & 0xffffffff) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       MD5Transform(ctx->buf, ctx->in, ctx);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       MD5Transform(ctx->buf, ctx->in, ctx);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       MD5Transform(ctx->buf, ctx->in, ctx);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+
+    /* Append length in bits and transform */
+    PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
+    PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
+
+    MD5Transform(ctx->buf, ctx->in, ctx);
+    PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
+    PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
+    PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
+    PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
+    memset(ctx, 0, sizeof(*ctx));      /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+/* debugging version: */
+/*
+#define MD5STEP(f, w, x, y, z, data, s) \
+       printf("MD5STEP:  w: %x x: %x y: %x z: %x data: %x s: %x\n",    \
+               w, x, y, z, data, s);   \
+       printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
+       ( w += f(x, y, z) + data,  printf(" - w: %x ", w), \
+       w = w<<s | w>>(32-s),  printf(" - w: %x\n", w), w += x )
+*/
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
+       struct MD5Context *ctx)
+{
+    register uint32_t a, b, c, d, i;
+    uint32_t in[16];
+    
+    for (i = 0; i < 16; i++)
+      in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#ifdef DEBUG_MD5
+
+#include <stdio.h>
+
+void MD5DumpBytes(unsigned char *b, int len)
+{
+       int i;
+       for (i=0; i<len; i++) {
+               if (i%32==0 && i!=0) {
+                       printf("\n");
+               }
+               printf("%02x", b[i]&0xff);
+       }
+       printf("\n");
+}
+
+#endif /* DEBUG_MD5 */
+
+#endif /* !MD5_ASM */
diff --git a/src/libstrophe/md5.h b/src/libstrophe/md5.h
new file mode 100644 (file)
index 0000000..3095f95
--- /dev/null
@@ -0,0 +1,41 @@
+/* md5.h
+** interface to MD5 hash function
+**
+** This code is in the Public Domain.
+*/
+
+/** @file
+ *  MD5 hash API.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* we use the uint32_t type from stdint.h
+ * if it is not available, add a typedef here:
+ */
+/* make sure the stdint.h types are available */
+#if defined(_MSC_VER) /* Microsoft Visual C++ */
+  typedef unsigned int              uint32_t;
+#else
+#include <stdint.h>
+#endif
+
+struct MD5Context {
+       uint32_t buf[4];
+       uint32_t bits[2];
+       unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+              uint32_t len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], const unsigned char in[64],
+                       struct MD5Context *ctx);
+
+#ifdef DEBUG_MD5
+void MD5DumpBytes(unsigned char *b, int len);
+#endif
+
+#endif /* !MD5_H */
diff --git a/src/libstrophe/oocontext.cpp b/src/libstrophe/oocontext.cpp
new file mode 100644 (file)
index 0000000..50717e4
--- /dev/null
@@ -0,0 +1,88 @@
+/* oocontext.cpp
+** strophe XMPP client library -- C++ context implementation
+** 
+** 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.
+*/
+
+#include <stdlib.h>
+
+#include "strophe.h"
+#include "strophepp.h"
+
+XMPP::Context::Context() 
+{
+    m_mem.alloc = callAlloc;
+    m_mem.realloc = callRealloc;
+    m_mem.free = callFree;
+    m_mem.userdata = (void *)this;
+
+    m_log.handler = callLog;
+    m_log.userdata = (void *)this;
+
+    m_ctx = ::xmpp_ctx_new(&m_mem, &m_log);
+}
+
+XMPP::Context::~Context()
+{
+    ::xmpp_ctx_free(m_ctx);
+}
+
+void *XMPP::Context::alloc(const size_t size)
+{
+    return ::malloc(size);
+}
+
+void *XMPP::Context::realloc(void *p, const size_t size)
+{
+    return ::realloc(p, size);
+}
+
+void XMPP::Context::free(void *p)
+{
+    ::free(p);
+}
+
+void XMPP::Context::log(const xmpp_log_level_t level,
+                       const char * const area,
+                       const char * const msg)
+{
+    /* do nothing by default */
+}
+
+xmpp_ctx_t *XMPP::Context::getContext()
+{
+    return m_ctx;
+}
+
+void *XMPP::Context::callAlloc(const size_t size, void * const userdata)
+{
+    return reinterpret_cast<Context *>(userdata)->alloc(size);
+}
+
+void *XMPP::Context::callRealloc(void *p, const size_t size,
+                                void * const userdata)
+{
+    return reinterpret_cast<Context *>(userdata)->realloc(p, size);
+}
+
+void XMPP::Context::callFree(void *p, void * const userdata)
+{
+    reinterpret_cast<Context *>(userdata)->free(p);
+}
+
+void XMPP::Context::callLog(void * const userdata,
+                           const xmpp_log_level_t level,
+                           const char * const area,
+                           const char * const msg)
+{
+    reinterpret_cast<Context *>(userdata)->log(level, area, msg);
+}
+
diff --git a/src/libstrophe/oostanza.cpp b/src/libstrophe/oostanza.cpp
new file mode 100644 (file)
index 0000000..178bfbf
--- /dev/null
@@ -0,0 +1,82 @@
+/* oostanza.cpp
+** strophe XMPP client library -- C++ context implementation
+** 
+** 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.
+*/
+
+#include "strophe.h"
+#include "strophepp.h"
+
+using namespace XMPP;
+
+void *Stanza::operator new(size_t size, Context *ctx)
+{
+    void *p;
+
+    /* we must allocate extra room for the Context object so that the
+    destructor can access it to free the object.  C++ does not allow
+    us to access normal members in the destructor, so we have to hide
+    it.  This must be prepended as well, since C++ will add stuff to
+    the end in subclasses. */
+
+    p = ctx->alloc(size + sizeof(Context *));
+    if (!p) return p;
+
+    *reinterpret_cast<Context **>(p) = ctx;
+    p = reinterpret_cast<void *>(reinterpret_cast<char *>(p) + 
+                                sizeof(Context *));
+
+    return p;
+}
+
+void Stanza::operator delete(void *p)
+{
+    Context *ctx;
+
+    ctx = *reinterpret_cast<Context **>(reinterpret_cast<char *>(p) - 4);
+    ctx->free(reinterpret_cast<char *>(p) - 4);
+}
+
+Stanza::Stanza(Context *ctx)
+{
+    m_ctx = ctx;
+    m_stanza = ::xmpp_stanza_new(ctx->getContext());
+    // TODO: check for errors
+}
+
+Stanza::~Stanza()
+{
+}
+
+Stanza *Stanza::create(Context *ctx)
+{
+    return new (ctx) Stanza(ctx);
+}
+
+void Stanza::release()
+{
+    if (::xmpp_stanza_release(m_stanza))
+       delete this;
+}
+
+Stanza *Stanza::clone()
+{
+    ::xmpp_stanza_clone(m_stanza);
+    return this;
+}
+
+Stanza *Stanza::copy()
+{
+    // TODO
+    return NULL;
+}
+
+
diff --git a/src/libstrophe/ostypes.h b/src/libstrophe/ostypes.h
new file mode 100644 (file)
index 0000000..0794b2b
--- /dev/null
@@ -0,0 +1,27 @@
+/* ostypes.h
+** strophe XMPP client library -- type definitions for platforms 
+**     without stdint.h
+**
+** 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.
+*/
+
+/** @file
+ *  Type definitions for platforms without stdint.h.
+ */
+
+#ifndef __LIBSTROPHE_OSTYPES_H__
+#define __LIBSTROPHE_OSTYPES_H__
+
+#ifdef _WIN32
+typedef unsigned __int64 uint64_t; 
+#endif
+
+#endif /* __LIBSTROPHE_OSTYPES_H__ */
diff --git a/src/libstrophe/parser.h b/src/libstrophe/parser.h
new file mode 100644 (file)
index 0000000..32329f6
--- /dev/null
@@ -0,0 +1,43 @@
+/* parser.h
+** strophe XMPP client library -- parser structures and functions
+**
+** 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.
+*/
+
+/** @file
+ *  Internally used functions and structures.
+ */
+
+#ifndef __LIBSTROPHE_PARSER_H__
+#define __LIBSTROPHE_PARSER_H__
+
+#include "strophe.h"
+
+typedef struct _parser_t parser_t;
+
+typedef void (*parser_start_callback)(char *name,
+                                      char **attrs,
+                                      void * const userdata);
+typedef void (*parser_end_callback)(char *name, void * const userdata);
+typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza,
+                                       void * const userdata);
+
+
+parser_t *parser_new(xmpp_ctx_t *ctx, 
+                     parser_start_callback startcb,
+                     parser_end_callback endcb,
+                     parser_stanza_callback stanzacb,
+                     void *userdata);
+void parser_free(parser_t *parser);
+int parser_reset(parser_t *parser);
+int parser_feed(parser_t *parser, char *chunk, int len);
+
+#endif /* __LIBSTROPHE_PARSER_H__ */
diff --git a/src/libstrophe/parser_expat.c b/src/libstrophe/parser_expat.c
new file mode 100644 (file)
index 0000000..a5e346c
--- /dev/null
@@ -0,0 +1,198 @@
+/* parser.c
+** strophe XMPP client library -- xml parser handlers and utility functions
+**
+** 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.
+*/
+
+/** @file
+ *  XML parser handlers.
+ */
+
+#include <nms_common.h>
+#include <expat.h>
+#include <strophe.h>
+#include "common.h"
+#include "parser.h"
+
+struct _parser_t {
+    xmpp_ctx_t *ctx;
+    XML_Parser expat;
+    parser_start_callback startcb;
+    parser_end_callback endcb;
+    parser_stanza_callback stanzacb;
+    void *userdata;
+    int depth;
+    xmpp_stanza_t *stanza;
+};
+
+static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
+{
+    int i;
+
+    if (!attrs) return;
+
+    for (i = 0; attrs[i]; i += 2) {
+        xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]);
+    }
+}
+
+static void _start_element(void *userdata,
+                           const XML_Char *name,
+                           const XML_Char **attrs)
+{
+    parser_t *parser = (parser_t *)userdata;
+    xmpp_stanza_t *child;
+
+    if (parser->depth == 0) {
+        /* notify the owner */
+        if (parser->startcb)
+            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);
+       } 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);
+
+           /* 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;
+       }
+    }
+
+    parser->depth++;
+}
+
+static void _end_element(void *userdata, const XML_Char *name)
+{
+    parser_t *parser = (parser_t *)userdata;
+
+    parser->depth--;
+
+    if (parser->depth == 0) {
+        /* notify the owner */
+        if (parser->endcb)
+            parser->endcb((char *)name, parser->userdata);
+    } else {
+       if (parser->stanza->parent) {
+           /* we're finishing a child stanza, so set current to the parent */
+           parser->stanza = parser->stanza->parent;
+       } else {
+            if (parser->stanzacb)
+                parser->stanzacb(parser->stanza,
+                                 parser->userdata);
+           xmpp_stanza_release(parser->stanza);
+           parser->stanza = NULL;
+       }
+    }
+}
+
+static void _characters(void *userdata, const XML_Char *s, int len)
+{
+    parser_t *parser = (parser_t *)userdata;
+    xmpp_stanza_t *stanza;
+
+    if (parser->depth < 2) return;
+
+    /* create and populate stanza */
+    stanza = xmpp_stanza_new(parser->ctx);
+    if (!stanza) {
+       /* FIXME: allocation error, disconnect */
+       return;
+    }
+    xmpp_stanza_set_text_with_size(stanza, s, len);
+
+    xmpp_stanza_add_child(parser->stanza, stanza);
+    xmpp_stanza_release(stanza);
+}
+
+parser_t *parser_new(xmpp_ctx_t *ctx,
+                     parser_start_callback startcb,
+                     parser_end_callback endcb,
+                     parser_stanza_callback stanzacb,
+                     void *userdata)
+{
+    parser_t *parser;
+
+    parser = xmpp_alloc(ctx, sizeof(parser_t));
+    if (parser != NULL) {
+        parser->ctx = ctx;
+        parser->expat = NULL;
+        parser->startcb = startcb;
+        parser->endcb = endcb;
+        parser->stanzacb = stanzacb;
+        parser->userdata = userdata;
+        parser->depth = 0;
+        parser->stanza = NULL;
+
+        parser_reset(parser);
+    }
+
+    return parser;
+}
+
+/* free a parser */
+void parser_free(parser_t *parser)
+{
+    if (parser->expat)
+        XML_ParserFree(parser->expat);
+
+    xmpp_free(parser->ctx, parser);
+}
+
+/* shuts down and restarts XML parser.  true on success */
+int parser_reset(parser_t *parser)
+{
+    if (parser->expat)
+       XML_ParserFree(parser->expat);
+
+    if (parser->stanza) 
+       xmpp_stanza_release(parser->stanza);
+
+    parser->expat = XML_ParserCreate(NULL);
+    if (!parser->expat) return 0;
+
+    parser->depth = 0;
+    parser->stanza = NULL;
+
+    XML_SetUserData(parser->expat, parser);
+    XML_SetElementHandler(parser->expat, _start_element, _end_element);
+    XML_SetCharacterDataHandler(parser->expat, _characters);
+
+    return 1;
+}
+
+int parser_feed(parser_t *parser, char *chunk, int len)
+{
+    return XML_Parse(parser->expat, chunk, len, 0);
+}
diff --git a/src/libstrophe/sasl.c b/src/libstrophe/sasl.c
new file mode 100644 (file)
index 0000000..eb6d542
--- /dev/null
@@ -0,0 +1,614 @@
+/* sasl.c
+** strophe XMPP client library -- SASL authentication helpers
+** 
+** 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.
+*/
+
+/** @file
+ *  SASL authentication.
+ */
+
+#include <nms_common.h>
+#include <strophe.h>
+#include "common.h"
+#include "sasl.h"
+#include "md5.h"
+
+/* make sure the stdint.h types are available */
+#if defined(_MSC_VER) /* Microsoft Visual C++ */
+  typedef signed char             int8_t;
+  typedef short int               int16_t;
+  typedef int                     int32_t;
+  typedef __int64                 int64_t;
+
+  typedef unsigned char             uint8_t;
+  typedef unsigned short int        uint16_t;
+  typedef unsigned int              uint32_t;
+  /* no uint64_t */
+#else
+#include <stdint.h>
+#endif
+
+
+/** generate authentication string for the SASL PLAIN mechanism */
+char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
+    int idlen, passlen;
+    char *result = NULL;
+    char *msg;
+    
+    /* our message is Base64(authzid,\0,authid,\0,password)
+       if there is no authzid, that field is left empty */
+
+    idlen = strlen(authid);
+    passlen = strlen(password);
+    msg = xmpp_alloc(ctx, 2 + idlen + passlen);
+    if (msg != NULL) {
+       msg[0] = '\0';
+       memcpy(msg+1, authid, idlen);
+       msg[1+idlen] = '\0';
+       memcpy(msg+1+idlen+1, password, passlen);
+       result = base64_encode(ctx, (unsigned char *)msg, 2 + idlen + passlen);
+       xmpp_free(ctx, msg);
+    }
+
+    return result;
+}
+
+/** helpers for digest auth */
+
+/* create a new, null-terminated string from a substring */
+static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
+{
+    char *result;
+
+    result = xmpp_alloc(ctx, len + 1);
+    if (result != NULL) {
+       memcpy(result, s, len);
+       result[len] = '\0';
+    }
+    return result;
+}
+
+/* create a new, null-terminated string quoting another string */
+static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
+{
+    char *result;
+    int len = strlen(s);
+
+    result = xmpp_alloc(ctx, len + 3);
+    if (result != NULL) {
+       result[0] = '"';
+       memcpy(result+1, s, len);
+       result[len+1] = '"';
+       result[len+2] = '\0';
+    }
+    return result;
+}
+
+/* split key, value pairs into a hash */
+static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
+{
+    hash_t *result;
+    unsigned char *text;
+    char *key, *value;
+    unsigned char *s, *t;
+
+    text = base64_decode(ctx, msg, strlen(msg));
+    if (text == NULL) {
+       xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
+       return NULL;
+    }
+
+    result = hash_new(ctx, 10, xmpp_free);
+    if (result != NULL) {
+       s = text;
+       while (*s != '\0') {
+           /* skip any leading commas and spaces */
+           while ((*s == ',') || (*s == ' ')) s++;
+           /* accumulate a key ending at '=' */
+           t = s;
+           while ((*t != '=') && (*t != '\0')) t++;
+           if (*t == '\0') break; /* bad string */
+           key = _make_string(ctx, (char *)s, (t-s));
+           if (key == NULL) break;
+            /* advance our start pointer past the key */
+           s = t + 1;
+           t = s;
+           /* if we see quotes, grab the string in between */
+           if ((*s == '\'') || (*s == '"')) {
+               t++;
+               while ((*t != *s) && (*t != '\0'))
+                   t++;
+               value = _make_string(ctx, (char *)s+1, (t-s-1));
+               if (*t == *s) {
+                   s = t + 1;
+               } else {
+                   s = t;
+               }
+           /* otherwise, accumulate a value ending in ',' or '\0' */
+           } else {
+               while ((*t != ',') && (*t != '\0')) t++;
+               value = _make_string(ctx, (char *)s, (t-s));
+               s = t;
+           }
+           if (value == NULL) {
+               xmpp_free(ctx, key);
+               break;
+           }
+           /* TODO: check for collisions per spec */
+           hash_add(result, key, value);
+           /* hash table now owns the value, free the key */
+           xmpp_free(ctx, key);
+       }
+    }
+    xmpp_free(ctx, text);
+
+    return result;
+}
+
+/** expand a 16 byte MD5 digest to a 32 byte hex representation */
+static void _digest_to_hex(const char *digest, char *hex)
+{
+    int i;
+    const char hexdigit[] = "0123456789abcdef";
+
+    for (i = 0; i < 16; i++) {
+       *hex++ = hexdigit[ (digest[i] >> 4) & 0x0F ];
+       *hex++ = hexdigit[ digest[i] & 0x0F ];
+    }
+}
+
+/** append 'key="value"' to a buffer, growing as necessary */
+static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, 
+                     char *buf, int *len, int quote)
+{
+    int olen,nlen;
+    int keylen, valuelen;
+    const char *value, *qvalue;
+    char *c;
+
+    /* allocate a zero-length string if necessary */
+    if (buf == NULL) {
+       buf = xmpp_alloc(ctx, 1);
+       buf[0] = '\0';
+    }
+    if (buf == NULL) return NULL;
+
+    /* get current string length */
+    olen = strlen(buf);
+    value = hash_get(table, key);
+    if (value == NULL) {
+       xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
+       value = "";
+    }
+    if (quote) {
+       qvalue = _make_quoted(ctx, value);
+    } else {
+       qvalue = value;
+    }
+    /* added length is key + '=' + value */
+    /*   (+ ',' if we're not the first entry   */
+    keylen = strlen(key);
+    valuelen = strlen(qvalue);
+    nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
+    buf = xmpp_realloc(ctx, buf, olen+nlen);
+
+    if (buf != NULL) {
+       c = buf + olen;
+       if (olen) *c++ = ',';
+       memcpy(c, key, keylen); c += keylen;
+       *c++ = '=';
+       memcpy(c, qvalue, valuelen); c += valuelen;
+       *c++ = '\0';
+    }
+
+    if (quote) xmpp_free(ctx, (char *)qvalue);
+
+    return buf;
+}
+
+/** generate auth response string for the SASL DIGEST-MD5 mechanism */
+char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
+                       const char *jid, const char *password) {
+    hash_t *table;
+    char *result = NULL;
+    char *node, *domain, *realm;
+    char *value;
+    char *response;
+    int rlen;
+    struct MD5Context MD5;
+    unsigned char digest[16], HA1[16], HA2[16];
+    char hex[32];
+
+    /* our digest response is 
+       Hex( KD( HEX(MD5(A1)),
+         nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
+       ))
+
+       where KD(k, s) = MD5(k ':' s),
+       A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
+       A2 = "AUTHENTICATE" ':' "xmpp/" domain
+
+       If there is an authzid it is ':'-appended to A1 */
+
+    /* parse the challenge */
+    table = _parse_digest_challenge(ctx, challenge);
+    if (table == NULL) {
+       xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
+       return NULL;
+    }
+
+    node = xmpp_jid_node(ctx, jid);
+    domain = xmpp_jid_domain(ctx, jid);
+
+    /* generate default realm of domain if one didn't come from the
+       server */
+    realm = hash_get(table, "realm");
+    if (realm == NULL || strlen(realm) == 0) {
+       hash_add(table, "realm", xmpp_strdup(ctx, domain));
+       realm = hash_get(table, "realm");
+    }
+
+    /* add our response fields */
+    hash_add(table, "username", xmpp_strdup(ctx, node));
+    /* TODO: generate a random cnonce */
+    hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
+    hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
+    hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
+    value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
+    memcpy(value, "xmpp/", 5);
+    memcpy(value+5, domain, strlen(domain));
+    value[5+strlen(domain)] = '\0';
+    hash_add(table, "digest-uri", value);
+    
+    /* generate response */
+
+    /* construct MD5(node : realm : password) */
+    MD5Init(&MD5);
+    MD5Update(&MD5, (unsigned char *)node, strlen(node));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    MD5Update(&MD5, (unsigned char *)realm, strlen(realm));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    MD5Update(&MD5, (unsigned char *)password, strlen(password));
+    MD5Final(digest, &MD5);
+
+    /* digest now contains the first field of A1 */
+
+    MD5Init(&MD5);
+    MD5Update(&MD5, digest, 16);
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    value = hash_get(table, "nonce");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    value = hash_get(table, "cnonce");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    MD5Final(digest, &MD5);
+
+    /* now digest is MD5(A1) */
+    memcpy(HA1, digest, 16);
+
+    /* construct MD5(A2) */
+    MD5Init(&MD5);
+    MD5Update(&MD5, (unsigned char *)"AUTHENTICATE:", 13);
+    value = hash_get(table, "digest-uri");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    if (strcmp(hash_get(table, "qop"), "auth") != 0) {
+       MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
+                 33);
+    }
+    MD5Final(digest, &MD5);
+
+    memcpy(HA2, digest, 16);
+
+    /* construct response */
+    MD5Init(&MD5);
+    _digest_to_hex((char *)HA1, hex);
+    MD5Update(&MD5, (unsigned char *)hex, 32);
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    value = hash_get(table, "nonce");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    value = hash_get(table, "nc");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    value = hash_get(table, "cnonce");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    value = hash_get(table, "qop");
+    MD5Update(&MD5, (unsigned char *)value, strlen(value));
+    MD5Update(&MD5, (unsigned char *)":", 1);
+    _digest_to_hex((char *)HA2, hex);
+    MD5Update(&MD5, (unsigned char *)hex, 32);
+    MD5Final(digest, &MD5);
+
+    response = xmpp_alloc(ctx, 32+1);
+    _digest_to_hex((char *)digest, hex);
+    memcpy(response, hex, 32);
+    response[32] = '\0';
+    hash_add(table, "response", response);
+
+    /* construct reply */
+    result = NULL;
+    rlen = 0;
+    result = _add_key(ctx, table, "username", result, &rlen, 1); 
+    result = _add_key(ctx, table, "realm", result, &rlen, 1); 
+    result = _add_key(ctx, table, "nonce", result, &rlen, 1); 
+    result = _add_key(ctx, table, "cnonce", result, &rlen, 1); 
+    result = _add_key(ctx, table, "nc", result, &rlen, 0); 
+    result = _add_key(ctx, table, "qop", result, &rlen, 0); 
+    result = _add_key(ctx, table, "digest-uri", result, &rlen, 1); 
+    result = _add_key(ctx, table, "response", result, &rlen, 0); 
+    result = _add_key(ctx, table, "charset", result, &rlen, 0);
+    xmpp_free(ctx, node);
+    xmpp_free(ctx, domain);
+    hash_release(table); /* also frees value strings */
+
+    /* reuse response for the base64 encode of our result */
+    response = base64_encode(ctx, (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;
+}
+
+char *base64_encode(xmpp_ctx_t *ctx, 
+                   const unsigned char * const buffer, const unsigned len)
+{
+    int clen;
+    char *cbuf, *c;
+    uint32_t word, hextet;
+    int i;
+
+    clen = base64_encoded_len(ctx, len);
+    cbuf = xmpp_alloc(ctx, clen + 1);
+    if (cbuf != NULL) {
+       c = cbuf;
+       /* loop over data, turning every 3 bytes into 4 characters */
+       for (i = 0; i < len - 2; i += 3) {
+           word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
+           hextet = (word & 0x00FC0000) >> 18;
+           *c++ = _base64_charmap[hextet];
+           hextet = (word & 0x0003F000) >> 12;
+           *c++ = _base64_charmap[hextet];
+           hextet = (word & 0x00000FC0) >> 6;
+           *c++ = _base64_charmap[hextet];
+           hextet = (word & 0x000003F);
+           *c++ = _base64_charmap[hextet];
+       }
+       /* zero, one or two bytes left */
+       switch (len - i) {
+           case 0:
+               break;
+           case 1:
+               hextet = (buffer[len-1] & 0xFC) >> 2;
+               *c++ = _base64_charmap[hextet];
+               hextet = (buffer[len-1] & 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;
+}
+
+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;
+       } 
+    }
+    if (nudge < 0) return 0; /* reject bad coding */
+
+    /* decoded steam is 3 bytes for every four */ 
+    return 3 * (len >> 2) - nudge;
+}
+
+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;
+    }
+
+    return 0; 
+}
+
+int test_charmap_range(void)
+{
+    int i, v;
+
+    for (i = 64; i < 256; i++) {
+       v = _base64_invcharmap[i];
+       if (i < 64) return 1;
+    }
+
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    int ret = 0;
+
+    printf("testing charmap identity...");
+    ret = test_charmap_identity();
+    if (ret) return ret;
+    printf(" ok.\n");
+
+    printf("testing charmap range...");
+    ret = test_charmap_range();
+    if (ret) return ret;
+    printf(" ok.\n");
+
+    printf("no error\n");
+    return 0;
+}
+
+#endif /* TEST */
diff --git a/src/libstrophe/sasl.h b/src/libstrophe/sasl.h
new file mode 100644 (file)
index 0000000..09d5b8d
--- /dev/null
@@ -0,0 +1,44 @@
+/* sasl.h
+** strophe XMPP client library -- SASL authentication helpers
+** 
+** 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.
+*/
+
+/** @file
+ * SASL authentication helpers.
+ */
+
+#ifndef __LIBSTROPHE_SASL_H__
+#define __LIBSTROPHE_SASL_H__
+
+#include "strophe.h"
+
+/** low-level sasl routines */
+
+char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
+char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
+                     const char *jid, const char *password);
+
+
+/** Base64 encoding routines. Implemented according to RFC 3548 */
+
+int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len);
+
+char *base64_encode(xmpp_ctx_t *ctx, 
+                   const unsigned char * const buffer, const unsigned len);
+
+int base64_decoded_len(xmpp_ctx_t *ctx,
+                      const char * const buffer, const unsigned len);
+
+unsigned char *base64_decode(xmpp_ctx_t *ctx,
+                            const char * const buffer, const unsigned  len);
+
+#endif /* _LIBXMPP_SASL_H__ */
diff --git a/src/libstrophe/sha1.c b/src/libstrophe/sha1.c
new file mode 100644 (file)
index 0000000..ec4cb6c
--- /dev/null
@@ -0,0 +1,389 @@
+/** @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
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define SHA1HANDSOFF  */
+
+#include <stdio.h>
+#include <string.h>
+
+/* make sure the stdint.h types are available */
+#if defined(_MSC_VER) /* Microsoft Visual C++ */
+  typedef signed char             int8_t;
+  typedef short int               int16_t;
+  typedef int                     int32_t;
+  typedef __int64                 int64_t;
+  typedef unsigned char             uint8_t;
+  typedef unsigned short int        uint16_t;
+  typedef unsigned int              uint32_t;
+  /* no uint64_t */
+#else
+#include <stdint.h>
+#endif
+
+#include "sha1.h"
+
+void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+/* FIXME: can we do this in an endian-proof way? */
+#ifdef WORDS_BIGENDIAN
+#define blk0(i) block->l[i]
+#else
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+#ifdef VERBOSE  /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg){
+  printf("%s (%d,%d) %x %x %x %x %x\n",
+        msg,
+        context->count[0], context->count[1], 
+        context->state[0],
+        context->state[1],
+        context->state[2],
+        context->state[3],
+        context->state[4]);
+}
+#endif /* VERBOSE */
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
+{
+    uint32_t a, b, c, d, e;
+    typedef union {
+        uint8_t c[64];
+        uint32_t l[16];
+    } CHAR64LONG16;
+    CHAR64LONG16* block;
+
+#ifdef SHA1HANDSOFF
+    static uint8_t workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16*)buffer;
+#endif
+
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+void SHA1_Init(SHA1_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
+{
+    size_t i, j;
+
+#ifdef VERBOSE
+    SHAPrintContext(context, "before");
+#endif
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1_Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1_Transform(context->state, data + i);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+
+#ifdef VERBOSE
+    SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
+{
+    uint32_t i;
+    uint8_t  finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (uint8_t *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1_Update(context, (uint8_t *)"\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1_Transform() */
+    for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+        digest[i] = (uint8_t)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    
+    /* Wipe variables */
+    i = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(finalcount, 0, 8);  /* SWR */
+
+#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite its own static vars */
+    SHA1_Transform(context->state, context->buffer);
+#endif
+}
+  
+/*************************************************************/
+
+#if 0
+int main(int argc, char** argv)
+{
+int i, j;
+SHA1_CTX context;
+unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
+FILE* file;
+
+    if (argc > 2) {
+        puts("Public domain SHA-1 implementation - by Steve Reid <sreid@sea-to-sky.net>");
+        puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>");       /* JHB */
+        puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
+        return(0);
+    }
+    if (argc < 2) {
+        file = stdin;
+    }
+    else {
+        if (!(file = fopen(argv[1], "rb"))) {
+            fputs("Unable to open file.", stderr);
+            return(-1);
+        }
+    } 
+    SHA1_Init(&context);
+    while (!feof(file)) {  /* note: what if ferror(file) */
+        i = fread(buffer, 1, 16384, file);
+        SHA1_Update(&context, buffer, i);
+    }
+    SHA1_Final(&context, digest);
+    fclose(file);
+    for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
+        for (j = 0; j < 4; j++) {
+            printf("%02X", digest[i*4+j]);
+        }
+        putchar(' ');
+    }
+    putchar('\n');
+    return(0); /* JHB */
+}
+#endif
+
+/* self test */
+
+#ifdef TEST
+
+static char *test_data[] = {
+    "abc",
+    "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+    "A million repetitions of 'a'"};
+static char *test_results[] = {
+    "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
+    "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
+    "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"};
+    
+
+void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
+{
+    int i,j;
+    char *c = output;
+    
+    for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
+        for (j = 0; j < 4; j++) {
+            sprintf(c,"%02X", digest[i*4+j]);
+            c += 2;
+        }
+        sprintf(c, " ");
+        c += 1;
+    }
+    *(c - 1) = '\0';
+}
+    
+int main(int argc, char** argv)
+{
+    int k;
+    SHA1_CTX context;
+    uint8_t digest[20];
+    char output[80];
+
+    fprintf(stdout, "verifying SHA-1 implementation... ");
+    
+    for (k = 0; k < 2; k++){ 
+        SHA1_Init(&context);
+        SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k]));
+        SHA1_Final(&context, digest);
+       digest_to_hex(digest, output);
+
+        if (strcmp(output, test_results[k])) {
+            fprintf(stdout, "FAIL\n");
+            fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]);
+            fprintf(stderr,"\t%s returned\n", output);
+            fprintf(stderr,"\t%s is correct\n", test_results[k]);
+            return (1);
+        }    
+    }
+    /* million 'a' vector we feed separately */
+    SHA1_Init(&context);
+    for (k = 0; k < 1000000; k++)
+        SHA1_Update(&context, (uint8_t*)"a", 1);
+    SHA1_Final(&context, digest);
+    digest_to_hex(digest, output);
+    if (strcmp(output, test_results[2])) {
+        fprintf(stdout, "FAIL\n");
+        fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]);
+        fprintf(stderr,"\t%s returned\n", output);
+        fprintf(stderr,"\t%s is correct\n", test_results[2]);
+        return (1);
+    }
+
+    /* success */
+    fprintf(stdout, "ok\n");
+    return(0);
+}
+#endif /* TEST */
diff --git a/src/libstrophe/sha1.h b/src/libstrophe/sha1.h
new file mode 100644 (file)
index 0000000..b637f66
--- /dev/null
@@ -0,0 +1,31 @@
+/* public api for steve reid's public domain SHA-1 implementation */
+/* this file is in the public domain */
+
+/** @file
+ *  SHA-1 hash API.
+ */
+
+#ifndef __SHA1_H
+#define __SHA1_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    uint8_t  buffer[64];
+} SHA1_CTX;
+
+#define SHA1_DIGEST_SIZE 20
+
+void SHA1_Init(SHA1_CTX* context);
+void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
+void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SHA1_H */
diff --git a/src/libstrophe/snprintf.c b/src/libstrophe/snprintf.c
new file mode 100644 (file)
index 0000000..bd1cdc3
--- /dev/null
@@ -0,0 +1,839 @@
+/*
+ * 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.
+ */
+
+/* JAM: we don't need this - #include "config.h" */
+
+/* JAM: changed declarations to xmpp_snprintf and xmpp_vsnprintf to
+   avoid namespace collision. */
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+/* Define this as a fall through, HAVE_STDARG_H is probably already set */
+
+#define HAVE_VARARGS_H
+#define HAVE_STDARG_H /* JAM: set always */
+
+
+/* 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;
+