changeset 1685:1342df44c814

Improved events interface * User can pass additional arguments to event handler * MUC invitation reject now can be supplied a reason
author Myhailo Danylenko <isbear@ukrpost.net>
date Tue, 02 Feb 2010 22:44:18 +0100
parents 95df4ea512c8
children 393c05fba337
files mcabber/doc/help/en/hlp_event.txt mcabber/doc/help/ru/hlp_event.txt mcabber/doc/help/uk/hlp_event.txt mcabber/mcabber/commands.c mcabber/mcabber/compl.c mcabber/mcabber/events.c mcabber/mcabber/events.h mcabber/mcabber/main.c mcabber/mcabber/xmpp.c mcabber/mcabber/xmpp_muc.c mcabber/mcabber/xmpp_muc.h mcabber/mcabber/xmpp_s10n.c mcabber/mcabber/xmpp_s10n.h
diffstat 13 files changed, 263 insertions(+), 229 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/doc/help/en/hlp_event.txt	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/doc/help/en/hlp_event.txt	Tue Feb 02 22:44:18 2010 +0100
@@ -1,15 +1,16 @@
 
- /EVENT #N|* accept|ignore|reject
+ /EVENT #N|* accept|ignore|reject [event-specific arguments]
  /EVENT list
 
 Tell mcabber what to do about pending events.
 If the first parameter is '*', the command will apply to all queued events.
+Event-specific arguments will be interpreted on event-to event basis. The only built-in case, when argument is used is MUC invitation reject - argument, if present, will be interpreted as reject reason.
 
-/event #N|* accept
+/event #N|* accept [event-specific arguments]
  Event number #N/All events will be accepted
-/event #N|* ignore
+/event #N|* ignore [event-specific arguments]
  Event number #N/All events will be ignored
-/event #N|* reject
+/event #N|* reject [event-specific arguments]
  Event number #N/All events will be rejected
 /event list
  List all pending events
--- a/mcabber/doc/help/ru/hlp_event.txt	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/doc/help/ru/hlp_event.txt	Tue Feb 02 22:44:18 2010 +0100
@@ -1,15 +1,16 @@
 
