Add two functions to respond to Jingle IQ, jingle_error_iq and jingle_ack_iq.
authorNicolas Cornu <nicolas.cornu@ensi-bourges.fr>
Wed, 26 May 2010 04:29:17 +0200
changeset 6 a035ec60dc7f
parent 5 2155c65c7455
child 7 031bfc6d1770
Add two functions to respond to Jingle IQ, jingle_error_iq and jingle_ack_iq. Clean up the code.
CMakeLists.txt
jingle.c
jingle.h
parse.c
parse.h
--- a/CMakeLists.txt	Tue May 25 16:44:52 2010 +0200
+++ b/CMakeLists.txt	Wed May 26 04:29:17 2010 +0200
@@ -35,7 +35,7 @@
 				 ${MCABBER_LIBRARY_DIRS})
 
 ## Target definitions
-add_library(jingle MODULE jingle.c)
+add_library(jingle MODULE jingle.c parse.c)
 
 ## Compiler setup
 configure_file(config.h.in config.h)
--- a/jingle.c	Tue May 25 16:44:52 2010 +0200
+++ b/jingle.c	Wed May 26 04:29:17 2010 +0200
@@ -27,77 +27,131 @@
 #include <mcabber/hooks.h>
 #include <mcabber/modules.h>
 #include <mcabber/logprint.h>
+#include <mcabber/xmpp_helper.h>
+#include <mcabber/xmpp_defines.h>
 
 #include "jingle.h"
 #include "parse.h"
 
 
-static void mcjingle_init  (void);
-static void mcjingle_uninit(void);
+static void jingle_init  (void);
+static void jingle_uninit(void);
 
 
 LmMessageHandler* jingle_iq_handler = NULL;
 
 
 module_info_t info_jingle = {
-	.branch          = MCABBER_BRANCH,
-	.api             = MCABBER_API_VERSION,
-	.version         = MCABBER_VERSION,
-	.description     = "Main Jingle module\n"
-		" required for file transport, voip...\n",
-	.requires        = NULL,
-	.init            = mcjingle_init,
-	.uninit          = mcjingle_uninit,
-	.next            = NULL,
+  .branch          = MCABBER_BRANCH,
+  .api             = MCABBER_API_VERSION,
+  .version         = MCABBER_VERSION,
+  .description     = "Main Jingle module,"
+                     " required for file transport, voip...\n",
+  .requires        = NULL,
+  .init            = jingle_init,
+  .uninit          = jingle_uninit,
+  .next            = NULL,
 };
 
 
-LmHandlerResult jingle_iq_event_handler(LmMessageHandler *handler,
-		LmConnection *connection,
-		LmMessage *message,
-		gpointer user_data)
+LmHandlerResult jingle_handle_iq(LmMessageHandler *handler,
+                                 LmConnection *connection,
+                                 LmMessage *message,
+                                 gpointer user_data)
 {
-	LmMessageNode *test = lm_message_get_node(message)->children;
-	struct info_iq ii;
-	struct info_jingle ij;
+  //struct iq_data ii;
+  struct jingle_data ij;
+  LmMessageNode *root = lm_message_get_node(message)->children;
+  LmMessageNode *node = lm_message_node_get_child(root, "jingle");
 
-	parse_iq(lm_message_get_node(message), &ii);
+  if (!node)
+    return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; // no <jingle> element found
+  
+  if (g_strcmp0(lm_message_node_get_attribute(node, "xmlns"), NS_JINGLE)) {
+    scr_log_print(LPRINT_DEBUG, "jingle: Received a jingle IQ with an invalid namespace");
+    return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+  }
 
-	if (parse_jingle(test, &ij) == PARSE_ERROR_NAME)
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+  //parse_iq(lm_message_get_node(message), &ii);
 
-	if (strcmp(lm_message_node_get_attribute(test, "xmlns"), NS_JINGLE)) {
-		scr_log_print(LPRINT_NORMAL, "jingle : Namespace inconnu (%s)", lm_message_node_get_attribute(test, "xmlns"));
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-	}
+  if (parse_jingle(node, &ij) != PARSE_OK)
+    return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+    
+  scr_log_print(LPRINT_DEBUG, "jingle: Received a jingle IQ");
+
+  jingle_error_iq(message, "cancel", "feature-not-implemented", "unsupported-info");
 
-	if (!strcmp(ij.action, "content-accept")) {
-	} else if (!strcmp(ij.action, "content-add")) {
-	} else if (!strcmp(ij.action, "content-modify")) {
-	} else if (!strcmp(ij.action, "content-reject")) {
-	} else if (!strcmp(ij.action, "content-remove")) {
-	} else if (!strcmp(ij.action, "description-info")) {
-	} else if (!strcmp(ij.action, "security-info")) {
-	} else if (!strcmp(ij.action, "session-accept")) {
-	} else if (!strcmp(ij.action, "session-info")) {
-	} else if (!strcmp(ij.action, "session-initiate")) {
-	} else if (!strcmp(ij.action, "session-terminate")) {
-	} else if (!strcmp(ij.action, "transport-accept")) {
-	} else if (!strcmp(ij.action, "transport-info")) {
-	} else if (!strcmp(ij.action, "transport-reject")) {
-	} else if (!strcmp(ij.action, "transport-replace")) {
-	}
+  /*if (!strcmp(ij.action, "content-accept")) {
+  } else if (!strcmp(ij.action, "content-add")) {
+  } else if (!strcmp(ij.action, "content-modify")) {
+  } else if (!strcmp(ij.action, "content-reject")) {
+  } else if (!strcmp(ij.action, "content-remove")) {
+  } else if (!strcmp(ij.action, "description-info")) {
+  } else if (!strcmp(ij.action, "security-info")) {
+  } else if (!strcmp(ij.action, "session-accept")) {
+  } else if (!strcmp(ij.action, "session-info")) {
+  } else if (!strcmp(ij.action, "session-initiate")) {
+  } else if (!strcmp(ij.action, "session-terminate")) {
+  } else if (!strcmp(ij.action, "transport-accept")) {
+  } else if (!strcmp(ij.action, "transport-info")) {
+  } else if (!strcmp(ij.action, "transport-reject")) {
+  } else if (!strcmp(ij.action, "transport-replace")) {
+  }*/
+
+  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
 }
 
