about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2014-09-06 22:40:57 +0100
committerJames Booth <boothj5@gmail.com>2014-09-06 22:40:57 +0100
commit2599c43d66915854d6f8658d95ce79eeb1123c35 (patch)
tree4ae99b2e4feb635259b18125b2769dbdec989612 /src
parent157a1b5ff75d706d5dda11c4208b05daa1552791 (diff)
downloadprofani-tty-2599c43d66915854d6f8658d95ce79eeb1123c35.tar.gz
Rewrite form parser
Diffstat (limited to 'src')
-rw-r--r--src/main.c1
-rw-r--r--src/server_events.c7
-rw-r--r--src/xmpp/capabilities.c8
-rw-r--r--src/xmpp/form.c268
-rw-r--r--src/xmpp/iq.c3
-rw-r--r--src/xmpp/xmpp.h14
6 files changed, 227 insertions, 74 deletions
diff --git a/src/main.c b/src/main.c
index 898cdaf3..e4643d2e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -63,6 +63,7 @@ _init_modules(void)
     message_init_module();
     presence_init_module();
     roster_init_module();
+    form_init_module();
 
     ui_init_module();
     console_init_module();
diff --git a/src/server_events.c b/src/server_events.c
index 2fe6ace1..3cf299ab 100644
--- a/src/server_events.c
+++ b/src/server_events.c
@@ -467,11 +467,6 @@ void
 handle_room_configure(const char * const room, DataForm *form)
 {
     cons_show("Recieved configuration form for %s", room);
-    if (form->form_type != NULL) {
-        cons_show("Form type: %s", form->form_type);
-    } else {
-        cons_show("No form type specified");
-    }
 
     GSList *fields = form->fields;
     GSList *curr = fields;
@@ -492,6 +487,8 @@ handle_room_configure(const char * const room, DataForm *form)
 
         curr = g_slist_next(curr);
     }
+
+    form_destroy(form);
 }
 
 void
diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c
index c0c46ab7..942d0697 100644
--- a/src/xmpp/capabilities.c
+++ b/src/xmpp/capabilities.c
@@ -172,8 +172,9 @@ caps_create_sha1_str(xmpp_stanza_t * const query)
         } else if (g_strcmp0(xmpp_stanza_get_name(child), STANZA_NAME_X) == 0) {
             if (strcmp(xmpp_stanza_get_ns(child), STANZA_NS_DATA) == 0) {
                 form = form_create(child);
-                form_names = g_slist_insert_sorted(form_names, g_strdup(form->form_type), (GCompareFunc)strcmp);
-                g_hash_table_insert(forms, g_strdup(form->form_type), form);
+                char *form_type = form_get_field_by_var(form, "FORM_TYPE");
+                form_names = g_slist_insert_sorted(form_names, g_strdup(form_type), (GCompareFunc)strcmp);
+                g_hash_table_insert(forms, g_strdup(form_type), form);
             }
         }
         child = xmpp_stanza_get_next(child);