- /EVENT #N|* accept|ignore|reject
+ /EVENT #N|* accept|ignore|reject [дополнительные аргументы]
  /EVENT list
 
 Говорит mcabber`у что делать с событиями.
 Если первый параметр '*', команда будет применена ко всей очереди событий.
+Дополнительные аргументы используются в зависимости от типа события. Из встроенных событий единственный случай, когда они используются - это отклонение приглашения в чат, если есть дополнительный аргумент, он будет использован в качестве сообщения о причине отказа.
 
-/event #N|* accept
+/event #N|* accept [дополнительные аргументы]
  Событие номер #N/All будет разрешено
-/event #N|* ignore
+/event #N|* ignore [дополнительные аргументы]
  Событие номер #N/All будет проигнорировано
-/event #N|* reject
+/event #N|* reject [дополнительные аргументы]
  Событие номер #N/All будет отклонено
 /event list
  Показать список всех событий
--- a/mcabber/doc/help/uk/hlp_event.txt	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/doc/help/uk/hlp_event.txt	Tue Feb 02 22:44:18 2010 +0100
@@ -1,15 +1,16 @@
 
- /EVENT #N|* accept|ignore|reject
+ /EVENT #N|* accept|ignore|reject [додаткові аргументи]
  /EVENT list
 
 Розібратися з подіями, що трапилися (запити на авторизацію ітп).
 Якщо перший параметр - зірочка (*), відповідь буде застосована до всіх подій.
+Значення додаткового аргументу залежить від типу події. Єдиний випадок використання додаткового аргументу вбудованими подіями - відмова увійти у багатокористувацької розмови, якщо аргумент вказано, його буде надіслано, як причину відмови.
 
-/event N|* accept
+/event N|* accept [додаткові аргументи]
  Подія номер #N отримає позитивну відповідь.
-/event N|* ignore
+/event N|* ignore [додаткові аргументи]
  Не відповідати на подію номер N.
-/event N|* reject
+/event N|* reject [додаткові аргументи]
  Подія номер N отримає негативну відповідь.
 /event list
  Друкує перелік подій.
--- a/mcabber/mcabber/commands.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/commands.c	Tue Feb 02 22:44:18 2010 +0100
@@ -3305,9 +3305,8 @@
   char **paramlst;
   char *evid, *subcmd;
   int action = -1;
-  GSList *evidlst;
-
-  paramlst = split_arg(arg, 2, 0); // id, subcmd
+
+  paramlst = split_arg(arg, 3, 1); // id, subcmd, optional arg
   evid = *paramlst;
   subcmd = *(paramlst+1);
 
@@ -3317,41 +3316,37 @@
       evs_display_list();
     else
       scr_LogPrint(LPRINT_NORMAL,
-                   "Missing parameter.  Usage: /event num action");
+                   "Missing parameter.  Usage: /event num action "
+                   "[event-specific args]");
     free_arg_lst(paramlst);
     return;
   }
 
   if (!strcasecmp(subcmd, "reject"))
-    action = 0;
+    action = EVS_CONTEXT_REJECT;
   else if (!strcasecmp(subcmd, "accept"))
-    action = 1;
+    action = EVS_CONTEXT_ACCEPT;
   else if (!strcasecmp(subcmd, "ignore"))
-    action = 2;
+    action = EVS_CONTEXT_CANCEL;
 
   if (action == -1) {
     scr_LogPrint(LPRINT_NORMAL, "Wrong action parameter.");
-  } else if (action >= 0 && action <= 2) {
+  } else {
     GSList *p;
-
-    if (action == 2) {
-      action = EVS_CONTEXT_CANCEL;
-    } else {
-      action += EVS_CONTEXT_USER;
-    }
+    GSList *evidlst;
 
     if (!strcmp(evid, "*")) {
       // Use completion list
-      evidlst = evs_geteventslist(FALSE);
+      evidlst = evs_geteventslist();
     } else {
       // Let's create a slist with the provided event id
-      evidlst = g_slist_append(NULL, g_strdup(evid));
+      evidlst = g_slist_append(NULL, evid);
     }
     for (p = evidlst; p; p = g_slist_next(p)) {
-      if (evs_callback(p->data, action) == -1) {
+      if (evs_callback(p->data, action,
+                       (const char*)(paramlst+2)) == -1) {
         scr_LogPrint(LPRINT_NORMAL, "Event %s not found.", p->data);
       }
-      g_free(p->data);
     }
     g_slist_free(evidlst);
   }
--- a/mcabber/mcabber/compl.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/compl.c	Tue Feb 02 22:44:18 2010 +0100
@@ -276,7 +276,12 @@
     return buddy_getresources_locale(NULL);
   }
   if (cat_flags == COMPL_EVENTSID) {
-    return evs_geteventslist(TRUE);
+    GSList *compl = evs_geteventslist();
+    GSList *cel;
+    for (cel = compl; cel; cel = cel->next)
+      cel->data = g_strdup(cel->data);
+    compl = g_slist_append(compl, g_strdup("list"));
+    return compl;
   }
 
   *dynlist = FALSE;
--- a/mcabber/mcabber/events.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/events.c	Tue Feb 02 22:44:18 2010 +0100
@@ -2,6 +2,7 @@
  * events.c     -- Events fonctions
  *
  * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
+ * Copyrigth (C) 2010      Myhailo Danylenko <isbear@ukrposte.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,157 +25,191 @@
 #include "events.h"
 #include "logprint.h"
 
+typedef struct {
+  char           *id;
+  char           *description;
+  time_t          timeout;
+  guint           source;
+  evs_callback_t  callback;
+  gpointer        data;
+  GDestroyNotify  notify;
+} evs_t;
+
 static GSList *evs_list; // Events list
 
-static eviqs *evs_find(const char *evid);
+static evs_t *evs_find(const char *evid);
+
+static gboolean evs_check_timeout (gpointer userdata)
+{
+  evs_t *event = userdata;
+  if (event->callback &&
+      !event->callback(EVS_CONTEXT_TIMEOUT, NULL, event->data)) {
+    evs_del(event->id);
+    return FALSE;
+  }
+  return TRUE; // XXX
+}
 
 //  evs_new(type, timeout)
-// Create an events structure.
-eviqs *evs_new(guint8 type, time_t timeout)
+// Create new event. If id is omitted, generates unique
+// numerical id (recommended). If timeout is specified, sets
+// up timeout source, that will call handler in timeout
+// context after specified number of seconds. If supplied id
+// already exists, returns NULL, calling destroy notifier, if
+// one is specified.
+const char *evs_new(const char *desc, const char *id, time_t timeout, evs_callback_t callback, gpointer udata, GDestroyNotify notify)
 {
   static guint evs_idn;
-  eviqs *new_evs;
-  time_t now_t;
+  evs_t *event;
   char *stridn;
 
-  if (!++evs_idn)
-    evs_idn = 1;
-  /* Check for wrapping, we shouldn't reuse ids */
-  stridn = g_strdup_printf("%d", evs_idn);
-  if (evs_find(stridn))  {
-    g_free(stridn);
-    // We could try another id but for now giving up should be fine...
+  if (!id) {
+    if (!++evs_idn)
+      evs_idn = 1;
+    /* Check for wrapping, we shouldn't reuse ids */
+    stridn = g_strdup_printf("%d", evs_idn);
+    if (evs_find(stridn))  {
+      g_free(stridn);
+      // We could try another id but for now giving up should be fine...
+      if (notify)
+        notify(udata);
+      return NULL;
+    }
+  } else if (!evs_find(id))
+    stridn = g_strdup(id);
+  else {
+    if (notify)
+      notify(udata);
     return NULL;
   }
 
-  new_evs = g_new0(eviqs, 1);
-  time(&now_t);
-  new_evs->ts_create = now_t;
+  event = g_new(evs_t, 1);
+
+  event->id          = stridn;
+  event->description = g_strdup(desc);
+  event->timeout     = timeout;
+  event->callback    = callback;
+  event->data        = udata;
+  event->notify      = notify;
+
   if (timeout)
-    new_evs->ts_expire = now_t + timeout;
-  new_evs->type = type;
-  new_evs->id = stridn;
+    g_timeout_add_seconds(timeout, evs_check_timeout, event);
 
-  if(!g_slist_length(evs_list))
-    g_timeout_add_seconds(20, evs_check_timeout, NULL);
-  evs_list = g_slist_append(evs_list, new_evs);
-  return new_evs;
+  evs_list = g_slist_append(evs_list, event);
+  return stridn;
 }
 
-int evs_del(const char *evid)
+static evs_t *evs_find(const char *evid)
 {
   GSList *p;
-  eviqs *i;
 
-  if (!evid) return 1;
+  if (!evid)
+    return NULL;
 
   for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
-    if (!strcmp(evid, i->id))
-      break;
-  }
-  if (p) {
-    g_free(i->id);
-    g_free(i->data);
-    g_free(i->desc);
-    g_free(i);
-    evs_list = g_slist_remove(evs_list, p->data);
-    return 0; // Ok, deleted
-  }
-  return -1;  // Not found
-}
-
-static eviqs *evs_find(const char *evid)
-{
-  GSList *p;
-  eviqs *i;
-
-  if (!evid) return NULL;
-
-  for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
+    evs_t *i = p->data;
     if (!strcmp(evid, i->id))
       return i;
   }
   return NULL;
 }
 
-//  evs_callback(evid, evcontext)
-// Callback processing for the specified event.
-// Return 0 in case of success, -1 if the evid hasn't been found.
-int evs_callback(const char *evid, guint evcontext)
+//  evs_del(evid)
+// Deletes event.
+// This will not call event handler, however this will
+// call destroy notify function.
+// Returns 0 in case of success, -1 if the evid hasn't been found.
+int evs_del(const char *evid)
 {
-  eviqs *i;
+  evs_t *event = evs_find(evid);
+
+  if (!event)
+    return -1;
+
+  if (event->notify)
+    event->notify(event->data);
+  if (event->source)
+    g_source_remove(event->source);
 
-  i = evs_find(evid);
-  if (!i) return -1;
+  evs_list = g_slist_remove(evs_list, event);
+  g_free(event->id);
+  g_free(event->description);
+  g_free(event);
+
+  return 0; // Ok, deleted
+}
 
-  // IQ processing
-  // Note: If xml_result is NULL, this is a timeout
-  if (i->callback)
-    (void)(*i->callback)(i, evcontext);
+//  evs_callback(evid, evcontext, argument)
+// Callback processing for the specified event.
+// If event handler will return FALSE, event will be destroyed.
+// Return 0 in case of success, -1 if the evid hasn't been found.
+// evcontext and argument are transparently passed to event handler.
+int evs_callback(const char *evid, guint context, const char *arg)
+{
+  evs_t *event;
 
-  evs_del(evid);
+  event = evs_find(evid);
+  if (!event)
+    return -1;
+
+  if (event->callback &&
+      !event->callback(context, arg, event->data))
+    evs_del(evid);
   return 0;
 }
 
-gboolean evs_check_timeout()
-{
-  time_t now_t;
-  GSList *p;
-  eviqs *i;
-
-  time(&now_t);
-  p = evs_list;
-  if (!p)
-    return FALSE;
-  while (p) {
-    i = p->data;
-    // We must get next IQ eviqs element now because the current one
-    // could be freed.
-    p = g_slist_next(p);
-
-    if ((!i->ts_expire && now_t > i->ts_create + EVS_MAX_TIMEOUT) ||
-        (i->ts_expire && now_t > i->ts_expire)) {
-      evs_callback(i->id, EVS_CONTEXT_TIMEOUT);
-    }
-  }
-  return TRUE;
-}
-
+//  evs_display_list()
+// Prints list of events to mcabber log window.
 void evs_display_list(void)
 {
   GSList *p;
-  eviqs *i;
 
   scr_LogPrint(LPRINT_LOGNORM, "Events list:");
   for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
+    evs_t *i = p->data;
     scr_LogPrint(LPRINT_LOGNORM,
-                 "Id: %-3s %s", i->id, (i->desc ? i->desc : ""));
+                 "Id: %-3s %s", i->id,
+                 (i->description ? i->description : ""));
   }
   scr_LogPrint(LPRINT_LOGNORM, "End of events list.");
 }
 
-//  evs_geteventslist(bool comp)
-// Return a singly-linked-list of events ids, for the completion system.
-// If comp is true, the string "list" is added (it's a completion argument).
-// Note: the caller should free the list (and data) after use.
-GSList *evs_geteventslist(int compl)
+//  evs_geteventslist()
+// Return a singly-linked-list of events ids.
+// Data in list should not be modified and can disappear,
+// you must strdup them, if you want them to persist.
+// Note: the caller should free the list after use.
+GSList *evs_geteventslist(void)
 {
   GSList *evidlist = NULL, *p;
-  eviqs *i;
 
   for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
-    evidlist = g_slist_append(evidlist, g_strdup(i->id));
+    evs_t *i = p->data;
+    evidlist = g_slist_append(evidlist, i->id);
   }
 
-  if (compl) {
-    // Last item is the "list" subcommand.
-    evidlist = g_slist_append(evidlist, g_strdup("list"));
-  }
   return evidlist;
 }
 
+//  evs_deinit()
+// Frees all events.
+void evs_deinit(void)
+{
+  GSList *eel;
+  for (eel = evs_list; eel; eel = eel->next) {
+    evs_t *event = eel->data;
+    if (event->notify)
+      event->notify(event->data);
+    if (event->source)
+      g_source_remove(event->source);
+  
+    evs_list = g_slist_remove(evs_list, event);
+    g_free(event->id);
+    g_free(event->description);
+    g_free(event);
+  }
+  g_slist_free(evs_list);
+  evs_list = NULL;
+}
+
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/mcabber/events.h	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/events.h	Tue Feb 02 22:44:18 2010 +0100
@@ -8,40 +8,18 @@
 
 #define EVS_CONTEXT_TIMEOUT 0U
 #define EVS_CONTEXT_CANCEL  1U
-#define EVS_CONTEXT_USER    2U
+#define EVS_CONTEXT_ACCEPT  2U
+#define EVS_CONTEXT_REJECT  3U
+/* There can be other user-defined contexts */
 
-typedef enum {
-  EVS_TYPE_SUBSCRIPTION = 1,
-  EVS_TYPE_INVITATION = 2,
-#ifdef MODULES_ENABLE
-  EVS_TYPE_USER = 3,
-#endif
-} evs_type;
+typedef gboolean (*evs_callback_t)(guint context, const char *arg, gpointer userdata);
 
-/* Common structure for events (evs) and IQ requests (iqs) */
-typedef struct {
-  char *id;
-  time_t ts_create;
-  time_t ts_expire;
-  guint8 type;
-  gpointer data;
-  int (*callback)();
-  char *desc;
-} eviqs;
-
-typedef struct {
-  char* to;
-  char* from;
-  char* passwd;
-  char* reason;
-} event_muc_invitation;
-
-eviqs   *evs_new(guint8 type, time_t timeout);
-int      evs_del(const char *evid);
-int      evs_callback(const char *evid, guint evcontext);
-gboolean evs_check_timeout();
-void     evs_display_list(void);
-GSList  *evs_geteventslist(int forcompl);
+const char *evs_new(const char *description, const char *id, time_t timeout, evs_callback_t callback, gpointer userdata, GDestroyNotify notify);
+int         evs_del(const char *evid);
+int         evs_callback(const char *evid, guint evcontext, const char *arg);
+void        evs_display_list(void);
+GSList     *evs_geteventslist(void);
+void        evs_deinit(void);
 
 #endif /* __MCABBER_EVENTS_H__ */
 
--- a/mcabber/mcabber/main.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/main.c	Tue Feb 02 22:44:18 2010 +0100
@@ -45,6 +45,7 @@
 #include "fifo.h"
 #include "xmpp.h"
 #include "help.h"
+#include "events.h"
 
 #ifdef ENABLE_HGCSET
 # include "hgcset.h"
@@ -464,6 +465,7 @@
     g_source_unref(mc_source);
   }
 
+  evs_deinit();
   scr_TerminateCurses();
 #ifdef MODULES_ENABLE
   cmd_deinit();
--- a/mcabber/mcabber/xmpp.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/xmpp.c	Tue Feb 02 22:44:18 2010 +0100
@@ -1473,7 +1473,6 @@
   if (mstype == LM_MESSAGE_SUB_TYPE_SUBSCRIBE) {
     /* The sender wishes to subscribe to our presence */
     const char *msg;
-    eviqs *evn;
 
     msg = lm_message_node_get_child_value(m->node, "status");
 
@@ -1492,15 +1491,18 @@
     }
 
     // Create a new event item
-    evn = evs_new(EVS_TYPE_SUBSCRIPTION, EVS_MAX_TIMEOUT);
-    if (evn) {
-      evn->callback = &evscallback_subscription;
-      evn->data = g_strdup(r);
-      evn->desc = g_strdup_printf("<%s> wants to subscribe to your "
-                                  "presence updates", r);
-      buf = g_strdup_printf("Please use /event %s accept|reject", evn->id);
-    } else {
-      buf = g_strdup_printf("Unable to create a new event!");
+    {
+      const char *id;
+      char *desc = g_strdup_printf("<%s> wants to subscribe to your "
+                                   "presence updates", r);
+
+      id = evs_new(desc, NULL, 0, evscallback_subscription, g_strdup(r),
+                   (GDestroyNotify)g_free);
+      g_free(desc);
+      if (id)
+        buf = g_strdup_printf("Please use /event %s accept|reject", id);
+      else
+        buf = g_strdup_printf("Unable to create a new event!");
     }
     scr_WriteIncomingMessage(r, buf, 0, HBB_PREFIX_INFO, 0);
     scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
--- a/mcabber/mcabber/xmpp_muc.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/xmpp_muc.c	Tue Feb 02 22:44:18 2010 +0100
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 
 #include "xmpp_helper.h"
+#include "xmpp_muc.h"
 #include "events.h"
 #include "hooks.h"
 #include "screen.h"
@@ -37,7 +38,7 @@
 extern enum imstatus mystatus;
 extern gchar *mystatusmsg;
 
-static void decline_invitation(event_muc_invitation *invitation, char *reason)
+static void decline_invitation(event_muc_invitation *invitation, const char *reason)
 {
   // cut and paste from xmpp_room_invite
   LmMessage *m;
@@ -62,47 +63,51 @@
   lm_message_unref(m);
 }
 
-static int evscallback_invitation(eviqs *evp, guint evcontext)
+void destroy_event_muc_invitation(event_muc_invitation *invitation)
 {
-  event_muc_invitation *invitation = evp->data;
+  g_free(invitation->to);
+  g_free(invitation->from);
+  g_free(invitation->passwd);
+  g_free(invitation->reason);
+  g_free(invitation);
+}
+
+// invitation event handler
+// TODO: if event is accepted, check if other events to the same room exist and
+// destroy them? (need invitation registry list for that)
+static gboolean evscallback_invitation(guint evcontext, const char *arg, gpointer userdata)
+{
+  event_muc_invitation *invitation = userdata;
 
   // Sanity check
-  if (!invitation) {
+  if (G_UNLIKELY(!invitation)) {
     // Shouldn't happen.
     scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
-    return 0;
+    return FALSE;
   }
 
   if (evcontext == EVS_CONTEXT_TIMEOUT) {
-    scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.", evp->id);
-    goto evscallback_invitation_free;
+    scr_LogPrint(LPRINT_LOGNORM, "Invitation event %s timed out, cancelled.", invitation->to);
+    return FALSE;
   }
   if (evcontext == EVS_CONTEXT_CANCEL) {
-    scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
-    goto evscallback_invitation_free;
+    scr_LogPrint(LPRINT_LOGNORM, "Invitation event %s cancelled.", invitation->to);
+    return FALSE;
   }
-  if (!(evcontext & EVS_CONTEXT_USER))
-    goto evscallback_invitation_free;
-  // Ok, let's work now.
-  // evcontext: 0, 1 == reject, accept
+  if (!(evcontext == EVS_CONTEXT_ACCEPT || evcontext == EVS_CONTEXT_REJECT))
+    return FALSE;
 
-  if (evcontext & ~EVS_CONTEXT_USER) {
+  // Ok, let's work now
+  if (evcontext == EVS_CONTEXT_ACCEPT) {
     char *nickname = default_muc_nickname(invitation->to);
     xmpp_room_join(invitation->to, nickname, invitation->passwd);
     g_free(nickname);
   } else {
     scr_LogPrint(LPRINT_LOGNORM, "Invitation to %s refused.", invitation->to);
-    decline_invitation(invitation, NULL);
+    decline_invitation(invitation, arg);
   }
 
-evscallback_invitation_free:
-  g_free(invitation->to);
-  g_free(invitation->from);
-  g_free(invitation->passwd);
-  g_free(invitation->reason);
-  g_free(invitation);
-  evp->data = NULL;
-  return 0;
+  return FALSE;
 }
 
 // Join a MUC room
@@ -642,11 +647,11 @@
 // This function should be called when receiving an invitation from user
 // "from", to enter the room "to".  Optional reason and room password can
 // be provided.
+// TODO: check for duplicate invites (need an existing invitation registry
+// for that).
 static void got_invite(const char* from, const char *to, const char* reason,
                        const char* passwd)
 {
-  eviqs *evn;
-  event_muc_invitation *invitation;
   GString *sbuf;
   char *barejid;
   GSList *room_elt;
@@ -665,19 +670,24 @@
   scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
   scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
 
-  evn = evs_new(EVS_TYPE_INVITATION, EVS_MAX_TIMEOUT);
-  if (evn) {
-    evn->callback = &evscallback_invitation;
+  {
+    const char *id;
+    char *desc = g_strdup_printf("<%s> invites you to %s", from, to);
+    event_muc_invitation *invitation;
+    
     invitation = g_new(event_muc_invitation, 1);
     invitation->to = g_strdup(to);
     invitation->from = g_strdup(from);
     invitation->passwd = g_strdup(passwd);
     invitation->reason = g_strdup(reason);
-    evn->data = invitation;
-    evn->desc = g_strdup_printf("<%s> invites you to %s ", from, to);
-    g_string_printf(sbuf, "Please use /event %s accept|reject", evn->id);
-  } else {
-    g_string_printf(sbuf, "Unable to create a new event!");
+
+    id = evs_new(desc, NULL, 0, evscallback_invitation, invitation,
+                 (GDestroyNotify)destroy_event_muc_invitation);
+    g_free(desc);
+    if (id)
+      g_string_printf(sbuf, "Please use /event %s accept|reject", id);
+    else
+      g_string_printf(sbuf, "Unable to create a new event!");
   }
   scr_WriteIncomingMessage(barejid, sbuf->str, 0, HBB_PREFIX_INFO, 0);
   scr_LogPrint(LPRINT_LOGNORM, "%s", sbuf->str);
--- a/mcabber/mcabber/xmpp_muc.h	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/xmpp_muc.h	Tue Feb 02 22:44:18 2010 +0100
@@ -1,6 +1,14 @@
 #ifndef __MCABBER_XMPP_MUC_H__
 #define __MCABBER_XMPP_MUC_H__ 1
 
+typedef struct {
+  char *to;
+  char *from;
+  char *passwd;
+  char *reason;
+} event_muc_invitation;
+
+void destroy_event_muc_invitation(event_muc_invitation *invitation);
 void roompresence(gpointer room, void *presencedata);
 void got_muc_message(const char *from, LmMessageNode *x);
 void handle_muc_presence(const char *from, LmMessageNode * xmldata,
--- a/mcabber/mcabber/xmpp_s10n.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/xmpp_s10n.c	Tue Feb 02 22:44:18 2010 +0100
@@ -37,36 +37,32 @@
   lm_message_unref(x);
 }
 
-int evscallback_subscription(eviqs *evp, guint evcontext)
+gboolean evscallback_subscription(guint evcontext, const char *arg, gpointer userdata)
 {
-  char *barejid;
+  char *barejid = userdata;
   char *buf;
 
+  // Sanity check
+  if (G_UNLIKELY(!barejid)) {
+    // Shouldn't happen, data should be set to the barejid.
+    scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
+    return FALSE;
+  }
+
   if (evcontext == EVS_CONTEXT_TIMEOUT) {
-    scr_LogPrint(LPRINT_LOGNORM, "Event %s timed out, cancelled.",
-                 evp->id);
-    return 0;
+    scr_LogPrint(LPRINT_LOGNORM, "Subscription event for %s timed out, cancelled.",
+                 barejid);
+    return FALSE;
   }
   if (evcontext == EVS_CONTEXT_CANCEL) {
-    scr_LogPrint(LPRINT_LOGNORM, "Event %s cancelled.", evp->id);
-    return 0;
+    scr_LogPrint(LPRINT_LOGNORM, "Subscription event for %s cancelled.", barejid);
+    return FALSE;
   }
-  if (!(evcontext & EVS_CONTEXT_USER))
-    return 0;
-
-  // Sanity check
-  if (!evp->data) {
-    // Shouldn't happen, data should be set to the barejid.
-    scr_LogPrint(LPRINT_LOGNORM, "Error in evs callback.");
-    return 0;
-  }
+  if (!(evcontext == EVS_CONTEXT_REJECT || evcontext == EVS_CONTEXT_ACCEPT))
+    return FALSE;
 
   // Ok, let's work now.
-  // evcontext: 0, 1 == reject, accept
-
-  barejid = evp->data;
-
-  if (evcontext & ~EVS_CONTEXT_USER) {
+  if (evcontext == EVS_CONTEXT_ACCEPT) {
     // Accept subscription request
     xmpp_send_s10n(barejid, LM_MESSAGE_SUB_TYPE_SUBSCRIBED);
     buf = g_strdup_printf("<%s> is allowed to receive your presence updates",
@@ -84,7 +80,7 @@
   scr_WriteIncomingMessage(barejid, buf, 0, HBB_PREFIX_INFO, 0);
   scr_LogPrint(LPRINT_LOGNORM, "%s", buf);
   g_free(buf);
-  return 0;
+  return FALSE;
 }
 
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/mcabber/xmpp_s10n.h	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/xmpp_s10n.h	Tue Feb 02 22:44:18 2010 +0100
@@ -3,7 +3,7 @@
 
 #include <mcabber/events.h>
 
-int evscallback_subscription(eviqs *evp, guint evcontext);
+gboolean evscallback_subscription(guint evcontext, const char *arg, gpointer userdata);
 
 #endif /* __MCABBER_XMPP_S10N_H__ */