From 26d160cae861865191150c73853a748fcd9cfad1 Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 29 Sep 2015 00:01:38 +0100 Subject: WIP: Added last activity request and response --- src/command/command.c | 17 ++++++ src/command/commands.c | 25 ++++++++ src/command/commands.h | 1 + src/event/server_events.c | 25 ++++++++ src/event/server_events.h | 1 + src/jid.c | 1 - src/xmpp/iq.c | 119 ++++++++++++++++++++++++++++++++++++++- src/xmpp/stanza.c | 19 +++++++ src/xmpp/stanza.h | 3 + src/xmpp/xmpp.h | 1 + tests/unittests/xmpp/stub_xmpp.c | 1 + 11 files changed, 211 insertions(+), 2 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 186155b0..603b0f12 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -695,6 +695,23 @@ static struct cmd_t command_defs[] = "/disco info myfriend@server.com/laptop") }, + { "/lastactivity", + cmd_lastactivity, parse_args, 0, 1, NULL, + CMD_TAGS( + CMD_TAG_PRESENCE) + CMD_SYN( + "/lastactivity []") + CMD_DESC( + "Send a last activity query to the supplied JID, omitting the JID will send the query to your server.") + CMD_ARGS( + { "", "The JID of the entity to which the query will be sent." }) + CMD_EXAMPLES( + "/lastactivity", + "/lastactivity alice@securechat.org", + "/lastactivity alice@securechat.org/laptop", + "/lastactivity someserver.com") + }, + { "/nick", cmd_nick, parse_args_with_freetext, 1, 1, NULL, CMD_TAGS( diff --git a/src/command/commands.c b/src/command/commands.c index e6d1e277..6b301057 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -3261,6 +3261,31 @@ cmd_disco(ProfWin *window, const char * const command, gchar **args) return TRUE; } +gboolean +cmd_lastactivity(ProfWin *window, const char * const command, gchar **args) +{ + jabber_conn_status_t conn_status = jabber_get_connection_status(); + + if (conn_status != JABBER_CONNECTED) { + cons_show("You are not currenlty connected."); + return TRUE; + } + + if (args[0] == NULL) { + Jid *jidp = jid_create(jabber_get_fulljid()); + GString *jid = g_string_new(jidp->domainpart); + + iq_last_activity_request(jid->str); + + g_string_free(jid, TRUE); + jid_destroy(jidp); + } else { + iq_last_activity_request(args[0]); + } + + return TRUE; +} + gboolean cmd_nick(ProfWin *window, const char * const command, gchar **args) { diff --git a/src/command/commands.h b/src/command/commands.h index e456e291..b3f83a70 100644 --- a/src/command/commands.h +++ b/src/command/commands.h @@ -85,6 +85,7 @@ gboolean cmd_connect(ProfWin *window, const char * const command, gchar **args); gboolean cmd_tls(ProfWin *window, const char * const command, gchar **args); gboolean cmd_decline(ProfWin *window, const char * const command, gchar **args); gboolean cmd_disco(ProfWin *window, const char * const command, gchar **args); +gboolean cmd_lastactivity(ProfWin *window, const char * const command, gchar **args); gboolean cmd_disconnect(ProfWin *window, const char * const command, gchar **args); gboolean cmd_dnd(ProfWin *window, const char * const command, gchar **args); gboolean cmd_flash(ProfWin *window, const char * const command, gchar **args); diff --git a/src/event/server_events.c b/src/event/server_events.c index 4eb4f785..7b24f6b9 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -730,3 +730,28 @@ sv_ev_certfail(const char * const errormsg, const char * const certname, const c return 0; } } + +void +sv_ev_lastactivity_response(const char * const from, const int seconds, const char * const msg) +{ + Jid *jidp = jid_create(from); + + if (!jidp) { + return; + } + + // full jid or bare jid + if (jidp->resourcepart || jidp->localpart) { + if (msg) { + cons_show("%s last active %d, status: %s", from, seconds, msg); + } else { + cons_show("%s last active %d", from, seconds); + } + + // domain only + } else { + cons_show("%s uptime %d seconds", from, seconds); + } + + jid_destroy(jidp); +} diff --git a/src/event/server_events.h b/src/event/server_events.h index 2aa90754..3770dd94 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -88,5 +88,6 @@ void sv_ev_roster_update(const char * const barejid, const char * const name, void sv_ev_roster_received(void); int sv_ev_certfail(const char * const errormsg, const char * const certname, const char * const certfp, const char * const notbefore, const char * const notafter); +void sv_ev_lastactivity_response(const char * const from, const int seconds, const char * const msg); #endif diff --git a/src/jid.c b/src/jid.c index 4eb05e87..8b40d16a 100644 --- a/src/jid.c +++ b/src/jid.c @@ -79,7 +79,6 @@ jid_create(const gchar * const str) gchar *slashp = g_utf8_strchr(trimmed, -1, '/'); gchar *domain_start = trimmed; - if (atp) { result->localpart = g_utf8_substring(trimmed, 0, g_utf8_pointer_to_offset(trimmed, atp)); domain_start = atp + 1; diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index f1f7d212..29aa0944 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -40,7 +40,7 @@ #include #include - +#include #include #ifdef HAVE_LIBMESODE @@ -76,6 +76,8 @@ static int _version_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const static int _version_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _disco_info_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); +static int _last_activity_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); +static int _last_activity_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _room_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _disco_items_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _disco_items_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); @@ -108,6 +110,8 @@ iq_add_handlers(void) HANDLE(XMPP_NS_DISCO_ITEMS, STANZA_TYPE_GET, _disco_items_get_handler); HANDLE(XMPP_NS_DISCO_ITEMS, STANZA_TYPE_RESULT, _disco_items_result_handler); + HANDLE("jabber:iq:last", STANZA_TYPE_GET, _last_activity_get_handler); + HANDLE(STANZA_NS_VERSION, STANZA_TYPE_GET, _version_get_handler); HANDLE(STANZA_NS_PING, STANZA_TYPE_GET, _ping_get_handler); @@ -189,6 +193,22 @@ iq_disco_info_request(gchar *jid) xmpp_stanza_release(iq); } +void +iq_last_activity_request(gchar *jid) +{ + xmpp_conn_t * const conn = connection_get_conn(); + xmpp_ctx_t * const ctx = connection_get_ctx(); + char *id = create_unique_id("lastactivity"); + xmpp_stanza_t *iq = stanza_create_last_activity_iq(ctx, id, jid); + + xmpp_id_handler_add(conn, _last_activity_response_handler, id, NULL); + + free(id); + + xmpp_send(conn, iq); + xmpp_stanza_release(iq); +} + void iq_room_info_request(const char * const room, gboolean display_result) { @@ -209,6 +229,8 @@ iq_room_info_request(const char * const room, gboolean display_result) xmpp_stanza_release(iq); } + + void iq_send_caps_request_for_jid(const char * const to, const char * const id, const char * const node, const char * const ver) @@ -1065,6 +1087,46 @@ _disco_items_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, return 1; } +static int +_last_activity_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + + /* + + + + */ + + if (from) { + int idls_secs = ui_get_idle_time() / 1000; + char str[50]; + sprintf(str, "%d", idls_secs); + + xmpp_stanza_t *response = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(response, STANZA_NAME_IQ); + xmpp_stanza_set_id(response, xmpp_stanza_get_id(stanza)); + xmpp_stanza_set_attribute(response, STANZA_ATTR_TO, from); + xmpp_stanza_set_type(response, STANZA_TYPE_RESULT); + + xmpp_stanza_t *query = xmpp_stanza_new(ctx); + xmpp_stanza_set_attribute(query, STANZA_ATTR_XMLNS, "jabber:iq:last"); + xmpp_stanza_set_attribute(query, "seconds", str); + + xmpp_stanza_add_child(response, query); + xmpp_send(conn, response); + + xmpp_stanza_release(query); + xmpp_stanza_release(response); + } + + return 1; +} static int _disco_info_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, @@ -1489,6 +1551,61 @@ _room_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stan return 0; } +static int +_last_activity_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + if (!from) { + cons_show_error("Invalid last activity response received."); + log_info("Received last activity response with no from attribute."); + return 0; + } + + const char *type = xmpp_stanza_get_type(stanza); + + // handle error responses + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + char *error_message = stanza_get_error_message(stanza); + if (from) { + cons_show_error("Last activity request failed for %s: %s", from, error_message); + } else { + cons_show_error("Last activity request failed: %s", error_message); + } + free(error_message); + return 0; + } + + xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + + if (!query) { + cons_show_error("Invalid last activity response received."); + log_info("Received last activity response with no query element."); + return 0; + } + + char *seconds_str = xmpp_stanza_get_attribute(query, "seconds"); + if (!seconds_str) { + cons_show_error("Invalid last activity response received."); + log_info("Received last activity response with no seconds attribute."); + return 0; + } + + int seconds = atoi(seconds_str); + if (seconds < 0) { + cons_show_error("Invalid last activity response received."); + log_info("Received last activity response with negative value."); + return 0; + } + + char *msg = xmpp_stanza_get_text(query); + + sv_ev_lastactivity_response(from, seconds, msg); + + xmpp_free(connection_get_ctx(), msg); + return 0; +} + static int _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 68ace83c..7bd648f7 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -965,6 +965,25 @@ stanza_create_disco_items_iq(xmpp_ctx_t *ctx, const char * const id, return iq; } +xmpp_stanza_t * +stanza_create_last_activity_iq(xmpp_ctx_t *ctx, const char * const id, const char * const to) +{ + xmpp_stanza_t *iq = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(iq, STANZA_NAME_IQ); + xmpp_stanza_set_type(iq, STANZA_TYPE_GET); + xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, to); + xmpp_stanza_set_id(iq, id); + + xmpp_stanza_t *query = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(query, STANZA_NAME_QUERY); + xmpp_stanza_set_ns(query, "jabber:iq:last"); + + xmpp_stanza_add_child(iq, query); + xmpp_stanza_release(query); + + return iq; +} + xmpp_stanza_t * stanza_create_room_config_submit_iq(xmpp_ctx_t *ctx, const char * const room, DataForm *form) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index dbb09ab3..80a4fd93 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -227,6 +227,9 @@ xmpp_stanza_t* stanza_create_ping_iq(xmpp_ctx_t *ctx, const char * const target) xmpp_stanza_t* stanza_create_disco_info_iq(xmpp_ctx_t *ctx, const char * const id, const char * const to, const char * const node); +xmpp_stanza_t* stanza_create_last_activity_iq(xmpp_ctx_t *ctx, const char * const id, + const char * const to); + xmpp_stanza_t* stanza_create_invite(xmpp_ctx_t *ctx, const char * const room, const char * const contact, const char * const reason, const char * const password); xmpp_stanza_t* stanza_create_mediated_invite(xmpp_ctx_t *ctx, const char * const room, diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 5c5fa3ef..2a8cbb59 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -186,6 +186,7 @@ void iq_send_software_version(const char * const fulljid); void iq_room_list_request(gchar *conferencejid); void iq_disco_info_request(gchar *jid); void iq_disco_items_request(gchar *jid); +void iq_last_activity_request(gchar *jid); void iq_set_autoping(int seconds); void iq_confirm_instant_room(const char * const room_jid); void iq_destroy_room(const char * const room_jid); diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index 79f2595a..34c52a63 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -165,6 +165,7 @@ void iq_room_kick_occupant(const char * const room, const char * const nick, con void iq_room_role_set(const char * const room, const char * const nick, char *role, const char * const reason) {} void iq_room_role_list(const char * const room, char *role) {} +void iq_last_activity_request(gchar *jid) {} // caps functions Capabilities* caps_lookup(const char * const jid) -- cgit 1.4.1-2-gfad0