diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/command/command.c | 13 | ||||
-rw-r--r-- | src/command/commands.c | 42 | ||||
-rw-r--r-- | src/main.c | 1 | ||||
-rw-r--r-- | src/server_events.c | 12 | ||||
-rw-r--r-- | src/server_events.h | 2 | ||||
-rw-r--r-- | src/ui/core.c | 114 | ||||
-rw-r--r-- | src/ui/ui.h | 1 | ||||
-rw-r--r-- | src/ui/window.h | 1 | ||||
-rw-r--r-- | src/ui/windows.c | 15 | ||||
-rw-r--r-- | src/xmpp/capabilities.c | 13 | ||||
-rw-r--r-- | src/xmpp/form.c | 258 | ||||
-rw-r--r-- | src/xmpp/form.h | 40 | ||||
-rw-r--r-- | src/xmpp/iq.c | 84 | ||||
-rw-r--r-- | src/xmpp/stanza.c | 135 | ||||
-rw-r--r-- | src/xmpp/stanza.h | 15 | ||||
-rw-r--r-- | src/xmpp/xmpp.h | 28 |
16 files changed, 655 insertions, 119 deletions
diff --git a/src/command/command.c b/src/command/command.c index 6d707afe..f6080468 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -306,10 +306,13 @@ static struct cmd_t command_defs[] = { "/room", cmd_room, parse_args, 2, 2, NULL, - { "/room config accept|cancel", "Room configuration.", - { "/room config accept|cncel", - "-------------------------", - "Accept or cancel default room configuration.", + { "/room config accept|destroy|edit|cancel", "Room configuration.", + { "/room config accept|destroy|edit|cancel", + "---------------------------------------", + "config accept - Accept default room configuration.", + "config destroy - Cancel default room configuration.", + "config edit - Edit room configuration.", + "config cancel - Cancel room configuration.", NULL } } }, { "/rooms", @@ -1210,6 +1213,8 @@ cmd_init(void) room_config_ac = autocomplete_new(); autocomplete_add(room_config_ac, "accept"); + autocomplete_add(room_config_ac, "destroy"); + autocomplete_add(room_config_ac, "edit"); autocomplete_add(room_config_ac, "cancel"); cmd_history_init(); diff --git a/src/command/commands.c b/src/command/commands.c index 7b49ad61..17eb9157 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -1809,7 +1809,9 @@ cmd_room(gchar **args, struct cmd_help_t help) } if ((g_strcmp0(args[1], "accept") != 0) && - (g_strcmp0(args[1], "cancel") != 0)) { + (g_strcmp0(args[1], "cancel") != 0) && + (g_strcmp0(args[1], "destroy") != 0) && + (g_strcmp0(args[1], "edit") != 0)) { cons_show("Usage: %s", help.usage); return TRUE; } @@ -1821,22 +1823,42 @@ cmd_room(gchar **args, struct cmd_help_t help) if (ui_index == 10) { ui_index = 0; } - gboolean requires_config = muc_requires_config(room); - if (!requires_config) { - win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "Current room does not require configuration."); + + if (g_strcmp0(args[1], "accept") == 0) { + gboolean requires_config = muc_requires_config(room); + if (!requires_config) { + win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "Current room does not require configuration."); + return TRUE; + } else { + iq_confirm_instant_room(room); + muc_set_requires_config(room, FALSE); + win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "Room unlocked."); + cons_show("Room unlocked: %s (%d)", room, ui_index); + return TRUE; + } + } + + if (g_strcmp0(args[1], "destroy") == 0) { + iq_destroy_instant_room(room); return TRUE; } - if (g_strcmp0(args[1], "accept") == 0) { - iq_confirm_instant_room(room); - muc_set_requires_config(room, FALSE); - win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "Room unlocked."); - cons_show("Room unlocked: %s (%d)", room, ui_index); + if (g_strcmp0(args[1], "edit") == 0) { + GString *win_title = g_string_new(room); + g_string_append(win_title, " config"); + ProfWin *window = wins_get_by_recipient(win_title->str); + g_string_free(win_title, TRUE); + if (window != NULL) { + int num = wins_get_num(window); + ui_switch_win(num); + } else { + iq_request_room_config_form(room); + } return TRUE; } if (g_strcmp0(args[1], "cancel") == 0) { - iq_destroy_instant_room(room); + iq_room_config_cancel(room); return TRUE; } 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 408e4e3e..6bc06248 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -464,6 +464,18 @@ handle_room_destroy(const char * const room) } void +handle_room_configure(const char * const room, DataForm *form) +{ + ui_handle_room_configuration(room, form); +} + +void +handle_room_configuration_form_error(void) +{ + cons_show("Error parsing room configuration form."); +} + +void handle_room_roster_complete(const char * const room) { if (muc_room_is_autojoin(room)) { diff --git a/src/server_events.h b/src/server_events.h index aaad9923..054d39e4 100644 --- a/src/server_events.h +++ b/src/server_events.h @@ -96,5 +96,7 @@ void handle_presence_error(const char *from, const char * const type, void handle_xmpp_stanza(const char * const msg); void handle_ping_result(const char * const from, int millis); void handle_ping_error_result(const char * const from, const char * const error); +void handle_room_configure(const char * const room, DataForm *form); +void handle_room_configuration_form_error(void); #endif diff --git a/src/ui/core.c b/src/ui/core.c index 4a7bb365..4f34a210 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -1601,7 +1601,8 @@ _ui_room_requires_config(const char * const room_jid) ui_index = 0; } - win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "Room requires configuration, use '/room config accept' or '/room config cancel'"); + win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", + "Room requires configuration, use '/room config accept' or '/room config destroy'"); // currently in groupchat window if (wins_is_current(window)) { @@ -1870,6 +1871,116 @@ _ui_draw_term_title(void) } static void +_ui_handle_room_configuration(const char * const room, DataForm *form) +{ + GString *title = g_string_new(room); + g_string_append(title, " config"); + ProfWin *window = wins_new(title->str, WIN_MUC_CONFIG); + g_string_free(title, TRUE); + int num = wins_get_num(window); + ui_switch_win(num); + + if (form->title != NULL) { + win_save_print(window, '-', NULL, 0, 0, "", form->title); + } else { + win_save_vprint(window, '-', NULL, 0, 0, "", "Configuration for room %s.", room); + } + win_save_print(window, '-', NULL, 0, 0, "", ""); + + if (form->instructions != NULL) { + win_save_vprint(window, '-', NULL, 0, 0, "", "Instructions:"); + win_save_print(window, '-', NULL, 0, 0, "", form->instructions); + win_save_print(window, '-', NULL, 0, 0, "", ""); + } + + GSList *fields = form->fields; + GSList *curr_field = fields; + while (curr_field != NULL) { + FormField *field = curr_field->data; + + if (g_strcmp0(field->type, "hidden") != 0) { + if (field->required) { + win_save_vprint(window, '-', NULL, NO_EOL, 0, "", "%s (%s) Required: ", field->label, field->var); + } else { + win_save_vprint(window, '-', NULL, NO_EOL, 0, "", "%s (%s): ", field->label, field->var); + } +/* +TODO add command to get help for a field + if (field->description != NULL) { + win_save_print(window, '-', NULL, 0, 0, "", field->description); + } +*/ + + GSList *values = field->values; + GSList *curr_value = values; + if (g_strcmp0(field->type, "text-single") == 0) { + if (curr_value != NULL) { + char *value = curr_value->data; + if (value != NULL) { + if (g_strcmp0(field->var, "muc#roomconfig_roomsecret") == 0) { + win_save_print(window, '-', NULL, NO_DATE | NO_EOL, COLOUR_ONLINE, "", "[hidden]"); + } else { + win_save_print(window, '-', NULL, NO_DATE | NO_EOL, COLOUR_ONLINE, "", value); + } + } + } + win_save_newline(window); + } + if (g_strcmp0(field->type, "text-private") == 0) { + if (curr_value != NULL) { + char *value = curr_value->data; + if (value != NULL) { + win_save_print(window, '-', NULL, NO_DATE | NO_EOL, COLOUR_ONLINE, "", "[hidden]"); + } + } + win_save_newline(window); + } + if (g_strcmp0(field->type, "boolean") == 0) { + if (curr_value == NULL) { + win_save_print(window, '-', NULL, NO_DATE, COLOUR_OFFLINE, "", "FALSE"); + } else { + char *value = curr_value->data; + if (value == NULL) { + win_save_print(window, '-', NULL, NO_DATE, COLOUR_OFFLINE, "", "FALSE"); + } else { + if (g_strcmp0(value, "0") == 0) { + win_save_print(window, '-', NULL, NO_DATE, COLOUR_OFFLINE, "", "FALSE"); + } else { + win_save_print(window, '-', NULL, NO_DATE, COLOUR_ONLINE, "", "TRUE"); + } + } + } + } + if (g_strcmp0(field->type, "list-single") == 0) { + if (curr_value != NULL) { + win_save_newline(window); + char *value = curr_value->data; + GSList *options = field->options; + GSList *curr_option = options; + while (curr_option != NULL) { + FormOption *option = curr_option->data; + if (g_strcmp0(option->value, value) == 0) { + win_save_vprint(window, '-', NULL, 0, COLOUR_ONLINE, "", " %s (%s)", option->label, option->value); + } else { + win_save_vprint(window, '-', NULL, 0, 0, "", " %s (%s)", option->label, option->value); + } + curr_option = g_slist_next(curr_option); + } + } + } + if (g_strcmp0(field->type, "list-multi") == 0) { + } + if (g_strcmp0(field->type, "jid-multi") == 0) { + } + } + + curr_field = g_slist_next(curr_field); + } + + form_destroy(form); +} + +static void _win_handle_switch(const wint_t * const ch) { if (*ch == KEY_F(1)) { @@ -2106,4 +2217,5 @@ ui_init_module(void) ui_update = _ui_update; ui_room_requires_config = _ui_room_requires_config; ui_room_destroyed = _ui_room_destroyed; + ui_handle_room_configuration = _ui_handle_room_configuration; } diff --git a/src/ui/ui.h b/src/ui/ui.h index 7e8967c0..cb1a92ee 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -160,6 +160,7 @@ void (*ui_handle_recipient_error)(const char * const recipient, const char * con void (*ui_handle_error)(const char * const err_msg); void (*ui_clear_win_title)(void); void (*ui_handle_room_join_error)(const char * const room, const char * const err); +void (*ui_handle_room_configuration)(const char * const room, DataForm *form); // contact status functions void (*ui_status_room)(const char * const contact); diff --git a/src/ui/window.h b/src/ui/window.h index ed0c64b4..5a72f18f 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -58,6 +58,7 @@ typedef enum { WIN_CONSOLE, WIN_CHAT, WIN_MUC, + WIN_MUC_CONFIG, WIN_PRIVATE, WIN_DUCK, WIN_XML diff --git a/src/ui/windows.c b/src/ui/windows.c index 3d835a0e..5156684b 100644 --- a/src/ui/windows.c +++ b/src/ui/windows.c @@ -385,7 +385,11 @@ wins_get_prune_recipients(void) while (curr != NULL) { ProfWin *window = curr->data; - if (window->unread == 0 && window->type != WIN_MUC && window->type != WIN_CONSOLE) { + if (window->unread == 0 && + window->type != WIN_MUC && + window->type != WIN_MUC_CONFIG && + window->type != WIN_XML && + window->type != WIN_CONSOLE) { result = g_slist_append(result, window->from); } curr = g_list_next(curr); @@ -539,6 +543,7 @@ wins_create_summary(void) GString *chat_string; GString *priv_string; GString *muc_string; + GString *muc_config_string; GString *duck_string; GString *xml_string; @@ -606,6 +611,14 @@ wins_create_summary(void) break; + case WIN_MUC_CONFIG: + muc_config_string = g_string_new(""); + g_string_printf(muc_config_string, "%d: %s", ui_index, window->from); + result = g_slist_append(result, strdup(muc_config_string->str)); + g_string_free(muc_config_string, TRUE); + + break; + case WIN_DUCK: duck_string = g_string_new(""); g_string_printf(duck_string, "%d: DuckDuckGo search", ui_index); 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 |