summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--examples/weechat.c33
-rw-r--r--src/rexmpp.c62
-rw-r--r--src/rexmpp.h2
4 files changed, 98 insertions, 1 deletions
diff --git a/README b/README
index 3a43f2a..8daedda 100644
--- a/README
+++ b/README
@@ -71,7 +71,7 @@ A rough roadmap:
- Additional state tracking:
-[ ] XMPP IM (RFC 6121): track presences of contacts.
+[+] XMPP IM (RFC 6121): track presences of contacts.
[ ] XEP-0030: Service Discovery: track features provided by known
entities.
[ ] XEP-0115: Entity Capabilities: maintain a capability database,
diff --git a/examples/weechat.c b/examples/weechat.c
index 0c26f6e..ce2ffef 100644
--- a/examples/weechat.c
+++ b/examples/weechat.c
@@ -119,6 +119,7 @@ int muc_close_cb (struct weechat_rexmpp_muc *wrm, void *data,
}
int my_xml_in_cb (struct weechat_rexmpp *wr, xmlNodePtr node) {
+ rexmpp_t *s = &wr->rexmpp_state;
char *xml_buf = rexmpp_xml_serialize(node);
weechat_printf(wr->server_buffer, "recv: %s\n", xml_buf);
/* free(xml_buf); */
@@ -161,6 +162,7 @@ int my_xml_in_cb (struct weechat_rexmpp *wr, xmlNodePtr node) {
if (rexmpp_xml_match(node, "jabber:client", "presence")) {
char *presence_type = xmlGetProp(node, "type");
char *jid = xmlGetProp(node, "from");
+ char *full_jid = strdup(jid);
int i;
char *resource = "";
for (i = 0; i < strlen(jid); i++) {
@@ -185,8 +187,39 @@ int my_xml_in_cb (struct weechat_rexmpp *wr, xmlNodePtr node) {
"bar_fg", "", "lightgreen", 1);
}
}
+ } else if (rexmpp_roster_find_item(s, jid, NULL) != NULL) {
+ /* A roster item. */
+ struct t_gui_nick *nick = weechat_nicklist_search_nick(wr->server_buffer, NULL, jid);
+ if (presence_type == NULL) {
+ /* An "available" presence: just ensure that it's shown as
+ online. */
+ weechat_nicklist_nick_set(wr->server_buffer, nick, "prefix", "+");
+ } else if (strcmp(presence_type, "unavailable") == 0) {
+ /* An "unavailable" presence: set it to "offline" if there's
+ no remaining online resources (i.e., if we can find an
+ online resource for this bare JID other than the one that
+ just went offline). */
+ xmlNodePtr cur;
+ int found = 0;
+ for (cur = s->roster_presence;
+ cur != NULL;
+ cur = xmlNextElementSibling(cur)) {
+ char *cur_from = xmlGetProp(cur, "from");
+ if (strcmp(cur_from, full_jid) != 0 &&
+ strncmp(cur_from, jid, strlen(jid)) == 0 &&
+ strlen(cur_from) > strlen(jid) &&
+ cur_from[strlen(jid)] == '/') {
+ found = 1;
+ }
+ free(cur_from);
+ }
+ if (! found) {
+ weechat_nicklist_nick_set(wr->server_buffer, nick, "prefix", "");
+ }
+ }
}
free(jid);
+ free(full_jid);
if (presence_type != NULL) {
free(presence_type);
}
diff --git a/src/rexmpp.c b/src/rexmpp.c
index c4c1e1c..deee805 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -238,6 +238,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
s->enable_service_discovery = 1;
s->manage_roster = 1;
s->roster_cache_file = NULL;
+ s->track_roster_presence = 1;
s->send_buffer = NULL;
s->send_queue = NULL;
s->server_srv = NULL;
@@ -250,6 +251,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid)
s->stream_features = NULL;
s->roster_items = NULL;
s->roster_ver = NULL;
+ s->roster_presence = NULL;
s->stanza_queue = NULL;
s->stream_id = NULL;
s->active_iq = NULL;
@@ -419,6 +421,10 @@ void rexmpp_done (rexmpp_t *s) {
xmlFreeNodeList(s->roster_items);
s->roster_items = NULL;
}
+ if (s->roster_presence != NULL) {
+ xmlFreeNodeList(s->roster_presence);
+ s->roster_presence = NULL;
+ }
if (s->roster_ver != NULL) {
free(s->roster_ver);
s->roster_ver = NULL;
@@ -1493,6 +1499,62 @@ void rexmpp_process_element (rexmpp_t *s) {
free(type);
}
+ /* Incoming presence information. */
+ if (rexmpp_xml_match(elem, "jabber:client", "presence") &&
+ s->manage_roster &&
+ s->track_roster_presence) {
+ char *from = xmlGetProp(elem, "from");
+ if (from != NULL) {
+ size_t i;
+ int resource_removed = 0;
+ for (i = 0; i < strlen(from); i++) {
+ if (from[i] == '/') {
+ from[i] = '\0';
+ resource_removed = i;
+ break;
+ }
+ }
+ if (rexmpp_roster_find_item(s, from, NULL) != NULL) {
+ /* The bare JID is in the roster. */
+ if (resource_removed) {
+ /* Restore full JID. */
+ from[resource_removed] = '/';
+ }
+ char *type = xmlGetProp(elem, "type");
+ xmlNodePtr cur, prev;
+ if (type == NULL || strcmp(type, "unavailable") == 0) {
+ /* Either a new "available" presence or an "unavailable"
+ one: remove the previously stored presence for this
+ JID. */
+ for (prev = NULL, cur = s->roster_presence;
+ cur != NULL;
+ prev = cur, cur = xmlNextElementSibling(cur)) {
+ char *cur_from = xmlGetProp(cur, "from");
+ if (strcmp(cur_from, from) == 0) {
+ if (prev == NULL) {
+ s->roster_presence = cur->next;
+ } else {
+ prev->next = cur->next;
+ }
+ xmlFreeNode(cur);
+ cur = NULL;
+ }
+ free(cur_from);
+ }
+ }
+ if (type == NULL) {
+ /* An "available" presence: add it. */
+ xmlNodePtr presence = xmlCopyNode(elem, 1);
+ presence->next = s->roster_presence;
+ s->roster_presence = presence;
+ } else {
+ free(type);
+ }
+ }
+ free(from);
+ }
+ }
+
/* Stream negotiation,
https://tools.ietf.org/html/rfc6120#section-4.3 */
if (s->stream_state == REXMPP_STREAM_NEGOTIATION &&
diff --git a/src/rexmpp.h b/src/rexmpp.h
index 6157f86..7e2376f 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -238,6 +238,7 @@ struct rexmpp
int enable_service_discovery;
int manage_roster;
const char *roster_cache_file;
+ int track_roster_presence;
/* Resource limits. */
uint32_t stanza_queue_size;
@@ -256,6 +257,7 @@ struct rexmpp
xmlNodePtr stream_features;
xmlNodePtr roster_items;
char *roster_ver;
+ xmlNodePtr roster_presence;
/* Other dynamic data. */
xmlNodePtr disco_info;