about summary refs log tree commit diff stats
path: root/src/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpp')
-rw-r--r--src/xmpp/capabilities.c13
-rw-r--r--src/xmpp/form.c258
-rw-r--r--src/xmpp/form.h40
-rw-r--r--src/xmpp/iq.c84
-rw-r--r--src/xmpp/stanza.c135
-rw-r--r--src/xmpp/stanza.h15
-rw-r--r--src/xmpp/xmpp.h28
7 files changed, 470 insertions, 103 deletions
diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c
index 2fb9f065..942d0697 100644
--- a/src/xmpp/capabilities.c
+++ b/src/xmpp/capabilities.c
@@ -47,6 +47,7 @@
 #include "common.h"
 #include "xmpp/xmpp.h"
 #include "xmpp/stanza.h"
+#include "xmpp/form.h"
 
 static GHashTable *capabilities;
 
@@ -137,7 +138,7 @@ caps_create_sha1_str(xmpp_stanza_t * const query)
     GSList *form_names = NULL;
     DataForm *form = NULL;
     FormField *field = NULL;
-    GHashTable *forms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)stanza_destroy_form);
+    GHashTable *forms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)form_destroy);
 
     GString *s = g_string_new("");
 
@@ -170,9 +171,10 @@ caps_create_sha1_str(xmpp_stanza_t * const query)
             features = g_slist_insert_sorted(features, g_strdup(feature_str), (GCompareFunc)strcmp);
         } 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 = stanza_create_form(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);
