/** @file rexmpp.h @brief rexmpp, a reusable XMPP IM client library. @author defanor @date 2020 @copyright MIT license. */ #ifndef REXMPP_H #define REXMPP_H #include #include #include #include #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