/* mesode.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 dual licensed under the MIT and GPLv3 licenses.
*/

/** @file
 *  Strophe public C API definitions.
 */

#ifndef __LIBMESODE_MESODE_H__
#define __LIBMESODE_MESODE_H__

#include <stddef.h>     /* size_t */

#ifdef __cplusplus
extern "C" {
#endif

/* 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 xmpp_initialize(void);
void xmpp_shutdown(void);

/* version */
int xmpp_version_check(int major, int minor);

/* run-time contexts */

/* user-replaceable memory allocator */
typedef struct _xmpp_mem_t xmpp_mem_t;

/* user-replaceable log object */
typedef struct _xmpp_log_t xmpp_log_t;

/* opaque run time context containing the above hooks */
typedef struct _xmpp_ctx_t xmpp_ctx_t;

typedef struct _tlscert_t xmpp_tlscert_t;

xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
			 const xmpp_log_t * const log);
void xmpp_ctx_free(xmpp_ctx_t * const ctx);

/* free some blocks returned by other APIs, for example the
   buffer you get from xmpp_stanza_to_text */
void xmpp_free(const xmpp_ctx_t * const ctx, void *p);

struct _xmpp_mem_t {
    void *(*alloc)(const size_t size, void * const userdata);
    void (*free)(void *p, void * const userdata);
    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;
};

/* return a default logger filtering at a given level */
xmpp_log_t *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;

/* connection flags */
#define XMPP_CONN_FLAG_DISABLE_TLS   (1UL << 0)
#define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1)
#define XMPP_CONN_FLAG_LEGACY_SSL    (1UL << 2)
/** @def XMPP_CONN_FLAG_TRUST_TLS
 *  Trust server's certificate even if it is invalid.
 */
#define XMPP_CONN_FLAG_TRUST_TLS     (1UL << 3)

/* connect callback */
typedef enum {
    XMPP_CONN_CONNECT,
    XMPP_CONN_RAW_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);

typedef int (*xmpp_certfail_handler)(xmpp_tlscert_t *cert, const char * const errormsg);
void xmpp_send_error(xmpp_conn_t * const conn, xmpp_error_type_t const type, char * const text);
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx);
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t * const conn);
int xmpp_conn_release(xmpp_conn_t * const conn);

long xmpp_conn_get_flags(const xmpp_conn_t * const conn);
int xmpp_conn_set_flags(xmpp_conn_t * const conn, long flags);
const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn);
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t * const conn);
void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid);
const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn);
void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass);
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t * const conn);
void xmpp_conn_disable_tls(xmpp_conn_t * const conn);
void xmpp_conn_tlscert_path(xmpp_conn_t * const conn, char *path);
int xmpp_conn_is_secured(xmpp_conn_t * const conn);
void xmpp_conn_set_keepalive(xmpp_conn_t * const conn, int timeout, int interval);

xmpp_tlscert_t *xmpp_conn_tls_peer_cert(xmpp_conn_t * const conn);
int xmpp_conn_tlscert_version(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_serialnumber(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_subjectname(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_issuername(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_fingerprint(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_notbefore(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_notafter(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_signature_algorithm(xmpp_tlscert_t *cert);
char *xmpp_conn_tlscert_key_algorithm(xmpp_tlscert_t *cert);
void xmpp_conn_free_tlscert(xmpp_ctx_t *ctx, xmpp_tlscert_t *cert);

int xmpp_connect_client(xmpp_conn_t * const conn,
			  const char * const altdomain,
			  unsigned short altport,
              xmpp_certfail_handler certfail_cb,
			  xmpp_conn_handler callback,
			  void * const userdata);

int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server,
                           unsigned short port, xmpp_conn_handler callback,
                           void * const userdata);

int xmpp_connect_raw(xmpp_conn_t * const conn,
                     const char * const altdomain,
                     unsigned short altport,
                     xmpp_certfail_handler certfail_cb,
                     xmpp_conn_handler callback,
                     void * const userdata);
int xmpp_conn_open_stream_default(xmpp_conn_t * const conn);
int xmpp_conn_open_stream(xmpp_conn_t * const conn, char **attributes,
                          size_t attributes_len);
int xmpp_conn_tls_start(xmpp_conn_t * const conn);

void xmpp_disconnect(xmpp_conn_t * const conn);

void xmpp_send(xmpp_conn_t * const conn,
	       xmpp_stanza_t * const stanza);

void xmpp_send_raw_string(xmpp_conn_t * const conn,
			  const char * const fmt, ...);
void xmpp_send_raw(xmpp_conn_t * const conn,
		   const char * const data, const size_t len);


/* handlers */

/* if the handle returns false it is removed */
typedef int (*xmpp_timed_handler)(xmpp_conn_t * const conn,
				  void * const userdata);

void xmpp_timed_handler_add(xmpp_conn_t * const conn,
			    xmpp_timed_handler handler,
			    const unsigned long period,
			    void * const userdata);
void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
			       xmpp_timed_handler handler);


/* if the handler returns false it is removed */
typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
			     xmpp_stanza_t * const stanza,
			     void * const userdata);

void xmpp_handler_add(xmpp_conn_t * const conn,
		      xmpp_handler handler,
		      const char * const ns,
		      const char * const name,
		      const char * const type,
		      void * const userdata);
void xmpp_handler_delete(xmpp_conn_t * const conn,
			 xmpp_handler handler);

void xmpp_id_handler_add(xmpp_conn_t * const conn,
			 xmpp_handler handler,
			 const char * const id,
			 void * const userdata);
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
			    xmpp_handler handler,
			    const char * const id);

/*
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
*/

/* stanzas */

/* allocate and initialize a blank stanza */
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx);

/* clone a stanza */
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza);

/* copies a stanza and all children */
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza);

