summaryrefslogtreecommitdiff
path: root/src/rexmpp.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/rexmpp.h')
-rw-r--r--src/rexmpp.h286
1 files changed, 286 insertions, 0 deletions
diff --git a/src/rexmpp.h b/src/rexmpp.h
new file mode 100644
index 0000000..662d540
--- /dev/null
+++ b/src/rexmpp.h
@@ -0,0 +1,286 @@
+/**
+ @file rexmpp.h
+ @brief rexmpp, a reusable XMPP IM client library.
+ @author defanor <defanor@uberspace.net>
+ @date 2020
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_H
+#define REXMPP_H
+
+#include <ares.h>
+#include <gnutls/gnutls.h>
+#include <gsasl.h>
+#include <libxml/tree.h>
+#include "rexmpp_tcp.h"
+
+
+typedef struct rexmpp rexmpp_t;
+typedef void (*rexmpp_iq_callback_t) (rexmpp_t *s, xmlNodePtr request, xmlNodePtr response);
+
+typedef struct rexmpp_iq rexmpp_iq_t;
+struct rexmpp_iq
+{
+ xmlNodePtr request;
+ rexmpp_iq_callback_t cb;
+ rexmpp_iq_t *next;
+};
+
+
+/** @brief DNS resolver state */
+enum resolver_st {
+ REXMPP_RESOLVER_NONE,
+ REXMPP_RESOLVER_READY,
+ REXMPP_RESOLVER_SRV,
+ REXMPP_RESOLVER_SRV_2,
+ REXMPP_RESOLVER_FAILURE
+};
+
+/** @brief TCP connection state */
+enum tcp_st {
+ REXMPP_TCP_NONE,
+ REXMPP_TCP_CONNECTING,
+ REXMPP_TCP_CONNECTED,
+ REXMPP_TCP_CLOSED,
+ REXMPP_TCP_CONNECTION_FAILURE,
+ REXMPP_TCP_ERROR
+};
+
+/** @brief XML stream state */
+enum stream_st {
+ REXMPP_STREAM_NONE,
+ REXMPP_STREAM_OPENING,
+ REXMPP_STREAM_NEGOTIATION,
+ REXMPP_STREAM_STARTTLS,
+ REXMPP_STREAM_SASL,
+ REXMPP_STREAM_BIND,
+ REXMPP_STREAM_SM_FULL,
+ REXMPP_STREAM_SM_ACKS,
+ REXMPP_STREAM_SM_RESUME,
+ REXMPP_STREAM_RESTART,
+ REXMPP_STREAM_READY,
+ REXMPP_STREAM_CLOSE_REQUESTED,
+ REXMPP_STREAM_CLOSING,
+ REXMPP_STREAM_CLOSED,
+ REXMPP_STREAM_ERROR
+};
+
+/** @brief TLS state */
+enum tls_st {
+ REXMPP_TLS_INACTIVE,
+ REXMPP_TLS_AWAITING_DIRECT,
+ REXMPP_TLS_HANDSHAKE,
+ REXMPP_TLS_ACTIVE,
+ REXMPP_TLS_CLOSING,
+ REXMPP_TLS_CLOSED,
+ REXMPP_TLS_ERROR
+};
+
+/** @brief SASL state */
+enum sasl_st {
+ REXMPP_SASL_INACTIVE,
+ REXMPP_SASL_NEGOTIATION,
+ REXMPP_SASL_ACTIVE,
+ REXMPP_SASL_ERROR
+};
+
+/** @brief Stream management state */
+enum sm_st {
+ REXMPP_SM_INACTIVE,
+ REXMPP_SM_NEGOTIATION,
+ REXMPP_SM_ACTIVE
+};
+
+/** @brief Carbons state */
+enum carbons_st {
+ REXMPP_CARBONS_INACTIVE,
+ REXMPP_CARBONS_NEGOTIATION,
+ REXMPP_CARBONS_ACTIVE,
+ REXMPP_CARBONS_DISABLED
+};
+
+/** Error codes. */
+enum rexmpp_err {
+ /** An operation is finished. */
+ REXMPP_SUCCESS,
+ /** An operation is in progress. */
+ REXMPP_E_AGAIN,
+ /** A message can't be queued for sending, because the queue is
+ full. */
+ REXMPP_E_SEND_QUEUE_FULL,
+ /** The library can't take responsibility for message delivery (and
+ doesn't try to send it), because XEP-0198 stanza queue is
+ full. */
+ REXMPP_E_STANZA_QUEUE_FULL,
+ /** An operation (reading or sending) was cancelled by a user. */
+ REXMPP_E_CANCELLED,
+ /** An attempt to send while send buffer is empty. */
+ REXMPP_E_SEND_BUFFER_EMPTY,
+ /** An attempt to start sending while send buffer is not empty. */
+ REXMPP_E_SEND_BUFFER_NOT_EMPTY,
+ /** SASL-related error. */
+ REXMPP_E_SASL,
+ /** TLS-related error. */
+ REXMPP_E_TLS,
+ /** TCP-related error. */
+ REXMPP_E_TCP,
+ /** DNS-related error. */
+ REXMPP_E_DNS,
+ /** XML-related error. */
+ REXMPP_E_XML,
+ /** Failure to allocate memory. */
+ REXMPP_E_MALLOC
+};
+typedef enum rexmpp_err rexmpp_err_t;
+
+typedef void (*log_function_t) (rexmpp_t *s, int priority, const char *format, va_list args);
+typedef int (*sasl_property_cb_t) (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
+typedef int (*xml_in_cb_t) (rexmpp_t *s, xmlNodePtr node);
+typedef int (*xml_out_cb_t) (rexmpp_t *s, xmlNodePtr node);
+
+/** @brief Complete connection state */
+struct rexmpp
+{
+ /* Numeric states: governing future actions, helping to recall where
+ we were at before returning from rexmpp_run, and communicating
+ the overall current state to a user. */
+ enum resolver_st resolver_state;
+ enum tcp_st tcp_state;
+ enum stream_st stream_state;
+ enum tls_st tls_state;
+ enum sasl_st sasl_state;
+ enum sm_st sm_state;
+ enum carbons_st carbons_state;
+
+ /* Basic configuration. */
+ const char *initial_jid;
+
+ /* Resource limits. */
+ uint32_t stanza_queue_size;
+ uint32_t send_queue_size;
+ uint32_t iq_queue_size;
+
+ /* Callbacks. */
+ log_function_t log_function;
+ sasl_property_cb_t sasl_property_cb;
+ xml_in_cb_t xml_in_cb;
+ xml_out_cb_t xml_out_cb;
+
+ /* Stream-related state. */
+ char *assigned_jid;
+ xmlNodePtr stream_features;
+
+ /* IQs we're waiting for responses to. */
+ rexmpp_iq_t *active_iq;
+
+ /* Connection and stream management. */
+ unsigned int id_counter;
+ unsigned int reconnect_number;
+ struct timeval next_reconnect_time;
+ xmlNodePtr stanza_queue;
+ uint32_t stanzas_out_count;
+ uint32_t stanzas_out_acknowledged;
+ uint32_t stanzas_in_count;
+ char *stream_id;
+
+ /* DNS-related structures. */
+ ares_channel resolver_channel;
+ struct ares_srv_reply *server_srv;
+ struct ares_srv_reply *server_srv_cur;
+ struct ares_srv_reply *server_srv_tls;
+ struct ares_srv_reply *server_srv_tls_cur;
+
+ /* The primary socket used for communication with the server. */
+ int server_socket;
+
+ /* A structure used to establish a TCP connection. */
+ rexmpp_tcp_conn_t server_connection;
+
+ /* Send buffer. NULL if there is nothing to send (and must not be
+ NULL if there is anything in the send queue). Not appending data
+ to it, see send_queue for queuing. */
+ char *send_buffer;
+ ssize_t send_buffer_len;
+ ssize_t send_buffer_sent;
+
+ /* A queue of XML elements to send. */
+ xmlNodePtr send_queue;
+
+ /* XML parser context, and current element pointer for building
+ XML nodes with a SAX2 parser interface. */
+ xmlParserCtxtPtr xml_parser;
+ xmlNodePtr current_element_root;
+ xmlNodePtr current_element;
+
+ /* TLS structures. */
+ void *tls_session_data;
+ size_t tls_session_data_size;
+ gnutls_session_t gnutls_session;
+ gnutls_certificate_credentials_t gnutls_cred;
+
+ /* SASL structures. */
+ Gsasl *sasl_ctx;
+ Gsasl_session *sasl_session;
+};
+
+/**
+ @brief ::rexmpp structure initialisation.
+ @param[out] s An allocated structure.
+ @param[in] jid Initial bare JID.
+ @param[in] log_function A user-provided logging function, can be
+ NULL.
+ @param[in] sasl_property_cb A callback to ask for SASL properties
+ (such as password).
+ @param[in] xml_in_cb A function to handle incoming XML elements. It
+ is called before other processing, so it can alter the elements, or
+ interrupt processing by returning a non-zero value. Optional.
+ @param[in] xml_out_cb Akin to the previous one, but for outbound
+ elements.
+ @returns ::REXMPP_SUCCESS or some ::rexmpp_err error.
+ */
+rexmpp_err_t rexmpp_init (rexmpp_t *s,
+ const char *jid,
+ log_function_t log_function,
+ sasl_property_cb_t sasl_property_cb,
+ xml_in_cb_t xml_in_cb,
+ xml_out_cb_t xml_out_cb);
+/**
+ @brief ::rexmpp structure deinitialisation. This will free all the
+ allocated resources.
+ @param[in,out] s A structure to deinitialise.
+*/
+void rexmpp_done (rexmpp_t *s);
+
+/**
+ @brief Runs a single iteration.
+ @param[in,out] s An initialised :rexmpp structure.
+ @param[in] File descriptors available for reading from.
+ @param[in] write_fds File descriptors available for writing to.
+
+ \callergraph
+*/
+rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
+
+/**
+ @brief Requests stream closing.
+*/
+rexmpp_err_t rexmpp_stop (rexmpp_t *s);
+
+/**
+ @brief Sends (or queues, or at least tries to, if everything goes
+ well) an XML element.
+ @param[in,out] s A ::rexmpp structure.
+ @param[in] node An XML element to send. The library assumes
+ ownership of the element, so it must not be freed by the caller.
+*/
+rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node);
+
+struct timeval *rexmpp_timeout (rexmpp_t *s, struct timeval *max_tv, struct timeval *tv);
+int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
+
+
+char *rexmpp_xml_serialize (xmlNodePtr node);
+xmlNodePtr rexmpp_xml_add_id (rexmpp_t *s, xmlNodePtr node);
+
+#endif