-static void mcjingle_init(void)
-{
-	jingle_iq_handler = lm_message_handler_new(jingle_iq_event_handler, NULL, NULL);
-	lm_connection_register_message_handler(lconnection, jingle_iq_handler, LM_MESSAGE_TYPE_IQ, LM_HANDLER_PRIORITY_FIRST);
+/**
+ * According to the specifications:
+ * "An entity that receives an IQ request of type "get" or "set" MUST
+ * reply with an IQ response of type "result" or "error"."
+ * For Jingle's IQ, we have to reply with an empty "result" IQ to acknowledge
+ * receipt.
+ */
+void jingle_ack_iq(LmMessage *m) {
+  LmMessage *r;
+
+  r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+  lm_connection_send(lconnection, r, NULL);
+  lm_message_unref(r);
 }
 
-static void mcjingle_uninit(void)
+/**
+ * Reply to a Jingle IQ with an error.
+ */
+void jingle_error_iq(LmMessage *m, const gchar *errtype,
+                     const gchar *cond, const gchar *jinglecond)
 {
-	lm_connection_unregister_message_handler(lconnection, jingle_iq_handler, LM_MESSAGE_TYPE_IQ);
-	lm_message_handler_invalidate(jingle_iq_handler);
-	lm_message_handler_unref(jingle_iq_handler);
+  LmMessage *r;
+  LmMessageNode *err, *tmpnode;
+
+  r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_ERROR);
+  err = lm_message_node_add_child(r->node, "error", NULL);
+  lm_message_node_set_attribute(err, "type", errtype);
+  
+  // error condition as defined by RFC 3920bis section 8.3.3
+  tmpnode = lm_message_node_add_child(err, cond, NULL);
+  lm_message_node_set_attribute(tmpnode, "xmlns", NS_XMPP_STANZAS);
+  
+  // jingle error condition as defined by XEP-0166 section 10
+  tmpnode = lm_message_node_add_child(err, jinglecond, NULL);
+  lm_message_node_set_attribute(tmpnode, "xmlns", NS_JINGLE_ERRORS);
+  
+  lm_connection_send(lconnection, r, NULL);
+  lm_message_unref(r);
 }
