Add a timeout to the AckHandlers.
authorNicolas Cornu <nicolas.cornu@ensi-bourges.fr>
Mon, 16 Aug 2010 02:04:50 +0200
changeset 129 2d5c3efda92a
parent 128 cded9f40039e
child 130 da01e4b2d7fa
Add a timeout to the AckHandlers.
jingle/jingle.c
jingle/jingle.h
jingle/send.c
--- a/jingle/jingle.c	Sun Aug 15 22:12:04 2010 +0200
+++ b/jingle/jingle.c	Mon Aug 16 02:04:50 2010 +0200
@@ -49,6 +49,7 @@
 
 static LmMessageHandler* jingle_iq_handler = NULL;
 static GSList *ack_handlers = NULL;
+static guint ack_timeout_checker = 0;
 static guint connect_hid = 0;
 static guint disconn_hid = 0;
 
@@ -144,27 +145,56 @@
                                      LmConnection *connection, 
                                      LmMessage *message, gpointer user_data)
 {
-  ack_handlers = g_slist_remove(ack_handlers, handler);
-  lm_message_handler_unref(handler);
-
   // TODO: check subtype
   if (user_data != NULL) {
     JingleAckHandle *ah = user_data;
     if(ah->callback != NULL)
-      ah->callback(message, ah->user_data);
+      ah->callback(JINGLE_ACK_RESPONSE, message, ah->user_data);
 
-    g_free(ah);
+    jingle_ack_handler_free(ah);
   }
 
   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
 }
 
+gboolean jingle_ack_timeout_checker(gpointer user_data)
+{
+  GSList *el;
+  time_t now = time(NULL);
+  JingleAckHandle *ah;
+
+  for (el = ack_handlers; el; el = g_slist_next(el)) {
+	JingleAckHandle *ah = el->data;
+
+    if (ah->timeout == 0 || ah->_inserted + ah->timeout > now)
+	  continue;
+
+    if(ah->callback != NULL)
+      ah->callback(JINGLE_ACK_TIMEOUT, NULL, ah->user_data);
+
+    lm_message_handler_unref(ah->_handler);
+    jingle_ack_handler_free(ah);
+  }
+  return TRUE;
+}
+
 LmMessageHandler *jingle_new_ack_handler(JingleAckHandle *ah)
 {
-  LmMessageHandler *h = lm_message_handler_new(jingle_handle_ack_iq,
-                                               (gpointer) ah, NULL);
-  ack_handlers = g_slist_append(ack_handlers, h);
-  return h;
+  if(ack_timeout_checker == 0)
+	  ack_timeout_checker = g_timeout_add_seconds(3, jingle_ack_timeout_checker, NULL);
+  
+  ah->_inserted = time(NULL);
+  ah->_handler = lm_message_handler_new(jingle_handle_ack_iq,
+                                        (gpointer) ah, NULL);
+  ack_handlers = g_slist_append(ack_handlers, ah);
+  return ah->_handler;
+}
+
+void jingle_ack_handler_free(JingleAckHandle *ah)
+{
+  lm_message_handler_unref(ah->_handler);
+  ack_handlers = g_slist_remove(ack_handlers, ah);
+  g_free(ah);
 }
 
 /**
@@ -382,6 +412,11 @@
 
   lm_message_handler_invalidate(jingle_iq_handler);
   lm_message_handler_unref(jingle_iq_handler);
+
+  if (ack_timeout_checker != 0) {
+    GSource *s = g_main_context_find_source_by_id(NULL, ack_timeout_checker);
+    g_source_destroy(s);
+  }
 }
 
 void handle_trans_data(const gchar *xmlns, gconstpointer data, const gchar *data2, guint len)
--- a/jingle/jingle.h	Sun Aug 15 22:12:04 2010 +0200
+++ b/jingle/jingle.h	Mon Aug 16 02:04:50 2010 +0200
@@ -108,11 +108,31 @@
   void (*handler)(JingleNode *);
 };
 
-typedef void (*JingleAckCallback) (LmMessage *, gpointer);
+typedef enum {
+  JINGLE_ACK_RESPONSE,
+  JINGLE_ACK_TIMEOUT
+} JingleAckType;
+
+typedef void (*JingleAckCallback) (JingleAckType type, LmMessage *, gpointer);
 
 typedef struct {
+  /* function to be called when we receive a response to the IQ */
   JingleAckCallback callback;
+
+  /* additional data to pass to callback */
   gpointer *user_data;
