#include #include #include #include #include #include #include "gui.h" #include "net.h" #include "data.h" // the state of net typedef enum { DISCONNECTED, // initial state CONNECTING, // connection was requested but is not yet established CONNECTED, // there is an active XMPP connetion to the server SUSPENDED // not connected, but there is an open XMPP stream to be // reconnected (see XEP-0198 for details) } net_state_t; // these variables stay initialized for the whole runtime static net_state_t state; static xmpp_ctx_t* ctx; // these variables stay initialized while the application is not disconnected static xmpp_conn_t* conn; static long flags; static int message_handler(xmpp_conn_t* conn, xmpp_stanza_t* stanza, void* userdata) { xmpp_stanza_t* body = xmpp_stanza_get_child_by_name(stanza, "body"); if (body == NULL) { fprintf(stderr, "DEBUG: Got message stanza of type char without body!\n"); return 1; } const char* content = xmpp_stanza_get_text(body); const char* sender = xmpp_stanza_get_from(stanza); data_add_incoming_message(sender, content); return 1; } static int roster_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { // iterate over roster result xmpp_stanza_t* query = xmpp_stanza_get_child_by_name(stanza, "query"); for (xmpp_stanza_t* item = xmpp_stanza_get_children(query); item; item = xmpp_stanza_get_next(item)) { data_add_roster_item(xmpp_stanza_get_attribute(item, "jid"), xmpp_stanza_get_attribute(item, "subscription"), xmpp_stanza_get_attribute(item, "name")); } return 1; } static void conn_handler(xmpp_conn_t* conn, xmpp_conn_event_t status,int error, xmpp_stream_error_t* stream_error, void* userdata) { // FIXME handle error and stream_error (this could be a major bug) // this implements at least debugging: if (error != 0) { fprintf(stderr, "conn_handler got error=%d\n", error); } if (stream_error != NULL) { fprintf(stderr, "conn_handler got stream_error of type %d\n", stream_error->type); } switch (status) { case XMPP_CONN_CONNECT: fprintf(stderr, "DEBUG: Got XMPP_CONN_CONNECT\n"); // add handler for stanzas xmpp_handler_add(conn, message_handler, NULL, "message", "chat", NULL); // add handler for roster response xmpp_handler_add(conn, roster_handler, "jabber:iq:roster", "iq", "result", NULL); // send initial presence xmpp_stanza_t* presence; presence = xmpp_presence_new(ctx); xmpp_send(conn, presence); xmpp_stanza_release(presence); // send roster request xmpp_stanza_t* iq = xmpp_iq_new(ctx, "get", "initial_roster"); xmpp_stanza_t* query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, "query"); xmpp_stanza_set_ns(query, XMPP_NS_ROSTER); xmpp_stanza_add_child(iq, query); xmpp_stanza_release(query); xmpp_send(conn, iq); xmpp_stanza_release(iq); break; case XMPP_CONN_RAW_CONNECT: fprintf(stderr, "DEBUG: Got XMPP_CONN_RAW_CONNECT\n"); break; case XMPP_CONN_DISCONNECT: fprintf(stderr, "DEBUG: Got XMPP_CONN_DISCONNECT\n"); break; case XMPP_CONN_FAIL: fprintf(stderr, "DEBUG: Got XMPP_CONN_FAIL\n"); break; default: fprintf(stderr, "DEBUG: Got unknown connection status '%d'!\n", status); exit(1); } } void net_init(void) { fprintf(stderr, "net_init()\n"); xmpp_initialize(); ctx = xmpp_ctx_new(NULL, xmpp_get_default_logger(XMPP_LEVEL_DEBUG)); state = DISCONNECTED; } static void delay(unsigned long int micros) { unsigned long int now = clock(); while ((clock() - now) < micros) {} } void net_run_once(void) { if (state == CONNECTING) { if (xmpp_connect_client(conn, NULL, 0, conn_handler, NULL) == XMPP_EOK) { state = CONNECTED; } else { net_disconnect(); } } if (state != DISCONNECTED) { xmpp_run_once(ctx, 200); } // TODO This delay is useless. But without it this function gets only // called once by GTK. This could be a GTK bug. delay(10000); } void net_quit(void) { fprintf(stderr, "net_quit()\n"); net_disconnect(); xmpp_shutdown(); } void net_connect(const char* jid, const char* password) { fprintf(stderr, "net_connect()\n"); conn = xmpp_conn_new(ctx); flags = 0; flags |= XMPP_CONN_FLAG_MANDATORY_TLS; xmpp_conn_set_flags(conn, flags); xmpp_conn_set_jid(conn, jid); xmpp_conn_set_pass(conn, password); state = CONNECTING; } void net_disconnect(void) { fprintf(stderr, "net_disconnect()\n"); if (conn != NULL && xmpp_conn_is_connected(conn)) { xmpp_disconnect(conn); while (xmpp_conn_is_connected(conn)) { xmpp_run_once(ctx, 200); // TODO avoid with additional // state "disconnecting" } if (!xmpp_conn_release(conn)) { fprintf(stderr, "DEBUG: Could not free connection!\n"); } } flags = 0; state = DISCONNECTED; } void net_send_message(const char* sender, const char* content, const char* recipient) { if (xmpp_conn_is_connected(conn)) { char* uuid = xmpp_uuid_gen(ctx); xmpp_stanza_t* msg = xmpp_message_new(ctx, "chat", recipient, uuid); xmpp_stanza_set_from(msg, sender); xmpp_message_set_body(msg, content); xmpp_send(conn, msg); } else { fprintf(stderr, "net_send_message() called while not connected\n"); } }