telepathygabble/src/gabble-muc-channel.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * gabble-muc-channel.c - Source for GabbleMucChannel
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  * 
       
     5  *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Lesser General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2.1 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Lesser General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Lesser General Public
       
    18  * License along with this library; if not, write to the Free Software
       
    19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    20  */
       
    21 
       
    22 #include <dbus/dbus-glib.h>
       
    23 #include <stdio.h>
       
    24 #include <stdlib.h>
       
    25 #include <string.h>
       
    26 #include <time.h>
       
    27 
       
    28 
       
    29 #include "ansi.h"
       
    30 #include "debug.h"
       
    31 #include "disco.h"
       
    32 #include "gabble-connection.h"
       
    33 #include "gabble-error.h"
       
    34 #include "namespaces.h"
       
    35 #include "util.h"
       
    36 
       
    37 #include "telepathy-errors.h"
       
    38 #include "telepathy-helpers.h"
       
    39 #include "telepathy-interfaces.h"
       
    40 #include "tp-channel-iface.h"
       
    41 
       
    42 #include "gabble-muc-channel.h"
       
    43 #include "gabble-muc-channel-signals-marshal.h"
       
    44 
       
    45 #include "gabble-muc-channel-glue.h"
       
    46 
       
    47 #include "gabble_enums.h"
       
    48 
       
    49 #define DEBUG_FLAG GABBLE_DEBUG_MUC
       
    50 #define DEFAULT_JOIN_TIMEOUT (180 * 1000)
       
    51 #define MAX_NICK_RETRIES 3
       
    52 
       
    53 #define PROPS_POLL_INTERVAL_LOW  (60 * 1000 * 5)
       
    54 #define PROPS_POLL_INTERVAL_HIGH (60 * 1000)
       
    55 
       
    56 #ifdef DEBUG_FLAG
       
    57 //#define DEBUG(format, ...)
       
    58 #define DEBUGGING 0
       
    59 #define NODE_DEBUG(n, s)
       
    60 #endif /* DEBUG_FLAG */
       
    61 
       
    62 #ifndef EMULATOR
       
    63 G_DEFINE_TYPE_WITH_CODE (GabbleMucChannel, gabble_muc_channel,
       
    64     G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
       
    65 #endif
       
    66 /* signal enum */
       
    67 enum
       
    68 {
       
    69     READY,
       
    70     JOIN_ERROR,
       
    71     CLOSED,
       
    72     PASSWORD_FLAGS_CHANGED,
       
    73     LAST_SIGNAL 
       
    74 #ifdef EMULATOR    
       
    75     = LAST_SIGNAL_MUC
       
    76 #endif
       
    77     
       
    78 };
       
    79 
       
    80 
       
    81 #ifdef EMULATOR
       
    82 #include "libgabble_wsd_solution.h"
       
    83 
       
    84 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_muc,guint)
       
    85 	#define signals (GET_WSD_VAR_NAME(signals,gabble_muc, s)())	
       
    86     
       
    87     
       
    88     GET_STATIC_VAR_FROM_TLS(gabble_muc_channel_parent_class,gabble_muc,gpointer)
       
    89 	#define gabble_muc_channel_parent_class (*GET_WSD_VAR_NAME(gabble_muc_channel_parent_class,gabble_muc,s)())
       
    90 	
       
    91 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_muc,GType)
       
    92 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_muc,s)())
       
    93 	
       
    94 	
       
    95 	/*gchar** _s_gabble_muc_muc_roles() { return (gchar**)((libgabble_ImpurePtr()->_s_gabble_muc_muc_roles)); }
       
    96 
       
    97 	#define muc_roles (GET_WSD_VAR_NAME(muc_roles,gabble_muc, s)())	
       
    98 	
       
    99 	
       
   100 	gchar** _s_gabble_muc_muc_affiliations() { return (gchar**)((libgabble_ImpurePtr()->_s_gabble_muc_muc_affiliations)); }
       
   101 
       
   102 	#define muc_affiliations (GET_WSD_VAR_NAME(muc_affiliations,gabble_muc, s)())*/	
       
   103 
       
   104 
       
   105 static void gabble_muc_channel_init (GabbleMucChannel *self); 
       
   106 static void gabble_muc_channel_class_init (GabbleMucChannelClass *klass); 
       
   107 static void gabble_muc_channel_class_intern_init (gpointer klass) 
       
   108 {
       
   109  gabble_muc_channel_parent_class = g_type_class_peek_parent (klass);
       
   110   gabble_muc_channel_class_init ((GabbleMucChannelClass*) klass);
       
   111 } 
       
   112 EXPORT_C GType gabble_muc_channel_get_type (void)
       
   113  { 
       
   114  if ((g_define_type_id == 0)) 
       
   115  { 
       
   116  static const GTypeInfo g_define_type_info = { sizeof (GabbleMucChannelClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_muc_channel_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMucChannel), 0, (GInstanceInitFunc) gabble_muc_channel_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMucChannel"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) ((void *)0) }; g_type_add_interface_static (g_define_type_id, tp_channel_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id; 
       
   117  };
       
   118 		
       
   119 #else
       
   120 
       
   121 	static guint signals[LAST_SIGNAL] = {0};
       
   122 
       
   123 #endif
       
   124 
       
   125 /* properties */
       
   126 enum
       
   127 {
       
   128   PROP_OBJECT_PATH = 1,
       
   129   PROP_CHANNEL_TYPE,
       
   130   PROP_HANDLE_TYPE,
       
   131   PROP_HANDLE,
       
   132   PROP_CONNECTION,
       
   133   PROP_STATE,
       
   134   PROP_INVITE_SELF,
       
   135   LAST_PROPERTY
       
   136 };
       
   137 
       
   138 typedef enum {
       
   139     MUC_STATE_CREATED = 0,
       
   140     MUC_STATE_INITIATED,
       
   141     MUC_STATE_AUTH,
       
   142     MUC_STATE_JOINED,
       
   143     MUC_STATE_ENDED,
       
   144 } GabbleMucState;
       
   145 
       
   146 #define ENABLE_DEBUG
       
   147 #ifdef ENABLE_DEBUG
       
   148 static const gchar *muc_states[] =
       
   149 {
       
   150   "MUC_STATE_CREATED",
       
   151   "MUC_STATE_INITIATED",
       
   152   "MUC_STATE_AUTH",
       
   153   "MUC_STATE_JOINED",
       
   154   "MUC_STATE_ENDED",
       
   155 };
       
   156 #endif
       
   157 
       
   158 /* role and affiliation enums */
       
   159 typedef enum {
       
   160     ROLE_NONE = 0,
       
   161     ROLE_VISITOR,
       
   162     ROLE_PARTICIPANT,
       
   163     ROLE_MODERATOR,
       
   164 
       
   165     NUM_ROLES,
       
   166 
       
   167     INVALID_ROLE,
       
   168 } GabbleMucRole;
       
   169 
       
   170 typedef enum {
       
   171     AFFILIATION_NONE = 0,
       
   172     AFFILIATION_MEMBER,
       
   173     AFFILIATION_ADMIN,
       
   174     AFFILIATION_OWNER,
       
   175 
       
   176     NUM_AFFILIATIONS,
       
   177 
       
   178     INVALID_AFFILIATION,
       
   179 } GabbleMucAffiliation;
       
   180 
       
   181 //#ifndef EMULATOR
       
   182 
       
   183 static const gchar *muc_roles[NUM_ROLES] =
       
   184 {
       
   185   "none",
       
   186   "visitor",
       
   187   "participant",
       
   188   "moderator",
       
   189 };
       
   190 
       
   191 static const gchar *muc_affiliations[NUM_AFFILIATIONS] =
       
   192 {
       
   193   "none",
       
   194   "member",
       
   195   "admin",
       
   196   "owner",
       
   197 };
       
   198 
       
   199 //#endif
       
   200 
       
   201 /* room properties */
       
   202 enum
       
   203 {
       
   204   ROOM_PROP_ANONYMOUS = 0,
       
   205   ROOM_PROP_INVITE_ONLY,
       
   206   ROOM_PROP_MODERATED,
       
   207   ROOM_PROP_NAME,
       
   208   ROOM_PROP_DESCRIPTION,
       
   209   ROOM_PROP_PASSWORD,
       
   210   ROOM_PROP_PASSWORD_REQUIRED,
       
   211   ROOM_PROP_PERSISTENT,
       
   212   ROOM_PROP_PRIVATE,
       
   213   ROOM_PROP_SUBJECT,
       
   214   ROOM_PROP_SUBJECT_CONTACT,
       
   215   ROOM_PROP_SUBJECT_TIMESTAMP,
       
   216 
       
   217   NUM_ROOM_PROPS,
       
   218 
       
   219   INVALID_ROOM_PROP,
       
   220 };
       
   221 
       
   222 const GabblePropertySignature room_property_signatures[NUM_ROOM_PROPS] = {
       
   223       { "anonymous",         G_TYPE_BOOLEAN },  /* impl: READ, WRITE */
       
   224       { "invite-only",       G_TYPE_BOOLEAN },  /* impl: READ, WRITE */
       
   225       { "moderated",         G_TYPE_BOOLEAN },  /* impl: READ, WRITE */
       
   226       { "name",              G_TYPE_STRING },   /* impl: READ, WRITE */
       
   227       { "description",       G_TYPE_STRING },   /* impl: READ, WRITE */
       
   228       { "password",          G_TYPE_STRING },   /* impl: WRITE */
       
   229       { "password-required", G_TYPE_BOOLEAN },  /* impl: READ, WRITE */
       
   230       { "persistent",        G_TYPE_BOOLEAN },  /* impl: READ, WRITE */
       
   231       { "private",           G_TYPE_BOOLEAN },  /* impl: READ, WRITE */
       
   232       { "subject",           G_TYPE_STRING },   /* impl: READ, WRITE */
       
   233       { "subject-contact",   G_TYPE_UINT },     /* impl: READ */
       
   234       { "subject-timestamp", G_TYPE_UINT },     /* impl: READ */
       
   235 };
       
   236 
       
   237 /* private structures */
       
   238 
       
   239 typedef struct _GabbleMucChannelPrivate GabbleMucChannelPrivate;
       
   240 
       
   241 struct _GabbleMucChannelPrivate
       
   242 {
       
   243   GabbleConnection *conn;
       
   244   gchar *object_path;
       
   245 
       
   246   GabbleMucState state;
       
   247 
       
   248   guint join_timer_id;
       
   249   guint poll_timer_id;
       
   250 
       
   251   TpChannelPasswordFlags password_flags;
       
   252   DBusGMethodInvocation *password_ctx;
       
   253   gchar *password;
       
   254 
       
   255   GabbleHandle handle;
       
   256   const gchar *jid;
       
   257 
       
   258   guint nick_retry_count;
       
   259   GString *self_jid;
       
   260   GabbleMucRole self_role;
       
   261   GabbleMucAffiliation self_affil;
       
   262 
       
   263   guint recv_id;
       
   264 
       
   265   GabblePropertiesContext *properties_ctx;
       
   266 
       
   267   gboolean ready_emitted;
       
   268 
       
   269   gboolean closed;
       
   270   gboolean dispose_has_run;
       
   271 
       
   272   gboolean invite_self;
       
   273 };
       
   274 
       
   275 #define GABBLE_MUC_CHANNEL_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_CHANNEL, GabbleMucChannelPrivate))
       
   276 
       
   277 static void
       
   278 gabble_muc_channel_init (GabbleMucChannel *obj)
       
   279 {
       
   280   /* do nothing? */
       
   281 }
       
   282 
       
   283 static void contact_handle_to_room_identity (GabbleMucChannel *, GabbleHandle, GabbleHandle *, GString **);
       
   284 
       
   285 static GObject *
       
   286 gabble_muc_channel_constructor (GType type, guint n_props,
       
   287                                 GObjectConstructParam *props)
       
   288 {
       
   289   GObject *obj;
       
   290   GabbleMucChannelPrivate *priv;
       
   291   DBusGConnection *bus;
       
   292   GabbleHandleRepo *handles;
       
   293   GabbleHandle self_handle;
       
   294   gboolean valid;
       
   295 
       
   296   obj = G_OBJECT_CLASS (gabble_muc_channel_parent_class)->
       
   297            constructor (type, n_props, props);
       
   298 
       
   299   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (obj);
       
   300 
       
   301   handles = priv->conn->handles;
       
   302 
       
   303   /* ref our room handle */
       
   304   valid = gabble_handle_ref (handles, TP_HANDLE_TYPE_ROOM, priv->handle);
       
   305   g_assert (valid);
       
   306 
       
   307   /* get the room's jid */
       
   308   priv->jid = gabble_handle_inspect (handles, TP_HANDLE_TYPE_ROOM, priv->handle);
       
   309 
       
   310   /* get our own identity in the room */
       
   311   contact_handle_to_room_identity (GABBLE_MUC_CHANNEL (obj), priv->conn->self_handle,
       
   312                                    &self_handle, &priv->self_jid);
       
   313 
       
   314   valid = gabble_handle_ref (handles, TP_HANDLE_TYPE_CONTACT, self_handle);
       
   315   g_assert (valid);
       
   316 
       
   317   /* initialize our own role and affiliation */
       
   318   priv->self_role = ROLE_NONE;
       
   319   priv->self_affil = AFFILIATION_NONE;
       
   320 
       
   321   /* register object on the bus */
       
   322   bus = tp_get_bus ();
       
   323   dbus_g_connection_register_g_object (bus, priv->object_path, obj);
       
   324 
       
   325   /* initialize group mixin */
       
   326   gabble_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMucChannel, group),
       
   327                            handles, self_handle);
       
   328 
       
   329   /* set initial group flags */
       
   330   gabble_group_mixin_change_flags (obj,
       
   331       TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES |
       
   332       TP_CHANNEL_GROUP_FLAG_CAN_ADD,
       
   333       0);
       
   334 
       
   335   /* initialize properties mixin */
       
   336   gabble_properties_mixin_init (obj, G_STRUCT_OFFSET (
       
   337         GabbleMucChannel, properties));
       
   338 
       
   339   /* initialize text mixin */
       
   340   gabble_text_mixin_init (obj, G_STRUCT_OFFSET (GabbleMucChannel, text), handles, FALSE);
       
   341 
       
   342   gabble_text_mixin_set_message_types (obj,
       
   343       TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
       
   344       TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
       
   345       G_MAXUINT);
       
   346 
       
   347   /* add ourselves to group mixin if needed */
       
   348   if (priv->invite_self)
       
   349     {
       
   350       GError *error = NULL;
       
   351       GArray *members = g_array_sized_new (FALSE, FALSE, sizeof (GabbleHandle), 1);
       
   352       g_array_append_val (members, self_handle);
       
   353       gabble_group_mixin_add_members (obj, members, "", &error);
       
   354       g_assert (error == NULL);
       
   355       g_array_free (members, TRUE);
       
   356     }
       
   357   return obj;
       
   358 }
       
   359 
       
   360 static void
       
   361 properties_disco_cb (GabbleDisco *disco,
       
   362                      GabbleDiscoRequest *request,
       
   363                      const gchar *jid,
       
   364                      const gchar *node,
       
   365                      LmMessageNode *query_result,
       
   366                      GError *error,
       
   367                      gpointer user_data)
       
   368 {
       
   369   GabbleMucChannel *chan = user_data;
       
   370   GabbleMucChannelPrivate *priv;
       
   371   GArray *changed_props_val, *changed_props_flags;
       
   372   LmMessageNode *lm_node;
       
   373   const gchar *str;
       
   374   GValue val = { 0, };
       
   375 
       
   376   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
   377 
       
   378   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   379 
       
   380   if (error)
       
   381     {
       
   382       gabble_debug (DEBUG_FLAG, "got error %s", error->message);
       
   383       return;
       
   384     }
       
   385 
       
   386   NODE_DEBUG (query_result, "disco query result");
       
   387 
       
   388   changed_props_val = changed_props_flags = NULL;
       
   389 
       
   390 
       
   391   /*
       
   392    * Update room definition.
       
   393    */
       
   394 
       
   395   /* ROOM_PROP_NAME */
       
   396   lm_node = lm_message_node_get_child (query_result, "identity");
       
   397   if (lm_node)
       
   398     {
       
   399       const gchar *type, *category, *name;
       
   400 
       
   401       type = lm_message_node_get_attribute (lm_node, "type");
       
   402       category = lm_message_node_get_attribute (lm_node, "category");
       
   403       name = lm_message_node_get_attribute (lm_node, "name");
       
   404 
       
   405       if (NULL != type && 0 == strcmp (type, "text") &&
       
   406           NULL != category && 0 == strcmp (category, "conference") &&
       
   407           NULL != name)
       
   408         {
       
   409           g_value_init (&val, G_TYPE_STRING);
       
   410           g_value_set_string (&val, name);
       
   411 
       
   412           gabble_properties_mixin_change_value (G_OBJECT (chan), ROOM_PROP_NAME,
       
   413                                                 &val, &changed_props_val);
       
   414 
       
   415           gabble_properties_mixin_change_flags (G_OBJECT (chan), ROOM_PROP_NAME,
       
   416                                                 TP_PROPERTY_FLAG_READ,
       
   417                                                 0, &changed_props_flags);
       
   418 
       
   419           g_value_unset (&val);
       
   420         }
       
   421     }
       
   422 
       
   423   for (lm_node = query_result->children; lm_node; lm_node = lm_node->next)
       
   424     {
       
   425       guint prop_id = INVALID_ROOM_PROP;
       
   426 
       
   427       if (strcmp (lm_node->name, "feature") == 0)
       
   428         {
       
   429           str = lm_message_node_get_attribute (lm_node, "var");
       
   430           if (str == NULL)
       
   431             continue;
       
   432 
       
   433           /* ROOM_PROP_ANONYMOUS */
       
   434           if (strcmp (str, "muc_nonanonymous") == 0)
       
   435             {
       
   436               prop_id = ROOM_PROP_ANONYMOUS;
       
   437               g_value_init (&val, G_TYPE_BOOLEAN);
       
   438               g_value_set_boolean (&val, FALSE);
       
   439             }
       
   440           else if (strcmp (str, "muc_semianonymous") == 0 ||
       
   441                    strcmp (str, "muc_anonymous") == 0)
       
   442             {
       
   443               prop_id = ROOM_PROP_ANONYMOUS;
       
   444               g_value_init (&val, G_TYPE_BOOLEAN);
       
   445               g_value_set_boolean (&val, TRUE);
       
   446             }
       
   447 
       
   448           /* ROOM_PROP_INVITE_ONLY */
       
   449           else if (strcmp (str, "muc_open") == 0)
       
   450             {
       
   451               prop_id = ROOM_PROP_INVITE_ONLY;
       
   452               g_value_init (&val, G_TYPE_BOOLEAN);
       
   453               g_value_set_boolean (&val, FALSE);
       
   454             }
       
   455           else if (strcmp (str, "muc_membersonly") == 0)
       
   456             {
       
   457               prop_id = ROOM_PROP_INVITE_ONLY;
       
   458               g_value_init (&val, G_TYPE_BOOLEAN);
       
   459               g_value_set_boolean (&val, TRUE);
       
   460             }
       
   461 
       
   462           /* ROOM_PROP_MODERATED */
       
   463           else if (strcmp (str, "muc_unmoderated") == 0)
       
   464             {
       
   465               prop_id = ROOM_PROP_MODERATED;
       
   466               g_value_init (&val, G_TYPE_BOOLEAN);
       
   467               g_value_set_boolean (&val, FALSE);
       
   468             }
       
   469           else if (strcmp (str, "muc_moderated") == 0)
       
   470             {
       
   471               prop_id = ROOM_PROP_MODERATED;
       
   472               g_value_init (&val, G_TYPE_BOOLEAN);
       
   473               g_value_set_boolean (&val, TRUE);
       
   474             }
       
   475 
       
   476           /* ROOM_PROP_PASSWORD_REQUIRED */
       
   477           else if (strcmp (str, "muc_unsecure") == 0 ||
       
   478                    strcmp (str, "muc_unsecured") == 0)
       
   479             {
       
   480               prop_id = ROOM_PROP_PASSWORD_REQUIRED;
       
   481               g_value_init (&val, G_TYPE_BOOLEAN);
       
   482               g_value_set_boolean (&val, FALSE);
       
   483             }
       
   484           else if (strcmp (str, "muc_passwordprotected") == 0)
       
   485             {
       
   486               prop_id = ROOM_PROP_PASSWORD_REQUIRED;
       
   487               g_value_init (&val, G_TYPE_BOOLEAN);
       
   488               g_value_set_boolean (&val, TRUE);
       
   489             }
       
   490 
       
   491           /* ROOM_PROP_PERSISTENT */
       
   492           else if (strcmp (str, "muc_temporary") == 0)
       
   493             {
       
   494               prop_id = ROOM_PROP_PERSISTENT;
       
   495               g_value_init (&val, G_TYPE_BOOLEAN);
       
   496               g_value_set_boolean (&val, FALSE);
       
   497             }
       
   498           else if (strcmp (str, "muc_persistent") == 0)
       
   499             {
       
   500               prop_id = ROOM_PROP_PERSISTENT;
       
   501               g_value_init (&val, G_TYPE_BOOLEAN);
       
   502               g_value_set_boolean (&val, TRUE);
       
   503             }
       
   504 
       
   505           /* ROOM_PROP_PRIVATE */
       
   506           else if (strcmp (str, "muc_public") == 0)
       
   507             {
       
   508               prop_id = ROOM_PROP_PRIVATE;
       
   509               g_value_init (&val, G_TYPE_BOOLEAN);
       
   510               g_value_set_boolean (&val, FALSE);
       
   511             }
       
   512           else if (strcmp (str, "muc_hidden") == 0)
       
   513             {
       
   514               prop_id = ROOM_PROP_PRIVATE;
       
   515               g_value_init (&val, G_TYPE_BOOLEAN);
       
   516               g_value_set_boolean (&val, TRUE);
       
   517             }
       
   518 
       
   519           /* Ignored */
       
   520           else if (strcmp (str, NS_MUC) == 0)
       
   521             {
       
   522             }
       
   523 
       
   524           /* Unhandled */
       
   525           else
       
   526             {
       
   527               g_warning ("%s: unhandled feature '%s'", G_STRFUNC, str);
       
   528             }
       
   529         }
       
   530       else if (strcmp (lm_node->name, "x") == 0)
       
   531         {
       
   532           if (lm_message_node_has_namespace (lm_node, NS_X_DATA, NULL))
       
   533             {
       
   534               LmMessageNode *field, *value_node;
       
   535 
       
   536               for (field = lm_node->children; field; field = field->next)
       
   537                 {
       
   538                   if (strcmp (field->name, "field") != 0)
       
   539                     continue;
       
   540 
       
   541                   str = lm_message_node_get_attribute (field, "var");
       
   542                   if (str == NULL)
       
   543                     continue;
       
   544 
       
   545                   if (strcmp (str, "muc#roominfo_description") != 0)
       
   546                     continue;
       
   547 
       
   548                   value_node = lm_message_node_get_child (field, "value");
       
   549                   if (value_node == NULL)
       
   550                     continue;
       
   551 
       
   552                   str = lm_message_node_get_value (value_node);
       
   553                   if (str == NULL)
       
   554                     {
       
   555                       str = "";
       
   556                     }
       
   557 
       
   558                   prop_id = ROOM_PROP_DESCRIPTION;
       
   559                   g_value_init (&val, G_TYPE_STRING);
       
   560                   g_value_set_string (&val, str);
       
   561                 }
       
   562             }
       
   563         }
       
   564 
       
   565       if (prop_id != INVALID_ROOM_PROP)
       
   566         {
       
   567           gabble_properties_mixin_change_value (G_OBJECT (chan), prop_id, &val,
       
   568                                                 &changed_props_val);
       
   569 
       
   570           gabble_properties_mixin_change_flags (G_OBJECT (chan), prop_id,
       
   571                                                 TP_PROPERTY_FLAG_READ,
       
   572                                                 0, &changed_props_flags);
       
   573 
       
   574           g_value_unset (&val);
       
   575         }
       
   576     }
       
   577 
       
   578   /*
       
   579    * Emit signals.
       
   580    */
       
   581   gabble_properties_mixin_emit_changed (G_OBJECT (chan), &changed_props_val);
       
   582   gabble_properties_mixin_emit_flags (G_OBJECT (chan), &changed_props_flags);
       
   583 }
       
   584 
       
   585 static void
       
   586 room_properties_update (GabbleMucChannel *chan)
       
   587 {
       
   588   GabbleMucChannelPrivate *priv;
       
   589   GError *error = NULL;
       
   590 
       
   591   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
   592 
       
   593   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   594 
       
   595   if (gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO,
       
   596         priv->jid, NULL, properties_disco_cb, chan, G_OBJECT (chan),
       
   597         &error) == NULL)
       
   598     {
       
   599       g_warning ("%s: disco query failed: '%s'", G_STRFUNC, error->message);
       
   600       g_error_free (error);
       
   601     }
       
   602 }
       
   603 
       
   604 static void
       
   605 contact_handle_to_room_identity (GabbleMucChannel *chan, GabbleHandle main_handle,
       
   606                                  GabbleHandle *room_handle, GString **room_jid)
       
   607 {
       
   608   GabbleMucChannelPrivate *priv;
       
   609   GabbleHandleRepo *handles;
       
   610   const gchar *main_jid;
       
   611   gchar *username, *server;
       
   612   gchar *jid;
       
   613 
       
   614   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   615 
       
   616   handles = priv->conn->handles;
       
   617 
       
   618   main_jid = gabble_handle_inspect (handles, TP_HANDLE_TYPE_CONTACT,
       
   619                                     main_handle);
       
   620 
       
   621   gabble_decode_jid (main_jid, &username, &server, NULL);
       
   622 
       
   623   jid = g_strdup_printf ("%s/%s", priv->jid, username);
       
   624 
       
   625   g_free (username);
       
   626   g_free (server);
       
   627 
       
   628   if (room_handle)
       
   629     {
       
   630       *room_handle = gabble_handle_for_contact (handles, jid, TRUE);
       
   631     }
       
   632 
       
   633   if (room_jid)
       
   634     {
       
   635       *room_jid = g_string_new (jid);
       
   636     }
       
   637 
       
   638   g_free (jid);
       
   639 }
       
   640 
       
   641 static gboolean
       
   642 send_join_request (GabbleMucChannel *channel,
       
   643                    const gchar *password,
       
   644                    GError **error)
       
   645 {
       
   646   GabbleMucChannelPrivate *priv;
       
   647   LmMessage *msg;
       
   648   LmMessageNode *x_node;
       
   649   gboolean ret;
       
   650 
       
   651   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (channel);
       
   652 
       
   653   /* build the message */
       
   654   msg = lm_message_new (priv->self_jid->str, LM_MESSAGE_TYPE_PRESENCE);
       
   655 
       
   656   x_node = lm_message_node_add_child (msg->node, "x", NULL);
       
   657   lm_message_node_set_attribute (x_node, "xmlns", NS_MUC);
       
   658 
       
   659   g_free (priv->password);
       
   660 
       
   661   if (password != NULL)
       
   662     {
       
   663       priv->password = g_strdup (password);
       
   664       lm_message_node_add_child (x_node, "password", password);
       
   665     }
       
   666 
       
   667   /* send it */
       
   668   ret = _gabble_connection_send (priv->conn, msg, error);
       
   669   if (!ret)
       
   670     {
       
   671       g_warning ("%s: _gabble_connection_send_with_reply failed", G_STRFUNC);
       
   672     }
       
   673   else
       
   674     {
       
   675       gabble_debug (DEBUG_FLAG, "join request sent");
       
   676     }
       
   677 
       
   678   lm_message_unref (msg);
       
   679 
       
   680   return ret;
       
   681 }
       
   682 
       
   683 static gboolean
       
   684 send_leave_message (GabbleMucChannel *channel,
       
   685                     const gchar *reason)
       
   686 {
       
   687   GabbleMucChannelPrivate *priv;
       
   688   LmMessage *msg;
       
   689   GError *error = NULL;
       
   690   gboolean ret;
       
   691 
       
   692   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (channel);
       
   693 
       
   694   /* build the message */
       
   695   msg = lm_message_new_with_sub_type (priv->self_jid->str,
       
   696                                       LM_MESSAGE_TYPE_PRESENCE,
       
   697                                       LM_MESSAGE_SUB_TYPE_UNAVAILABLE);
       
   698 
       
   699   if (reason != NULL)
       
   700     {
       
   701       lm_message_node_add_child (msg->node, "status", reason);
       
   702     }
       
   703 
       
   704   /* send it */
       
   705   ret = _gabble_connection_send (priv->conn, msg, &error);
       
   706   if (!ret)
       
   707     {
       
   708       g_warning ("%s: _gabble_connection_send_with_reply failed", G_STRFUNC);
       
   709       g_error_free (error);
       
   710     }
       
   711   else
       
   712     {
       
   713       gabble_debug (DEBUG_FLAG, "leave message sent");
       
   714     }
       
   715 
       
   716   lm_message_unref (msg);
       
   717 
       
   718   return ret;
       
   719 }
       
   720 
       
   721 static void
       
   722 gabble_muc_channel_get_property (GObject    *object,
       
   723                                  guint       property_id,
       
   724                                  GValue     *value,
       
   725                                  GParamSpec *pspec)
       
   726 {
       
   727   GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
       
   728   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   729 
       
   730   switch (property_id) {
       
   731     case PROP_OBJECT_PATH:
       
   732       g_value_set_string (value, priv->object_path);
       
   733       break;
       
   734     case PROP_CHANNEL_TYPE:
       
   735       g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT);
       
   736       break;
       
   737     case PROP_HANDLE_TYPE:
       
   738       g_value_set_uint (value, TP_HANDLE_TYPE_ROOM);
       
   739       break;
       
   740     case PROP_HANDLE:
       
   741       g_value_set_uint (value, priv->handle);
       
   742       break;
       
   743     case PROP_CONNECTION:
       
   744       g_value_set_object (value, priv->conn);
       
   745       break;
       
   746     case PROP_STATE:
       
   747       g_value_set_uint (value, priv->state);
       
   748       break;
       
   749     default:
       
   750       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   751       break;
       
   752   }
       
   753 }
       
   754 
       
   755 static void channel_state_changed (GabbleMucChannel *chan,
       
   756                                    GabbleMucState prev_state,
       
   757                                    GabbleMucState new_state);
       
   758 
       
   759 static void
       
   760 gabble_muc_channel_set_property (GObject     *object,
       
   761                                  guint        property_id,
       
   762                                  const GValue *value,
       
   763                                  GParamSpec   *pspec)
       
   764 {
       
   765   GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
       
   766   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   767   GabbleMucState prev_state;
       
   768 
       
   769   switch (property_id) {
       
   770     case PROP_OBJECT_PATH:
       
   771       g_free (priv->object_path);
       
   772       priv->object_path = g_value_dup_string (value);
       
   773       break;
       
   774     case PROP_HANDLE:
       
   775       priv->handle = g_value_get_uint (value);
       
   776       break;
       
   777     case PROP_CONNECTION:
       
   778       priv->conn = g_value_get_object (value);
       
   779       break;
       
   780     case PROP_STATE:
       
   781       prev_state = priv->state;
       
   782       priv->state = g_value_get_uint (value);
       
   783 
       
   784       if (priv->state != prev_state)
       
   785         channel_state_changed (chan, prev_state, priv->state);
       
   786 
       
   787       break;
       
   788     case PROP_INVITE_SELF:
       
   789       priv->invite_self = g_value_get_boolean (value);
       
   790       break;
       
   791     default:
       
   792       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   793       break;
       
   794   }
       
   795 }
       
   796 
       
   797 static void gabble_muc_channel_dispose (GObject *object);
       
   798 static void gabble_muc_channel_finalize (GObject *object);
       
   799 static gboolean gabble_muc_channel_add_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error);
       
   800 static gboolean gabble_muc_channel_remove_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error);
       
   801 static gboolean gabble_muc_channel_do_set_properties (GObject *obj, GabblePropertiesContext *ctx, GError **error);
       
   802 
       
   803 static void
       
   804 gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class)
       
   805 {
       
   806   GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_channel_class);
       
   807   GParamSpec *param_spec;
       
   808 
       
   809   g_type_class_add_private (gabble_muc_channel_class, sizeof (GabbleMucChannelPrivate));
       
   810 
       
   811   object_class->constructor = gabble_muc_channel_constructor;
       
   812 
       
   813   object_class->get_property = gabble_muc_channel_get_property;
       
   814   object_class->set_property = gabble_muc_channel_set_property;
       
   815 
       
   816   object_class->dispose = gabble_muc_channel_dispose;
       
   817   object_class->finalize = gabble_muc_channel_finalize;
       
   818 
       
   819   g_object_class_override_property (object_class, PROP_OBJECT_PATH, "object-path");
       
   820   g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, "channel-type");
       
   821   g_object_class_override_property (object_class, PROP_HANDLE_TYPE, "handle-type");
       
   822   g_object_class_override_property (object_class, PROP_HANDLE, "handle");
       
   823 
       
   824   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   825                                     "Gabble connection object that owns this "
       
   826                                     "MUC channel object.",
       
   827                                     GABBLE_TYPE_CONNECTION,
       
   828                                     G_PARAM_CONSTRUCT_ONLY |
       
   829                                     G_PARAM_READWRITE |
       
   830                                     G_PARAM_STATIC_NICK |
       
   831                                     G_PARAM_STATIC_BLURB);
       
   832   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
       
   833 
       
   834   param_spec = g_param_spec_uint ("state", "Channel state",
       
   835                                   "The current state that the channel is in.",
       
   836                                   0, G_MAXUINT32, 0,
       
   837                                   G_PARAM_READWRITE |
       
   838                                   G_PARAM_STATIC_NAME |
       
   839                                   G_PARAM_STATIC_BLURB);
       
   840   g_object_class_install_property (object_class, PROP_STATE, param_spec);
       
   841 
       
   842   param_spec = g_param_spec_boolean ("invite-self", "Invite self",
       
   843                                      "Whether the user should be added to members list.",
       
   844                                      FALSE,
       
   845                                      G_PARAM_CONSTRUCT_ONLY |
       
   846                                      G_PARAM_WRITABLE |
       
   847                                      G_PARAM_STATIC_NAME |
       
   848                                      G_PARAM_STATIC_BLURB);
       
   849   g_object_class_install_property (object_class, PROP_INVITE_SELF, param_spec);
       
   850 
       
   851   signals[READY] =
       
   852     g_signal_new ("ready",
       
   853                   G_OBJECT_CLASS_TYPE (gabble_muc_channel_class),
       
   854                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   855                   0,
       
   856                   NULL, NULL,
       
   857                   g_cclosure_marshal_VOID__VOID,
       
   858                   G_TYPE_NONE, 0);
       
   859 
       
   860   signals[JOIN_ERROR] =
       
   861     g_signal_new ("join-error",
       
   862                   G_OBJECT_CLASS_TYPE (gabble_muc_channel_class),
       
   863                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   864                   0,
       
   865                   NULL, NULL,
       
   866                   g_cclosure_marshal_VOID__POINTER,
       
   867                   G_TYPE_NONE, 1, G_TYPE_POINTER);
       
   868 
       
   869   signals[CLOSED] =
       
   870     g_signal_new ("closed",
       
   871                   G_OBJECT_CLASS_TYPE (gabble_muc_channel_class),
       
   872                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   873                   0,
       
   874                   NULL, NULL,
       
   875                   g_cclosure_marshal_VOID__VOID,
       
   876                   G_TYPE_NONE, 0);
       
   877 
       
   878   signals[PASSWORD_FLAGS_CHANGED] =
       
   879     g_signal_new ("password-flags-changed",
       
   880                   G_OBJECT_CLASS_TYPE (gabble_muc_channel_class),
       
   881                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   882                   0,
       
   883                   NULL, NULL,
       
   884                   gabble_muc_channel_marshal_VOID__UINT_UINT,
       
   885                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
       
   886 
       
   887   gabble_group_mixin_class_init (object_class,
       
   888                                  G_STRUCT_OFFSET (GabbleMucChannelClass, group_class),
       
   889                                  gabble_muc_channel_add_member,
       
   890                                  gabble_muc_channel_remove_member);
       
   891 
       
   892   gabble_properties_mixin_class_init (object_class,
       
   893                                       G_STRUCT_OFFSET (GabbleMucChannelClass, properties_class),
       
   894                                       room_property_signatures, NUM_ROOM_PROPS,
       
   895                                       gabble_muc_channel_do_set_properties);
       
   896 
       
   897   gabble_text_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleMucChannelClass, text_class));
       
   898 
       
   899   dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (gabble_muc_channel_class), &dbus_glib_gabble_muc_channel_object_info);
       
   900 }
       
   901 
       
   902 static void clear_join_timer (GabbleMucChannel *chan);
       
   903 static void clear_poll_timer (GabbleMucChannel *chan);
       
   904 
       
   905 void
       
   906 gabble_muc_channel_dispose (GObject *object)
       
   907 {
       
   908   GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object);
       
   909   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
   910 
       
   911   if (priv->dispose_has_run)
       
   912     return;
       
   913 
       
   914   gabble_debug (DEBUG_FLAG, "called");
       
   915 
       
   916   priv->dispose_has_run = TRUE;
       
   917 
       
   918   clear_join_timer (self);
       
   919   clear_poll_timer (self);
       
   920 
       
   921   if (G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose)
       
   922     G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose (object);
       
   923 }
       
   924 
       
   925 void
       
   926 gabble_muc_channel_finalize (GObject *object)
       
   927 {
       
   928   GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object);
       
   929   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
   930   GabbleHandleRepo *handles = priv->conn->handles;
       
   931 
       
   932   gabble_debug (DEBUG_FLAG, "called");
       
   933 
       
   934   /* free any data held directly by the object here */
       
   935   gabble_handle_unref (handles, TP_HANDLE_TYPE_ROOM, priv->handle);
       
   936 
       
   937   g_free (priv->object_path);
       
   938 
       
   939   if (priv->self_jid)
       
   940     {
       
   941       g_string_free (priv->self_jid, TRUE);
       
   942     }
       
   943 
       
   944   g_free (priv->password);
       
   945 
       
   946   gabble_properties_mixin_finalize (object);
       
   947 
       
   948   gabble_group_mixin_finalize (object);
       
   949 
       
   950   gabble_text_mixin_finalize (object);
       
   951 
       
   952   G_OBJECT_CLASS (gabble_muc_channel_parent_class)->finalize (object);
       
   953 }
       
   954 
       
   955 static void clear_join_timer (GabbleMucChannel *chan)
       
   956 {
       
   957   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   958 
       
   959   if (priv->join_timer_id != 0)
       
   960     {
       
   961       g_source_remove (priv->join_timer_id);
       
   962       priv->join_timer_id = 0;
       
   963     }
       
   964 }
       
   965 
       
   966 static void clear_poll_timer (GabbleMucChannel *chan)
       
   967 {
       
   968   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   969 
       
   970   if (priv->poll_timer_id != 0)
       
   971     {
       
   972       g_source_remove (priv->poll_timer_id);
       
   973       priv->poll_timer_id = 0;
       
   974     }
       
   975 }
       
   976 
       
   977 static void
       
   978 change_password_flags (GabbleMucChannel *chan,
       
   979                        TpChannelPasswordFlags add,
       
   980                        TpChannelPasswordFlags remove)
       
   981 {
       
   982   GabbleMucChannelPrivate *priv;
       
   983   TpChannelPasswordFlags added, removed;
       
   984 
       
   985   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
   986 
       
   987   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
   988 
       
   989   added = add & ~priv->password_flags;
       
   990   priv->password_flags |= added;
       
   991 
       
   992   removed = remove & priv->password_flags;
       
   993   priv->password_flags &= ~removed;
       
   994 
       
   995   if (add != 0 || remove != 0)
       
   996     {
       
   997       gabble_debug (DEBUG_FLAG, "emitting password flags changed, added 0x%X, removed 0x%X",
       
   998               added, removed);
       
   999 
       
  1000       g_signal_emit (chan, signals[PASSWORD_FLAGS_CHANGED], 0, added, removed);
       
  1001     }
       
  1002 }
       
  1003 
       
  1004 static void
       
  1005 provide_password_return_if_pending (GabbleMucChannel *chan, gboolean success)
       
  1006 {
       
  1007   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1008 
       
  1009   if (priv->password_ctx)
       
  1010     {
       
  1011       dbus_g_method_return (priv->password_ctx, success);
       
  1012       priv->password_ctx = NULL;
       
  1013     }
       
  1014 
       
  1015   if (success)
       
  1016     {
       
  1017       change_password_flags (chan, 0, TP_CHANNEL_PASSWORD_FLAG_PROVIDE);
       
  1018     }
       
  1019 }
       
  1020 
       
  1021 static void close_channel (GabbleMucChannel *chan, const gchar *reason, gboolean inform_muc, GabbleHandle actor, guint reason_code);
       
  1022 
       
  1023 static gboolean
       
  1024 timeout_join (gpointer data)
       
  1025 {
       
  1026   GabbleMucChannel *chan = data;
       
  1027 
       
  1028   gabble_debug (DEBUG_FLAG, "join timed out, closing channel");
       
  1029 
       
  1030   provide_password_return_if_pending (chan, FALSE);
       
  1031 
       
  1032   close_channel (chan, NULL, FALSE, 0, 0);
       
  1033 
       
  1034   return FALSE;
       
  1035 }
       
  1036 
       
  1037 static gboolean
       
  1038 timeout_poll (gpointer data)
       
  1039 {
       
  1040   GabbleMucChannel *chan = data;
       
  1041 
       
  1042   gabble_debug (DEBUG_FLAG, "polling for room properties");
       
  1043 
       
  1044   room_properties_update (chan);
       
  1045 
       
  1046   return TRUE;
       
  1047 }
       
  1048 
       
  1049 static void
       
  1050 channel_state_changed (GabbleMucChannel *chan,
       
  1051                        GabbleMucState prev_state,
       
  1052                        GabbleMucState new_state)
       
  1053 {
       
  1054   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1055 
       
  1056   gabble_debug (DEBUG_FLAG, "state changed from %s to %s", muc_states[prev_state], muc_states[new_state]);
       
  1057 
       
  1058   if (new_state == MUC_STATE_INITIATED)
       
  1059     {
       
  1060       priv->join_timer_id =
       
  1061         g_timeout_add (DEFAULT_JOIN_TIMEOUT, timeout_join, chan);
       
  1062     }
       
  1063   else if (new_state == MUC_STATE_JOINED)
       
  1064     {
       
  1065       gboolean low_bandwidth;
       
  1066       gint interval;
       
  1067 
       
  1068       provide_password_return_if_pending (chan, TRUE);
       
  1069 
       
  1070       clear_join_timer (chan);
       
  1071 
       
  1072       g_object_get (priv->conn, "low-bandwidth", &low_bandwidth, NULL);
       
  1073 
       
  1074       if (low_bandwidth)
       
  1075         interval = PROPS_POLL_INTERVAL_LOW;
       
  1076       else
       
  1077         interval = PROPS_POLL_INTERVAL_HIGH;
       
  1078 
       
  1079       priv->poll_timer_id = g_timeout_add (interval, timeout_poll, chan);
       
  1080 
       
  1081       /* no need to keep this around any longer, if it's set */
       
  1082       g_free (priv->password);
       
  1083       priv->password = NULL;
       
  1084     }
       
  1085   else if (new_state == MUC_STATE_ENDED)
       
  1086     {
       
  1087       clear_poll_timer (chan);
       
  1088     }
       
  1089 
       
  1090   if (new_state == MUC_STATE_JOINED || new_state == MUC_STATE_AUTH)
       
  1091     {
       
  1092       if (!priv->ready_emitted)
       
  1093         {
       
  1094           g_signal_emit (chan, signals[READY], 0);
       
  1095 
       
  1096           priv->ready_emitted = TRUE;
       
  1097         }
       
  1098     }
       
  1099 }
       
  1100 
       
  1101 
       
  1102 static void
       
  1103 close_channel (GabbleMucChannel *chan, const gchar *reason,
       
  1104                gboolean inform_muc, GabbleHandle actor, guint reason_code)
       
  1105 {
       
  1106   GabbleMucChannelPrivate *priv;
       
  1107   GIntSet *set;
       
  1108 
       
  1109   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
  1110 
       
  1111   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1112 
       
  1113   if (priv->closed)
       
  1114     return;
       
  1115 
       
  1116   priv->closed = TRUE;
       
  1117 
       
  1118   /* Remove us from member list */
       
  1119   set = g_intset_new ();
       
  1120   g_intset_add (set, GABBLE_GROUP_MIXIN (chan)->self_handle);
       
  1121 
       
  1122   gabble_group_mixin_change_members (G_OBJECT (chan),
       
  1123                                      (reason != NULL) ? reason : "",
       
  1124                                      NULL, set, NULL, NULL, actor,
       
  1125                                      reason_code);
       
  1126 
       
  1127   g_intset_destroy (set);
       
  1128 
       
  1129   /* Inform the MUC if requested */
       
  1130   if (inform_muc && priv->state >= MUC_STATE_INITIATED)
       
  1131     {
       
  1132       send_leave_message (chan, reason);
       
  1133     }
       
  1134 
       
  1135   /* Update state and emit Closed signal */
       
  1136   g_object_set (chan, "state", MUC_STATE_ENDED, NULL);
       
  1137 
       
  1138   g_signal_emit (chan, signals[CLOSED], 0);
       
  1139 }
       
  1140 
       
  1141 gboolean
       
  1142 _gabble_muc_channel_is_ready (GabbleMucChannel *chan)
       
  1143 {
       
  1144   GabbleMucChannelPrivate *priv;
       
  1145 
       
  1146   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
  1147 
       
  1148   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1149 
       
  1150   return priv->ready_emitted;
       
  1151 }
       
  1152 
       
  1153 /**
       
  1154  * _gabble_muc_channel_presence_error
       
  1155  */
       
  1156 void
       
  1157 _gabble_muc_channel_presence_error (GabbleMucChannel *chan,
       
  1158                                     const gchar *jid,
       
  1159                                     LmMessageNode *pres_node)
       
  1160 {
       
  1161   GabbleMucChannelPrivate *priv;
       
  1162   LmMessageNode *error_node;
       
  1163   GabbleXmppError error;
       
  1164   GabbleHandle actor = 0;
       
  1165   guint reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
       
  1166 
       
  1167   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
  1168 
       
  1169   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1170 
       
  1171   if (strcmp (jid, priv->self_jid->str) != 0)
       
  1172     {
       
  1173       g_warning ("%s: presence error from other jids than self not handled",
       
  1174                  G_STRFUNC);
       
  1175       return;
       
  1176     }
       
  1177 
       
  1178   error_node = lm_message_node_get_child (pres_node, "error");
       
  1179   if (error_node == NULL)
       
  1180     {
       
  1181       g_warning ("%s: missing required node 'error'", G_STRFUNC);
       
  1182       return;
       
  1183     }
       
  1184 
       
  1185   error = gabble_xmpp_error_from_node (error_node);
       
  1186 
       
  1187   if (priv->state >= MUC_STATE_JOINED)
       
  1188     {
       
  1189       g_warning ("%s: presence error while already member of the channel -- NYI",
       
  1190                  G_STRFUNC);
       
  1191       return;
       
  1192     }
       
  1193 
       
  1194   /* We're not a member, find out why the join request failed
       
  1195    * and act accordingly. */
       
  1196   if (error == XMPP_ERROR_NOT_AUTHORIZED)
       
  1197     {
       
  1198       /* channel can sit requiring a password indefinitely */
       
  1199       clear_join_timer (chan);
       
  1200 
       
  1201       /* Password already provided and incorrect? */
       
  1202       if (priv->state == MUC_STATE_AUTH)
       
  1203         {
       
  1204           provide_password_return_if_pending (chan, FALSE);
       
  1205 
       
  1206           return;
       
  1207         }
       
  1208 
       
  1209       gabble_debug (DEBUG_FLAG, "password required to join, changing password flags");
       
  1210 
       
  1211       change_password_flags (chan, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0);
       
  1212 
       
  1213       g_object_set (chan, "state", MUC_STATE_AUTH, NULL);
       
  1214     }
       
  1215   else
       
  1216     {
       
  1217       GError *tp_error;
       
  1218 
       
  1219       switch (error) {
       
  1220         case XMPP_ERROR_FORBIDDEN:
       
  1221           tp_error = g_error_new (TELEPATHY_ERRORS, ChannelBanned,
       
  1222                                   "banned from room");
       
  1223           reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
       
  1224           break;
       
  1225         case XMPP_ERROR_SERVICE_UNAVAILABLE:
       
  1226           tp_error = g_error_new (TELEPATHY_ERRORS, ChannelFull,
       
  1227                                   "room is full");
       
  1228           break;
       
  1229         case XMPP_ERROR_REGISTRATION_REQUIRED:
       
  1230           tp_error = g_error_new (TELEPATHY_ERRORS, ChannelInviteOnly,
       
  1231                                   "room is invite only");
       
  1232           break;
       
  1233         case XMPP_ERROR_CONFLICT:
       
  1234           if (priv->nick_retry_count < MAX_NICK_RETRIES)
       
  1235             {
       
  1236               g_string_append_c (priv->self_jid, '_');
       
  1237 
       
  1238               if (send_join_request (chan, priv->password, &tp_error))
       
  1239                 {
       
  1240                   priv->nick_retry_count++;
       
  1241                   return;
       
  1242                 }
       
  1243             }
       
  1244           else
       
  1245             {
       
  1246               tp_error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
  1247                   "nickname already in use and retry count exceeded");
       
  1248             }
       
  1249           break;
       
  1250         default:
       
  1251           if (error != INVALID_XMPP_ERROR)
       
  1252             {
       
  1253               tp_error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
  1254                                       gabble_xmpp_error_description (error));
       
  1255             }
       
  1256           else
       
  1257             {
       
  1258               tp_error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
  1259                                       "unknown error");
       
  1260             }
       
  1261           break;
       
  1262       }
       
  1263 
       
  1264       g_signal_emit (chan, signals[JOIN_ERROR], 0, tp_error);
       
  1265 
       
  1266       close_channel (chan, tp_error->message, FALSE, actor, reason_code);
       
  1267 
       
  1268       g_error_free (tp_error);
       
  1269     }
       
  1270 }
       
  1271 
       
  1272 static GabbleMucRole
       
  1273 get_role_from_string (const gchar *role)
       
  1274 {
       
  1275   guint i;
       
  1276 
       
  1277   if (role == NULL)
       
  1278     {
       
  1279       return ROLE_VISITOR;
       
  1280     }
       
  1281 
       
  1282   for (i = 0; i < NUM_ROLES; i++)
       
  1283     {
       
  1284       if (strcmp (role, muc_roles[i]) == 0)
       
  1285         {
       
  1286           return i;
       
  1287         }
       
  1288     }
       
  1289 
       
  1290   g_warning ("%s: unknown role '%s' -- defaulting to ROLE_VISITOR",
       
  1291              G_STRFUNC, role);
       
  1292 
       
  1293   return ROLE_VISITOR;
       
  1294 }
       
  1295 
       
  1296 static GabbleMucAffiliation
       
  1297 get_affiliation_from_string (const gchar *affil)
       
  1298 {
       
  1299   guint i;
       
  1300 
       
  1301   if (affil == NULL)
       
  1302     {
       
  1303       return AFFILIATION_NONE;
       
  1304     }
       
  1305 
       
  1306   for (i = 0; i < NUM_AFFILIATIONS; i++)
       
  1307     {
       
  1308       if (strcmp (affil, muc_affiliations[i]) == 0)
       
  1309         {
       
  1310           return i;
       
  1311         }
       
  1312     }
       
  1313 
       
  1314   g_warning ("%s: unknown affiliation '%s' -- defaulting to "
       
  1315              "AFFILIATION_NONE", G_STRFUNC, affil);
       
  1316 
       
  1317   return AFFILIATION_NONE;
       
  1318 }
       
  1319 
       
  1320 static LmHandlerResult
       
  1321 room_created_submit_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
       
  1322                               LmMessage *reply_msg, GObject *object,
       
  1323                               gpointer user_data)
       
  1324 {
       
  1325   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  1326     {
       
  1327       g_warning ("%s: failed to submit room config", G_STRFUNC);
       
  1328     }
       
  1329 
       
  1330   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1331 }
       
  1332 
       
  1333 static LmMessageNode *
       
  1334 config_form_get_form_node (LmMessage *msg)
       
  1335 {
       
  1336   LmMessageNode *node;
       
  1337 
       
  1338   /* find the query node */
       
  1339   node = lm_message_node_get_child (msg->node, "query");
       
  1340   if (node == NULL)
       
  1341     return NULL;
       
  1342 
       
  1343   /* then the form node */
       
  1344   for (node = node->children; node; node = node->next)
       
  1345     {
       
  1346       if (strcmp (node->name, "x") == 0)
       
  1347         {
       
  1348           if (!lm_message_node_has_namespace (node, NS_X_DATA, NULL))
       
  1349             {
       
  1350               continue;
       
  1351             }
       
  1352 
       
  1353           if (strcmp (lm_message_node_get_attribute (node, "type"),
       
  1354                       "form") != 0)
       
  1355             {
       
  1356               continue;
       
  1357             }
       
  1358 
       
  1359           return node;
       
  1360         }
       
  1361     }
       
  1362 
       
  1363   return NULL;
       
  1364 }
       
  1365 
       
  1366 static LmHandlerResult
       
  1367 perms_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
       
  1368                             LmMessage *reply_msg, GObject *object,
       
  1369                             gpointer user_data)
       
  1370 {
       
  1371   GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
       
  1372   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1373   LmMessageNode *form_node, *node;
       
  1374 
       
  1375   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  1376     {
       
  1377       g_warning ("%s: request for config form denied, property permissions "
       
  1378                  "will be inaccurate", G_STRFUNC);
       
  1379       goto OUT;
       
  1380     }
       
  1381 
       
  1382   /* just in case our affiliation has changed in the meantime */
       
  1383   if (priv->self_affil != AFFILIATION_OWNER)
       
  1384     goto OUT;
       
  1385 
       
  1386   form_node = config_form_get_form_node (reply_msg);
       
  1387   if (form_node == NULL)
       
  1388     {
       
  1389       g_warning ("%s: form node node found, property permissions will be "
       
  1390                  "inaccurate", G_STRFUNC);
       
  1391       goto OUT;
       
  1392     }
       
  1393 
       
  1394   for (node = form_node->children; node; node = node->next)
       
  1395     {
       
  1396       const gchar *var;
       
  1397 
       
  1398       if (strcmp (node->name, "field") != 0)
       
  1399         continue;
       
  1400 
       
  1401       var = lm_message_node_get_attribute (node, "var");
       
  1402       if (var == NULL)
       
  1403         continue;
       
  1404 
       
  1405       if (strcmp (var, "muc#roomconfig_roomdesc") == 0 ||
       
  1406           strcmp (var, "muc#owner_roomdesc") == 0)
       
  1407         {
       
  1408           if (gabble_properties_mixin_is_readable (G_OBJECT (chan),
       
  1409                                                    ROOM_PROP_DESCRIPTION))
       
  1410             {
       
  1411               gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1412                   ROOM_PROP_DESCRIPTION, TP_PROPERTY_FLAG_WRITE, 0,
       
  1413                   NULL);
       
  1414 
       
  1415               goto OUT;
       
  1416             }
       
  1417         }
       
  1418     }
       
  1419 
       
  1420 OUT:
       
  1421   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1422 }
       
  1423 
       
  1424 static void
       
  1425 update_permissions (GabbleMucChannel *chan)
       
  1426 {
       
  1427   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1428   TpChannelGroupFlags grp_flags_add, grp_flags_rem;
       
  1429   TpPropertyFlags prop_flags_add, prop_flags_rem;
       
  1430   GArray *changed_props_val, *changed_props_flags;
       
  1431 
       
  1432   /*
       
  1433    * Update group flags.
       
  1434    */
       
  1435   grp_flags_add = TP_CHANNEL_GROUP_FLAG_CAN_ADD |
       
  1436                   TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD;
       
  1437   grp_flags_rem = 0;
       
  1438 
       
  1439   if (priv->self_role == ROLE_MODERATOR)
       
  1440     {
       
  1441       grp_flags_add |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
       
  1442                        TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE;
       
  1443     }
       
  1444   else
       
  1445     {
       
  1446       grp_flags_rem |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
       
  1447                        TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE;
       
  1448     }
       
  1449 
       
  1450   gabble_group_mixin_change_flags (G_OBJECT (chan), grp_flags_add,
       
  1451                                    grp_flags_rem);
       
  1452 
       
  1453 
       
  1454   /*
       
  1455    * Update write capabilities based on room configuration
       
  1456    * and own role and affiliation.
       
  1457    */
       
  1458 
       
  1459   changed_props_val = changed_props_flags = NULL;
       
  1460 
       
  1461   /*
       
  1462    * Subject
       
  1463    *
       
  1464    * FIXME: this might be allowed for participants/moderators only,
       
  1465    *        so for now just rely on the server making that call.
       
  1466    */
       
  1467 
       
  1468   if (priv->self_role >= ROLE_VISITOR)
       
  1469     {
       
  1470       prop_flags_add = TP_PROPERTY_FLAG_WRITE;
       
  1471       prop_flags_rem = 0;
       
  1472     }
       
  1473   else
       
  1474     {
       
  1475       prop_flags_add = 0;
       
  1476       prop_flags_rem = TP_PROPERTY_FLAG_WRITE;
       
  1477     }
       
  1478 
       
  1479   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1480       ROOM_PROP_SUBJECT, prop_flags_add, prop_flags_rem,
       
  1481       &changed_props_flags);
       
  1482 
       
  1483   /* Room definition */
       
  1484   if (priv->self_affil == AFFILIATION_OWNER)
       
  1485     {
       
  1486       prop_flags_add = TP_PROPERTY_FLAG_WRITE;
       
  1487       prop_flags_rem = 0;
       
  1488     }
       
  1489   else
       
  1490     {
       
  1491       prop_flags_add = 0;
       
  1492       prop_flags_rem = TP_PROPERTY_FLAG_WRITE;
       
  1493     }
       
  1494 
       
  1495   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1496       ROOM_PROP_ANONYMOUS, prop_flags_add, prop_flags_rem,
       
  1497       &changed_props_flags);
       
  1498 
       
  1499   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1500       ROOM_PROP_INVITE_ONLY, prop_flags_add, prop_flags_rem,
       
  1501       &changed_props_flags);
       
  1502 
       
  1503   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1504       ROOM_PROP_MODERATED, prop_flags_add, prop_flags_rem,
       
  1505       &changed_props_flags);
       
  1506 
       
  1507   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1508       ROOM_PROP_NAME, prop_flags_add, prop_flags_rem,
       
  1509       &changed_props_flags);
       
  1510 
       
  1511   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1512       ROOM_PROP_PASSWORD, prop_flags_add, prop_flags_rem,
       
  1513       &changed_props_flags);
       
  1514 
       
  1515   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1516       ROOM_PROP_PASSWORD_REQUIRED, prop_flags_add, prop_flags_rem,
       
  1517       &changed_props_flags);
       
  1518 
       
  1519   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1520       ROOM_PROP_PERSISTENT, prop_flags_add, prop_flags_rem,
       
  1521       &changed_props_flags);
       
  1522 
       
  1523   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1524       ROOM_PROP_PRIVATE, prop_flags_add, prop_flags_rem,
       
  1525       &changed_props_flags);
       
  1526 
       
  1527   gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1528       ROOM_PROP_SUBJECT, prop_flags_add, prop_flags_rem,
       
  1529       &changed_props_flags);
       
  1530 
       
  1531   if (priv->self_affil == AFFILIATION_OWNER)
       
  1532     {
       
  1533       /* request the configuration form purely to see if the description
       
  1534        * is writable by us in this room. sigh. GO MUC!!! */
       
  1535       LmMessage *msg;
       
  1536       LmMessageNode *node;
       
  1537       GError *error = NULL;
       
  1538       gboolean success;
       
  1539 
       
  1540       msg = lm_message_new_with_sub_type (priv->jid,
       
  1541           LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
       
  1542       node = lm_message_node_add_child (msg->node, "query", NULL);
       
  1543       lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER);
       
  1544 
       
  1545       success = _gabble_connection_send_with_reply (priv->conn, msg,
       
  1546           perms_config_form_reply_cb, G_OBJECT (chan), NULL,
       
  1547           &error);
       
  1548 
       
  1549       lm_message_unref (msg);
       
  1550 
       
  1551       if (!success)
       
  1552         {
       
  1553           g_warning ("%s: failed to request config form: %s",
       
  1554               G_STRFUNC, error->message);
       
  1555           g_error_free (error);
       
  1556         }
       
  1557     }
       
  1558   else
       
  1559     {
       
  1560       /* mark description unwritable if we're no longer an owner */
       
  1561       gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1562           ROOM_PROP_DESCRIPTION, 0, TP_PROPERTY_FLAG_WRITE,
       
  1563           &changed_props_flags);
       
  1564     }
       
  1565 
       
  1566   /*
       
  1567    * Emit signals.
       
  1568    */
       
  1569   gabble_properties_mixin_emit_changed (G_OBJECT (chan), &changed_props_val);
       
  1570   gabble_properties_mixin_emit_flags (G_OBJECT (chan), &changed_props_flags);
       
  1571 }
       
  1572 
       
  1573 /**
       
  1574  * _gabble_muc_channel_member_presence_updated
       
  1575  */
       
  1576 void
       
  1577 _gabble_muc_channel_member_presence_updated (GabbleMucChannel *chan,
       
  1578                                              GabbleHandle handle,
       
  1579                                              LmMessage *message,
       
  1580                                              LmMessageNode *x_node)
       
  1581 {
       
  1582   GabbleMucChannelPrivate *priv;
       
  1583   GIntSet *set;
       
  1584   GabbleGroupMixin *mixin;
       
  1585   LmMessageNode *item_node, *node;
       
  1586   const gchar *affil, *role, *owner_jid, *status_code;
       
  1587   GabbleHandle actor = 0;
       
  1588   guint reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
       
  1589 
       
  1590   gabble_debug (DEBUG_FLAG, "called");
       
  1591 
       
  1592   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
  1593 
       
  1594   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1595 
       
  1596   mixin = GABBLE_GROUP_MIXIN (chan);
       
  1597 
       
  1598   item_node = lm_message_node_get_child (x_node, "item");
       
  1599   if (item_node == NULL)
       
  1600     {
       
  1601       g_warning ("%s: node missing 'item' child, ignoring", G_STRFUNC);
       
  1602       return;
       
  1603     }
       
  1604 
       
  1605   node = lm_message_node_get_child (x_node, "status");
       
  1606   if (node)
       
  1607     {
       
  1608       status_code = lm_message_node_get_attribute (node, "code");
       
  1609     }
       
  1610   else
       
  1611     {
       
  1612       status_code = NULL;
       
  1613     }
       
  1614 
       
  1615   role = lm_message_node_get_attribute (item_node, "role");
       
  1616   affil = lm_message_node_get_attribute (item_node, "affiliation");
       
  1617   owner_jid = lm_message_node_get_attribute (item_node, "jid");
       
  1618 
       
  1619   /* update channel members according to presence */
       
  1620   set = g_intset_new ();
       
  1621   g_intset_add (set, handle);
       
  1622 
       
  1623   if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_UNAVAILABLE)
       
  1624     {
       
  1625       if (!handle_set_is_member (mixin->members, handle))
       
  1626         {
       
  1627           gabble_group_mixin_change_members (G_OBJECT (chan), "", set, NULL,
       
  1628                                              NULL, NULL, 0, 0);
       
  1629 
       
  1630           if (owner_jid != NULL)
       
  1631             {
       
  1632               GabbleHandle owner_handle;
       
  1633 
       
  1634               owner_handle = gabble_handle_for_contact (
       
  1635                   chan->group.handle_repo, owner_jid, FALSE);
       
  1636 
       
  1637               gabble_group_mixin_add_handle_owner (G_OBJECT (chan), handle,
       
  1638                                                    owner_handle);
       
  1639             }
       
  1640 
       
  1641           if (handle == mixin->self_handle)
       
  1642             {
       
  1643               g_object_set (chan, "state", MUC_STATE_JOINED, NULL);
       
  1644             }
       
  1645         }
       
  1646 
       
  1647       if (handle == mixin->self_handle)
       
  1648         {
       
  1649           GabbleMucRole new_role;
       
  1650           GabbleMucAffiliation new_affil;
       
  1651 
       
  1652           /* accept newly-created room settings before we send anything
       
  1653            * below which queryies them. */
       
  1654           if (status_code && strcmp (status_code, "201") == 0)
       
  1655             {
       
  1656               LmMessage *msg;
       
  1657               GError *error = NULL;
       
  1658 
       
  1659               msg = lm_message_new_with_sub_type (priv->jid, LM_MESSAGE_TYPE_IQ,
       
  1660                                                   LM_MESSAGE_SUB_TYPE_SET);
       
  1661 
       
  1662               node = lm_message_node_add_child (msg->node, "query", NULL);
       
  1663               lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER);
       
  1664 
       
  1665               node = lm_message_node_add_child (node, "x", NULL);
       
  1666               lm_message_node_set_attributes (node,
       
  1667                                               "xmlns", NS_X_DATA,
       
  1668                                               "type", "submit",
       
  1669                                               NULL);
       
  1670 
       
  1671               if (!_gabble_connection_send_with_reply (priv->conn, msg,
       
  1672                     room_created_submit_reply_cb, G_OBJECT (chan), NULL, &error))
       
  1673                 {
       
  1674                   g_warning ("%s: failed to send submit message: %s", G_STRFUNC,
       
  1675                              error->message);
       
  1676                   g_error_free (error);
       
  1677 
       
  1678                   close_channel (chan, NULL, TRUE, actor, reason_code);
       
  1679 
       
  1680                   goto OUT;
       
  1681                 }
       
  1682             }
       
  1683 
       
  1684           /* Update room properties */
       
  1685           room_properties_update (chan);
       
  1686 
       
  1687           /* update permissions after requesting new properties so that if we
       
  1688            * become an owner, we get our configuration form reply after the
       
  1689            * discovery reply, so we know whether there is a description
       
  1690            * property before we try and decide whether we can write to it. */
       
  1691           new_role = get_role_from_string (role);
       
  1692           new_affil = get_affiliation_from_string (affil);
       
  1693 
       
  1694           if (new_role != priv->self_role || new_affil != priv->self_affil)
       
  1695             {
       
  1696               priv->self_role = new_role;
       
  1697               priv->self_affil = new_affil;
       
  1698 
       
  1699               update_permissions (chan);
       
  1700             }
       
  1701         }
       
  1702     }
       
  1703   else
       
  1704     {
       
  1705       LmMessageNode *reason_node, *actor_node;
       
  1706       const gchar *reason = "", *actor_jid = "";
       
  1707 
       
  1708       actor_node = lm_message_node_get_child (item_node, "actor");
       
  1709       if (actor_node != NULL)
       
  1710         {
       
  1711           actor_jid = lm_message_node_get_attribute (actor_node, "jid");
       
  1712           if (actor_jid != NULL)
       
  1713             {
       
  1714               actor = gabble_handle_for_contact(chan->group.handle_repo, actor_jid, FALSE);
       
  1715             }
       
  1716         }
       
  1717 
       
  1718       /* Possible reasons we could have been removed from the room:
       
  1719        * 301 banned
       
  1720        * 307 kicked
       
  1721        * 321 "because of an affiliation change" - no reason_code
       
  1722        * 322 room has become members-only and we're not a member - no reason_code
       
  1723        * 332 system (server) is being shut down - no reason code
       
  1724        */
       
  1725       if (status_code)
       
  1726         {
       
  1727           if (strcmp (status_code, "301") == 0)
       
  1728             {
       
  1729               reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
       
  1730             }
       
  1731           else if (strcmp (status_code, "307") == 0)
       
  1732             {
       
  1733               reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
       
  1734             }
       
  1735         }
       
  1736 
       
  1737       reason_node = lm_message_node_get_child (item_node, "reason");
       
  1738       if (reason_node != NULL)
       
  1739         {
       
  1740           reason = lm_message_node_get_value (reason_node);
       
  1741         }
       
  1742 
       
  1743       if (handle != mixin->self_handle)
       
  1744         {
       
  1745           gabble_group_mixin_change_members (G_OBJECT (chan), reason,
       
  1746                                              NULL, set, NULL, NULL,
       
  1747                                              actor, reason_code);
       
  1748         }
       
  1749       else
       
  1750         {
       
  1751           close_channel (chan, reason, FALSE, actor, reason_code);
       
  1752         }
       
  1753     }
       
  1754 
       
  1755 OUT:
       
  1756   g_intset_destroy (set);
       
  1757 }
       
  1758 
       
  1759 
       
  1760 /**
       
  1761  * _gabble_muc_channel_receive
       
  1762  */
       
  1763 gboolean
       
  1764 _gabble_muc_channel_receive (GabbleMucChannel *chan,
       
  1765                              TpChannelTextMessageType msg_type,
       
  1766                              TpHandleType handle_type,
       
  1767                              GabbleHandle sender,
       
  1768                              time_t timestamp,
       
  1769                              const gchar *text,
       
  1770                              LmMessage *msg)
       
  1771 {
       
  1772   gboolean error;
       
  1773   GabbleMucChannelPrivate *priv;
       
  1774   LmMessageNode *subj_node, *node;
       
  1775   GValue val = { 0, };
       
  1776 
       
  1777   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
  1778 
       
  1779   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1780 
       
  1781   error = lm_message_get_sub_type (msg) == LM_MESSAGE_SUB_TYPE_ERROR;
       
  1782 
       
  1783   subj_node = lm_message_node_get_child (msg->node, "subject");
       
  1784 
       
  1785   if (subj_node)
       
  1786     {
       
  1787       GArray *changed_values, *changed_flags;
       
  1788 
       
  1789       if (priv->properties_ctx)
       
  1790         {
       
  1791           gabble_properties_context_remove (priv->properties_ctx,
       
  1792               ROOM_PROP_SUBJECT);
       
  1793         }
       
  1794 
       
  1795       if (error)
       
  1796         {
       
  1797           GabbleXmppError xmpp_error = INVALID_XMPP_ERROR;
       
  1798           const gchar *err_desc = NULL;
       
  1799 
       
  1800           node = lm_message_node_get_child (msg->node, "error");
       
  1801           if (node)
       
  1802             {
       
  1803               xmpp_error = gabble_xmpp_error_from_node (node);
       
  1804             }
       
  1805 
       
  1806           if (xmpp_error != INVALID_XMPP_ERROR)
       
  1807             {
       
  1808               err_desc = gabble_xmpp_error_description (xmpp_error);
       
  1809             }
       
  1810 
       
  1811           if (priv->properties_ctx)
       
  1812             {
       
  1813               GError *error = NULL;
       
  1814 
       
  1815               error = g_error_new (TELEPATHY_ERRORS, PermissionDenied,
       
  1816                   (err_desc) ? err_desc : "failed to change subject");
       
  1817 
       
  1818               gabble_properties_context_return (priv->properties_ctx, error);
       
  1819               priv->properties_ctx = NULL;
       
  1820 
       
  1821               /* Get the properties into a consistent state. */
       
  1822               room_properties_update (chan);
       
  1823             }
       
  1824 
       
  1825           return TRUE;
       
  1826         }
       
  1827 
       
  1828       changed_values = changed_flags = NULL;
       
  1829 
       
  1830       /* ROOM_PROP_SUBJECT */
       
  1831       g_value_init (&val, G_TYPE_STRING);
       
  1832       g_value_set_string (&val, lm_message_node_get_value (subj_node));
       
  1833 
       
  1834       gabble_properties_mixin_change_value (G_OBJECT (chan),
       
  1835           ROOM_PROP_SUBJECT, &val, &changed_values);
       
  1836 
       
  1837       gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1838           ROOM_PROP_SUBJECT, TP_PROPERTY_FLAG_READ, 0,
       
  1839           &changed_flags);
       
  1840 
       
  1841       g_value_unset (&val);
       
  1842 
       
  1843       if (handle_type == TP_HANDLE_TYPE_CONTACT)
       
  1844         {
       
  1845           /* ROOM_PROP_SUBJECT_CONTACT */
       
  1846           g_value_init (&val, G_TYPE_UINT);
       
  1847           g_value_set_uint (&val, sender);
       
  1848 
       
  1849           gabble_properties_mixin_change_value (G_OBJECT (chan),
       
  1850               ROOM_PROP_SUBJECT_CONTACT, &val, &changed_values);
       
  1851 
       
  1852           gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1853               ROOM_PROP_SUBJECT_CONTACT, TP_PROPERTY_FLAG_READ, 0,
       
  1854               &changed_flags);
       
  1855 
       
  1856           g_value_unset (&val);
       
  1857         }
       
  1858 
       
  1859       /* ROOM_PROP_SUBJECT_TIMESTAMP */
       
  1860       g_value_init (&val, G_TYPE_UINT);
       
  1861       g_value_set_uint (&val, timestamp);
       
  1862 
       
  1863       gabble_properties_mixin_change_value (G_OBJECT (chan),
       
  1864           ROOM_PROP_SUBJECT_TIMESTAMP, &val, &changed_values);
       
  1865 
       
  1866       gabble_properties_mixin_change_flags (G_OBJECT (chan),
       
  1867           ROOM_PROP_SUBJECT_TIMESTAMP, TP_PROPERTY_FLAG_READ, 0,
       
  1868           &changed_flags);
       
  1869 
       
  1870       g_value_unset (&val);
       
  1871 
       
  1872       /* Emit signals */
       
  1873       gabble_properties_mixin_emit_changed (G_OBJECT (chan), &changed_values);
       
  1874       gabble_properties_mixin_emit_flags (G_OBJECT (chan), &changed_flags);
       
  1875 
       
  1876       if (priv->properties_ctx)
       
  1877         {
       
  1878           if (gabble_properties_context_return_if_done (priv->properties_ctx))
       
  1879             {
       
  1880               priv->properties_ctx = NULL;
       
  1881             }
       
  1882         }
       
  1883 
       
  1884       return TRUE;
       
  1885     }
       
  1886   else if (handle_type == TP_HANDLE_TYPE_ROOM)
       
  1887     {
       
  1888       NODE_DEBUG (msg->node, "ignoring message from channel");
       
  1889 
       
  1890       return TRUE;
       
  1891     }
       
  1892   else if ((sender == chan->group.self_handle) && (timestamp == 0))
       
  1893     {
       
  1894       /* If we sent the message and it's not delayed, just emit the sent signal */
       
  1895       timestamp = time (NULL);
       
  1896       gabble_text_mixin_emit_sent (G_OBJECT (chan), timestamp, msg_type, text);
       
  1897 
       
  1898       return TRUE;
       
  1899     }
       
  1900 
       
  1901   /* Receive messages from other contacts and our own if they're delayed, and
       
  1902    * set the timestamp for non-delayed messages */
       
  1903   if (timestamp == 0)
       
  1904       timestamp = time (NULL);
       
  1905 
       
  1906   return gabble_text_mixin_receive (G_OBJECT (chan), msg_type, sender,
       
  1907       timestamp, text);
       
  1908 }
       
  1909 
       
  1910 void
       
  1911 _gabble_muc_channel_handle_invited (GabbleMucChannel *chan,
       
  1912                                     GabbleHandle inviter,
       
  1913                                     const gchar *message)
       
  1914 {
       
  1915   GabbleMucChannelPrivate *priv;
       
  1916   GabbleHandle self_handle;
       
  1917   GIntSet *set_members, *set_pending;
       
  1918 
       
  1919   g_assert (GABBLE_IS_MUC_CHANNEL (chan));
       
  1920 
       
  1921   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  1922 
       
  1923   /* add ourself to local pending and the inviter to members */
       
  1924   set_members = g_intset_new ();
       
  1925   set_pending = g_intset_new ();
       
  1926 
       
  1927   g_intset_add (set_members, inviter);
       
  1928 
       
  1929   /* get our own identity in the room */
       
  1930   contact_handle_to_room_identity (chan, priv->conn->self_handle,
       
  1931                                    &self_handle, &priv->self_jid);
       
  1932   g_intset_add (set_pending, self_handle);
       
  1933 
       
  1934   gabble_group_mixin_change_members (G_OBJECT (chan), message, set_members,
       
  1935                                      NULL, set_pending, NULL, inviter,
       
  1936                                      TP_CHANNEL_GROUP_CHANGE_REASON_INVITED);
       
  1937 
       
  1938   g_intset_destroy (set_members);
       
  1939   g_intset_destroy (set_pending);
       
  1940 
       
  1941   /* queue the message */
       
  1942   if (message && (message[0] != '\0'))
       
  1943     {
       
  1944       gabble_text_mixin_receive (G_OBJECT (chan), TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, inviter,
       
  1945                                   time(NULL), message);
       
  1946     }
       
  1947 
       
  1948   /* emit READY signal so NewChannel is emitted */
       
  1949   g_signal_emit (chan, signals[READY], 0);
       
  1950   priv->ready_emitted = TRUE;
       
  1951 }
       
  1952 
       
  1953 
       
  1954 /**
       
  1955  * gabble_muc_channel_acknowledge_pending_messages
       
  1956  *
       
  1957  * Implements D-Bus method AcknowledgePendingMessages
       
  1958  * on interface org.freedesktop.Telepathy.Channel.Type.Text
       
  1959  *
       
  1960  * @error: Used to return a pointer to a GError detailing any error
       
  1961  *         that occurred, D-Bus will throw the error only if this
       
  1962  *         function returns FALSE.
       
  1963  *
       
  1964  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  1965  */
       
  1966 gboolean
       
  1967 gabble_muc_channel_acknowledge_pending_messages (GabbleMucChannel *self,
       
  1968                                                  const GArray *ids,
       
  1969                                                  GError **error)
       
  1970 {
       
  1971   g_assert (GABBLE_IS_MUC_CHANNEL (self));
       
  1972 
       
  1973   return gabble_text_mixin_acknowledge_pending_messages (G_OBJECT (self), ids,
       
  1974       error);
       
  1975 }
       
  1976 
       
  1977 
       
  1978 /**
       
  1979  * gabble_muc_channel_add_members
       
  1980  *
       
  1981  * Implements D-Bus method AddMembers
       
  1982  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  1983  *
       
  1984  * @error: Used to return a pointer to a GError detailing any error
       
  1985  *         that occurred, D-Bus will throw the error only if this
       
  1986  *         function returns FALSE.
       
  1987  *
       
  1988  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  1989  */
       
  1990 gboolean
       
  1991 gabble_muc_channel_add_members (GabbleMucChannel *self,
       
  1992                                 const GArray *contacts,
       
  1993                                 const gchar *message,
       
  1994                                 GError **error)
       
  1995 {
       
  1996   return gabble_group_mixin_add_members (G_OBJECT (self), contacts, message,
       
  1997       error);
       
  1998 }
       
  1999 
       
  2000 
       
  2001 /**
       
  2002  * gabble_muc_channel_close
       
  2003  *
       
  2004  * Implements D-Bus method Close
       
  2005  * on interface org.freedesktop.Telepathy.Channel
       
  2006  *
       
  2007  * @error: Used to return a pointer to a GError detailing any error
       
  2008  *         that occurred, D-Bus will throw the error only if this
       
  2009  *         function returns FALSE.
       
  2010  *
       
  2011  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2012  */
       
  2013 gboolean
       
  2014 gabble_muc_channel_close (GabbleMucChannel *self,
       
  2015                           GError **error)
       
  2016 {
       
  2017   GabbleMucChannelPrivate *priv;
       
  2018 
       
  2019   g_assert (GABBLE_IS_MUC_CHANNEL (self));
       
  2020 
       
  2021   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
  2022 
       
  2023   gabble_debug (DEBUG_FLAG, "called on %p", self);
       
  2024 
       
  2025   if (priv->closed)
       
  2026     {
       
  2027       gabble_debug (DEBUG_FLAG, "channel already closed");
       
  2028 
       
  2029       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2030           "Channel already closed");
       
  2031 
       
  2032       return FALSE;
       
  2033     }
       
  2034 
       
  2035   close_channel (self, NULL, TRUE, 0, 0);
       
  2036 
       
  2037   return TRUE;
       
  2038 }
       
  2039 
       
  2040 
       
  2041 /**
       
  2042  * gabble_muc_channel_get_all_members
       
  2043  *
       
  2044  * Implements D-Bus method GetAllMembers
       
  2045  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2046  *
       
  2047  * @error: Used to return a pointer to a GError detailing any error
       
  2048  *         that occurred, D-Bus will throw the error only if this
       
  2049  *         function returns FALSE.
       
  2050  *
       
  2051  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2052  */
       
  2053 gboolean
       
  2054 gabble_muc_channel_get_all_members (GabbleMucChannel *self,
       
  2055                                     GArray **ret,
       
  2056                                     GArray **ret1,
       
  2057                                     GArray **ret2,
       
  2058                                     GError **error)
       
  2059 {
       
  2060   return gabble_group_mixin_get_all_members (G_OBJECT (self), ret, ret1, ret2,
       
  2061       error);
       
  2062 }
       
  2063 
       
  2064 
       
  2065 /**
       
  2066  * gabble_muc_channel_get_channel_type
       
  2067  *
       
  2068  * Implements D-Bus method GetChannelType
       
  2069  * on interface org.freedesktop.Telepathy.Channel
       
  2070  *
       
  2071  * @error: Used to return a pointer to a GError detailing any error
       
  2072  *         that occurred, D-Bus will throw the error only if this
       
  2073  *         function returns FALSE.
       
  2074  *
       
  2075  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2076  */
       
  2077 gboolean
       
  2078 gabble_muc_channel_get_channel_type (GabbleMucChannel *self,
       
  2079                                      gchar **ret,
       
  2080                                      GError **error)
       
  2081 {
       
  2082   *ret = g_strdup (TP_IFACE_CHANNEL_TYPE_TEXT);
       
  2083 
       
  2084   return TRUE;
       
  2085 }
       
  2086 
       
  2087 
       
  2088 /**
       
  2089  * gabble_muc_channel_get_group_flags
       
  2090  *
       
  2091  * Implements D-Bus method GetGroupFlags
       
  2092  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2093  *
       
  2094  * @error: Used to return a pointer to a GError detailing any error
       
  2095  *         that occurred, D-Bus will throw the error only if this
       
  2096  *         function returns FALSE.
       
  2097  *
       
  2098  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2099  */
       
  2100 gboolean
       
  2101 gabble_muc_channel_get_group_flags (GabbleMucChannel *self,
       
  2102                                     guint *ret,
       
  2103                                     GError **error)
       
  2104 {
       
  2105   return gabble_group_mixin_get_group_flags (G_OBJECT (self), ret, error);
       
  2106 }
       
  2107 
       
  2108 
       
  2109 /**
       
  2110  * gabble_muc_channel_get_handle
       
  2111  *
       
  2112  * Implements D-Bus method GetHandle
       
  2113  * on interface org.freedesktop.Telepathy.Channel
       
  2114  *
       
  2115  * @error: Used to return a pointer to a GError detailing any error
       
  2116  *         that occurred, D-Bus will throw the error only if this
       
  2117  *         function returns FALSE.
       
  2118  *
       
  2119  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2120  */
       
  2121 gboolean
       
  2122 gabble_muc_channel_get_handle (GabbleMucChannel *self,
       
  2123                                guint *ret,
       
  2124                                guint *ret1,
       
  2125                                GError **error)
       
  2126 {
       
  2127   GabbleMucChannelPrivate *priv;
       
  2128 
       
  2129   g_assert (GABBLE_IS_MUC_CHANNEL (self));
       
  2130 
       
  2131   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
  2132 
       
  2133   *ret = TP_HANDLE_TYPE_ROOM;
       
  2134   *ret1 = priv->handle;
       
  2135 
       
  2136   return TRUE;
       
  2137 }
       
  2138 
       
  2139 
       
  2140 /**
       
  2141  * gabble_muc_channel_get_handle_owners
       
  2142  *
       
  2143  * Implements D-Bus method GetHandleOwners
       
  2144  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2145  *
       
  2146  * @error: Used to return a pointer to a GError detailing any error
       
  2147  *         that occurred, D-Bus will throw the error only if this
       
  2148  *         function returns FALSE.
       
  2149  *
       
  2150  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2151  */
       
  2152 gboolean
       
  2153 gabble_muc_channel_get_handle_owners (GabbleMucChannel *self,
       
  2154                                       const GArray *handles,
       
  2155                                       GArray **ret,
       
  2156                                       GError **error)
       
  2157 {
       
  2158   return gabble_group_mixin_get_handle_owners (G_OBJECT (self), handles, ret,
       
  2159       error);
       
  2160 }
       
  2161 
       
  2162 
       
  2163 /**
       
  2164  * gabble_muc_channel_get_interfaces
       
  2165  *
       
  2166  * Implements D-Bus method GetInterfaces
       
  2167  * on interface org.freedesktop.Telepathy.Channel
       
  2168  *
       
  2169  * @error: Used to return a pointer to a GError detailing any error
       
  2170  *         that occurred, D-Bus will throw the error only if this
       
  2171  *         function returns FALSE.
       
  2172  *
       
  2173  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2174  */
       
  2175 gboolean
       
  2176 gabble_muc_channel_get_interfaces (GabbleMucChannel *self,
       
  2177                                    gchar ***ret,
       
  2178                                    GError **error)
       
  2179 {
       
  2180   const gchar *interfaces[] = {
       
  2181       TP_IFACE_CHANNEL_INTERFACE_GROUP,
       
  2182       TP_IFACE_CHANNEL_INTERFACE_PASSWORD,
       
  2183       TP_IFACE_PROPERTIES,
       
  2184       NULL
       
  2185   };
       
  2186 
       
  2187   *ret = g_strdupv ((gchar **) interfaces);
       
  2188 
       
  2189   return TRUE;
       
  2190 }
       
  2191 
       
  2192 
       
  2193 /**
       
  2194  * gabble_muc_channel_get_local_pending_members
       
  2195  *
       
  2196  * Implements D-Bus method GetLocalPendingMembers
       
  2197  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2198  *
       
  2199  * @error: Used to return a pointer to a GError detailing any error
       
  2200  *         that occurred, D-Bus will throw the error only if this
       
  2201  *         function returns FALSE.
       
  2202  *
       
  2203  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2204  */
       
  2205 gboolean
       
  2206 gabble_muc_channel_get_local_pending_members (GabbleMucChannel *self,
       
  2207                                               GArray **ret,
       
  2208                                               GError **error)
       
  2209 {
       
  2210   return gabble_group_mixin_get_local_pending_members (G_OBJECT (self), ret,
       
  2211       error);
       
  2212 }
       
  2213 
       
  2214 
       
  2215 /**
       
  2216  * gabble_muc_channel_get_members
       
  2217  *
       
  2218  * Implements D-Bus method GetMembers
       
  2219  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2220  *
       
  2221  * @error: Used to return a pointer to a GError detailing any error
       
  2222  *         that occurred, D-Bus will throw the error only if this
       
  2223  *         function returns FALSE.
       
  2224  *
       
  2225  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2226  */
       
  2227 gboolean
       
  2228 gabble_muc_channel_get_members (GabbleMucChannel *self,
       
  2229                                 GArray **ret,
       
  2230                                 GError **error)
       
  2231 {
       
  2232   return gabble_group_mixin_get_members (G_OBJECT (self), ret, error);
       
  2233 }
       
  2234 
       
  2235 
       
  2236 
       
  2237 
       
  2238 /**
       
  2239  * gabble_muc_channel_get_message_types
       
  2240  *
       
  2241  * Implements D-Bus method GetMessageTypes
       
  2242  * on interface org.freedesktop.Telepathy.Channel.Type.Text
       
  2243  *
       
  2244  * @error: Used to return a pointer to a GError detailing any error
       
  2245  *         that occurred, D-Bus will throw the error only if this
       
  2246  *         function returns FALSE.
       
  2247  *
       
  2248  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2249  */
       
  2250 gboolean
       
  2251 gabble_muc_channel_get_message_types (GabbleMucChannel *self,
       
  2252                                       GArray **ret,
       
  2253                                       GError **error)
       
  2254 {
       
  2255   return gabble_text_mixin_get_message_types (G_OBJECT (self), ret, error);
       
  2256 }
       
  2257 
       
  2258 
       
  2259 /**
       
  2260  * gabble_muc_channel_get_password_flags
       
  2261  *
       
  2262  * Implements D-Bus method GetPasswordFlags
       
  2263  * on interface org.freedesktop.Telepathy.Channel.Interface.Password
       
  2264  *
       
  2265  * @error: Used to return a pointer to a GError detailing any error
       
  2266  *         that occurred, D-Bus will throw the error only if this
       
  2267  *         function returns FALSE.
       
  2268  *
       
  2269  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2270  */
       
  2271 gboolean
       
  2272 gabble_muc_channel_get_password_flags (GabbleMucChannel *self,
       
  2273                                        guint *ret,
       
  2274                                        GError **error)
       
  2275 {
       
  2276   GabbleMucChannelPrivate *priv;
       
  2277 
       
  2278   g_assert (GABBLE_IS_MUC_CHANNEL (self));
       
  2279 
       
  2280   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
  2281 
       
  2282   *ret = priv->password_flags;
       
  2283 
       
  2284   return TRUE;
       
  2285 }
       
  2286 
       
  2287 
       
  2288 /**
       
  2289  * gabble_muc_channel_get_remote_pending_members
       
  2290  *
       
  2291  * Implements D-Bus method GetRemotePendingMembers
       
  2292  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2293  *
       
  2294  * @error: Used to return a pointer to a GError detailing any error
       
  2295  *         that occurred, D-Bus will throw the error only if this
       
  2296  *         function returns false.
       
  2297  *
       
  2298  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2299  */
       
  2300 gboolean
       
  2301 gabble_muc_channel_get_remote_pending_members (GabbleMucChannel *self,
       
  2302                                                GArray **ret,
       
  2303                                                GError **error)
       
  2304 {
       
  2305   return gabble_group_mixin_get_remote_pending_members (G_OBJECT (self), ret,
       
  2306       error);
       
  2307 }
       
  2308 
       
  2309 
       
  2310 /**
       
  2311  * gabble_muc_channel_get_self_handle
       
  2312  *
       
  2313  * Implements D-Bus method GetSelfHandle
       
  2314  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2315  *
       
  2316  * @error: Used to return a pointer to a GError detailing any error
       
  2317  *         that occurred, D-Bus will throw the error only if this
       
  2318  *         function returns false.
       
  2319  *
       
  2320  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2321  */
       
  2322 gboolean
       
  2323 gabble_muc_channel_get_self_handle (GabbleMucChannel *self,
       
  2324                                     guint *ret,
       
  2325                                     GError **error)
       
  2326 {
       
  2327   return gabble_group_mixin_get_self_handle (G_OBJECT (self), ret, error);
       
  2328 }
       
  2329 
       
  2330 
       
  2331 /**
       
  2332  * gabble_muc_channel_list_pending_messages
       
  2333  *
       
  2334  * Implements D-Bus method ListPendingMessages
       
  2335  * on interface org.freedesktop.Telepathy.Channel.Type.Text
       
  2336  *
       
  2337  * @error: Used to return a pointer to a GError detailing any error
       
  2338  *         that occurred, D-Bus will throw the error only if this
       
  2339  *         function returns false.
       
  2340  *
       
  2341  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2342  */
       
  2343 gboolean
       
  2344 gabble_muc_channel_list_pending_messages (GabbleMucChannel *self,
       
  2345                                           gboolean clear,
       
  2346                                           GPtrArray **ret,
       
  2347                                           GError **error)
       
  2348 {
       
  2349   return gabble_text_mixin_list_pending_messages (G_OBJECT (self), clear, ret,
       
  2350       error);
       
  2351 }
       
  2352 
       
  2353 
       
  2354 /**
       
  2355  * gabble_muc_channel_provide_password
       
  2356  *
       
  2357  * Implements D-Bus method ProvidePassword
       
  2358  * on interface org.freedesktop.Telepathy.Channel.Interface.Password
       
  2359  *
       
  2360  * @context: The D-Bus invocation context to use to return values
       
  2361  *           or throw an error.
       
  2362  */
       
  2363 void
       
  2364 gabble_muc_channel_provide_password (GabbleMucChannel *self,
       
  2365                                      const gchar *password,
       
  2366                                      DBusGMethodInvocation *context)
       
  2367 {
       
  2368   GError *error = NULL;
       
  2369   GabbleMucChannelPrivate *priv;
       
  2370 
       
  2371   g_assert (GABBLE_IS_MUC_CHANNEL (self));
       
  2372 
       
  2373   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
  2374 
       
  2375   if ((priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE) == 0 ||
       
  2376       priv->password_ctx != NULL)
       
  2377     {
       
  2378       error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
  2379                            "password cannot be provided in the current state");
       
  2380       dbus_g_method_return_error (context, error);
       
  2381       g_error_free (error);
       
  2382 
       
  2383       return;
       
  2384     }
       
  2385 
       
  2386   if (!send_join_request (self, password, &error))
       
  2387     {
       
  2388       dbus_g_method_return_error (context, error);
       
  2389       g_error_free (error);
       
  2390 
       
  2391       return;
       
  2392     }
       
  2393 
       
  2394   priv->password_ctx = context;
       
  2395 }
       
  2396 
       
  2397 
       
  2398 /**
       
  2399  * gabble_muc_channel_remove_members
       
  2400  *
       
  2401  * Implements D-Bus method RemoveMembers
       
  2402  * on interface org.freedesktop.Telepathy.Channel.Interface.Group
       
  2403  *
       
  2404  * @error: Used to return a pointer to a GError detailing any error
       
  2405  *         that occurred, D-Bus will throw the error only if this
       
  2406  *         function returns false.
       
  2407  *
       
  2408  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2409  */
       
  2410 gboolean gabble_muc_channel_remove_members (GabbleMucChannel *obj, const GArray * contacts, const gchar * message, GError **error)
       
  2411 {
       
  2412   return gabble_group_mixin_remove_members (G_OBJECT (obj), contacts, message, error);
       
  2413 }
       
  2414 
       
  2415 /**
       
  2416  * gabble_muc_channel_send
       
  2417  *
       
  2418  * Implements D-Bus method Send
       
  2419  * on interface org.freedesktop.Telepathy.Channel.Type.Text
       
  2420  *
       
  2421  * @error: Used to return a pointer to a GError detailing any error
       
  2422  *         that occurred, D-Bus will throw the error only if this
       
  2423  *         function returns false.
       
  2424  *
       
  2425  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2426  */
       
  2427 gboolean
       
  2428 gabble_muc_channel_send (GabbleMucChannel *self,
       
  2429                          guint type,
       
  2430                          const gchar *text,
       
  2431                          GError **error)
       
  2432 {
       
  2433   GabbleMucChannelPrivate *priv;
       
  2434 
       
  2435   g_assert (GABBLE_IS_MUC_CHANNEL (self));
       
  2436 
       
  2437   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
       
  2438 
       
  2439   return gabble_text_mixin_send (G_OBJECT (self), type,
       
  2440       LM_MESSAGE_SUB_TYPE_GROUPCHAT, priv->jid, text, priv->conn,
       
  2441       FALSE /* emit_signal */, error);
       
  2442 }
       
  2443 
       
  2444 
       
  2445 static gboolean
       
  2446 gabble_muc_channel_add_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error)
       
  2447 {
       
  2448   GabbleMucChannelPrivate *priv;
       
  2449   GabbleGroupMixin *mixin;
       
  2450   const gchar *jid;
       
  2451   LmMessage *msg;
       
  2452   LmMessageNode *x_node, *invite_node;
       
  2453   gboolean result;
       
  2454 
       
  2455   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (GABBLE_MUC_CHANNEL (obj));
       
  2456 
       
  2457   mixin = GABBLE_GROUP_MIXIN (obj);
       
  2458 
       
  2459   if (handle == mixin->self_handle)
       
  2460     {
       
  2461       GIntSet *set_empty, *set_members, *set_pending;
       
  2462       GArray *arr_members;
       
  2463 
       
  2464       /* are we already a member or in remote pending? */
       
  2465       if (handle_set_is_member (mixin->members, handle) ||
       
  2466           handle_set_is_member (mixin->remote_pending, handle))
       
  2467         {
       
  2468           g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2469               "already a member or in remote pending");
       
  2470 
       
  2471           return FALSE;
       
  2472         }
       
  2473 
       
  2474       /* add ourself to remote pending and remove the inviter's
       
  2475        * main jid from the member list */
       
  2476       set_empty = g_intset_new ();
       
  2477       set_members = g_intset_new ();
       
  2478       set_pending = g_intset_new ();
       
  2479 
       
  2480       arr_members = handle_set_to_array (mixin->members);
       
  2481       if (arr_members->len > 0)
       
  2482         {
       
  2483           g_intset_add (set_members, g_array_index (arr_members, guint32, 0));
       
  2484         }
       
  2485       g_array_free (arr_members, TRUE);
       
  2486 
       
  2487       g_intset_add (set_pending, handle);
       
  2488 
       
  2489       gabble_group_mixin_change_members (obj, "", set_empty, set_members,
       
  2490                                          set_empty, set_pending, 0,
       
  2491                                          TP_CHANNEL_GROUP_CHANGE_REASON_INVITED);
       
  2492 
       
  2493       g_intset_destroy (set_empty);
       
  2494       g_intset_destroy (set_members);
       
  2495       g_intset_destroy (set_pending);
       
  2496 
       
  2497       /* seek to enter the room */
       
  2498       result = send_join_request (GABBLE_MUC_CHANNEL (obj), NULL, error);
       
  2499 
       
  2500       g_object_set (obj, "state",
       
  2501                     (result) ? MUC_STATE_INITIATED : MUC_STATE_ENDED,
       
  2502                     NULL);
       
  2503 
       
  2504       /* deny adding */
       
  2505       gabble_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD);
       
  2506 
       
  2507       /* clear message queue (which might contain an invite reason) */
       
  2508       gabble_text_mixin_clear (G_OBJECT (obj));
       
  2509 
       
  2510       return result;
       
  2511     }
       
  2512 
       
  2513   /* check that we're indeed a member when attempting to invite others */
       
  2514   if (priv->state < MUC_STATE_JOINED)
       
  2515     {
       
  2516       g_set_error (error, TELEPATHY_ERRORS, NotAvailable,
       
  2517           "channel membership is required for inviting others");
       
  2518 
       
  2519       return FALSE;
       
  2520     }
       
  2521 
       
  2522   msg = lm_message_new (priv->jid, LM_MESSAGE_TYPE_MESSAGE);
       
  2523 
       
  2524   x_node = lm_message_node_add_child (msg->node, "x", NULL);
       
  2525   lm_message_node_set_attribute (x_node, "xmlns", NS_MUC_USER);
       
  2526 
       
  2527   invite_node = lm_message_node_add_child (x_node, "invite", NULL);
       
  2528 
       
  2529   jid = gabble_handle_inspect (GABBLE_GROUP_MIXIN (obj)->handle_repo,
       
  2530                                TP_HANDLE_TYPE_CONTACT, handle);
       
  2531 
       
  2532   lm_message_node_set_attribute (invite_node, "to", jid);
       
  2533 
       
  2534   if (*message != '\0')
       
  2535     {
       
  2536       lm_message_node_add_child (invite_node, "reason", message);
       
  2537     }
       
  2538 
       
  2539   NODE_DEBUG (msg->node, "sending MUC invitation");
       
  2540 
       
  2541   result = _gabble_connection_send (priv->conn, msg, error);
       
  2542   lm_message_unref (msg);
       
  2543 
       
  2544   return result;
       
  2545 }
       
  2546 
       
  2547 static LmHandlerResult
       
  2548 kick_request_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
       
  2549                        LmMessage *reply_msg, GObject *object,
       
  2550                        gpointer user_data)
       
  2551 {
       
  2552   const gchar *jid = user_data;
       
  2553 
       
  2554   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  2555     {
       
  2556       g_warning ("%s: Failed to kick user %s from room", G_STRFUNC, jid);
       
  2557     }
       
  2558 
       
  2559   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  2560 }
       
  2561 
       
  2562 static gboolean
       
  2563 gabble_muc_channel_remove_member (GObject *obj, GabbleHandle handle, const gchar *message, GError **error)
       
  2564 {
       
  2565   GabbleMucChannelPrivate *priv;
       
  2566   LmMessage *msg;
       
  2567   LmMessageNode *query_node, *item_node;
       
  2568   const gchar *jid;
       
  2569   gchar *nick;
       
  2570   gboolean result;
       
  2571 
       
  2572   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (GABBLE_MUC_CHANNEL (obj));
       
  2573 
       
  2574   msg = lm_message_new_with_sub_type (priv->jid, LM_MESSAGE_TYPE_IQ,
       
  2575                                       LM_MESSAGE_SUB_TYPE_SET);
       
  2576 
       
  2577   query_node = lm_message_node_add_child (msg->node, "query", NULL);
       
  2578   lm_message_node_set_attribute (query_node, "xmlns", NS_MUC_ADMIN);
       
  2579 
       
  2580   item_node = lm_message_node_add_child (query_node, "item", NULL);
       
  2581 
       
  2582   jid = gabble_handle_inspect (GABBLE_GROUP_MIXIN (obj)->handle_repo,
       
  2583                                TP_HANDLE_TYPE_CONTACT, handle);
       
  2584 
       
  2585   gabble_decode_jid (jid, NULL, NULL, &nick);
       
  2586 
       
  2587   lm_message_node_set_attributes (item_node,
       
  2588                                   "nick", nick,
       
  2589                                   "role", "none",
       
  2590                                   NULL);
       
  2591 
       
  2592   g_free (nick);
       
  2593 
       
  2594   if (*message != '\0')
       
  2595     {
       
  2596       lm_message_node_add_child (item_node, "reason", message);
       
  2597     }
       
  2598 
       
  2599   NODE_DEBUG (msg->node, "sending MUC kick request");
       
  2600 
       
  2601   result = _gabble_connection_send_with_reply (priv->conn, msg,
       
  2602                                                kick_request_reply_cb,
       
  2603                                                obj, (gpointer) jid,
       
  2604                                                error);
       
  2605 
       
  2606   lm_message_unref (msg);
       
  2607 
       
  2608   return result;
       
  2609 }
       
  2610 
       
  2611 
       
  2612 /**
       
  2613  * gabble_muc_channel_list_properties
       
  2614  *
       
  2615  * Implements D-Bus method ListProperties
       
  2616  * on interface org.freedesktop.Telepathy.Properties
       
  2617  *
       
  2618  * @error: Used to return a pointer to a GError detailing any error
       
  2619  *         that occurred, D-Bus will throw the error only if this
       
  2620  *         function returns false.
       
  2621  *
       
  2622  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2623  */
       
  2624 gboolean
       
  2625 gabble_muc_channel_list_properties (GabbleMucChannel *self,
       
  2626                                     GPtrArray **ret,
       
  2627                                     GError **error)
       
  2628 {
       
  2629   return gabble_properties_mixin_list_properties (G_OBJECT (self), ret, error);
       
  2630 }
       
  2631 
       
  2632 
       
  2633 /**
       
  2634  * gabble_muc_channel_get_properties
       
  2635  *
       
  2636  * Implements D-Bus method GetProperties
       
  2637  * on interface org.freedesktop.Telepathy.Properties
       
  2638  *
       
  2639  * @error: Used to return a pointer to a GError detailing any error
       
  2640  *         that occurred, D-Bus will throw the error only if this
       
  2641  *         function returns FALSE.
       
  2642  *
       
  2643  * Returns: TRUE if successful, FALSE if an error was thrown.
       
  2644  */
       
  2645 gboolean
       
  2646 gabble_muc_channel_get_properties (GabbleMucChannel *self,
       
  2647                                    const GArray *properties,
       
  2648                                    GPtrArray **ret,
       
  2649                                    GError **error)
       
  2650 {
       
  2651   return gabble_properties_mixin_get_properties (G_OBJECT (self), properties,
       
  2652       ret, error);
       
  2653 }
       
  2654 
       
  2655 /**
       
  2656  * gabble_muc_channel_set_properties
       
  2657  *
       
  2658  * Implements D-Bus method SetProperties
       
  2659  * on interface org.freedesktop.Telepathy.Properties
       
  2660  *
       
  2661  * @context: The D-Bus invocation context to use to return values
       
  2662  *           or throw an error.
       
  2663  */
       
  2664 void
       
  2665 gabble_muc_channel_set_properties (GabbleMucChannel *self,
       
  2666                                    const GPtrArray *properties,
       
  2667                                    DBusGMethodInvocation *context)
       
  2668 {
       
  2669   gabble_properties_mixin_set_properties (G_OBJECT (self), properties,
       
  2670       context);
       
  2671 }
       
  2672 
       
  2673 static LmHandlerResult request_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, LmMessage *reply_msg, GObject *object, gpointer user_data);
       
  2674 
       
  2675 static gboolean
       
  2676 gabble_muc_channel_do_set_properties (GObject *obj, GabblePropertiesContext *ctx, GError **error)
       
  2677 {
       
  2678   GabbleMucChannelPrivate *priv;
       
  2679   LmMessage *msg;
       
  2680   LmMessageNode *node;
       
  2681   gboolean success;
       
  2682 
       
  2683   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (GABBLE_MUC_CHANNEL (obj));
       
  2684 
       
  2685   g_assert (priv->properties_ctx == NULL);
       
  2686 
       
  2687   /* Changing subject? */
       
  2688   if (gabble_properties_context_has (ctx, ROOM_PROP_SUBJECT))
       
  2689     {
       
  2690       const gchar *str;
       
  2691 
       
  2692       str = g_value_get_string (gabble_properties_context_get (ctx, ROOM_PROP_SUBJECT));
       
  2693 
       
  2694       msg = lm_message_new_with_sub_type (priv->jid,
       
  2695           LM_MESSAGE_TYPE_MESSAGE, LM_MESSAGE_SUB_TYPE_GROUPCHAT);
       
  2696       lm_message_node_add_child (msg->node, "subject", str);
       
  2697 
       
  2698       success = _gabble_connection_send (priv->conn, msg, error);
       
  2699 
       
  2700       lm_message_unref (msg);
       
  2701 
       
  2702       if (!success)
       
  2703         return FALSE;
       
  2704     }
       
  2705 
       
  2706   /* Changing any other properties? */
       
  2707   if (gabble_properties_context_has_other_than (ctx, ROOM_PROP_SUBJECT))
       
  2708     {
       
  2709       msg = lm_message_new_with_sub_type (priv->jid,
       
  2710           LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
       
  2711       node = lm_message_node_add_child (msg->node, "query", NULL);
       
  2712       lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER);
       
  2713 
       
  2714       success = _gabble_connection_send_with_reply (priv->conn, msg,
       
  2715           request_config_form_reply_cb, G_OBJECT (obj), NULL,
       
  2716           error);
       
  2717 
       
  2718       lm_message_unref (msg);
       
  2719 
       
  2720       if (!success)
       
  2721         return FALSE;
       
  2722     }
       
  2723 
       
  2724   priv->properties_ctx = ctx;
       
  2725 
       
  2726   return TRUE;
       
  2727 }
       
  2728 
       
  2729 static LmHandlerResult request_config_form_submit_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, LmMessage *reply_msg, GObject *object, gpointer user_data);
       
  2730 
       
  2731 static LmHandlerResult
       
  2732 request_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
       
  2733                               LmMessage *reply_msg, GObject *object,
       
  2734                               gpointer user_data)
       
  2735 {
       
  2736   GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
       
  2737   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  2738   GabblePropertiesContext *ctx = priv->properties_ctx;
       
  2739   GError *error = NULL;
       
  2740   LmMessage *msg = NULL;
       
  2741   LmMessageNode *submit_node, *form_node, *node, *query_node;
       
  2742   guint i, props_left;
       
  2743 
       
  2744   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  2745     {
       
  2746       error = g_error_new (TELEPATHY_ERRORS, PermissionDenied,
       
  2747                            "request for configuration form denied");
       
  2748 
       
  2749       goto OUT;
       
  2750     }
       
  2751 
       
  2752   form_node = config_form_get_form_node (reply_msg);
       
  2753   if (form_node == NULL)
       
  2754     goto PARSE_ERROR;
       
  2755 
       
  2756   /* initialize */
       
  2757   msg = lm_message_new_with_sub_type (priv->jid, LM_MESSAGE_TYPE_IQ,
       
  2758                                       LM_MESSAGE_SUB_TYPE_SET);
       
  2759 
       
  2760   node = lm_message_node_add_child (msg->node, "query", NULL);
       
  2761   lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER);
       
  2762 
       
  2763   submit_node = lm_message_node_add_child (node, "x", NULL);
       
  2764   lm_message_node_set_attributes (submit_node,
       
  2765                                   "xmlns", NS_X_DATA,
       
  2766                                   "type", "submit",
       
  2767                                   NULL);
       
  2768 
       
  2769   /* find the query node */
       
  2770   query_node = lm_message_node_get_child (reply_msg->node, "query");
       
  2771   if (query_node == NULL)
       
  2772     goto PARSE_ERROR;
       
  2773 
       
  2774   /* then the form node */
       
  2775   form_node = NULL;
       
  2776   for (node = query_node->children; node; node = node->next)
       
  2777     {
       
  2778       if (strcmp (node->name, "x") == 0)
       
  2779         {
       
  2780           const gchar *type = lm_message_node_get_attribute (node, "type");
       
  2781 
       
  2782           if (!lm_message_node_has_namespace (node, NS_X_DATA, NULL))
       
  2783             continue;
       
  2784 
       
  2785           if (g_strdiff (type, "form"))
       
  2786             continue;
       
  2787 
       
  2788           form_node = node;
       
  2789           break;
       
  2790         }
       
  2791     }
       
  2792 
       
  2793   if (form_node == NULL)
       
  2794     goto PARSE_ERROR;
       
  2795 
       
  2796   props_left = 0;
       
  2797   for (i = 0; i < NUM_ROOM_PROPS; i++)
       
  2798     {
       
  2799       if (i == ROOM_PROP_SUBJECT)
       
  2800         continue;
       
  2801 
       
  2802       if (gabble_properties_context_has (ctx, i))
       
  2803         props_left |= 1 << i;
       
  2804     }
       
  2805 
       
  2806   for (node = form_node->children; node; node = node->next)
       
  2807     {
       
  2808       const gchar *var, *prev_value;
       
  2809       LmMessageNode *field_node, *value_node;
       
  2810       guint id;
       
  2811       GType type;
       
  2812       gboolean invert;
       
  2813       gchar buf[16];
       
  2814       const gchar *val_str;
       
  2815       gboolean val_bool;
       
  2816 
       
  2817       if (strcmp (node->name, "field") != 0)
       
  2818         {
       
  2819           gabble_debug (DEBUG_FLAG, "skipping node '%s'", node->name);
       
  2820           continue;
       
  2821         }
       
  2822 
       
  2823       var = lm_message_node_get_attribute (node, "var");
       
  2824       if (var == NULL) {
       
  2825         gabble_debug (DEBUG_FLAG, "skipping node '%s' because of lacking var attribute",
       
  2826                node->name);
       
  2827         continue;
       
  2828       }
       
  2829 
       
  2830       value_node = lm_message_node_get_child (node, "value");
       
  2831       if (value_node == NULL)
       
  2832         {
       
  2833           gabble_debug (DEBUG_FLAG, "skipping var '%s' because of lacking value attribute",
       
  2834                  var);
       
  2835           continue;
       
  2836         }
       
  2837 
       
  2838       prev_value = lm_message_node_get_value (value_node);
       
  2839 
       
  2840       /* add the corresponding field node to the reply message */
       
  2841       field_node = lm_message_node_add_child (submit_node, "field", NULL);
       
  2842 
       
  2843       lm_message_node_set_attribute (field_node, "var", var);
       
  2844 
       
  2845       val_str = lm_message_node_get_attribute (node, "type");
       
  2846       if (val_str)
       
  2847         {
       
  2848           lm_message_node_set_attribute (field_node, "type", val_str);
       
  2849         }
       
  2850 
       
  2851       value_node = lm_message_node_add_child (field_node, "value", prev_value);
       
  2852 
       
  2853       id = INVALID_ROOM_PROP;
       
  2854       type = G_TYPE_BOOLEAN;
       
  2855       invert = FALSE;
       
  2856       val_str = NULL;
       
  2857 
       
  2858       if (strcmp (var, "anonymous") == 0)
       
  2859         {
       
  2860           id = ROOM_PROP_ANONYMOUS;
       
  2861         }
       
  2862       else if (strcmp (var, "muc#roomconfig_whois") == 0)
       
  2863         {
       
  2864           id = ROOM_PROP_ANONYMOUS;
       
  2865 
       
  2866           if (gabble_properties_context_has (ctx, id))
       
  2867             {
       
  2868               val_bool = g_value_get_boolean (
       
  2869                   gabble_properties_context_get (ctx, id));
       
  2870               val_str = (val_bool) ? "moderators" : "anyone";
       
  2871             }
       
  2872         }
       
  2873       else if (strcmp (var, "muc#owner_whois") == 0)
       
  2874         {
       
  2875           id = ROOM_PROP_ANONYMOUS;
       
  2876 
       
  2877           if (gabble_properties_context_has (ctx, id))
       
  2878             {
       
  2879               val_bool = g_value_get_boolean (
       
  2880                   gabble_properties_context_get (ctx, id));
       
  2881               val_str = (val_bool) ? "admins" : "anyone";
       
  2882             }
       
  2883         }
       
  2884       else if (strcmp (var, "members_only") == 0 ||
       
  2885                strcmp (var, "muc#roomconfig_membersonly") == 0 ||
       
  2886                strcmp (var, "muc#owner_inviteonly") == 0)
       
  2887         {
       
  2888           id = ROOM_PROP_INVITE_ONLY;
       
  2889         }
       
  2890       else if (strcmp (var, "moderated") == 0 ||
       
  2891                strcmp (var, "muc#roomconfig_moderatedroom") == 0 ||
       
  2892                strcmp (var, "muc#owner_moderatedroom") == 0)
       
  2893         {
       
  2894           id = ROOM_PROP_MODERATED;
       
  2895         }
       
  2896       else if (strcmp (var, "title") == 0 ||
       
  2897                strcmp (var, "muc#roomconfig_roomname") == 0 ||
       
  2898                strcmp (var, "muc#owner_roomname") == 0)
       
  2899         {
       
  2900           id = ROOM_PROP_NAME;
       
  2901           type = G_TYPE_STRING;
       
  2902         }
       
  2903       else if (strcmp (var, "muc#roomconfig_roomdesc") == 0 ||
       
  2904                strcmp (var, "muc#owner_roomdesc") == 0)
       
  2905         {
       
  2906           id = ROOM_PROP_DESCRIPTION;
       
  2907           type = G_TYPE_STRING;
       
  2908         }
       
  2909       else if (strcmp (var, "password") == 0 ||
       
  2910                strcmp (var, "muc#roomconfig_roomsecret") == 0 ||
       
  2911                strcmp (var, "muc#owner_roomsecret") == 0)
       
  2912         {
       
  2913           id = ROOM_PROP_PASSWORD;
       
  2914           type = G_TYPE_STRING;
       
  2915         }
       
  2916       else if (strcmp (var, "password_protected") == 0 ||
       
  2917                strcmp (var, "muc#roomconfig_passwordprotectedroom") == 0 ||
       
  2918                strcmp (var, "muc#owner_passwordprotectedroom") == 0)
       
  2919         {
       
  2920           id = ROOM_PROP_PASSWORD_REQUIRED;
       
  2921         }
       
  2922       else if (strcmp (var, "persistent") == 0 ||
       
  2923                strcmp (var, "muc#roomconfig_persistentroom") == 0 ||
       
  2924                strcmp (var, "muc#owner_persistentroom") == 0)
       
  2925         {
       
  2926           id = ROOM_PROP_PERSISTENT;
       
  2927         }
       
  2928       else if (strcmp (var, "public") == 0 ||
       
  2929                strcmp (var, "muc#roomconfig_publicroom") == 0 ||
       
  2930                strcmp (var, "muc#owner_publicroom") == 0)
       
  2931         {
       
  2932           id = ROOM_PROP_PRIVATE;
       
  2933           invert = TRUE;
       
  2934         }
       
  2935       else
       
  2936         {
       
  2937           g_warning ("%s: ignoring field '%s'", G_STRFUNC, var);
       
  2938           continue;
       
  2939         }
       
  2940 
       
  2941       gabble_debug (DEBUG_FLAG, "looking up %s", room_property_signatures[id].name);
       
  2942 
       
  2943       if (!gabble_properties_context_has (ctx, id))
       
  2944         continue;
       
  2945 
       
  2946       if (!val_str)
       
  2947         {
       
  2948           const GValue *provided_value;
       
  2949 
       
  2950           provided_value = gabble_properties_context_get (ctx, id);
       
  2951 
       
  2952           switch (type) {
       
  2953             case G_TYPE_BOOLEAN:
       
  2954               val_bool = g_value_get_boolean (provided_value);
       
  2955               sprintf (buf, "%d", (invert) ? !val_bool : val_bool);
       
  2956               val_str = buf;
       
  2957               break;
       
  2958             case G_TYPE_STRING:
       
  2959               val_str = g_value_get_string (provided_value);
       
  2960               break;
       
  2961             default:
       
  2962               g_assert_not_reached ();
       
  2963           }
       
  2964         }
       
  2965 
       
  2966       lm_message_node_set_value (value_node, val_str);
       
  2967 
       
  2968       props_left &= ~(1 << id);
       
  2969     }
       
  2970 
       
  2971   if (props_left != 0)
       
  2972     {
       
  2973       g_message (ANSI_BOLD_ON ANSI_FG_WHITE ANSI_BG_RED
       
  2974               "\n%s: the following properties were not substituted:\n",
       
  2975               G_STRFUNC);
       
  2976 
       
  2977       for (i = 0; i < NUM_ROOM_PROPS; i++)
       
  2978         {
       
  2979           if ((props_left & (1 << i)) != 0)
       
  2980             {
       
  2981               g_message ("  %s\n", room_property_signatures[i].name);
       
  2982             }
       
  2983         }
       
  2984 
       
  2985       g_message ("\nthis is a MUC server compatibility bug in gabble, please "
       
  2986               "report it with a full debug log attached (running gabble "
       
  2987               "with LM_DEBUG=net)" ANSI_RESET "\n\n");
       
  2988       fflush (stdout);
       
  2989 
       
  2990       error = g_error_new (TELEPATHY_ERRORS, InvalidArgument,
       
  2991                            "not all properties were substituted");
       
  2992       goto OUT;
       
  2993     }
       
  2994 
       
  2995   _gabble_connection_send_with_reply (priv->conn, msg,
       
  2996       request_config_form_submit_reply_cb, G_OBJECT (object),
       
  2997       NULL, &error);
       
  2998 
       
  2999   goto OUT;
       
  3000 
       
  3001 PARSE_ERROR:
       
  3002   error = g_error_new (TELEPATHY_ERRORS, NotAvailable,
       
  3003                        "error parsing reply from server");
       
  3004 
       
  3005 OUT:
       
  3006   if (error)
       
  3007     {
       
  3008       gabble_properties_context_return (ctx, error);
       
  3009       priv->properties_ctx = NULL;
       
  3010     }
       
  3011 
       
  3012   if (msg)
       
  3013     lm_message_unref (msg);
       
  3014 
       
  3015   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  3016 }
       
  3017 
       
  3018 static LmHandlerResult
       
  3019 request_config_form_submit_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
       
  3020                                      LmMessage *reply_msg, GObject *object,
       
  3021                                      gpointer user_data)
       
  3022 {
       
  3023   GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
       
  3024   GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
       
  3025   GabblePropertiesContext *ctx = priv->properties_ctx;
       
  3026   GError *error = NULL;
       
  3027   gboolean returned;
       
  3028 
       
  3029   if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
       
  3030     {
       
  3031       error = g_error_new (TELEPATHY_ERRORS, PermissionDenied,
       
  3032                            "submitted configuration form was rejected");
       
  3033     }
       
  3034 
       
  3035   if (!error)
       
  3036     {
       
  3037       guint i;
       
  3038 
       
  3039       for (i = 0; i < NUM_ROOM_PROPS; i++)
       
  3040         {
       
  3041           if (i != ROOM_PROP_SUBJECT)
       
  3042             gabble_properties_context_remove (ctx, i);
       
  3043         }
       
  3044 
       
  3045       returned = gabble_properties_context_return_if_done (ctx);
       
  3046     }
       
  3047   else
       
  3048     {
       
  3049       gabble_properties_context_return (ctx, error);
       
  3050       returned = TRUE;
       
  3051 
       
  3052       /* Get the properties into a consistent state. */
       
  3053       room_properties_update (chan);
       
  3054     }
       
  3055 
       
  3056   if (returned)
       
  3057     priv->properties_ctx = NULL;
       
  3058 
       
  3059   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  3060 }
       
  3061