+
+static void jingle_init(void)
+{
+  jingle_iq_handler = lm_message_handler_new(jingle_handle_iq, NULL, NULL);
+  lm_connection_register_message_handler(lconnection, jingle_iq_handler, LM_MESSAGE_TYPE_IQ, LM_HANDLER_PRIORITY_FIRST);
+  xmpp_add_feature(NS_JINGLE);
+  xmpp_add_feature("urn:xmpp:jingle:apps:file-transfer:info:1"); // for debugging puposes only...
+}
+
+static void jingle_uninit(void)
+{
+  lm_connection_unregister_message_handler(lconnection, jingle_iq_handler, LM_MESSAGE_TYPE_IQ);
+  lm_message_handler_invalidate(jingle_iq_handler);
+  lm_message_handler_unref(jingle_iq_handler);
+}
--- a/jingle.h	Tue May 25 16:44:52 2010 +0200
+++ b/jingle.h	Wed May 26 04:29:17 2010 +0200
@@ -2,5 +2,11 @@
 #define __JINGLE_H__ 1
 
 #define NS_JINGLE "urn:xmpp:jingle:1"
+#define NS_JINGLE_ERRORS "urn:xmpp:jingle:errors:1"
+
+void jingle_error_iq(LmMessage *m, const gchar *errtype,
+                     const gchar *cond, const gchar *jinglecond);
+
+void jingle_ack_iq(LmMessage *m);
 
 #endif
--- a/parse.c	Tue May 25 16:44:52 2010 +0200
+++ b/parse.c	Wed May 26 04:29:17 2010 +0200
@@ -2,100 +2,108 @@
 #include <string.h>
 
 #include "parse.h"
+#include "jingle.h"
 
-int parse_jingle(LmMessageNode *node, struct info_jingle *ij)
+const gchar *jingle_actions[] = {
+  "content-accept",
+  "content-add",
+  "content-modify",
+  "content-reject",
+  "content-remove",
+  "description-info",
+  "security-info",
+  "session-accept",
+  "session-info",
+  "session-initiate",
+  "session-terminate",
+  "transport-accept",
+  "transport-info",
+  "transport-reject",
+  "transport-replace",
+  NULL
+};
+
+const gchar *jingle_content_creator[] = {
+  "initiator",
+  "responder",
+  NULL
+};
+
+const gchar *jingle_content_senders[] = {
+  "both",
+  "initiator",
+  "none",
+  "responder",
+  NULL
+};
+
+/**
+ * Populate a jingle_data struct from a <jingle> element.
+ * Check if the element is in compliance with the XEP.
+ */
+int check_jingle(LmMessageNode *node, struct jingle_data *ij)
 {
   int nb_reason = 0;
   LmMessageNode *child = NULL;
-  
-  if (!strcmp(ij->name, "jingle"))
-    return PARSE_ERROR_NAME;
 
-  ij->action    = g_strdup(lm_message_node_get_attribute(node, "action"));
-  ij->initiator = g_strdup(lm_message_node_get_attribute(node, "initiator"));
-  ij->responder = g_strdup(lm_message_node_get_attribute(node, "responder"));
-  ij->sid       = g_strdup(lm_message_node_get_attribute(node, "sid"));
-   
-  // check required
+  ij->action    = lm_message_node_get_attribute(node, "action");
+  ij->initiator = lm_message_node_get_attribute(node, "initiator");
+  ij->responder = lm_message_node_get_attribute(node, "responder");
+  ij->sid       = lm_message_node_get_attribute(node, "sid");
+
   if (ij->action == NULL || ij->sid == NULL)
-    return PARSE_ERROR_REQUIRED;
-    
-  // check restrictions
-  if (!check_restriction(ij->action, {"content-accept", "content-add",
-  "content-modify", "content-reject", "content-remove", "description-info",
-  "security-info", "session-accept", "session-info", "session-initiate",
-  "session-terminate", "transport-accept", "transport-info", "transport-reject",
-  "transport-replace", NULL}))
+    return PARSE_ERROR_REQUIRED; // those elements are required
+
+  if (!str_in_array(ij->action, jingle_actions))
    return PARSE_ERROR_RESTRICTION;
-  
+
   // check childs
   for (child = node->children; child; child = child->next) {
     if (!strcmp(child->name, "reason"))
       nb_reason++;
   }
-  
-  if (reason > 1)
+
+  if (nb_reason > 1)
     return PARSE_ERROR_TOO_MANY_CHILDS;
-    
+
   return PARSE_OK;
 }
 
