diff options
Diffstat (limited to 'src/rexmpp_roster.c')
-rw-r--r-- | src/rexmpp_roster.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/rexmpp_roster.c b/src/rexmpp_roster.c new file mode 100644 index 0000000..8479c14 --- /dev/null +++ b/src/rexmpp_roster.c @@ -0,0 +1,146 @@ +/** + @file rexmpp_roster.c + @brief Roster-related functions. + @author defanor <defanor@uberspace.net> + @date 2020 + @copyright MIT license. +*/ + +#include "rexmpp.h" +#include <syslog.h> +#include <string.h> +#include <libxml/tree.h> +#include <libxml/xmlsave.h> + +xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s, + const char *jid, + xmlNodePtr *prev_item) +{ + xmlNodePtr prev = NULL, cur = s->roster_items; + while (cur != NULL) { + char *cur_jid = xmlGetProp(cur, "jid"); + if (cur_jid == NULL) { + rexmpp_log(s, LOG_ALERT, "No jid found in a roster item."); + return NULL; + } + int match = (strcmp(cur_jid, jid) == 0); + free(cur_jid); + if (match) { + if (prev_item != NULL) { + *prev_item = prev; + } + return cur; + } + prev = cur; + cur = cur->next; + } + return NULL; +} + +rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item) { + rexmpp_err_t ret = REXMPP_SUCCESS; + if (! rexmpp_xml_match(item, "jabber:iq:roster", "item")) { + rexmpp_log(s, LOG_ERR, "No roster item."); + return REXMPP_E_PARAM; + } + char *subscription = xmlGetProp(item, "subscription"); + char *jid = xmlGetProp(item, "jid"); + if (subscription != NULL && strcmp(subscription, "remove") == 0) { + /* Delete the item. */ + xmlNodePtr prev, cur; + cur = rexmpp_roster_find_item(s, jid, &prev); + if (cur != NULL) { + if (prev != NULL) { + prev->next = cur->next; + } else { + s->roster_items = cur->next; + } + xmlFreeNode(cur); + } else { + ret = REXMPP_E_ROSTER_ITEM_NOT_FOUND; + } + } else { + /* Add or modify the item. */ + xmlNodePtr cur, prev; + cur = rexmpp_roster_find_item(s, jid, &prev); + /* Remove the item if it was in the roster before. */ + if (cur != NULL) { + if (prev != NULL) { + prev->next = cur->next; + } else { + s->roster_items = cur->next; + } + xmlFreeNode(cur); + } + /* Add the new item. */ + xmlNodePtr new_item = xmlCopyNode(item, 1); + new_item->next = s->roster_items; + s->roster_items = new_item; + } + free(jid); + if (subscription != NULL) { + free(subscription); + } + return ret; +} + +void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query) { + if (s->roster_items != NULL) { + xmlFreeNodeList(s->roster_items); + } + if (s->roster_ver != NULL) { + free(s->roster_ver); + } + s->roster_ver = xmlGetProp(query, "ver"); + s->roster_items = xmlCopyNodeList(xmlFirstElementChild(query)); +} + +void rexmpp_roster_cache_read (rexmpp_t *s) { + if (s->roster_cache_file == NULL) { + rexmpp_log(s, LOG_WARNING, "No roster cache file path is set."); + return; + } + xmlDocPtr doc = xmlReadFile(s->roster_cache_file, "utf-8", XML_PARSE_NONET); + xmlNodePtr query = xmlDocGetRootElement(doc); + rexmpp_roster_set(s, query); + xmlFreeDoc(doc); +} + +void rexmpp_roster_cache_write (rexmpp_t *s) { + if (s->roster_cache_file == NULL) { + rexmpp_log(s, LOG_WARNING, "No roster cache file path is set."); + return; + } + xmlDocPtr doc = xmlNewDoc("1.0"); + xmlNodePtr query = xmlNewDocNode(doc, NULL, "query", NULL); + xmlDocSetRootElement(doc, query); + xmlNewNs(query, "jabber:iq:roster", NULL); + if (s->roster_ver != NULL) { + xmlNewProp(query, "ver", s->roster_ver); + } + if (s->roster_items != NULL) { + xmlAddChild(query, xmlDocCopyNodeList(doc, s->roster_items)); + } + xmlSaveFileEnc(s->roster_cache_file, doc, "utf-8"); + xmlFreeDoc(doc); +} + +void rexmpp_iq_roster_get (rexmpp_t *s, + xmlNodePtr req, + xmlNodePtr response, + int success) +{ + if (! success) { + rexmpp_log(s, LOG_ERR, "Roster loading failed."); + return; + } + xmlNodePtr query = xmlFirstElementChild(response); + if (! rexmpp_xml_match(query, "jabber:iq:roster", "query")) { + rexmpp_log(s, LOG_DEBUG, "No roster query in reply."); + return; + } + rexmpp_roster_set(s, query); + if (s->roster_cache_file != NULL) { + rexmpp_roster_cache_write(s); + } +} |