+                form = form_create(child);
+                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);
@@ -194,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
new file mode 100644
index 00000000..002f366c
--- /dev/null
+++ b/src/xmpp/form.c
@@ -0,0 +1,258 @@
+/*
+ * form.c
+ *
+ * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#include <string.h>
+#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 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;
+    }
+
+    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 *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_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) {
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+DataForm *
+form_create(xmpp_stanza_t * const form_stanza)
+{
+    xmpp_ctx_t *ctx = connection_get_ctx();
+
+    if (!_is_valid_form_element(form_stanza)) {
+        return NULL;
+    }
+
+    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) {
+                child_name = xmpp_stanza_get_name(field_child);
+
+                // 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, strdup(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");
+
+                    field->options = g_slist_append(field->options, option);
+                }
+
+                field_child = xmpp_stanza_get_next(field_child);
+            }
+
+            form->fields = g_slist_append(form->fields, field);
+        }
+
+        form_child = xmpp_stanza_get_next(form_child);
+    }
+
+    return form;
+}
+
+static void
+_free_option(FormOption *option)
+{
+    if (option != NULL) {
+        free(option->label);
+        free(option->value);
+        free(option);
+    }
+}
+
+static void
+_free_field(FormField *field)
+{
+    if (field != NULL) {
+        free(field->label);
+        free(field->type);
+        free(field->var);
+        free(field->description);
+        g_slist_free_full(field->values, free);
+        g_slist_free_full(field->options, (GDestroyNotify)_free_option);
+        free(field);
+    }
+}
+
+static void
+_form_destroy(DataForm *form)
+{
+    if (form != NULL) {
+        free(form->type);
+        free(form->title);
+        free(form->instructions);
+        g_slist_free_full(form->fields, (GDestroyNotify)_free_field);
+        free(form);
+    }
+}
+
+static char *
+_form_get_field_by_var(DataForm *form, const char * const 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/form.h b/src/xmpp/form.h
new file mode 100644
index 00000000..b0c32675
--- /dev/null
+++ b/src/xmpp/form.h
@@ -0,0 +1,40 @@
+/*
+ * form.h
+ *
+ * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef FORM_H
+#define FROM_H
+
+DataForm* form_create(xmpp_stanza_t * const stanza);
+
+#endif
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index 2224173e..9d72207e 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -52,6 +52,7 @@
 #include "xmpp/capabilities.h"
 #include "xmpp/connection.h"
 #include "xmpp/stanza.h"
+#include "xmpp/form.h"
 #include "roster_list.h"
 #include "xmpp/xmpp.h"
 
@@ -75,6 +76,8 @@ static int _disco_items_get_handler(xmpp_conn_t * const conn,
     xmpp_stanza_t * const stanza, void * const userdata);
 static int _destroy_room_result_handler(xmpp_conn_t * const conn,
     xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_config_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, void * const userdata);
 static int _manual_pong_handler(xmpp_conn_t *const conn,
     xmpp_stanza_t * const stanza, void * const userdata);
 static int _ping_timed_handler(xmpp_conn_t * const conn,
@@ -99,8 +102,6 @@ iq_add_handlers(void)
 
     HANDLE(STANZA_NS_PING,      STANZA_TYPE_GET,    _ping_get_handler);
 
-    HANDLE(NULL,                STANZA_TYPE_RESULT, _destroy_room_result_handler);
-
     if (prefs_get_autoping() != 0) {
         int millis = prefs_get_autoping() * 1000;
         xmpp_timed_handler_add(conn, _ping_timed_handler, millis, ctx);
@@ -189,6 +190,30 @@ _iq_destroy_instant_room(const char * const room_jid)
 }
 
 static void
+_iq_request_room_config_form(const char * const room_jid)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+    xmpp_stanza_t *iq = stanza_create_room_config_request_iq(ctx, room_jid);
+
+    char *id = xmpp_stanza_get_id(iq);
+    xmpp_id_handler_add(conn, _room_config_handler, id, NULL);
+
+    xmpp_send(conn, iq);
+    xmpp_stanza_release(iq);
+}
+
+static void
+_iq_room_config_cancel(const char * const room_jid)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+    xmpp_stanza_t *iq = stanza_create_room_config_cancel_iq(ctx, room_jid);
+    xmpp_send(conn, iq);
+    xmpp_stanza_release(iq);
+}
+
+static void
 _iq_send_ping(const char * const target)
 {
     xmpp_conn_t * const conn = connection_get_conn();
@@ -565,6 +590,52 @@ _destroy_room_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const sta
     return 0;
 }
 
+static int
+_room_config_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    void * const userdata)
+{
+    // TODO handle errors
+    const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
+
+    if (id != NULL) {
+        log_debug("IQ room config handler fired, id: %s.", id);
+    } else {
+        log_debug("IQ room config handler fired.");
+    }
+
+    const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+    if (from == NULL) {
+        log_error("No from attribute for IQ destroy room result");
+    } else {
+        // get form
+        xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
+        if (query == NULL) {
+            log_error("No query element found parsing room config response");
+            handle_room_configuration_form_error();
+            return 0;
+        }
+
+        xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(query, STANZA_NS_DATA);
+        if (x == NULL) {
+            log_error("No x element found with %s namespace parsing room config response", STANZA_NS_DATA);
+            handle_room_configuration_form_error();
+            return 0;
+        }
+
+        char *type = xmpp_stanza_get_attribute(x, STANZA_ATTR_TYPE);
+        if (g_strcmp0(type, "form") != 0) {
+            log_error("x element not of type 'form' parsing room config response");
+            handle_room_configuration_form_error();
+            return 0;
+        }
+
+        DataForm *form = form_create(x);
+        handle_room_configure(from, form);
+    }
+
+    return 0;
+}
+
 static void
 _identity_destroy(DiscoIdentity *identity)
 {
@@ -711,10 +782,11 @@ _disco_info_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanz
 
         xmpp_stanza_t *softwareinfo = xmpp_stanza_get_child_by_ns(query, STANZA_NS_DATA);
         if (softwareinfo != NULL) {
-            DataForm *form = stanza_create_form(softwareinfo);
+            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;
@@ -733,7 +805,7 @@ _disco_info_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanz
                 }
             }
 
-            stanza_destroy_form(form);
+            form_destroy(form);
         }
 
         xmpp_stanza_t *child = xmpp_stanza_get_children(query);
@@ -814,4 +886,6 @@ iq_init_module(void)
     iq_confirm_instant_room = _iq_confirm_instant_room;
     iq_destroy_instant_room = _iq_destroy_instant_room;
     iq_send_ping = _iq_send_ping;
+    iq_request_room_config_form = _iq_request_room_config_form;
+    iq_room_config_cancel = _iq_room_config_cancel;
 }
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index 654ba121..519862f7 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -46,8 +46,6 @@
 
 #include "muc.h"
 
-static int _field_compare(FormField *f1, FormField *f2);
-
 #if 0
 xmpp_stanza_t *
 stanza_create_bookmarks_pubsub_request(xmpp_ctx_t *ctx)
@@ -430,7 +428,7 @@ stanza_create_instant_room_request_iq(xmpp_ctx_t *ctx, const char * const room_j
     xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
     xmpp_stanza_set_type(iq, STANZA_TYPE_SET);
     xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, room_jid);
-    char *id = create_unique_id("leave");
+    char *id = create_unique_id("room");
     xmpp_stanza_set_id(iq, id);
     free(id);
 
@@ -459,7 +457,7 @@ stanza_create_instant_room_destroy_iq(xmpp_ctx_t *ctx, const char * const room_j
     xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
     xmpp_stanza_set_type(iq, STANZA_TYPE_SET);
     xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, room_jid);
-    char *id = create_unique_id("leave");
+    char *id = create_unique_id("room");
     xmpp_stanza_set_id(iq, id);
     free(id);
 
@@ -480,6 +478,56 @@ stanza_create_instant_room_destroy_iq(xmpp_ctx_t *ctx, const char * const room_j
 }
 
 xmpp_stanza_t *
+stanza_create_room_config_request_iq(xmpp_ctx_t *ctx, const char * const room_jid)
+{
+    xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
+    xmpp_stanza_set_type(iq, STANZA_TYPE_GET);
+    xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, room_jid);
+    char *id = create_unique_id("room");
+    xmpp_stanza_set_id(iq, id);
+    free(id);
+
+    xmpp_stanza_t *query = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(query, STANZA_NAME_QUERY);
+    xmpp_stanza_set_ns(query, STANZA_NS_MUC_OWNER);
+
+    xmpp_stanza_add_child(iq, query);
+    xmpp_stanza_release(query);
+
+    return iq;
+}
+
+xmpp_stanza_t *
+stanza_create_room_config_cancel_iq(xmpp_ctx_t *ctx, const char * const room_jid)
+{
+    xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
+    xmpp_stanza_set_type(iq, STANZA_TYPE_SET);
+    xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, room_jid);
+    char *id = create_unique_id("room");
+    xmpp_stanza_set_id(iq, id);
+    free(id);
+
+    xmpp_stanza_t *query = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(query, STANZA_NAME_QUERY);
+    xmpp_stanza_set_ns(query, STANZA_NS_MUC_OWNER);
+
+    xmpp_stanza_t *x = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(x, STANZA_NAME_X);
+    xmpp_stanza_set_type(x, "cancel");
+    xmpp_stanza_set_ns(x, STANZA_NS_DATA);
+
+    xmpp_stanza_add_child(query, x);
+    xmpp_stanza_release(x);
+
+    xmpp_stanza_add_child(iq, query);
+    xmpp_stanza_release(query);
+
+    return iq;
+}
+
+xmpp_stanza_t *
 stanza_create_presence(xmpp_ctx_t * const ctx)
 {
     xmpp_stanza_t *presence = xmpp_stanza_new(ctx);
@@ -1023,79 +1071,6 @@ stanza_get_error_message(xmpp_stanza_t *stanza)
     return strdup("unknown");
 }
 
-DataForm *
-stanza_create_form(xmpp_stanza_t * const stanza)
-{
-    DataForm *result = NULL;
-    xmpp_ctx_t *ctx = connection_get_ctx();
-
-    xmpp_stanza_t *child = xmpp_stanza_get_children(stanza);
-
-    if (child != NULL) {
-        result = malloc(sizeof(DataForm));
-        result->form_type = NULL;
-        result->fields = NULL;
-    }
-
-    //handle fields
-    while (child != NULL) {
-        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);
-            result->form_type = strdup(value_text);
-            xmpp_free(ctx, value_text);
-
-        // handle regular fields
-        } else {
-            FormField *field = malloc(sizeof(FormField));
-            field->var = strdup(var);
-            field->values = NULL;
-            xmpp_stanza_t *value = xmpp_stanza_get_children(child);
-
-            // handle values
-            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);
-                }
-                value = xmpp_stanza_get_next(value);
-            }
-
-            result->fields = g_slist_insert_sorted(result->fields, field, (GCompareFunc)_field_compare);
-        }
-
-        child = xmpp_stanza_get_next(child);
-    }
-
-    return result;
-}
-
-void
-stanza_destroy_form(DataForm *form)
-{
-    if (form != NULL) {
-        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);
-        }
-
-        free(form->form_type);
-        free(form);
-    }
-}
-
 void
 stanza_attach_priority(xmpp_ctx_t * const ctx, xmpp_stanza_t * const presence,
     const int pri)
@@ -1199,9 +1174,3 @@ stanza_get_presence_string_from_type(resource_presence_t presence_type)
             return NULL;
     }
 }
-
-static int
-_field_compare(FormField *f1, FormField *f2)
-{
-    return strcmp(f1->var, f2->var);
-}
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index 3d925787..b13c2960 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -36,6 +36,7 @@
 #define XMPP_STANZA_H
 
 #include <strophe.h>
+#include <xmpp/xmpp.h>
 
 #define STANZA_NAME_ACTIVE "active"
 #define STANZA_NAME_INACTIVE "inactive"
@@ -154,16 +155,6 @@
 
 #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
 
-typedef struct form_field_t {
-    char *var;
-    GSList *values;
-} FormField;
-
-typedef struct data_form_t {
-    char *form_type;
-    GSList *fields;
-} DataForm;
-
 xmpp_stanza_t* stanza_create_bookmarks_storage_request(xmpp_ctx_t *ctx);
 
 xmpp_stanza_t* stanza_create_chat_state(xmpp_ctx_t *ctx,
@@ -207,6 +198,10 @@ xmpp_stanza_t* stanza_create_instant_room_request_iq(xmpp_ctx_t *ctx,
     const char * const room_jid);
 xmpp_stanza_t* stanza_create_instant_room_destroy_iq(xmpp_ctx_t *ctx,
     const char * const room_jid);
+xmpp_stanza_t* stanza_create_room_config_request_iq(xmpp_ctx_t *ctx,
+    const char * const room_jid);
+xmpp_stanza_t* stanza_create_room_config_cancel_iq(xmpp_ctx_t *ctx,
+    const char * const room_jid);
 
 int stanza_get_idle_time(xmpp_stanza_t * const stanza);
 char * stanza_get_caps_str(xmpp_stanza_t * const stanza);
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 78703230..6510a50e 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -86,6 +86,28 @@ 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 *type;
+    char *title;
+    char *instructions;
+    GSList *fields;
+} DataForm;
+
 void jabber_init_module(void);
 void bookmark_init_module(void);
 void capabilities_init_module(void);
@@ -93,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);
@@ -141,6 +164,8 @@ void (*iq_disco_items_request)(gchar *jid);
 void (*iq_set_autoping)(int seconds);
 void (*iq_confirm_instant_room)(const char * const room_jid);
 void (*iq_destroy_instant_room)(const char * const room_jid);
+void (*iq_request_room_config_form)(const char * const room_jid);
+void (*iq_room_config_cancel)(const char * const room_jid);
 void (*iq_send_ping)(const char * const target);
 
 // caps functions
@@ -161,4 +186,7 @@ void (*roster_send_remove_from_group)(const char * const group, PContact contact
 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