# HG changeset patch # User Nathan Walp # Date 1072200967 0 # Node ID e87e7d9d0132e30f185cb1f7901715940b2df466 # Parent 1fb7a840b5c7db02f2120d1806b3f3d68fbf8758 [gaim-migrate @ 8592] you can now configure MUC chat rooms. yay! committer: Tailor Script diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/Makefile.am --- a/src/protocols/jabber/Makefile.am Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/Makefile.am Tue Dec 23 17:36:07 2003 +0000 @@ -28,7 +28,9 @@ roster.c \ roster.h \ si.c \ - si.h + si.h \ + xdata.c \ + xdata.h AM_CFLAGS = $(st) diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/Makefile.mingw --- a/src/protocols/jabber/Makefile.mingw Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/Makefile.mingw Tue Dec 23 17:36:07 2003 +0000 @@ -81,6 +81,7 @@ presence.c \ roster.c \ si.c \ + xdata.c \ win32/posix.uname.c diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/chat.c --- a/src/protocols/jabber/chat.c Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/chat.c Tue Dec 23 17:36:07 2003 +0000 @@ -27,6 +27,7 @@ #include "iq.h" #include "message.h" #include "presence.h" +#include "xdata.h" GList *jabber_chat_info(GaimConnection *gc) { @@ -266,14 +267,91 @@ return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who); } -void jabber_chat_start_room_configure(JabberChat *chat) { +static void jabber_chat_room_configure_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) +{ + JabberChat *chat = data; + xmlnode *query; + JabberIq *iq; + char *to = g_strdup_printf("%s@%s", chat->room, chat->server); + + iq = jabber_iq_new_query(js, JABBER_IQ_SET, "http://jabber.org/protocol/muc#owner"); + xmlnode_set_attrib(iq->node, "to", to); + g_free(to); + + query = xmlnode_get_child(iq->node, "query"); + + xmlnode_insert_child(query, result); + + jabber_iq_send(iq); +} + +static void jabber_chat_room_configure_cb(JabberStream *js, xmlnode *packet, gpointer data) +{ + xmlnode *query, *x; + const char *type = xmlnode_get_attrib(packet, "type"); + const char *from = xmlnode_get_attrib(packet, "from"); + JabberChat *chat; + JabberID *jid; + + if(!type || !from) + return; + + + if(!strcmp(type, "result")) { + jid = jabber_id_new(from); + + if(!jid) + return; + + chat = jabber_chat_find(js, jid->node, jid->domain); + jabber_id_free(jid); + + if(!chat) + return; + + if(!(query = xmlnode_get_child(packet, "query"))) + return; + + for(x = query->child; x; x = x->next) { + const char *xmlns; + if(strcmp(x->name, "x")) + continue; + + if(!(xmlns = xmlnode_get_attrib(x, "xmlns"))) + continue; + + if(!strcmp(xmlns, "jabber:x:data")) { + jabber_x_data_request(js, x, jabber_chat_room_configure_x_data_cb, chat); + return; + } + } + } else { + /* XXX: handle errors */ + } + + +} + +void jabber_chat_request_room_configure(JabberChat *chat) { + JabberIq *iq; + xmlnode *query; + char *room_jid; + if(!chat) return; - /* XXX: implement me! */ + iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET, + "http://jabber.org/protocol/muc#owner"); + query = xmlnode_get_child(iq->node, "query"); + room_jid = g_strdup_printf("%s@%s", chat->room, chat->server); - /* XXX: for now... */ - jabber_chat_create_instant_room(chat); + xmlnode_set_attrib(iq->node, "to", room_jid); + + jabber_iq_set_callback(iq, jabber_chat_room_configure_cb, NULL); + + jabber_iq_send(iq); + + g_free(room_jid); } void jabber_chat_create_instant_room(JabberChat *chat) { diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/chat.h --- a/src/protocols/jabber/chat.h Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/chat.h Tue Dec 23 17:36:07 2003 +0000 @@ -51,7 +51,7 @@ const char *name); void jabber_chat_leave(GaimConnection *gc, int id); char *jabber_chat_buddy_real_name(GaimConnection *gc, int id, const char *who); -void jabber_chat_start_room_configure(JabberChat *chat); +void jabber_chat_request_room_configure(JabberChat *chat); void jabber_chat_create_instant_room(JabberChat *chat); diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/jabber.c --- a/src/protocols/jabber/jabber.c Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/jabber.c Tue Dec 23 17:36:07 2003 +0000 @@ -41,6 +41,7 @@ #include "presence.h" #include "jabber.h" #include "roster.h" +#include "xdata.h" #define JABBER_CONNECT_STEPS (js->gsc ? 8 : 5) @@ -88,7 +89,7 @@ static void jabber_bind_result_cb(JabberStream *js, xmlnode *packet, gpointer data) { - /* XXX: check for errors, re-set our ow js->user JID */ + /* XXX: check for errors, re-set our own js->user JID */ jabber_session_init(js); } @@ -544,13 +545,27 @@ jabber_connection_schedule_close(js); } +static void jabber_register_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) +{ + xmlnode *query; + JabberIq *iq; + + iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); + query = xmlnode_get_child(iq->node, "query"); + + xmlnode_insert_child(query, result); + + jabber_iq_set_callback(iq, jabber_registration_result_cb, NULL); + jabber_iq_send(iq); +} + void jabber_register_parse(JabberStream *js, xmlnode *packet) { if(js->registration) { GaimRequestFields *fields; GaimRequestFieldGroup *group; GaimRequestField *field; - xmlnode *query, *y; + xmlnode *query, *x, *y; char *instructions; /* get rid of the login thingy */ @@ -565,6 +580,24 @@ return; } + for(x = packet->child; x; x = x->next) { + const char *xmlns; + if(strcmp(x->name, "x")) + continue; + + if(!(xmlns = xmlnode_get_attrib(x, "xmlns"))) + continue; + + if(!strcmp(xmlns, "jabber:x:data")) { + jabber_x_data_request(js, x, jabber_register_x_data_cb, NULL); + return; + } + } + + /* XXX: if no jabber:x:data, but jabber:x:oob is there, use that */ + + /* as a last resort, use the old jabber:iq:register syntax */ + fields = gaim_request_fields_new(); group = gaim_request_field_group_new(NULL); gaim_request_fields_add_group(fields, group); @@ -801,7 +834,8 @@ return g_strdup_printf("gaim%x", js->next_id++); } -void jabber_idle_set(GaimConnection *gc, int idle) + +static void jabber_idle_set(GaimConnection *gc, int idle) { JabberStream *js = gc->proto_data; diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/message.c --- a/src/protocols/jabber/message.c Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/message.c Tue Dec 23 17:36:07 2003 +0000 @@ -479,6 +479,11 @@ chat = jabber_chat_find_by_id(js, id); + if(!strcmp(msg, "/configure") || !strcmp(msg, "/config")) { + jabber_chat_request_room_configure(chat); + return 1; + } + jm = g_new0(JabberMessage, 1); jm->js = gc->proto_data; jm->type = JABBER_MESSAGE_GROUPCHAT; diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/presence.c --- a/src/protocols/jabber/presence.c Tue Dec 23 15:55:49 2003 +0000 +++ b/src/protocols/jabber/presence.c Tue Dec 23 17:36:07 2003 +0000 @@ -240,17 +240,14 @@ const char *code = xmlnode_get_attrib(z, "code"); if(code && !strcmp(code, "201")) { chat = jabber_chat_find(js, jid->node, jid->domain); - /* XXX: finish this gaim_request_action(js->gc, _("Create New Room"), _("Create New Room"), _("You are creating a new room. Would you like to " "configure it, or accept the default settings?"), 1, chat, 2, _("Configure Room"), - G_CALLBACK(jabber_chat_start_room_configure), + G_CALLBACK(jabber_chat_request_room_configure), _("Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); - */ - jabber_chat_create_instant_room(chat); } } } diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/xdata.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/jabber/xdata.c Tue Dec 23 17:36:07 2003 +0000 @@ -0,0 +1,344 @@ +/* + * gaim - Jabber Protocol Plugin + * + * Copyright (C) 2003, Nathan Walp + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "internal.h" +#include "request.h" +#include "server.h" + +#include "xdata.h" + +typedef enum { + JABBER_X_DATA_IGNORE = 0, + JABBER_X_DATA_TEXT_SINGLE, + JABBER_X_DATA_TEXT_MULTI, + JABBER_X_DATA_LIST_SINGLE, + JABBER_X_DATA_LIST_MULTI, + JABBER_X_DATA_BOOLEAN +} jabber_x_data_field_type; + +struct jabber_x_data_data { + GHashTable *fields; + GSList *values; + jabber_x_data_cb cb; + gpointer user_data; + JabberStream *js; +}; + +static void jabber_x_data_ok_cb(struct jabber_x_data_data *data, GaimRequestFields *fields) { + xmlnode *result = xmlnode_new("x"); + jabber_x_data_cb cb = data->cb; + gpointer user_data = data->user_data; + JabberStream *js = data->js; + GList *groups, *flds; + + xmlnode_set_attrib(result, "xmlns", "jabber:x:data"); + xmlnode_set_attrib(result, "type", "submit"); + + for(groups = gaim_request_fields_get_groups(fields); groups; groups = groups->next) { + for(flds = gaim_request_field_group_get_fields(groups->data); flds; flds = flds->next) { + xmlnode *fieldnode, *valuenode; + GaimRequestField *field = flds->data; + const char *id = gaim_request_field_get_id(field); + jabber_x_data_field_type type = GPOINTER_TO_INT(g_hash_table_lookup(data->fields, id)); + + switch(type) { + case JABBER_X_DATA_TEXT_SINGLE: + { + const char *value = gaim_request_field_string_get_value(field); + fieldnode = xmlnode_new_child(result, "field"); + xmlnode_set_attrib(fieldnode, "var", id); + valuenode = xmlnode_new_child(fieldnode, "value"); + if(value) + xmlnode_insert_data(valuenode, value, -1); + break; + } + case JABBER_X_DATA_TEXT_MULTI: + { + char **pieces, **p; + const char *value = gaim_request_field_string_get_value(field); + fieldnode = xmlnode_new_child(result, "field"); + xmlnode_set_attrib(fieldnode, "var", id); + + pieces = g_strsplit(value, "\n", -1); + for(p = pieces; *p != NULL; p++) { + valuenode = xmlnode_new_child(fieldnode, "value"); + xmlnode_insert_data(valuenode, *p, -1); + } + g_strfreev(pieces); + } + break; + case JABBER_X_DATA_LIST_SINGLE: + case JABBER_X_DATA_LIST_MULTI: + { + const GList *selected = gaim_request_field_list_get_selected(field); + char *value; + fieldnode = xmlnode_new_child(result, "field"); + xmlnode_set_attrib(fieldnode, "var", id); + + while(selected) { + value = gaim_request_field_list_get_data(field, selected->data); + valuenode = xmlnode_new_child(fieldnode, "value"); + if(value) + xmlnode_insert_data(valuenode, value, -1); + selected = selected->next; + } + } + break; + case JABBER_X_DATA_BOOLEAN: + fieldnode = xmlnode_new_child(result, "field"); + xmlnode_set_attrib(fieldnode, "var", id); + valuenode = xmlnode_new_child(fieldnode, "value"); + if(gaim_request_field_bool_get_value(field)) + xmlnode_insert_data(valuenode, "1", -1); + else + xmlnode_insert_data(valuenode, "0", -1); + break; + case JABBER_X_DATA_IGNORE: + break; + } + } + } + + g_hash_table_destroy(data->fields); + while(data->values) { + g_free(data->values->data); + data->values = g_slist_delete_link(data->values, data->values); + } + g_free(data); + + cb(js, result, user_data); +} + +static void jabber_x_data_cancel_cb(struct jabber_x_data_data *data, GaimRequestFields *fields) { + xmlnode *result = xmlnode_new("x"); + jabber_x_data_cb cb = data->cb; + gpointer user_data = data->user_data; + JabberStream *js = data->js; + g_hash_table_destroy(data->fields); + while(data->values) { + g_free(data->values->data); + data->values = g_slist_delete_link(data->values, data->values); + } + g_free(data); + + xmlnode_set_attrib(result, "xmlns", "jabber:x:data"); + xmlnode_set_attrib(result, "type", "cancel"); + + cb(js, result, user_data); +} + +void jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data) +{ + xmlnode *fn, *x; + GaimRequestFields *fields; + GaimRequestFieldGroup *group; + GaimRequestField *field; + + char *title = NULL; + char *instructions = NULL; + + struct jabber_x_data_data *data = g_new0(struct jabber_x_data_data, 1); + + data->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + data->user_data = user_data; + data->cb = cb; + data->js = js; + + fields = gaim_request_fields_new(); + group = gaim_request_field_group_new(NULL); + gaim_request_fields_add_group(fields, group); + + for(fn = packet->child; fn; fn = fn->next) { + if(fn->name && !strcmp(fn->name, "field")) { + xmlnode *valuenode; + const char *type = xmlnode_get_attrib(fn, "type"); + const char *label = xmlnode_get_attrib(fn, "label"); + const char *var = xmlnode_get_attrib(fn, "var"); + char *value = NULL; + + if(!type) + continue; + + if(!var && strcmp(type, "fixed")) + continue; + if(!label) + label = var; + + if((valuenode = xmlnode_get_child(fn, "value"))) + value = xmlnode_get_data(valuenode); + + + /* XXX: handle */ + + if(!strcmp(type, "text-private")) { + if((valuenode = xmlnode_get_child(fn, "value"))) + value = xmlnode_get_data(valuenode); + + field = gaim_request_field_string_new(var, label, + value ? value : "", FALSE); + gaim_request_field_string_set_masked(field, TRUE); + gaim_request_field_group_add_field(group, field); + + g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); + + if(value) + g_free(value); + } else if(!strcmp(type, "text-multi") || !strcmp(type, "jid-multi")) { + GString *str = g_string_new(""); + + for(valuenode = fn->child; valuenode; valuenode = valuenode->next) { + if(strcmp(valuenode->name, "value")) + continue; + + if(!(value = xmlnode_get_data(valuenode))) + continue; + + g_string_append_printf(str, "%s\n", value); + g_free(value); + } + + field = gaim_request_field_string_new(var, label, + str->str, TRUE); + gaim_request_field_group_add_field(group, field); + + g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_MULTI)); + + g_string_free(str, TRUE); + } else if(!strcmp(type, "list-single") || !strcmp(type, "list-multi")) { + xmlnode *optnode; + GList *selected = NULL; + + field = gaim_request_field_list_new(var, label); + + if(!strcmp(type, "list-multi")) { + gaim_request_field_list_set_multi_select(field, TRUE); + g_hash_table_replace(data->fields, g_strdup(var), + GINT_TO_POINTER(JABBER_X_DATA_LIST_MULTI)); + } else { + g_hash_table_replace(data->fields, g_strdup(var), + GINT_TO_POINTER(JABBER_X_DATA_LIST_SINGLE)); + } + + for(valuenode = fn->child; valuenode; valuenode = valuenode->next) { + if(strcmp(valuenode->name, "value")) + continue; + selected = g_list_prepend(selected, xmlnode_get_data(valuenode)); + } + + for(optnode = fn->child; optnode; optnode = optnode->next) { + const char *lbl; + + if(strcmp(optnode->name, "option")) + continue; + + if(!(valuenode = xmlnode_get_child(optnode, "value"))) + continue; + + if(!(value = xmlnode_get_data(valuenode))) + continue; + + if(!(lbl = xmlnode_get_attrib(optnode, "label"))) + label = value; + + data->values = g_slist_prepend(data->values, value); + + gaim_request_field_list_add(field, lbl, value); + if(g_list_find_custom(selected, value, g_str_equal)) + gaim_request_field_list_add_selected(field, lbl); + } + gaim_request_field_group_add_field(group, field); + + while(selected) { + g_free(selected->data); + selected = g_list_delete_link(selected, selected); + } + + } else if(!strcmp(type, "boolean")) { + gboolean def = FALSE; + + if((valuenode = xmlnode_get_child(fn, "value"))) + value = xmlnode_get_data(valuenode); + + if(value && (!strcasecmp(value, "yes") || + !strcasecmp(value, "true") || !strcasecmp(value, "1"))) + def = TRUE; + + field = gaim_request_field_bool_new(var, label, def); + gaim_request_field_group_add_field(group, field); + + g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN)); + + if(value) + g_free(value); + } else if(!strcmp(type, "fixed") && value) { + if((valuenode = xmlnode_get_child(fn, "value"))) + value = xmlnode_get_data(valuenode); + + field = gaim_request_field_label_new("", value); + gaim_request_field_group_add_field(group, field); + + if(value) + g_free(value); + } else if(!strcmp(type, "hidden")) { + if((valuenode = xmlnode_get_child(fn, "value"))) + value = xmlnode_get_data(valuenode); + + field = gaim_request_field_string_new(var, "", value ? value : "", + FALSE); + gaim_request_field_set_visible(field, FALSE); + gaim_request_field_group_add_field(group, field); + + g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); + + if(value) + g_free(value); + } else { /* text-single, jid-single, and the default */ + if((valuenode = xmlnode_get_child(fn, "value"))) + value = xmlnode_get_data(valuenode); + + field = gaim_request_field_string_new(var, label, + value ? value : "", FALSE); + gaim_request_field_group_add_field(group, field); + + g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_TEXT_SINGLE)); + + if(value) + g_free(value); + } + } + } + + if((x = xmlnode_get_child(packet, "title"))) + title = xmlnode_get_data(x); + + if((x = xmlnode_get_child(packet, "instructions"))) + instructions = xmlnode_get_data(x); + + gaim_request_fields(js->gc, title, title, instructions, fields, _("OK"), G_CALLBACK(jabber_x_data_ok_cb), + _("Cancel"), G_CALLBACK(jabber_x_data_cancel_cb), data); + + if(title) + g_free(title); + if(instructions) + g_free(instructions); + +} + + diff -r 1fb7a840b5c7 -r e87e7d9d0132 src/protocols/jabber/xdata.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/jabber/xdata.h Tue Dec 23 17:36:07 2003 +0000 @@ -0,0 +1,31 @@ +/** + * @file jutil.h utility functions + * + * gaim + * + * Copyright (C) 2003 Nathan Walp + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _GAIM_JABBER_XDATA_H_ +#define _GAIM_JABBER_XDATA_H_ + +#include "jabber.h" +#include "xmlnode.h" + +typedef void (*jabber_x_data_cb)(JabberStream *js, xmlnode *result, gpointer user_data); +void jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data); + +#endif /* _GAIM_JABBER_XDATA_H_ */