about summary refs log tree commit diff stats
path: root/src/xmpp/stanza.h
Commit message (Expand)AuthorAgeFilesLines
...
* Added /roster remove commandJames Booth2013-06-021-0/+2
* Add groups to roster contactsJames Booth2013-05-221-1/+2
* Added /roster command with nick option to change handleJames Booth2013-05-191-0/+3
* Added /duck command to perform searches using duckduckgo chat botJames Booth2013-05-051-1/+1
* Added /invite for direct MUC invitationsJames Booth2013-04-201-0/+3
* Show message in console when receiving chat room invitesJames Booth2013-03-241-0/+4
* Refactor show and status stanza handlingJames Booth2013-03-171-0/+3
* Implemented output for /disco infoJames Booth2013-03-141-0/+1
* Added /disco commandJames Booth2013-03-141-1/+1
* Added basic /rooms command to get chat room listJames Booth2013-03-131-0/+2
* Added autocomplete for /software commandJames Booth2013-02-171-0/+1
* Tidy up presence moduleJames Booth2013-02-101-0/+1
* Added resource_presence_t and contact_presence_tJames Booth2013-02-101-1/+1
* Renamed jabber_presence_t->presence_tJames Booth2013-02-101-1/+1
* Show detailed software/os information if available in capabilitiesJames Booth2013-02-051-0/+2
* Moved setting presence status and show into functionsJames Booth2013-02-041-8/+12
* Moved function to get presence string from type to stanza.cJames Booth2013-02-031-0/+2
* Send entity capabilities and status when joining roomJames Booth2013-02-031-1/+6
* Removed stanza functions from xmpp.hJames Booth2013-02-021-0/+152
' href='#n243'>243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
/* 
 * jabber.c
 *
 * Copyright (C) 2012 James Booth <boothj5@gmail.com>
 * 
 * This file is part of Profanity.
 *
 * Profanity 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.
 *
 * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <string.h>
#include <strophe.h>

#include "jabber.h"
#include "log.h"
#include "windows.h"

#define PING_INTERVAL 120000 // 2 minutes

// log reference
extern FILE *logp;

static struct _jabber_conn_t {
    xmpp_log_t *log;
    xmpp_ctx_t *ctx;
    xmpp_conn_t *conn;
    jabber_status_t conn_status;
    int tls_disabled;
} jabber_conn;

void xmpp_file_logger(void * const userdata, const xmpp_log_level_t level,
    const char * const area, const char * const msg);

static const xmpp_log_t file_log = { &xmpp_file_logger, XMPP_LEVEL_DEBUG };

xmpp_log_t *xmpp_get_file_logger()
{
    return (xmpp_log_t*) &file_log;
}

void xmpp_file_logger(void * const userdata, const xmpp_log_level_t level,
    const char * const area, const char * const msg)
{
    log_msg(area, msg);
}

// private XMPP handlers
static void _jabber_conn_handler(xmpp_conn_t * const conn, 
    const xmpp_conn_event_t status, const int error, 
    xmpp_stream_error_t * const stream_error, void * const userdata);

static int _jabber_message_handler(xmpp_conn_t * const conn, 
    xmpp_stanza_t * const stanza, void * const userdata);

static int _roster_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
    void * const userdata);

static int _jabber_presence_handler(xmpp_conn_t * const conn, 
    xmpp_stanza_t * const stanza, void * const userdata);

static int _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata);

void jabber_init(int disable_tls)
{
    jabber_conn.conn_status = JABBER_STARTED;
    jabber_conn.tls_disabled = disable_tls;
}

jabber_status_t jabber_connection_status(void)
{
    return (jabber_conn.conn_status);
}

jabber_status_t jabber_connect(char *user, char *passwd)
{
    xmpp_initialize();

    jabber_conn.log = xmpp_get_file_logger();
    jabber_conn.ctx = xmpp_ctx_new(NULL, jabber_conn.log);
    jabber_conn.conn = xmpp_conn_new(jabber_conn.ctx);

    xmpp_conn_set_jid(jabber_conn.conn, user);
    xmpp_conn_set_pass(jabber_conn.conn, passwd);

    if (jabber_conn.tls_disabled)
        xmpp_conn_disable_tls(jabber_conn.conn);

    int connect_status = xmpp_connect_client(jabber_conn.conn, NULL, 0, 
        _jabber_conn_handler, jabber_conn.ctx);

    if (connect_status == 0)
        jabber_conn.conn_status = JABBER_CONNECTING;
    else  
        jabber_conn.conn_status = JABBER_DISCONNECTED;

    return jabber_conn.conn_status;
}

void jabber_disconnect(void)
{
    if (jabber_conn.conn_status == JABBER_CONNECTED) {
        xmpp_conn_release(jabber_conn.conn);
        xmpp_ctx_free(jabber_conn.ctx);
        xmpp_shutdown();
        jabber_conn.conn_status = JABBER_DISCONNECTED;
    }
}

void jabber_process_events(void)
{
    if (jabber_conn.conn_status == JABBER_CONNECTED 
            || jabber_conn.conn_status == JABBER_CONNECTING)
        xmpp_run_once(jabber_conn.ctx, 10);
}

void jabber_send(char *msg, char *recipient)
{
    xmpp_stanza_t *reply, *body, *text;

    reply = xmpp_stanza_new(jabber_conn.ctx);
    xmpp_stanza_set_name(reply, "message");
    xmpp_stanza_set_type(reply, "chat");
    xmpp_stanza_set_attribute(reply, "to", recipient);

    body = xmpp_stanza_new(jabber_conn.ctx);
    xmpp_stanza_set_name(body, "body");

    text = xmpp_stanza_new(jabber_conn.ctx);
    xmpp_stanza_set_text(text, msg);
    xmpp_stanza_add_child(body, text);
    xmpp_stanza_add_child(reply, body);

    xmpp_send(jabber_conn.conn, reply);
    xmpp_stanza_release(reply);
}

void jabber_roster_request(void)
{
    xmpp_stanza_t *iq, *query;

    iq = xmpp_stanza_new(jabber_conn.ctx);
    xmpp_stanza_set_name(iq, "iq");
    xmpp_stanza_set_type(iq, "get");
    xmpp_stanza_set_id(iq, "roster");

    query = xmpp_stanza_new(jabber_conn.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(jabber_conn.conn, iq);
    xmpp_stanza_release(iq);
}

static int _jabber_message_handler(xmpp_conn_t * const conn, 
    xmpp_stanza_t * const stanza, void * const userdata)
{
    xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, "body");
    if(body == NULL)
        return 1;

    char *type = xmpp_stanza_get_attribute(stanza, "type");
    if(strcmp(type, "error") == 0)
        return 1;

    char *message = xmpp_stanza_get_text(body);
    char *from = xmpp_stanza_get_attribute(stanza, "from");
    win_show_incomming_msg(from, message);

    return 1;
}

static void _jabber_conn_handler(xmpp_conn_t * const conn, 
    const xmpp_conn_event_t status, const int error, 
    xmpp_stream_error_t * const stream_error, void * const userdata)
{
    xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;

    if (status == XMPP_CONN_CONNECT) {
        const char *jid = xmpp_conn_get_jid(conn);
        const char *msg = " logged in successfully.";
        char line[strlen(jid) + 1 + strlen(msg) + 1];
        sprintf(line, "%s %s", jid, msg);
        title_bar_connected();

        cons_good_show(line);
        status_bar_print_message(jid);
        status_bar_refresh();

        xmpp_stanza_t* pres;
        xmpp_handler_add(conn, _jabber_message_handler, NULL, "message", NULL, ctx);
        xmpp_handler_add(conn, _jabber_presence_handler, NULL, "presence", NULL, ctx);
        xmpp_id_handler_add(conn, _roster_handler, "roster", ctx);
        xmpp_timed_handler_add(conn, _ping_timed_handler, PING_INTERVAL, ctx);

        pres = xmpp_stanza_new(ctx);
        xmpp_stanza_set_name(pres, "presence");
        xmpp_send(conn, pres);
        xmpp_stanza_release(pres);
        jabber_conn.conn_status = JABBER_CONNECTED;
    }
    else {
        cons_bad_show("Login failed.");
        log_msg(CONN, "disconnected");
        xmpp_stop(ctx);
        jabber_conn.conn_status = JABBER_DISCONNECTED;
    }
}

static int _roster_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
    void * const userdata)
{
    xmpp_stanza_t *query, *item;
    char *type, *name, *jid;

    type = xmpp_stanza_get_type(stanza);
    
    if (strcmp(type, "error") == 0)
        log_msg(CONN, "ERROR: query failed");
    else {
        query = xmpp_stanza_get_child_by_name(stanza, "query");
        cons_show("Roster:");

        item = xmpp_stanza_get_children(query);
        while (item != NULL) {
            name = xmpp_stanza_get_attribute(item, "name");
            jid = xmpp_stanza_get_attribute(item, "jid");

            if (name != NULL) {
                char line[2 + strlen(name) + 2 + strlen(jid) + 1 + 1];
                sprintf(line, "  %s (%s)", name, jid);
                cons_show(line);

            } else {
                char line[2 + strlen(jid) + 1];
                sprintf(line, "  %s", jid);
                cons_show(line);
            }
        
            item = xmpp_stanza_get_next(item);
        }
    }
    
    return 1;
}

static int _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata)
{
    if (jabber_conn.conn_status == JABBER_CONNECTED) {
        xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
        
        xmpp_stanza_t *iq, *ping;

        iq = xmpp_stanza_new(ctx);
        xmpp_stanza_set_name(iq, "iq");
        xmpp_stanza_set_type(iq, "get");
        xmpp_stanza_set_id(iq, "c2s1");

        ping = xmpp_stanza_new(ctx);
        xmpp_stanza_set_name(ping, "ping");

        // FIXME add ping namespace to libstrophe
        xmpp_stanza_set_ns(ping, "urn:xmpp:ping");

        xmpp_stanza_add_child(iq, ping);
        xmpp_stanza_release(ping);
        xmpp_send(conn, iq);
        xmpp_stanza_release(iq);
    }

    return 1;
}

static int _jabber_presence_handler(xmpp_conn_t * const conn, 
    xmpp_stanza_t * const stanza, void * const userdata)
{
    char *from = xmpp_stanza_get_attribute(stanza, "from");
    char *short_from = strtok(from, "/");
    char *type = xmpp_stanza_get_attribute(stanza, "type");

    char *show_str, *status_str;
   
    xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, "show");
    if (show != NULL)
        show_str = xmpp_stanza_get_text(show);
    else
        show_str = NULL;

    xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, "status");
    if (status != NULL)    
        status_str = xmpp_stanza_get_text(status);
    else 
        status_str = NULL;

    if (type == NULL) { // online
        cons_show_contact_online(short_from, show_str, status_str);
        win_show_contact_online(short_from, show_str, status_str);
    } else { // offline
        cons_show_contact_offline(short_from, show_str, status_str);
        win_show_contact_offline(short_from, show_str, status_str);
    }

    return 1;
}