# HG changeset patch # User Mikael Berthe # Date 1162235880 -3600 # Node ID f47e312560aff73455ad1e71e1d2e2525f44bfe9 # Parent ed697234bd39e526f6cb26cb30ade07fc43daf9f Improve JEP22 + JEP85 support diff -r ed697234bd39 -r f47e312560af mcabber/libjabber/jabber.h --- a/mcabber/libjabber/jabber.h Sun Oct 29 11:43:00 2006 +0100 +++ b/mcabber/libjabber/jabber.h Mon Oct 30 20:18:00 2006 +0100 @@ -281,6 +281,7 @@ #define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" #define NS_IQ_AUTH "http://jabber.org/features/iq-auth" #define NS_REGISTER_FEATURE "http://jabber.org/features/iq-register" +#define NS_CHATSTATES "http://jabber.org/protocol/chatstates" #define NS_XDBGINSERT "jabber:xdb:ginsert" #define NS_XDBNSLIST "jabber:xdb:nslist" diff -r ed697234bd39 -r f47e312560af mcabber/src/jabglue.c --- a/mcabber/src/jabglue.c Sun Oct 29 11:43:00 2006 +0100 +++ b/mcabber/src/jabglue.c Mon Oct 30 20:18:00 2006 +0100 @@ -416,6 +416,13 @@ { xmlnode x; gchar *strtype; +#if defined JEP0022 || defined JEP0085 + xmlnode event; + char *rname, *barejid; + GSList *sl_buddy; + guint which_jep = 0; /* 0: none, 1: 85, 2: 22 */ + struct jep0085 *jep85 = NULL; +#endif if (!online) return; @@ -431,20 +438,49 @@ xmlnode_insert_cdata(y, subject, (unsigned) -1); } - // TODO: insert event notifications request -#undef USE_JEP_85 -#ifdef USE_JEP_85 -#define NS_CHAT_STATES "http://jabber.org/features/chatstates" - // JEP-85 - xmlnode event = xmlnode_insert_tag(x, "composing"); - xmlnode_put_attrib(event, "xmlns", NS_CHAT_STATES); -#else - // JEP-22 - xmlnode event = xmlnode_insert_tag(x, "x"); - xmlnode_put_attrib(event, "xmlns", NS_EVENT); - xmlnode_insert_tag(event, "composing"); +#if defined JEP0022 || defined JEP0085 + rname = strchr(jid, JID_RESOURCE_SEPARATOR); + barejid = jidtodisp(jid); + sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER); + g_free(barejid); + + // If we can get a resource name, we use it. Else we use NULL, + // which hopefully will give us the most likely resource. + if (rname) + rname++; + if (sl_buddy) + jep85 = buddy_resource_jep85(sl_buddy->data, rname); #endif +#ifdef JEP0085 + /* JEP-0085 5.1 + * "Until receiving a reply to the initial content message (or a standalone + * notification) from the Contact, the User MUST NOT send subsequent chat + * state notifications to the Contact." + * In our implementation support is initially "unknown", they it's "probed" + * and can become "ok". + */ + if (jep85 && (jep85->support == CHATSTATES_SUPPORT_OK || + jep85->support == CHATSTATES_SUPPORT_UNKNOWN)) { + event = xmlnode_insert_tag(x, "active"); + xmlnode_put_attrib(event, "xmlns", NS_CHATSTATES); + if (jep85->support == CHATSTATES_SUPPORT_UNKNOWN) + jep85->support = CHATSTATES_SUPPORT_PROBED; + else + which_jep = 1; + } +#endif +#ifdef JEP0022 + /* JEP-22 + * If the Contact supports JEP-0085, we do not use JEP-0022. + * If not, we try to fall back to JEP-0022. + */ + if (!which_jep) { + event = xmlnode_insert_tag(x, "x"); + xmlnode_put_attrib(event, "xmlns", NS_EVENT); + xmlnode_insert_tag(event, "composing"); + } +#endif jab_send(jc, x); xmlnode_free(x); @@ -1039,10 +1075,8 @@ x = xmlnode_get_firstchild(xmldata); for ( ; x; x = xmlnode_get_nextsibling(x)) { - if ((p = xmlnode_get_name(x)) && !strcmp(p, "x")) - if ((p = xmlnode_get_attrib(x, "xmlns")) && !strcmp(p, xmlns)) { - break; - } + if ((p = xmlnode_get_attrib(x, "xmlns")) && !strcmp(p, xmlns)) + break; } return x; } @@ -1392,7 +1426,7 @@ { char *p, *r, *s; xmlnode x; - char *body=NULL; + char *body = NULL; char *enc = NULL; char *tmp = NULL; time_t timestamp = 0; @@ -1459,50 +1493,111 @@ g_free(tmp); } -void handle_state_events(char* from, xmlnode xmldata) +void handle_state_events(char *from, xmlnode xmldata) { - xmlnode x = NULL; - char *rname = strchr(from, JID_RESOURCE_SEPARATOR) + 1; - char *jid = jidtodisp(from); - GSList *slist = roster_find(jid, jidsearch, ROSTER_TYPE_USER); - if (slist == NULL) return; - int jep85 = 0; +#if defined JEP0022 || defined JEP0085 + xmlnode state_ns; + const char *body; + char *rname, *jid; + GSList *sl_buddy; + guint events; + guint which_jep = 0; /* 0: none, 1: 85, 2: 22 */ + struct jep0022 *jep22; + struct jep0085 *jep85; + + rname = strchr(from, JID_RESOURCE_SEPARATOR); + jid = jidtodisp(from); + sl_buddy = roster_find(jid, jidsearch, ROSTER_TYPE_USER); + + /* XXX Actually that's wrong, since it filters out server "offline" + messages (for JEP-0022) */ + if (!sl_buddy || !rname++) { + g_free(jid); + return; + } - guint events = buddy_resource_getevents(slist->data, rname); + events = buddy_resource_getevents(sl_buddy->data, rname); + + jep85 = buddy_resource_jep85(sl_buddy->data, rname); + if (jep85) { + state_ns = xml_get_xmlns(xmldata, NS_CHATSTATES); + if (state_ns) + which_jep = 1; + } - x = xml_get_xmlns(xmldata, NS_EVENT); - if (x == NULL) { - x = xmldata; - jep85 = 1; + if (which_jep != 1) { /* Fall back to JEP-0022 */ + jep22 = buddy_resource_jep22(sl_buddy->data, rname); + if (jep22) { + state_ns = xml_get_xmlns(xmldata, NS_EVENT); + if (state_ns) + which_jep = 2; + } + } + + if (!which_jep) { /* Sender does not use chat states */ + g_free(jid); + return; } - xmlnode tag = xmlnode_get_tag(x, "composing"); - if (tag != NULL) { - events |= ROSTER_EVENT_COMPOSING; - } else if (!jep85) { - events &= ~ROSTER_EVENT_COMPOSING; - } + body = xmlnode_get_tag_data(xmldata, "body"); + + if (which_jep == 1) { /* JEP-0085 */ + const char *p; + jep85->support = CHATSTATES_SUPPORT_OK; + + p = xmlnode_get_name(state_ns); + if (!strcmp(p, "composing")) { + jep85->last_state_rcvd = ROSTER_EVENT_COMPOSING; + } else if (!strcmp(p, "active")) { + jep85->last_state_rcvd = ROSTER_EVENT_ACTIVE; + } else if (!strcmp(p, "paused")) { + jep85->last_state_rcvd = ROSTER_EVENT_PAUSED; + } else if (!strcmp(p, "inactive")) { + jep85->last_state_rcvd = ROSTER_EVENT_INACTIVE; + } else if (!strcmp(p, "gone")) { + jep85->last_state_rcvd = ROSTER_EVENT_GONE; + } - if (jep85) { - tag = xmlnode_get_tag(x, "paused"); - if (tag != NULL) { + if (jep85->last_state_rcvd == ROSTER_EVENT_COMPOSING) + events = ROSTER_EVENT_COMPOSING; + else + events = ROSTER_EVENT_NONE; + } else { /* JEP-0022 */ + const char *msgid; + jep22->support = CHATSTATES_SUPPORT_OK; + jep22->last_state_rcvd = ROSTER_EVENT_NONE; + + msgid = xmlnode_get_attrib(xmldata, "id"); + + if (xmlnode_get_tag(state_ns, "composing")) { + // Clear composing if the message contains a body + if (body) events &= ~ROSTER_EVENT_COMPOSING; - } + else + events |= ROSTER_EVENT_COMPOSING; + jep22->last_state_rcvd |= ROSTER_EVENT_COMPOSING; + + } else { + events &= ~ROSTER_EVENT_COMPOSING; + } + + if (xmlnode_get_tag(state_ns, "delivered")) + jep22->last_state_rcvd |= ROSTER_EVENT_DELIVERED; + + // Cache the message id + g_free(jep22->last_msgid_rcvd); + if (msgid) + jep22->last_msgid_rcvd = g_strdup(msgid); + else + jep22->last_msgid_rcvd = NULL; } - // clear composing and set new message event - // if message contains message body - if (xmlnode_get_tag_data(xmldata, "body") != NULL) { - events |= ROSTER_EVENT_MSG; - events &= ~ROSTER_EVENT_COMPOSING; - } + buddy_resource_setevents(sl_buddy->data, rname, events); - buddy_resource_setevents(slist->data, rname, events); - - scr_UpdateBuddyWindow(); - scr_DrawRoster(); + update_roster = TRUE; g_free(jid); +#endif } static void evscallback_subscription(eviqs *evp, guint evcontext) @@ -1643,10 +1738,6 @@ static void packethandler(jconn conn, jpacket packet) { char *p; - /* - char *r, *s; - const char *m; - */ char *from=NULL, *type=NULL; jb_reset_keepalive(); // reset keepalive timeout @@ -1664,7 +1755,7 @@ if (p) from = p; if (!from && packet->type != JPACKET_IQ) { - scr_LogPrint(LPRINT_LOGNORM, "Error in packet (could be UTF8-related)"); + scr_LogPrint(LPRINT_LOGNORM, "Error in stream packet"); return; } diff -r ed697234bd39 -r f47e312560af mcabber/src/roster.c --- a/mcabber/src/roster.c Sun Oct 29 11:43:00 2006 +0100 +++ b/mcabber/src/roster.c Mon Oct 30 20:18:00 2006 +0100 @@ -52,6 +52,12 @@ enum imaffiliation affil; gchar *realjid; /* for chatrooms, if buddy's real jid is known */ guint events; +#ifdef JEP0022 + struct jep0022 jep22; +#endif +#ifdef JEP0085 + struct jep0085 jep85; +#endif } res; /* This is a private structure type for the roster */ @@ -115,6 +121,10 @@ g_free((gchar*)p_res->status_msg); g_free((gchar*)p_res->name); g_free((gchar*)p_res->realjid); +#ifdef JEP0022 + g_free(p_res->jep22.last_msgid_sent); + g_free(p_res->jep22.last_msgid_rcvd); +#endif } // Free all nodes but the first (which is static) g_slist_free(*reslist); @@ -209,6 +219,10 @@ g_free(p_res->name); g_free(p_res->status_msg); g_free(p_res->realjid); +#ifdef JEP0022 + g_free(p_res->jep22.last_msgid_sent); + g_free(p_res->jep22.last_msgid_rcvd); +#endif rost->resource = g_slist_delete_link(rost->resource, p_res_elt); return; } @@ -1079,14 +1093,28 @@ res *p_res = get_resource(roster_usr, resname); if (p_res) p_res->events = events; +} - /* - // update group - roster_usr = roster_usr->list->data; - p_res = get_resource(roster_usr, ""); +struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname) +{ +#ifdef JEP0022 + roster *roster_usr = rosterdata; + res *p_res = get_resource(roster_usr, resname); if (p_res) - p_res->events = events; - */ + return &p_res->jep22; +#endif + return NULL; +} + +struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname) +{ +#ifdef JEP0085 + roster *roster_usr = rosterdata; + res *p_res = get_resource(roster_usr, resname); + if (p_res) + return &p_res->jep85; +#endif + return NULL; } enum imrole buddy_getrole(gpointer rosterdata, const char *resname) diff -r ed697234bd39 -r f47e312560af mcabber/src/roster.h --- a/mcabber/src/roster.h Sun Oct 29 11:43:00 2006 +0100 +++ b/mcabber/src/roster.h Mon Oct 30 20:18:00 2006 +0100 @@ -74,21 +74,42 @@ #define ROSTER_FLAG_USRLOCK (1U<<3) // Node should not be removed from buddylist // ROSTER_FLAG_LOCAL (1U<<4) // Buddy not on server's roster (??) +#define JEP0022 +#define JEP0085 + +struct jep0022 { + guint support; + guint last_state_sent; + gchar *last_msgid_sent; + guint last_state_rcvd; + gchar *last_msgid_rcvd; +}; +struct jep0085 { + guint support; + guint last_state_sent; + guint last_state_rcvd; +}; + +enum chatstate_support { + CHATSTATES_SUPPORT_UNKNOWN = 0, + CHATSTATES_SUPPORT_PROBED, + CHATSTATES_SUPPORT_NONE, + CHATSTATES_SUPPORT_OK +}; /* Message event and chat state flags */ #define ROSTER_EVENT_NONE 0U -#define ROSTER_EVENT_MSG 1U /* JEP-22 Message Events */ -#define ROSTER_EVENT_OFFLINE (1U<<1) -#define ROSTER_EVENT_DELIVERED (1U<<2) -#define ROSTER_EVENT_DISPLAYED (1U<<3) +#define ROSTER_EVENT_OFFLINE (1U<<0) +#define ROSTER_EVENT_DELIVERED (1U<<1) +#define ROSTER_EVENT_DISPLAYED (1U<<2) /* JEP-22 & JEP-85 */ -#define ROSTER_EVENT_COMPOSING (1U<<4) +#define ROSTER_EVENT_COMPOSING (1U<<3) /* JEP-85 Chat State Notifications */ -#define ROSTER_EVENT_ACTIVE (1U<<5) -#define ROSTER_EVENT_PAUSED (1U<<6) -#define ROSTER_EVENT_INACTIVE (1U<<7) -#define ROSTER_EVENT_GONE (1U<<8) +#define ROSTER_EVENT_ACTIVE (1U<<4) +#define ROSTER_EVENT_PAUSED (1U<<5) +#define ROSTER_EVENT_INACTIVE (1U<<6) +#define ROSTER_EVENT_GONE (1U<<7) extern GList *buddylist; extern GList *current_buddy; @@ -154,6 +175,8 @@ void buddy_resource_setevents(gpointer rosterdata, const char *resname, guint event); guint buddy_resource_getevents(gpointer rosterdata, const char *resname); +struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname); +struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname); enum imrole buddy_getrole(gpointer rosterdata, const char *resname); enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname); const char *buddy_getrjid(gpointer rosterdata, const char *resname); diff -r ed697234bd39 -r f47e312560af mcabber/src/screen.c --- a/mcabber/src/screen.c Sun Oct 29 11:43:00 2006 +0100 +++ b/mcabber/src/screen.c Mon Oct 30 20:18:00 2006 +0100 @@ -1191,14 +1191,9 @@ for ( ; resources ; resources = g_slist_next(resources) ) { guint events = buddy_resource_getevents(BUDDATA(buddy), resources ? resources->data : ""); - if (events) { - if (events & ROSTER_EVENT_MSG && false) { // FIXME: not yet. - pending = '#'; - break; - } else if (events & ROSTER_EVENT_COMPOSING) { - pending = '+'; - break; - } + if (events & ROSTER_EVENT_COMPOSING) { + pending = '+'; + break; } }