+
+  /* if no response was received after timeout seconds, callback
+   * will be called with JINGLE_ACK_TIMEOUT as type */
+  guint timeout;
+
+  /* (private) date at which the handler was inserted using 
+   * jingle_new_ack_handler */
+  time_t _inserted;
+  
+  /* (private) a pointer to the LmMessageHandler created
+   * using jingle_new_ack_handler */
+  LmMessageHandler *_handler;
 } JingleAckHandle;
 
 typedef struct {
@@ -125,6 +145,7 @@
                                      LmConnection *connection, 
                                      LmMessage *message, gpointer user_data);
 LmMessageHandler *jingle_new_ack_handler(JingleAckHandle *ri);
+void jingle_ack_handler_free(JingleAckHandle *ah);
 
 LmMessage *jingle_new_iq_error(LmMessage *m, const gchar *errtype,
                                const gchar *cond, const gchar *jinglecond);
--- a/jingle/send.c	Sun Aug 15 22:12:04 2010 +0200
+++ b/jingle/send.c	Mon Aug 16 02:04:50 2010 +0200
@@ -60,18 +60,26 @@
   lm_message_unref(r);
 }
 
-static void jingle_handle_ack_iq_sa(LmMessage *mess, gpointer data)
+static void jingle_handle_ack_iq_sa(JingleAckType acktype, LmMessage *mess,
+                                    gpointer data)
 {
   LmMessageNode *node;
   const gchar *type, *cause;
   GSList *child = NULL;
   JingleSession *sess = (JingleSession*)data;
   SessionContent *sc;
-  
+
+  if (acktype == JINGLE_ACK_TIMEOUT) {
+    // TODO: handle ack timeout...
+    scr_LogPrint(LPRINT_LOGNORM, "Jingle: session-accept %s: %s", type, cause);
+    session_delete(sess);
+    return;
+  }
+
   if(lm_message_get_sub_type(mess) == LM_MESSAGE_SUB_TYPE_RESULT) {
     return;
   }
-  
+
   if(lm_message_get_sub_type(mess) == LM_MESSAGE_SUB_TYPE_ERROR) {
     node = lm_message_get_node(mess);
     node = lm_message_node_get_child(node,"error");
@@ -97,22 +105,30 @@
     ackhandle = g_new0(JingleAckHandle, 1);
     ackhandle->callback = jingle_handle_ack_iq_sa;
     ackhandle->user_data = (gpointer)js;
-        scr_log_print(LPRINT_DEBUG,
-                  "%s", lm_message_node_to_string(mess->node));lm_connection_send_with_reply(lconnection, mess,
+    ackhandle->timeout = 60;
+    lm_connection_send_with_reply(lconnection, mess,
                                   jingle_new_ack_handler(ackhandle), NULL);
     lm_message_unref(mess);
   }
 }
 
-static void jingle_handle_ack_iq_si(LmMessage *mess, gpointer data)
+static void jingle_handle_ack_iq_si(JingleAckType acktype, LmMessage *mess,
+                                    gpointer data)
 {
   LmMessageNode *node;
   const gchar *type, *cause;
   JingleSession *sess = (JingleSession*)data;
-  
-  if(lm_message_get_sub_type(mess) == LM_MESSAGE_SUB_TYPE_RESULT)
+
+  if (acktype == JINGLE_ACK_TIMEOUT) {
+    // TODO: handle ack timeout...
+    scr_LogPrint(LPRINT_LOGNORM, "Jingle: did not receive the ack in time, aborting");
+    session_delete(sess);
     return;
-  if(lm_message_get_sub_type(mess) == LM_MESSAGE_SUB_TYPE_ERROR) {
+  }
+
+  if (lm_message_get_sub_type(mess) == LM_MESSAGE_SUB_TYPE_RESULT)
+    return;
+  if (lm_message_get_sub_type(mess) == LM_MESSAGE_SUB_TYPE_ERROR) {
     node = lm_message_get_node(mess);
     node = lm_message_node_get_child(node,"error");
     type = lm_message_node_get_attribute(node, "type");
@@ -128,7 +144,7 @@
 {
   JingleAckHandle *ackhandle;
   GSList *listentry;
-  GError *err;
+  GError *err = NULL;
   gboolean status;
   
   LmMessage *mess = lm_message_from_jinglesession(js, JINGLE_SESSION_INITIATE);
@@ -139,8 +155,7 @@
     ackhandle = g_new0(JingleAckHandle, 1);
     ackhandle->callback = jingle_handle_ack_iq_si;
     ackhandle->user_data = (gpointer)js;
-    scr_log_print(LPRINT_DEBUG,
-                  "%s", lm_message_node_to_string(mess->node));
+    ackhandle->timeout = 60;
     status = lm_connection_send_with_reply(lconnection, mess,
                                        jingle_new_ack_handler(ackhandle), &err);
     // TODO: delete the ack_handler