/* free a stanza object and it's contents */
int xmpp_stanza_release(xmpp_stanza_t * const stanza);

int xmpp_stanza_is_text(xmpp_stanza_t * const stanza);
int xmpp_stanza_is_tag(xmpp_stanza_t * const stanza);

/* marshall a stanza into text for transmission or display */
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
			char ** const buf, size_t * const buflen);

xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t * const stanza);
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza,
					     const char * const name);
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t * const stanza,
					   const char * const ns);
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t * const stanza);
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);

const char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
				      const char * const name);
int xmpp_stanza_get_attribute_count(xmpp_stanza_t * const stanza);
int xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza,
			       const char **attr, int attrlen);
/* concatenate all child text nodes.  this function
 * returns a string that must be freed by the caller */
char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza);
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza);
const char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
/* set_attribute adds/replaces attributes */
int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
			      const char * const key,
			      const char * const value);
int xmpp_stanza_set_name(xmpp_stanza_t *stanza,
			 const char * const name);
int xmpp_stanza_set_text(xmpp_stanza_t *stanza,
			 const char * const text);
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
				   const char * const text,
				   const size_t size);
int xmpp_stanza_del_attribute(xmpp_stanza_t * const stanza,
                              const char * const name);

/* common stanza helpers */
const char *xmpp_stanza_get_ns(xmpp_stanza_t * const stanza);
const char *xmpp_stanza_get_type(xmpp_stanza_t * const stanza);
const char *xmpp_stanza_get_id(xmpp_stanza_t * const stanza);
const char *xmpp_stanza_get_to(xmpp_stanza_t * const stanza);
const char *xmpp_stanza_get_from(xmpp_stanza_t * const stanza);
int xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
int xmpp_stanza_set_id(xmpp_stanza_t * const stanza, const char * const id);
int xmpp_stanza_set_type(xmpp_stanza_t * const stanza, const char * const type);
int xmpp_stanza_set_to(xmpp_stanza_t * const stanza, const char * const to);
int xmpp_stanza_set_from(xmpp_stanza_t * const stanza, const char * const from);

/* allocate and initialize a stanza in reply to another */
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t * const stanza);

/* stanza subclasses */
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx, const char * const type,
                                const char * const to, const char * const id);
char *xmpp_message_get_body(xmpp_stanza_t *msg);
int xmpp_message_set_body(xmpp_stanza_t *msg, const char * const text);

xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char * const type,
                           const char * const id);
xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx);
xmpp_stanza_t *xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t const type,
                              const char * const text);

/* 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);

/* event loop */

void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long  timeout);
void xmpp_run(xmpp_ctx_t *ctx);
void xmpp_stop(xmpp_ctx_t *ctx);
void xmpp_ctx_set_timeout(xmpp_ctx_t * const ctx, const unsigned long timeout);

/* UUID */

char *xmpp_uuid_gen(xmpp_ctx_t *ctx);

/* SHA1 */

/** @def XMPP_SHA1_DIGEST_SIZE
 *  Size of the SHA1 message digest.
 */
#define XMPP_SHA1_DIGEST_SIZE 20

typedef struct _xmpp_sha1_t xmpp_sha1_t;

char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
void xmpp_sha1_digest(const unsigned char *data, size_t len,
                      unsigned char *digest);

xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx);
void xmpp_sha1_free(xmpp_sha1_t *sha1);
void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len);
void xmpp_sha1_final(xmpp_sha1_t *sha1);
char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen);
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1);
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);

/* Base64 */

char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len);
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx, const char *base64, size_t len,
                            unsigned char **out, size_t *outlen);

#ifdef __cplusplus
}
#endif

#endif /* __LIBMESODE_MESODE_H__ */