-
-void free_jingle(struct info_jingle *ij)
-{
-  g_free(ij->action);
-  g_free(ij->initiator);
-  g_free(ij->responder);
-  g_free(ij->sid);
-}
-
-
-int parse_content(LmMessageNode* node, struct info_content* ic)
+int parse_content(LmMessageNode* node, struct content_data* ic)
 {
   if (!strcmp(ic->name, "content"))
     return PARSE_ERROR_NAME;
 
-  ic->creator     = g_strdup(lm_message_node_get_attribute(node, "creator"));
-  ic->disposition = g_strdup(lm_message_node_get_attribute(node, "disposition"));
-  ic->name        = g_strdup(lm_message_node_get_attribute(node, "name"));
-  ic->senders     = g_strdup(lm_message_node_get_attribute(node, "senders"));
+  ic->creator     = lm_message_node_get_attribute(node, "creator");
+  ic->disposition = lm_message_node_get_attribute(node, "disposition");
+  ic->name        = lm_message_node_get_attribute(node, "name");
+  ic->senders     = lm_message_node_get_attribute(node, "senders");
 
-  // Put default if none
   if (ic->disposition == NULL)
-    ic->disposition = g_strdup("session");
+    ic->disposition = "session";
 
-  // check required
   if (ic->creator == NULL || ic->name == NULL)
     return PARSE_ERROR_REQUIRED;
 
-  // check restrictions
-  if (!check_restriction(ic->creator, {"initiator", "responder", NULL}))
+  if (!str_in_array(ic->creator, jingle_content_creator))
     return PARSE_ERROR_RESTRICTION;
-  if (!check_restriction(ic->senders, {"both", "initiator", "none", "responder", NULL}))
-    ic->senders = NULL; // because it's optional
-    
+  if (!str_in_array(ic->senders, jingle_content_senders))
+    ic->senders = NULL;
+
   return PARSE_OK;
 }
 
-
-void free_content(struct info_content *ic)
+/**
+ * Check if needle exists in haystack.
+ * The last element of haystack must be NULL.
+ */
+gint str_in_array(const gchar* needle, const gchar** haystack)
 {
-  g_free(ic->creator);
-  g_free(ic->disposition);
-  g_free(ic->name);
-  g_free(ic->senders);
-}
-
+  const gchar* value;
+  gint found = 0;
+  for (value = haystack[0]; value && !found; value++)
+    if (!g_strcmp0(needle, value))
+      found = 1;
 
-int check_restriction(const gchar* name, const gchar** values)
-{
-  const char* value;
-  int found = 0;
-  value = values[0];
-  while (value && !found) {
-    if (!strcmp(name, value))
-      found = 1;
-    value++;
-  }
   return found;
 }
--- a/parse.h	Tue May 25 16:44:52 2010 +0200
+++ b/parse.h	Wed May 26 04:29:17 2010 +0200
@@ -10,36 +10,32 @@
 #define PARSE_ERROR_RESTRICTION     3
 #define PARSE_ERROR_TOO_MANY_CHILDS 4
 
-struct info_iq {
-  gchar* from;
-  gchar* id;
-  gchar* to;
-  gchar* set;
+
+struct iq_data {
+  const gchar *from;
+  const gchar *id;
+  const gchar *to;
+  const gchar *set;
+};
+
+struct jingle_data {
+  const gchar* action;    // see jingle_action in jingle.h
+  const gchar* initiator; // optional
+  const gchar* responder;  // optional
+  const gchar* sid;        // required
 };
 
-struct info_jingle {
-  gchar* action;      // required (content-accept, content-add,
-  // content-modify, content-reject, content-remove, description-info
-  // security-info, session-accept, session-info, session-initiate,
-  // session-terminate, transport-accept, transport-info, transport-reject,
-  // transport-replace)
-  gchar* initiator;   // optional
-  gchar* responder;   // optional
-  gchar* sid;         // required
+struct content_data {
+  const gchar* creator;     // required (initiator, responder)
+  const gchar* disposition; // optional, default=session
+  const gchar* name;        // required
+  const gchar* senders;     // optional (both, initiator, none, responder)
 };
 
-struct info_content {
-  gchar* creator;     // required (initiator, responder)
-  gchar* disposition; // optional, default=session
-  gchar* name;        // required
-  gchar* senders;     // optional (both, initiator, none, responder)
-};
 
-int parse_jingle(LmMessageNode* node, struct info_jingle* ij);
-void free_jingle(struct info_jingle* ij);
-int parse_content(LmMessageNode* node, struct info_content* ic);
-void free_content(struct info_content* ic);
+int parse_jingle(LmMessageNode* node, struct jingle_data* ij);
+int parse_content(LmMessageNode* node, struct content_data* ic);
 
-int check_restriction(const char* name, const char** values);
+gint str_in_array(const gchar* needle, const gchar** haystack);
 
 #endif