@@ -195,7 +196,8 @@ caps_create_sha1_str(xmpp_stanza_t * const query)
     curr = form_names;
     while (curr != NULL) {
         form = g_hash_table_lookup(forms, curr->data);
-        g_string_append(s, form->form_type);
+        char *form_type = form_get_field_by_var(form, "FORM_TYPE");
+        g_string_append(s, form_type);
         g_string_append(s, "<");
 
         GSList *curr_field = form->fields;
diff --git a/src/xmpp/form.c b/src/xmpp/form.c
index 237cbdd7..f352feab 100644
--- a/src/xmpp/form.c
+++ b/src/xmpp/form.c
@@ -36,109 +36,249 @@
 #include <stdlib.h>
 
 #include <strophe.h>
+#include <glib.h>
 
+#include "log.h"
 #include "xmpp/xmpp.h"
+#include "xmpp/stanza.h"
 #include "xmpp/connection.h"
 
-static int _field_compare(FormField *f1, FormField *f2);
+static gboolean
+_is_valid_form_element(xmpp_stanza_t *stanza)
+{
+    char *name = xmpp_stanza_get_name(stanza);
+    if (g_strcmp0(name, STANZA_NAME_X) != 0) {
+        log_error("Error parsing form, root element not <x/>.");
+        return FALSE;
+    }
 
-DataForm *
-form_create(xmpp_stanza_t * const stanza)
+    char *ns = xmpp_stanza_get_ns(stanza);
+    if (g_strcmp0(ns, STANZA_NS_DATA) != 0) {
+        log_error("Error parsing form, namespace not %s.", STANZA_NS_DATA);
+        return FALSE;
+    }
+
+    char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+    if ((g_strcmp0(type, "form") != 0) &&
+            (g_strcmp0(type, "submit") != 0) &&
+            (g_strcmp0(type, "cancel") != 0) &&
+            (g_strcmp0(type, "result") != 0)) {
+        log_error("Error parsing form, unknown type.");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static DataForm *
+_form_new(void)
 {
-    DataForm *result = NULL;
+    DataForm *form = malloc(sizeof(DataForm));
+    form->type = NULL;
+    form->title = NULL;
+    form->instructions = NULL;
+    form->fields = NULL;
+
+    return form;
+}
+
+static FormField *
+_field_new(void)
+{
+    FormField *field = malloc(sizeof(FormField));
+    field->label = NULL;
+    field->type = NULL;
+    field->var = NULL;
+    field->description = NULL;
+    field->required = FALSE;
+    field->values = NULL;
+    field->options = NULL;
+
+    return field;
+}
+
+static char *
+_get_property(xmpp_stanza_t * const stanza, const char * const property)
+{
+    char *result = NULL;
     xmpp_ctx_t *ctx = connection_get_ctx();
 
-    xmpp_stanza_t *child = xmpp_stanza_get_children(stanza);
+    xmpp_stanza_t *child = xmpp_stanza_get_child_by_name(stanza, property);
+    if (child != NULL) {
+        char *child_text = xmpp_stanza_get_text(child);
+        if (child_text != NULL) {
+            result = strdup(child_text);
+            xmpp_free(ctx, child_text);
+        }
+    }
+
+    return result;
+}
+
+static char *
+_get_attr(xmpp_stanza_t * const stanza, const char * const attr)
+{
+    char *result = xmpp_stanza_get_attribute(stanza, attr);
+    if (result != NULL) {
+        return strdup(result);
+    } else {
+        return NULL;
+    }
+}
 
+static gboolean
+_is_required(xmpp_stanza_t * const stanza)
+{
+    xmpp_stanza_t *child = xmpp_stanza_get_child_by_name(stanza, "required");
     if (child != NULL) {
-        result = malloc(sizeof(DataForm));
-        result->form_type = NULL;
-        result->fields = NULL;
+        return TRUE;
+    } else {
+        return FALSE;
     }
+}
 
-    //handle fields
-    while (child != NULL) {
-        char *label = xmpp_stanza_get_attribute(child, "label");
-        char *type = xmpp_stanza_get_attribute(child, "type");
-        char *var = xmpp_stanza_get_attribute(child, "var");
-
-        // handle FORM_TYPE
-        if (g_strcmp0(var, "FORM_TYPE") == 0) {
-            xmpp_stanza_t *value = xmpp_stanza_get_child_by_name(child, "value");
-            char *value_text = xmpp_stanza_get_text(value);
-            if (value_text != NULL) {
-                result->form_type = strdup(value_text);
-                xmpp_free(ctx, value_text);
-            }
+DataForm *
+form_create(xmpp_stanza_t * const form_stanza)
+{
+    xmpp_ctx_t *ctx = connection_get_ctx();
 
-        // handle regular fields
-        } else {
-            FormField *field = malloc(sizeof(FormField));
-            field->label = NULL;
-            field->type = NULL;
-            field->var = NULL;
+    if (!_is_valid_form_element(form_stanza)) {
+        return NULL;
+    }
 
-            if (label != NULL) {
-                field->label = strdup(label);
-            }
-            if (type != NULL) {
-                field->type = strdup(type);
-            }
-            if (var != NULL) {
-                field->var = strdup(var);
-            }
+    DataForm *form = _form_new();
+    form->type = _get_attr(form_stanza, "type");
+    form->title = _get_property(form_stanza, "title");
+    form->instructions = _get_property(form_stanza, "instructions");
+
+    // get fields
+    xmpp_stanza_t *form_child = xmpp_stanza_get_children(form_stanza);
+    while (form_child != NULL) {
+        char *child_name = xmpp_stanza_get_name(form_child);
+        if (g_strcmp0(child_name, "field") == 0) {
+            xmpp_stanza_t *field_stanza = form_child;
+
+            FormField *field = _field_new();
+            field->label = _get_attr(field_stanza, "label");
+            field->type = _get_attr(field_stanza, "type");
+            field->var = _get_attr(field_stanza, "var");
+            field->description = _get_property(field_stanza, "desc");
+            field->required = _is_required(field_stanza);
+
+            // handle repeated field children
+            xmpp_stanza_t *field_child = xmpp_stanza_get_children(field_stanza);
+            while (field_child != NULL) {
+                // handle values
+                if (g_strcmp0(child_name, "value") == 0) {
+                    char *value = xmpp_stanza_get_text(field_child);
+                    if (value != NULL) {
+                        field->values = g_slist_append(field->values, value);
+                        xmpp_free(ctx, value);
+                    }
+
+                // handle options
+                } else if (g_strcmp0(child_name, "option") == 0) {
+                    FormOption *option = malloc(sizeof(FormOption));
+                    option->label = _get_attr(field_child, "label");
+                    option->value = _get_property(field_child, "value");
 
-            // handle values
-            field->values = NULL;
-            xmpp_stanza_t *value = xmpp_stanza_get_children(child);
-            while (value != NULL) {
-                char *text = xmpp_stanza_get_text(value);
-                if (text != NULL) {
-                    field->values = g_slist_insert_sorted(field->values, strdup(text), (GCompareFunc)strcmp);
-                    xmpp_free(ctx, text);
+                    field->options = g_slist_append(field->options, option);
                 }
-                value = xmpp_stanza_get_next(value);
+
+                field_child = xmpp_stanza_get_next(field_child);
             }
 
-            result->fields = g_slist_insert_sorted(result->fields, field, (GCompareFunc)_field_compare);
+            form->fields = g_slist_append(form->fields, field);
         }
 
-        child = xmpp_stanza_get_next(child);
+        form_child = xmpp_stanza_get_next(form_child);
     }
 
-    return result;
+    return form;
 }
 
-void
+static void
+_free_option(FormOption *option)
+{
+    if (option != NULL) {
+        if (option->label != NULL) {
+            free(option->label);
+        }
+        if (option->value != NULL) {
+            free(option->value);
+        }
+
+        free(option);
+    }
+}
+
+static void
+_free_field(FormField *field)
+{
+    if (field != NULL) {
+        if (field->label != NULL) {
+            free(field->label);
+        }
+        if (field->type != NULL) {
+            free(field->type);
+        }
+        if (field->var != NULL) {
+            free(field->var);
+        }
+        if (field->description != NULL) {
+            free(field->description);
+        }
+        if (field->values != NULL) {
+            g_slist_free_full(field->values, free);
+        }
+        if (field->options != NULL) {
+            g_slist_free_full(field->options, (GDestroyNotify)_free_option);
+        }
+
+        free(field);
+    }
+}
+
+static void
 _form_destroy(DataForm *form)
 {
     if (form != NULL) {
+        if (form->type != NULL) {
+            free(form->type);
+        }
+        if (form->title != NULL) {
+            free(form->title);
+        }
+        if (form->instructions != NULL) {
+            free(form->instructions);
+        }
+
         if (form->fields != NULL) {
-            GSList *curr_field = form->fields;
-            while (curr_field != NULL) {
-                FormField *field = curr_field->data;
-                free(field->var);
-                if ((field->values) != NULL) {
-                    g_slist_free_full(field->values, free);
-                }
-                curr_field = curr_field->next;
-            }
-            g_slist_free_full(form->fields, free);
+            g_slist_free_full(form->fields, (GDestroyNotify)_free_field);
         }
 
-        free(form->form_type);
         free(form);
     }
 }
 
-static int
-_field_compare(FormField *f1, FormField *f2)
+static char *
+_form_get_field_by_var(DataForm *form, const char * const var)
 {
-    return strcmp(f1->var, f2->var);
+    GSList *curr = form->fields;
+    while (curr != NULL) {
+        FormField *field = curr->data;
+        if (g_strcmp0(field->var, var) == 0) {
+            return field->values->data;
+        }
+        curr = g_slist_next(curr);
+    }
+
+    return NULL;
 }
 
 void
 form_init_module(void)
 {
     form_destroy = _form_destroy;
+    form_get_field_by_var = _form_get_field_by_var;
 }
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index cff0da1b..2431f151 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -770,7 +770,8 @@ _disco_info_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanz
             DataForm *form = form_create(softwareinfo);
             FormField *formField = NULL;
 
-            if (g_strcmp0(form->form_type, STANZA_DATAFORM_SOFTWARE) == 0) {
+            char *form_type = form_get_field_by_var(form, "FORM_TYPE");
+            if (g_strcmp0(form_type, STANZA_DATAFORM_SOFTWARE) == 0) {
                 GSList *field = form->fields;
                 while (field != NULL) {
                     formField = field->data;
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 063062a0..6510a50e 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -86,15 +86,25 @@ typedef struct disco_identity_t {
     char *category;
 } DiscoIdentity;
 
+typedef struct form_option_t {
+    char *label;
+    char *value;
+} FormOption;
+
 typedef struct form_field_t {
     char *label;
     char *type;
     char *var;
+    char *description;
+    gboolean required;
     GSList *values;
+    GSList *options;
 } FormField;
 
 typedef struct data_form_t {
-    char *form_type;
+    char *type;
+    char *title;
+    char *instructions;
     GSList *fields;
 } DataForm;
 
@@ -105,6 +115,7 @@ void iq_init_module(void);
 void message_init_module(void);
 void presence_init_module(void);
 void roster_init_module(void);
+void form_init_module(void);
 
 // connection functions
 void (*jabber_init)(const int disable_tls);
@@ -176,5 +187,6 @@ void (*roster_send_add_new)(const char * const barejid, const char * const name)
 void (*roster_send_remove)(const char * const barejid);
 
 void (*form_destroy)(DataForm *form);
+char * (*form_get_field_by_var)(DataForm *form, const char * const var);
 
 #endif