# HG changeset patch # User Mikael Berthe # Date 1163545497 -3600 # Node ID 4d3c488447466cadb1722db91cd84c7e9af7c743 # Parent 579299b1c9b29c7e9ec028527231f0f5299d72cf Add /roster note Implement XEP-0145 (Annotations) Usage: /roster note [-|note_text] diff -r 579299b1c9b2 -r 4d3c48844746 mcabber/src/commands.c --- a/mcabber/src/commands.c Mon Nov 13 21:58:18 2006 +0100 +++ b/mcabber/src/commands.c Wed Nov 15 00:04:57 2006 +0100 @@ -157,6 +157,7 @@ compl_add_category_word(COMPL_ROSTER, "search"); compl_add_category_word(COMPL_ROSTER, "unread_first"); compl_add_category_word(COMPL_ROSTER, "unread_next"); + compl_add_category_word(COMPL_ROSTER, "note"); // Roster category compl_add_category_word(COMPL_BUFFER, "clear"); @@ -466,6 +467,47 @@ } } +static void roster_note(char *arg) +{ + const char *jid; + gchar *msg, *note; + guint type; + + if (!current_buddy) + return; + + jid = buddy_getjid(BUDDATA(current_buddy)); + type = buddy_gettype(BUDDATA(current_buddy)); + + if (!jid || (type != ROSTER_TYPE_USER && + type != ROSTER_TYPE_ROOM && + type != ROSTER_TYPE_AGENT)) { + scr_LogPrint(LPRINT_NORMAL, "This item can't have a note."); + return; + } + + if (arg && *arg) + msg = to_utf8(arg); + else + msg = NULL; + + if (msg) { // Set a note + if (!strcmp(msg, "-")) + note = NULL; // delete note + else + note = msg; + jb_set_storage_rosternotes(jid, note); + } else { // Display a note + note = jb_get_storage_rosternotes(jid); + if (note) + msg = g_strdup_printf("Note: %s", note); + else + msg = g_strdup_printf("This item doesn't have a note."); + scr_WriteIncomingMessage(jid, msg, 0, HBB_PREFIX_INFO); + } + g_free(msg); +} + /* Commands callback functions */ /* All these do_*() functions will be called with a "arg" parameter */ /* (with arg not null) */ @@ -533,6 +575,8 @@ scr_RosterUp(); } else if (!strcasecmp(subcmd, "down")) { scr_RosterDown(); + } else if (!strcasecmp(subcmd, "note")) { + roster_note(arg); } else scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); free_arg_lst(paramlst); @@ -1216,7 +1260,6 @@ type == ROSTER_TYPE_GROUP ? "group" : (type == ROSTER_TYPE_SPECIAL ? "special" : "unknown")); } - g_free(buffer); } diff -r 579299b1c9b2 -r 4d3c48844746 mcabber/src/jab_iq.c --- a/mcabber/src/jab_iq.c Mon Nov 13 21:58:18 2006 +0100 +++ b/mcabber/src/jab_iq.c Wed Nov 15 00:04:57 2006 +0100 @@ -36,6 +36,8 @@ // Bookmarks for IQ:private storage xmlnode bookmarks; +// Roster notes for IQ:private storage +xmlnode rosternotes; static GSList *iqs_list; @@ -684,6 +686,7 @@ if (p && !strcmp(p, "conference")) storage_bookmarks_parse_conference(x); } + // Copy the bookmarks node xmlnode_free(bookmarks); bookmarks = xmlnode_dup(ansqry); } @@ -702,6 +705,40 @@ jab_send(jc, iqn->xmldata); } +static void iqscallback_storage_rosternotes(eviqs *iqp, xmlnode xml_result, + guint iqcontext) +{ + xmlnode ansqry; + + // Leave now if we cannot process xml_result + if (!xml_result || iqcontext) return; + + ansqry = xmlnode_get_tag(xml_result, "query"); + ansqry = xmlnode_get_tag(ansqry, "storage"); + if (!ansqry) { + scr_LogPrint(LPRINT_LOG, "Invalid IQ:private result! " + "(storage:rosternotes)"); + return; + } + // Copy the rosternotes node + xmlnode_free(rosternotes); + rosternotes = xmlnode_dup(ansqry); +} + +static void request_storage_rosternotes(void) +{ + eviqs *iqn; + xmlnode x; + + iqn = iqs_new(JPACKET__GET, NS_PRIVATE, "storage", IQS_DEFAULT_TIMEOUT); + + x = xmlnode_insert_tag(xmlnode_get_tag(iqn->xmldata, "query"), "storage"); + xmlnode_put_attrib(x, "xmlns", "storage:rosternotes"); + + iqn->callback = &iqscallback_storage_rosternotes; + jab_send(jc, iqn->xmldata); +} + void iqscallback_auth(eviqs *iqp, xmlnode xml_result) { if (jstate == STATE_GETAUTH) { @@ -721,6 +758,7 @@ } else if (jstate == STATE_SENDAUTH) { request_roster(); request_storage_bookmarks(); + request_storage_rosternotes(); jstate = STATE_LOGGED; } } @@ -989,4 +1027,20 @@ iqs_del(iqn->id); // XXX } +// send_storage_rosternotes() +// Send the current rosternotes node to update the server. +// Note: the sender should check we're online. +void send_storage_rosternotes(void) +{ + eviqs *iqn; + + if (!rosternotes) return; + + iqn = iqs_new(JPACKET__SET, NS_PRIVATE, "storage", IQS_DEFAULT_TIMEOUT); + xmlnode_insert_node(xmlnode_get_tag(iqn->xmldata, "query"), rosternotes); + + jab_send(jc, iqn->xmldata); + iqs_del(iqn->id); // XXX +} + /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ diff -r 579299b1c9b2 -r 4d3c48844746 mcabber/src/jab_priv.h --- a/mcabber/src/jab_priv.h Mon Nov 13 21:58:18 2006 +0100 +++ b/mcabber/src/jab_priv.h Wed Nov 15 00:04:57 2006 +0100 @@ -29,7 +29,7 @@ #define IQS_CONTEXT_ERROR 2U extern enum enum_jstate jstate; -extern xmlnode bookmarks; +extern xmlnode bookmarks, rosternotes; extern char *mcabber_version(void); @@ -47,6 +47,7 @@ void request_last(const char *fulljid); void request_vcard(const char *barejid); void send_storage_bookmarks(void); +void send_storage_rosternotes(void); #endif /* __JAB_PRIV_H__ */ diff -r 579299b1c9b2 -r 4d3c48844746 mcabber/src/jabglue.c --- a/mcabber/src/jabglue.c Mon Nov 13 21:58:18 2006 +0100 +++ b/mcabber/src/jabglue.c Wed Nov 15 00:04:57 2006 +0100 @@ -1232,6 +1232,94 @@ "Warning: you're not connected to the server."); } +// jb_get_storage_rosternotes(barejid) +// Return thenote associated to this jid. +// The caller should g_free the string after use. +char *jb_get_storage_rosternotes(const char *barejid) +{ + xmlnode x; + + if (!barejid) + return NULL; + + // If we have no rosternotes, probably the server doesn't support them. + if (!rosternotes) { + scr_LogPrint(LPRINT_LOGNORM, + "Sorry, your server doesn't seem to support private storage."); + return NULL; + } + + // Walk through the storage tags + x = xmlnode_get_firstchild(rosternotes); + for ( ; x; x = xmlnode_get_nextsibling(x)) { + const char *jid; + const char *p; + p = xmlnode_get_name(x); + // If the current node is a conference item, see if we have to replace it. + if (p && !strcmp(p, "note")) { + jid = xmlnode_get_attrib(x, "jid"); + if (!jid) + continue; + if (!strcmp(jid, barejid)) { + // We've found a note for this contact. + return g_strdup(xmlnode_get_data(x)); + } + } + } + return NULL; // No note found +} + +// jb_set_storage_rosternotes(barejid, note) +// Update the private storage rosternotes: add/delete a note. +// If note is nil, we remove the existing note. +void jb_set_storage_rosternotes(const char *barejid, const char *note) +{ + xmlnode x; + + if (!barejid) + return; + + // If we have no rosternotes, probably the server doesn't support them. + if (!rosternotes) { + scr_LogPrint(LPRINT_LOGNORM, + "Sorry, your server doesn't seem to support private storage."); + return; + } + + // Walk through the storage tags + x = xmlnode_get_firstchild(rosternotes); + for ( ; x; x = xmlnode_get_nextsibling(x)) { + const char *jid; + const char *p; + p = xmlnode_get_name(x); + // If the current node is a conference item, see if we have to replace it. + if (p && !strcmp(p, "note")) { + jid = xmlnode_get_attrib(x, "jid"); + if (!jid) + continue; + if (!strcmp(jid, barejid)) { + // We've found a note for this jid. Let's hide it and we'll + // create a new one. + xmlnode_hide(x); + break; + } + } + } + + // Let's create a node for this jid, if the note is not NULL. + if (note) { + x = xmlnode_insert_tag(rosternotes, "note"); + xmlnode_put_attrib(x, "jid", barejid); + xmlnode_insert_cdata(x, note, -1); + } + + if (online) + send_storage_rosternotes(); + else + scr_LogPrint(LPRINT_LOGNORM, + "Warning: you're not connected to the server."); +} + static void gotmessage(char *type, const char *from, const char *body, const char *enc, time_t timestamp) { diff -r 579299b1c9b2 -r 4d3c48844746 mcabber/src/jabglue.h --- a/mcabber/src/jabglue.h Mon Nov 13 21:58:18 2006 +0100 +++ b/mcabber/src/jabglue.h Wed Nov 15 00:04:57 2006 +0100 @@ -71,6 +71,8 @@ void jb_set_storage_bookmark(const char *roomid, const char *name, const char *nick, const char *passwd, int autojoin); +char *jb_get_storage_rosternotes(const char *barejid); +void jb_set_storage_rosternotes(const char *barejid, const char *note); #endif /* __JABGLUE_H__ */