1 /* TpProtocol
2  *
3  * Copyright © 2010-2012 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 /**
21  * SECTION:protocol
22  * @title: TpProtocol
23  * @short_description: proxy for a Telepathy Protocol object
24  * @see_also: #TpConnectionManager
25  *
26  * #TpProtocol objects represent the protocols implemented by Telepathy
27  * connection managers. In modern connection managers, each protocol is
28  * represented by a D-Bus object; in older connection managers, the protocols
29  * are represented by data structures, and this object merely emulates a D-Bus
30  * object.
31  *
32  * Since: 0.11.11
33  */
34 
35 #include "config.h"
36 
37 #include <telepathy-glib/protocol.h>
38 #include <telepathy-glib/protocol-internal.h>
39 
40 #include <telepathy-glib/proxy-subclass.h>
41 #include <telepathy-glib/telepathy-glib.h>
42 
43 #define DEBUG_FLAG TP_DEBUG_PARAMS
44 #include "telepathy-glib/capabilities-internal.h"
45 #include "telepathy-glib/debug-internal.h"
46 #include "telepathy-glib/proxy-internal.h"
47 #include "telepathy-glib/util-internal.h"
48 #include "telepathy-glib/variant-util-internal.h"
49 
50 #include "telepathy-glib/_gen/tp-cli-protocol-body.h"
51 
52 #include <string.h>
53 
54 struct _TpProtocolClass
55 {
56   /*<private>*/
57   TpProxyClass parent_class;
58 };
59 
60 /**
61  * TpProtocol:
62  *
63  * A base class for connection managers' protocols.
64  *
65  * Since: 0.11.11
66  */
67 
68 /**
69  * TpProtocolClass:
70  *
71  * The class of a #TpProtocol.
72  *
73  * Since: 0.11.11
74  */
75 
G_DEFINE_TYPE(TpProtocol,tp_protocol,TP_TYPE_PROXY)76 G_DEFINE_TYPE(TpProtocol, tp_protocol, TP_TYPE_PROXY)
77 
78 /**
79  * TP_PROTOCOL_FEATURE_PARAMETERS:
80  *
81  * Expands to a call to a function that returns a quark for the parameters
82  * feature of a #TpProtocol.
83  *
84  * When this feature is prepared, the possible parameters for connections to
85  * this protocol have been retrieved and are available for use.
86  *
87  * Unlike %TP_PROTOCOL_FEATURE_CORE, this feature can even be available on
88  * connection managers that don't really have Protocol objects
89  * (on these older connection managers, the #TpProtocol uses information from
90  * ConnectionManager methods to provide the list of parameters).
91  *
92  * One can ask for a feature to be prepared using the
93  * tp_proxy_prepare_async() function, and waiting for it to callback.
94  *
95  * Since: 0.11.11
96  */
97 
98 GQuark
99 tp_protocol_get_feature_quark_parameters (void)
100 {
101   return g_quark_from_static_string ("tp-protocol-feature-parameters");
102 }
103 
104 /**
105  * TP_PROTOCOL_FEATURE_CORE:
106  *
107  * Expands to a call to a function that returns a quark for the core
108  * feature of a #TpProtocol.
109  *
110  * When this feature is prepared, at least the following basic information
111  * about the protocol is available:
112  *
113  * <itemizedlist>
114  *  <listitem>possible parameters for connections to this protocol</listitem>
115  *  <listitem>interfaces expected on connections to this protocol</listitem>
116  *  <listitem>classes of channel that could be requested from connections
117  *    to this protocol</listitem>
118  * </itemizedlist>
119  *
120  * (This feature implies that %TP_PROTOCOL_FEATURE_PARAMETERS is also
121  * available.)
122  *
123  * Unlike %TP_PROTOCOL_FEATURE_PARAMETERS, this feature can only become
124  * available on connection managers that implement Protocol objects.
125  *
126  * One can ask for a feature to be prepared using the
127  * tp_proxy_prepare_async() function, and waiting for it to callback.
128  *
129  * Since: 0.11.11
130  */
131 
132 GQuark
tp_protocol_get_feature_quark_core(void)133 tp_protocol_get_feature_quark_core (void)
134 {
135   return g_quark_from_static_string ("tp-protocol-feature-core");
136 }
137 
138 struct _TpProtocolPrivate
139 {
140   TpConnectionManagerProtocol protocol_struct;
141   GHashTable *protocol_properties;
142   gchar *vcard_field;
143   gchar *english_name;
144   gchar *icon_name;
145   GStrv authentication_types;
146   TpCapabilities *capabilities;
147   TpAvatarRequirements *avatar_req;
148   gchar *cm_name;
149   GStrv addressable_vcard_fields;
150   GStrv addressable_uri_schemes;
151   /* (transfer container) (element-type utf8 Simple_Status_Spec) */
152   GHashTable *presence_statuses;
153 };
154 
155 enum
156 {
157     PROP_PROTOCOL_NAME = 1,
158     PROP_PROTOCOL_PROPERTIES,
159     PROP_PROTOCOL_PROPERTIES_VARDICT,
160     PROP_ENGLISH_NAME,
161     PROP_VCARD_FIELD,
162     PROP_ICON_NAME,
163     PROP_CAPABILITIES,
164     PROP_PARAM_NAMES,
165     PROP_AUTHENTICATION_TYPES,
166     PROP_AVATAR_REQUIREMENTS,
167     PROP_CM_NAME,
168     PROP_ADDRESSABLE_VCARD_FIELDS,
169     PROP_ADDRESSABLE_URI_SCHEMES,
170     N_PROPS
171 };
172 
173 /* this is NULL-safe for @parameters, and callers rely on this */
174 static TpConnectionManagerParam *
tp_protocol_params_from_param_specs(const GPtrArray * parameters,const gchar * cm_debug_name,const gchar * protocol)175 tp_protocol_params_from_param_specs (const GPtrArray *parameters,
176     const gchar *cm_debug_name,
177     const gchar *protocol)
178 {
179   GArray *output;
180   guint i;
181 
182   DEBUG ("Protocol name: %s", protocol);
183 
184   if (parameters == NULL)
185     {
186       return g_new0 (TpConnectionManagerParam, 1);
187     }
188 
189   output = g_array_sized_new (TRUE, TRUE,
190       sizeof (TpConnectionManagerParam), parameters->len);
191 
192   for (i = 0; i < parameters->len; i++)
193     {
194       GValue structure = { 0 };
195       GValue *tmp;
196       TpConnectionManagerParam *param;
197 
198       g_value_init (&structure, TP_STRUCT_TYPE_PARAM_SPEC);
199       g_value_set_static_boxed (&structure, g_ptr_array_index (parameters, i));
200 
201       g_array_set_size (output, output->len + 1);
202       /* point to the new last item */
203       param = &g_array_index (output, TpConnectionManagerParam,
204           output->len - 1);
205 
206       if (!dbus_g_type_struct_get (&structure,
207             0, &param->name,
208             1, &param->flags,
209             2, &param->dbus_signature,
210             3, &tmp,
211             G_MAXUINT))
212         {
213           DEBUG ("Unparseable parameter #%d for %s, ignoring", i, protocol);
214           /* *shrug* that one didn't work, let's skip it */
215           g_array_set_size (output, output->len - 1);
216           continue;
217         }
218 
219       if (!g_variant_type_string_is_valid (param->dbus_signature))
220         {
221           DEBUG ("Parameter #%d for %s has type '%s' which is not a "
222               "single complete type, ignoring", i, protocol,
223               param->dbus_signature);
224           g_array_set_size (output, output->len - 1);
225           continue;
226         }
227 
228       g_value_init (&param->default_value,
229           G_VALUE_TYPE (tmp));
230       g_value_copy (tmp, &param->default_value);
231       g_value_unset (tmp);
232       g_free (tmp);
233 
234       param->priv = NULL;
235 
236       DEBUG ("\tParam name: %s", param->name);
237       DEBUG ("\tParam flags: 0x%x", param->flags);
238       DEBUG ("\tParam sig: %s", param->dbus_signature);
239 
240       if ((!tp_strdiff (param->name, "password") ||
241           g_str_has_suffix (param->name, "-password")) &&
242           (param->flags & TP_CONN_MGR_PARAM_FLAG_SECRET) == 0)
243         {
244           DEBUG ("\tTreating as secret due to its name (please fix %s)",
245               cm_debug_name);
246           param->flags |= TP_CONN_MGR_PARAM_FLAG_SECRET;
247         }
248 
249 #ifdef ENABLE_DEBUG
250         {
251           gchar *repr = g_strdup_value_contents (&(param->default_value));
252 
253           DEBUG ("\tParam default value: %s of type %s", repr,
254               G_VALUE_TYPE_NAME (&(param->default_value)));
255           g_free (repr);
256         }
257 #endif
258     }
259 
260   return (TpConnectionManagerParam *) g_array_free (output, FALSE);
261 }
262 
263 static void
tp_protocol_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)264 tp_protocol_get_property (GObject *object,
265     guint property_id,
266     GValue *value,
267     GParamSpec *pspec)
268 {
269   TpProtocol *self = (TpProtocol *) object;
270 
271   switch (property_id)
272     {
273     case PROP_PROTOCOL_NAME:
274       g_value_set_string (value, self->priv->protocol_struct.name);
275       break;
276 
277     case PROP_PROTOCOL_PROPERTIES:
278       g_value_set_boxed (value, self->priv->protocol_properties);
279       break;
280 
281     case PROP_PROTOCOL_PROPERTIES_VARDICT:
282       g_value_take_variant (value,
283           tp_protocol_dup_immutable_properties (self));
284       break;
285 
286     case PROP_ENGLISH_NAME:
287       g_value_set_string (value, tp_protocol_get_english_name (self));
288       break;
289 
290     case PROP_VCARD_FIELD:
291       g_value_set_string (value, tp_protocol_get_vcard_field (self));
292       break;
293 
294     case PROP_ICON_NAME:
295       g_value_set_string (value, tp_protocol_get_icon_name (self));
296       break;
297 
298     case PROP_CAPABILITIES:
299       g_value_set_object (value, tp_protocol_get_capabilities (self));
300       break;
301 
302     case PROP_PARAM_NAMES:
303       g_value_take_boxed (value, tp_protocol_dup_param_names (self));
304       break;
305 
306     case PROP_AUTHENTICATION_TYPES:
307       g_value_set_boxed (value, tp_protocol_get_authentication_types (self));
308       break;
309 
310     case PROP_AVATAR_REQUIREMENTS:
311       g_value_set_pointer (value, tp_protocol_get_avatar_requirements (self));
312       break;
313 
314     case PROP_CM_NAME:
315       g_value_set_string (value, tp_protocol_get_cm_name (self));
316       break;
317 
318     case PROP_ADDRESSABLE_VCARD_FIELDS:
319       g_value_set_boxed (value, tp_protocol_get_addressable_vcard_fields (
320             self));
321       break;
322 
323     case PROP_ADDRESSABLE_URI_SCHEMES:
324       g_value_set_boxed (value, tp_protocol_get_addressable_uri_schemes (self));
325       break;
326 
327     default:
328       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
329       break;
330     }
331 }
332 
333 static void
tp_protocol_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)334 tp_protocol_set_property (GObject *object,
335     guint property_id,
336     const GValue *value,
337     GParamSpec *pspec)
338 {
339   TpProtocol *self = (TpProtocol *) object;
340 
341   switch (property_id)
342     {
343     case PROP_PROTOCOL_NAME:
344       g_assert (self->priv->protocol_struct.name == NULL);
345       self->priv->protocol_struct.name = g_value_dup_string (value);
346       break;
347 
348     case PROP_PROTOCOL_PROPERTIES:
349       g_assert (self->priv->protocol_properties == NULL);
350       self->priv->protocol_properties = g_value_dup_boxed (value);
351       break;
352 
353     case PROP_CM_NAME:
354       g_assert (self->priv->cm_name == NULL);
355       self->priv->cm_name = g_value_dup_string (value);
356       break;
357 
358     default:
359       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
360       break;
361     }
362 }
363 
364 void
_tp_connection_manager_param_free_contents(TpConnectionManagerParam * param)365 _tp_connection_manager_param_free_contents (TpConnectionManagerParam *param)
366 {
367   g_free (param->name);
368   g_free (param->dbus_signature);
369 
370   if (G_IS_VALUE (&param->default_value))
371     g_value_unset (&param->default_value);
372 }
373 
374 void
_tp_connection_manager_protocol_free_contents(TpConnectionManagerProtocol * proto)375 _tp_connection_manager_protocol_free_contents (
376     TpConnectionManagerProtocol *proto)
377 {
378   g_free (proto->name);
379 
380   if (proto->params != NULL)
381     {
382       TpConnectionManagerParam *param;
383 
384       for (param = proto->params; param->name != NULL; param++)
385         _tp_connection_manager_param_free_contents (param);
386     }
387 
388   g_free (proto->params);
389 }
390 
391 static void
tp_protocol_dispose(GObject * object)392 tp_protocol_dispose (GObject *object)
393 {
394   TpProtocol *self = TP_PROTOCOL (object);
395   GObjectFinalizeFunc dispose =
396     ((GObjectClass *) tp_protocol_parent_class)->dispose;
397 
398   if (self->priv->capabilities != NULL)
399     {
400       g_object_unref (self->priv->capabilities);
401       self->priv->capabilities = NULL;
402     }
403 
404   if (self->priv->authentication_types)
405     {
406       g_strfreev (self->priv->authentication_types);
407       self->priv->authentication_types = NULL;
408     }
409 
410   tp_clear_pointer (&self->priv->avatar_req, tp_avatar_requirements_destroy);
411 
412   if (dispose != NULL)
413     dispose (object);
414 }
415 
416 static void
tp_protocol_finalize(GObject * object)417 tp_protocol_finalize (GObject *object)
418 {
419   TpProtocol *self = TP_PROTOCOL (object);
420   GObjectFinalizeFunc finalize =
421     ((GObjectClass *) tp_protocol_parent_class)->finalize;
422 
423   _tp_connection_manager_protocol_free_contents (&self->priv->protocol_struct);
424   g_free (self->priv->vcard_field);
425   g_free (self->priv->english_name);
426   g_free (self->priv->icon_name);
427   g_free (self->priv->cm_name);
428   g_strfreev (self->priv->addressable_vcard_fields);
429   g_strfreev (self->priv->addressable_uri_schemes);
430 
431   if (self->priv->presence_statuses != NULL)
432     g_hash_table_unref (self->priv->presence_statuses);
433 
434   if (self->priv->protocol_properties != NULL)
435     g_hash_table_unref (self->priv->protocol_properties);
436 
437   if (finalize != NULL)
438     finalize (object);
439 }
440 
441 static gboolean
tp_protocol_check_for_core(TpProtocol * self)442 tp_protocol_check_for_core (TpProtocol *self)
443 {
444   const GHashTable *props = self->priv->protocol_properties;
445   const GValue *value;
446 
447   /* this one can legitimately be NULL so we need to be more careful */
448   value = tp_asv_lookup (props, TP_PROP_PROTOCOL_CONNECTION_INTERFACES);
449 
450   if (value == NULL || !G_VALUE_HOLDS (value, G_TYPE_STRV))
451     {
452       DEBUG ("Interfaces not found");
453       return FALSE;
454     }
455 
456   if (tp_asv_get_boxed (props, TP_PROP_PROTOCOL_REQUESTABLE_CHANNEL_CLASSES,
457         TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST) == NULL)
458     {
459       DEBUG ("Requestable channel classes not found");
460       return FALSE;
461     }
462 
463   /* Interfaces has a sensible default, the empty list.
464    * VCardField, EnglishName and Icon have a sensible default, "". */
465 
466   DEBUG ("Core feature ready");
467   return TRUE;
468 }
469 
470 static gchar *
title_case(const gchar * s)471 title_case (const gchar *s)
472 {
473   gunichar u;
474   /* 6 bytes are enough for any Unicode character, 7th byte remains '\0' */
475   gchar buf[7] = { 0 };
476 
477   /* if s isn't UTF-8, give up and use it as-is */
478   if (!g_utf8_validate (s, -1, NULL))
479     return g_strdup (s);
480 
481   u = g_utf8_get_char (s);
482 
483   if (!g_unichar_islower (u))
484     return g_strdup (s);
485 
486   u = g_unichar_totitle (u);
487   g_unichar_to_utf8 (u, buf);
488   g_assert (buf [sizeof (buf) - 1] == '\0');
489 
490   return g_strdup_printf ("%s%s", buf, g_utf8_next_char (s));
491 }
492 
493 static GStrv
asv_strdupv_or_empty(const GHashTable * asv,const gchar * key)494 asv_strdupv_or_empty (const GHashTable *asv,
495     const gchar *key)
496 {
497   const gchar * const *strings = tp_asv_get_boxed (asv, key, G_TYPE_STRV);
498   static const gchar * const no_strings[] = { NULL };
499 
500   if (strings != NULL)
501     return g_strdupv ((GStrv) strings);
502   else
503     return g_strdupv ((GStrv) no_strings);
504 }
505 
506 static void
tp_protocol_constructed(GObject * object)507 tp_protocol_constructed (GObject *object)
508 {
509   TpProtocol *self = (TpProtocol *) object;
510   TpProxy *proxy = (TpProxy *) object;
511   void (*chain_up) (GObject *) =
512     ((GObjectClass *) tp_protocol_parent_class)->constructed;
513   const gchar *s;
514   const GPtrArray *rccs;
515   gboolean had_immutables = TRUE;
516   const gchar * const *interfaces;
517 
518   if (chain_up != NULL)
519     chain_up (object);
520 
521   g_assert (self->priv->protocol_struct.name != NULL);
522 
523   DEBUG ("%s/%s: new Protocol", self->priv->cm_name,
524       self->priv->protocol_struct.name);
525 
526   if (self->priv->protocol_properties == NULL)
527     {
528       DEBUG ("immutable properties not supplied");
529       had_immutables = FALSE;
530       self->priv->protocol_properties = g_hash_table_new_full (g_str_hash,
531           g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free);
532     }
533   else
534     {
535       GHashTableIter iter;
536       gpointer k, v;
537 
538       DEBUG ("immutable properties already supplied");
539 
540       g_hash_table_iter_init (&iter, self->priv->protocol_properties);
541 
542       while (g_hash_table_iter_next (&iter, &k, &v))
543         {
544           gchar *printed;
545 
546           printed = g_strdup_value_contents (v);
547           DEBUG ("%s = %s", (const gchar *) k, printed);
548           g_free (printed);
549         }
550     }
551 
552   self->priv->protocol_struct.params = tp_protocol_params_from_param_specs (
553         tp_asv_get_boxed (self->priv->protocol_properties,
554           TP_PROP_PROTOCOL_PARAMETERS,
555           TP_ARRAY_TYPE_PARAM_SPEC_LIST),
556         tp_proxy_get_bus_name (self), self->priv->protocol_struct.name);
557 
558   /* force vCard field to lower case, even if the CM is spec-incompliant */
559   s = tp_asv_get_string (self->priv->protocol_properties,
560       TP_PROP_PROTOCOL_VCARD_FIELD);
561 
562   if (tp_str_empty (s))
563     self->priv->vcard_field = NULL;
564   else
565     self->priv->vcard_field = g_utf8_strdown (s, -1);
566 
567   s = tp_asv_get_string (self->priv->protocol_properties,
568       TP_PROP_PROTOCOL_ENGLISH_NAME);
569 
570   if (tp_str_empty (s))
571     self->priv->english_name = title_case (self->priv->protocol_struct.name);
572   else
573     self->priv->english_name = g_strdup (s);
574 
575   s = tp_asv_get_string (self->priv->protocol_properties,
576       TP_PROP_PROTOCOL_ICON);
577 
578   if (tp_str_empty (s))
579     self->priv->icon_name = g_strdup_printf ("im-%s",
580         self->priv->protocol_struct.name);
581   else
582     self->priv->icon_name = g_strdup (s);
583 
584   rccs = tp_asv_get_boxed (self->priv->protocol_properties,
585         TP_PROP_PROTOCOL_REQUESTABLE_CHANNEL_CLASSES,
586         TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST);
587 
588   if (rccs != NULL)
589     self->priv->capabilities = _tp_capabilities_new (rccs, FALSE);
590 
591   self->priv->authentication_types = asv_strdupv_or_empty (
592       self->priv->protocol_properties,
593       TP_PROP_PROTOCOL_AUTHENTICATION_TYPES);
594 
595   interfaces = tp_asv_get_strv (self->priv->protocol_properties,
596       TP_PROP_PROTOCOL_INTERFACES);
597 
598   tp_proxy_add_interfaces (proxy, interfaces);
599 
600   if (tp_proxy_has_interface_by_id (self,
601         TP_IFACE_QUARK_PROTOCOL_INTERFACE_AVATARS))
602     {
603       DEBUG ("%s/%s implements Avatars", self->priv->cm_name,
604           self->priv->protocol_struct.name);
605 
606       self->priv->avatar_req = tp_avatar_requirements_new (
607           (GStrv) tp_asv_get_strv (self->priv->protocol_properties,
608             TP_PROP_PROTOCOL_INTERFACE_AVATARS_SUPPORTED_AVATAR_MIME_TYPES),
609           tp_asv_get_uint32 (self->priv->protocol_properties,
610             TP_PROP_PROTOCOL_INTERFACE_AVATARS_MINIMUM_AVATAR_WIDTH, NULL),
611           tp_asv_get_uint32 (self->priv->protocol_properties,
612             TP_PROP_PROTOCOL_INTERFACE_AVATARS_MINIMUM_AVATAR_HEIGHT, NULL),
613           tp_asv_get_uint32 (self->priv->protocol_properties,
614             TP_PROP_PROTOCOL_INTERFACE_AVATARS_RECOMMENDED_AVATAR_WIDTH, NULL),
615           tp_asv_get_uint32 (self->priv->protocol_properties,
616             TP_PROP_PROTOCOL_INTERFACE_AVATARS_RECOMMENDED_AVATAR_HEIGHT, NULL),
617           tp_asv_get_uint32 (self->priv->protocol_properties,
618             TP_PROP_PROTOCOL_INTERFACE_AVATARS_MAXIMUM_AVATAR_WIDTH, NULL),
619           tp_asv_get_uint32 (self->priv->protocol_properties,
620             TP_PROP_PROTOCOL_INTERFACE_AVATARS_MAXIMUM_AVATAR_HEIGHT, NULL),
621           tp_asv_get_uint32 (self->priv->protocol_properties,
622             TP_PROP_PROTOCOL_INTERFACE_AVATARS_MAXIMUM_AVATAR_BYTES, NULL));
623     }
624 
625   if (tp_proxy_has_interface_by_id (self,
626         TP_IFACE_QUARK_PROTOCOL_INTERFACE_ADDRESSING))
627     {
628       DEBUG ("%s/%s implements Addressing", self->priv->cm_name,
629           self->priv->protocol_struct.name);
630 
631       self->priv->addressable_vcard_fields = asv_strdupv_or_empty (
632           self->priv->protocol_properties,
633           TP_PROP_PROTOCOL_INTERFACE_ADDRESSING_ADDRESSABLE_VCARD_FIELDS);
634       self->priv->addressable_uri_schemes = asv_strdupv_or_empty (
635           self->priv->protocol_properties,
636           TP_PROP_PROTOCOL_INTERFACE_ADDRESSING_ADDRESSABLE_URI_SCHEMES);
637     }
638 
639   if (tp_proxy_has_interface_by_id (self,
640         TP_IFACE_QUARK_PROTOCOL_INTERFACE_PRESENCE))
641     {
642       DEBUG ("%s/%s implements Presence", self->priv->cm_name,
643           self->priv->protocol_struct.name);
644 
645       self->priv->presence_statuses = tp_asv_get_boxed (
646           self->priv->protocol_properties,
647           TP_PROP_PROTOCOL_INTERFACE_PRESENCE_STATUSES,
648           TP_HASH_TYPE_SIMPLE_STATUS_SPEC_MAP);
649 
650       if (self->priv->presence_statuses != NULL)
651         {
652           GHashTableIter iter;
653           gpointer k, v;
654 
655           g_hash_table_ref (self->priv->presence_statuses);
656 
657           DEBUG ("%s/%s presence statuses:", self->priv->cm_name,
658               self->priv->protocol_struct.name);
659           g_hash_table_iter_init (&iter, self->priv->presence_statuses);
660 
661           while (g_hash_table_iter_next (&iter, &k, &v))
662             {
663               guint type;
664               gboolean on_self, message;
665 
666               tp_value_array_unpack (v, 3,
667                   &type,
668                   &on_self,
669                   &message);
670               DEBUG ("\tstatus '%s': type %u%s%s",
671                   (const gchar *) k, type, on_self ? ", can set on self" : "",
672                   message ? ", has message" : "");
673             }
674         }
675     }
676 
677   /* become ready immediately */
678   _tp_proxy_set_feature_prepared (proxy, TP_PROTOCOL_FEATURE_PARAMETERS,
679       had_immutables);
680   _tp_proxy_set_feature_prepared (proxy, TP_PROTOCOL_FEATURE_CORE,
681       had_immutables && tp_protocol_check_for_core (self));
682 }
683 
684 enum {
685     FEAT_PARAMETERS,
686     FEAT_CORE,
687     N_FEAT
688 };
689 
690 static const TpProxyFeature *
tp_protocol_list_features(TpProxyClass * cls G_GNUC_UNUSED)691 tp_protocol_list_features (TpProxyClass *cls G_GNUC_UNUSED)
692 {
693   static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
694 
695   if (G_LIKELY (features[0].name != 0))
696     return features;
697 
698   /* we always try to prepare both of these features, and nothing else is
699    * allowed to complete until they have succeeded or failed */
700   features[FEAT_PARAMETERS].name = TP_PROTOCOL_FEATURE_PARAMETERS;
701   features[FEAT_PARAMETERS].core = TRUE;
702   features[FEAT_CORE].name = TP_PROTOCOL_FEATURE_CORE;
703   features[FEAT_CORE].core = TRUE;
704 
705   /* assert that the terminator at the end is there */
706   g_assert (features[N_FEAT].name == 0);
707 
708   return features;
709 }
710 
711 static void
tp_protocol_class_init(TpProtocolClass * klass)712 tp_protocol_class_init (TpProtocolClass *klass)
713 {
714   TpProxyClass *proxy_class = (TpProxyClass *) klass;
715   GObjectClass *object_class = (GObjectClass *) klass;
716 
717   g_type_class_add_private (klass, sizeof (TpProtocolPrivate));
718 
719   object_class->constructed = tp_protocol_constructed;
720   object_class->get_property = tp_protocol_get_property;
721   object_class->set_property = tp_protocol_set_property;
722   object_class->dispose = tp_protocol_dispose;
723   object_class->finalize = tp_protocol_finalize;
724 
725   /**
726    * TpProtocol:protocol-name:
727    *
728    * The machine-readable name of the protocol, taken from the Telepathy
729    * D-Bus Interface Specification, such as "jabber" or "local-xmpp".
730    *
731    * Since: 0.11.11
732    */
733   g_object_class_install_property (object_class, PROP_PROTOCOL_NAME,
734       g_param_spec_string ("protocol-name",
735         "Name of this protocol",
736         "The Protocol from telepathy-spec, such as 'jabber' or 'local-xmpp'",
737         NULL,
738         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
739 
740   /**
741    * TpProtocol:protocol-properties:
742    *
743    * The immutable properties of this Protocol, as provided at construction
744    * time. This is a map from string to #GValue, which must not be modified.
745    *
746    * If the immutable properties were not provided at construction time,
747    * the %TP_PROTOCOL_FEATURE_PARAMETERS and %TP_PROTOCOL_FEATURE_CORE features
748    * will both be unavailable, and this #TpProtocol object will only be useful
749    * as a way to access lower-level D-Bus calls.
750    *
751    * Since: 0.11.11
752    */
753   g_object_class_install_property (object_class, PROP_PROTOCOL_PROPERTIES,
754       g_param_spec_boxed ("protocol-properties",
755         "Protocol properties",
756         "The immutable properties of this Protocol",
757         TP_HASH_TYPE_QUALIFIED_PROPERTY_VALUE_MAP,
758         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
759 
760   /**
761    * TpProtocol:protocol-properties-vardict:
762    *
763    * The immutable properties of this Protocol, as provided at construction
764    * time. This is a #G_VARIANT_TYPE_VARDICT #GVariant,
765    * which must not be modified.
766    *
767    * If the immutable properties were not provided at construction time,
768    * the %TP_PROTOCOL_FEATURE_PARAMETERS and %TP_PROTOCOL_FEATURE_CORE features
769    * will both be unavailable, and this #TpProtocol object will only be useful
770    * as a way to access lower-level D-Bus calls.
771    *
772    * Since: 0.23.3
773    */
774   g_object_class_install_property (object_class,
775       PROP_PROTOCOL_PROPERTIES_VARDICT,
776       g_param_spec_variant ("protocol-properties-vardict",
777         "Protocol properties",
778         "The immutable properties of this Protocol",
779         G_VARIANT_TYPE_VARDICT, NULL,
780         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
781 
782   /**
783    * TpProtocol:english-name:
784    *
785    * The name of the protocol in a form suitable for display to users,
786    * such as "AIM" or "Yahoo!", or a string based on #TpProtocol:protocol-name
787    * (currently constructed by putting the first character in title case,
788    * but this is not guaranteed) if no better name is available or the
789    * %TP_PROTOCOL_FEATURE_CORE feature has not been prepared.
790    *
791    * This is effectively in the C locale (international English); user
792    * interfaces requiring a localized protocol name should look one up in their
793    * own message catalog based on either #TpProtocol:protocol-name or
794    * #TpProtocol:english-name, but should use this English version as a
795    * fallback if no translated version can be found.
796    *
797    * Since: 0.11.11
798    */
799   g_object_class_install_property (object_class, PROP_ENGLISH_NAME,
800       g_param_spec_string ("english-name",
801         "English name",
802         "A non-NULL English name for this Protocol",
803         NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
804 
805   /**
806    * TpProtocol:vcard-field:
807    *
808    * The most common vCard field used for this protocol's contact
809    * identifiers, normalized to lower case, or %NULL if there is no such field
810    * or the %TP_PROTOCOL_FEATURE_CORE feature has not been prepared.
811    *
812    * Since: 0.11.11
813    */
814   g_object_class_install_property (object_class, PROP_VCARD_FIELD,
815       g_param_spec_string ("vcard-field",
816         "vCard field",
817         "A lower-case vCard name for this Protocol, or NULL",
818         NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
819 
820   /**
821    * TpProtocol:icon-name:
822    *
823    * The name of an icon in the system's icon theme. If none was supplied
824    * by the Protocol, or the %TP_PROTOCOL_FEATURE_CORE feature has not been
825    * prepared, a default is used; currently, this is "im-" plus
826    * #TpProtocol:protocol-name.
827    *
828    * Since: 0.11.11
829    */
830   g_object_class_install_property (object_class, PROP_ICON_NAME,
831       g_param_spec_string ("icon-name",
832         "Icon name",
833         "A non-NULL Icon name for this Protocol",
834         NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
835 
836   /**
837    * TpProtocol:capabilities:
838    *
839    * The classes of channel that can be requested from connections to this
840    * protocol, or %NULL if this is unknown or the %TP_PROTOCOL_FEATURE_CORE
841    * feature has not been prepared.
842    *
843    * Since: 0.11.11
844    */
845   g_object_class_install_property (object_class, PROP_CAPABILITIES,
846       g_param_spec_object ("capabilities",
847         "Capabilities",
848         "Requestable channel classes for this Protocol",
849         TP_TYPE_CAPABILITIES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
850 
851   /**
852    * TpProtocol:param-names:
853    *
854    * A list of parameter names supported by this connection manager
855    * for this protocol, or %NULL if %TP_PROTOCOL_FEATURE_PARAMETERS has not
856    * been prepared.
857    *
858    * Since: 0.11.11
859    */
860   g_object_class_install_property (object_class, PROP_PARAM_NAMES,
861       g_param_spec_boxed ("param-names",
862         "Parameter names",
863         "A list of parameter names",
864         G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
865 
866   /**
867    * TpProtocol:authentication-types:
868    *
869    * A non-%NULL #GStrv of interfaces which provide information as to
870    * what kind of authentication channels can possibly appear before
871    * the connection reaches the CONNECTED state, or %NULL if
872    * %TP_PROTOCOL_FEATURE_CORE has not been prepared.
873    *
874    * Since: 0.13.9
875    */
876   g_object_class_install_property (object_class, PROP_AUTHENTICATION_TYPES,
877       g_param_spec_boxed ("authentication-types",
878         "AuthenticationTypes",
879         "A list of authentication types",
880         G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
881 
882   /**
883    * TpProtocol:avatar-requirements:
884    *
885    * A #TpAvatarRequirements representing the avatar requirements on this
886    * protocol, or %NULL if %TP_PROTOCOL_FEATURE_CORE has not been prepared or
887    * if the protocol doesn't support avatars.
888    *
889    * Since: 0.15.6
890    */
891   g_object_class_install_property (object_class, PROP_AVATAR_REQUIREMENTS,
892       g_param_spec_pointer ("avatar-requirements",
893         "Avatars requirements",
894         "Avatars requirements",
895         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
896 
897   /**
898    * TpProtocol:cm-name:
899    *
900    * The name of the connection manager this protocol is on.
901    *
902    * Since: 0.19.1
903    */
904   g_object_class_install_property (object_class, PROP_CM_NAME,
905       g_param_spec_string ("cm-name",
906         "Connection manager name",
907         "Name of the CM this protocol is on",
908         NULL,
909         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
910 
911   /**
912    * TpProtocol:addressable-vcard-fields:
913    *
914    * A non-%NULL #GStrv of vCard fields supported by this protocol.
915    * If this protocol does not support addressing contacts by a vCard field,
916    * the list is empty.
917    *
918    * For instance, a SIP connection manager that supports calling contacts
919    * by SIP URI (vCard field SIP) or telephone number (vCard field TEL)
920    * might have { "sip", "tel", NULL }.
921    *
922    * Since: 0.23.1
923    */
924   g_object_class_install_property (object_class, PROP_ADDRESSABLE_VCARD_FIELDS,
925       g_param_spec_boxed ("addressable-vcard-fields",
926         "AddressableVCardFields",
927         "A list of vCard fields",
928         G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
929 
930   /**
931    * TpProtocol:addressable-uri-schemes:
932    *
933    * A non-%NULL #GStrv of URI schemes supported by this protocol.
934    * If this protocol does not support addressing contacts by URI,
935    * the list is empty.
936    *
937    * For instance, a SIP connection manager that supports calling contacts
938    * by SIP URI (sip:alice&commat;example.com, sips:bob&commat;example.com)
939    * or telephone number (tel:+1-555-0123) might have
940    * { "sip", "sips", "tel", NULL }.
941    *
942    * Since: 0.23.1
943    */
944   g_object_class_install_property (object_class, PROP_ADDRESSABLE_URI_SCHEMES,
945       g_param_spec_boxed ("addressable-uri-schemes",
946         "AddressableURISchemes",
947         "A list of URI schemes",
948         G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
949 
950   proxy_class->list_features = tp_protocol_list_features;
951   proxy_class->must_have_unique_name = FALSE;
952   proxy_class->interface = TP_IFACE_QUARK_PROTOCOL;
953   tp_protocol_init_known_interfaces ();
954 }
955 
956 static void
tp_protocol_init(TpProtocol * self)957 tp_protocol_init (TpProtocol *self)
958 {
959   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_PROTOCOL,
960       TpProtocolPrivate);
961 }
962 
963 /**
964  * tp_protocol_new:
965  * @dbus: proxy for the D-Bus daemon; may not be %NULL
966  * @cm_name: the connection manager name (such as "gabble")
967  * @protocol_name: the protocol name (such as "jabber")
968  * @immutable_properties: the immutable D-Bus properties for this protocol
969  * @error: used to indicate the error if %NULL is returned
970  *
971  * <!-- -->
972  *
973  * Returns: a new protocol proxy, or %NULL on invalid arguments
974  *
975  * Since: 0.11.11
976  */
977 TpProtocol *
tp_protocol_new(TpDBusDaemon * dbus,const gchar * cm_name,const gchar * protocol_name,const GHashTable * immutable_properties,GError ** error)978 tp_protocol_new (TpDBusDaemon *dbus,
979     const gchar *cm_name,
980     const gchar *protocol_name,
981     const GHashTable *immutable_properties,
982     GError **error)
983 {
984   TpProtocol *ret = NULL;
985   gchar *bus_name = NULL;
986   gchar *object_path = NULL;
987 
988   g_return_val_if_fail (TP_IS_DBUS_DAEMON (dbus), NULL);
989 
990   if (!tp_connection_manager_check_valid_protocol_name (protocol_name, error))
991     goto finally;
992 
993   if (!tp_connection_manager_check_valid_name (cm_name, error))
994     goto finally;
995 
996   bus_name = g_strdup_printf ("%s%s", TP_CM_BUS_NAME_BASE, cm_name);
997   object_path = g_strdup_printf ("%s%s/%s", TP_CM_OBJECT_PATH_BASE, cm_name,
998       protocol_name);
999   /* e.g. local-xmpp -> local_xmpp */
1000   g_strdelimit (object_path, "-", '_');
1001 
1002   ret = TP_PROTOCOL (g_object_new (TP_TYPE_PROTOCOL,
1003         "dbus-daemon", dbus,
1004         "bus-name", bus_name,
1005         "object-path", object_path,
1006         "protocol-name", protocol_name,
1007         "protocol-properties", immutable_properties,
1008         "cm-name", cm_name,
1009         NULL));
1010 
1011 finally:
1012   g_free (bus_name);
1013   g_free (object_path);
1014   return ret;
1015 }
1016 
1017 /**
1018  * tp_protocol_new_vardict:
1019  * @dbus: proxy for the D-Bus daemon; may not be %NULL
1020  * @cm_name: the connection manager name (such as "gabble")
1021  * @protocol_name: the protocol name (such as "jabber")
1022  * @immutable_properties: the immutable D-Bus properties for this protocol
1023  * @error: used to indicate the error if %NULL is returned
1024  *
1025  * Create a new protocol proxy.
1026  *
1027  * If @immutable_properties is a floating reference, this function will
1028  * take ownership of it, much like g_variant_ref_sink(). See documentation of
1029  * that function for details.
1030  *
1031  * Returns: a new protocol proxy, or %NULL on invalid arguments
1032  *
1033  * Since: 0.23.3
1034  */
1035 TpProtocol *
tp_protocol_new_vardict(TpDBusDaemon * dbus,const gchar * cm_name,const gchar * protocol_name,GVariant * immutable_properties,GError ** error)1036 tp_protocol_new_vardict (TpDBusDaemon *dbus,
1037     const gchar *cm_name,
1038     const gchar *protocol_name,
1039     GVariant *immutable_properties,
1040     GError **error)
1041 {
1042   GHashTable *hash;
1043   TpProtocol *ret;
1044 
1045   g_return_val_if_fail (g_variant_is_of_type (immutable_properties,
1046         G_VARIANT_TYPE_VARDICT), NULL);
1047 
1048   g_variant_ref_sink (immutable_properties);
1049   hash = _tp_asv_from_vardict (immutable_properties);
1050   ret = tp_protocol_new (dbus, cm_name, protocol_name, hash, error);
1051   g_hash_table_unref (hash);
1052   g_variant_unref (immutable_properties);
1053   return ret;
1054 }
1055 
1056 /**
1057  * tp_protocol_init_known_interfaces:
1058  *
1059  * Ensure that the known interfaces for TpProtocol have been set up.
1060  * This is done automatically when necessary, but for correct
1061  * overriding of library interfaces by local extensions, you should
1062  * call this function before calling
1063  * tp_proxy_or_subclass_hook_on_interface_add() with first argument
1064  * %TP_TYPE_PROTOCOL.
1065  *
1066  * Since: 0.11.11
1067  */
1068 void
tp_protocol_init_known_interfaces(void)1069 tp_protocol_init_known_interfaces (void)
1070 {
1071   static gsize once = 0;
1072 
1073   if (g_once_init_enter (&once))
1074     {
1075       GType type = TP_TYPE_PROTOCOL;
1076 
1077       tp_proxy_init_known_interfaces ();
1078 
1079       tp_proxy_or_subclass_hook_on_interface_add (type,
1080           tp_cli_protocol_add_signals);
1081       tp_proxy_subclass_add_error_mapping (type,
1082           TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR);
1083 
1084       g_once_init_leave (&once, 1);
1085     }
1086 }
1087 
1088 TpConnectionManagerProtocol *
_tp_protocol_get_struct(TpProtocol * self)1089 _tp_protocol_get_struct (TpProtocol *self)
1090 {
1091   return &self->priv->protocol_struct;
1092 }
1093 
1094 /**
1095  * tp_protocol_get_name:
1096  * @self: a protocol object
1097  *
1098  * Return the same thing as the protocol-name property, for convenient use
1099  * in C code. The returned string is valid for as long as @self exists.
1100  *
1101  * Returns: the value of the #TpProtocol:protocol-name property
1102  *
1103  * Since: 0.11.11
1104  */
1105 const gchar *
tp_protocol_get_name(TpProtocol * self)1106 tp_protocol_get_name (TpProtocol *self)
1107 {
1108   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1109   return self->priv->protocol_struct.name;
1110 }
1111 
1112 /**
1113  * tp_protocol_has_param:
1114  * @self: a protocol
1115  * @param: a parameter name
1116  *
1117  * <!-- no more to say -->
1118  *
1119  * Returns: %TRUE if @self supports the parameter @param.
1120  *
1121  * Since: 0.11.11
1122  */
1123 gboolean
tp_protocol_has_param(TpProtocol * self,const gchar * param)1124 tp_protocol_has_param (TpProtocol *self,
1125     const gchar *param)
1126 {
1127   return (tp_protocol_get_param (self, param) != NULL);
1128 }
1129 
1130 /**
1131  * tp_protocol_get_param:
1132  * @self: a protocol
1133  * @param: a parameter name
1134  *
1135  * <!-- no more to say -->
1136  *
1137  * Returns: a structure representing the parameter @param, or %NULL if not
1138  *          supported
1139  *
1140  * Since: 0.11.11
1141  */
1142 const TpConnectionManagerParam *
tp_protocol_get_param(TpProtocol * self,const gchar * param)1143 tp_protocol_get_param (TpProtocol *self,
1144     const gchar *param)
1145 {
1146   g_return_val_if_fail (TP_IS_PROTOCOL (self), FALSE);
1147 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1148   return tp_connection_manager_protocol_get_param (
1149       &self->priv->protocol_struct, param);
1150 G_GNUC_END_IGNORE_DEPRECATIONS
1151 }
1152 
1153 /**
1154  * tp_protocol_dup_param:
1155  * @self: a protocol
1156  * @param: a parameter name
1157  *
1158  * <!-- no more to say -->
1159  *
1160  * Returns: (transfer full): a structure representing the parameter @param,
1161  *  or %NULL if not supported. Free with tp_connection_manager_param_free()
1162  *
1163  * Since: 0.17.6
1164  */
1165 TpConnectionManagerParam *
tp_protocol_dup_param(TpProtocol * self,const gchar * param)1166 tp_protocol_dup_param (TpProtocol *self,
1167     const gchar *param)
1168 {
1169   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1170 
1171   return tp_connection_manager_param_copy (tp_protocol_get_param (self, param));
1172 }
1173 
1174 /**
1175  * tp_protocol_can_register:
1176  * @self: a protocol
1177  *
1178  * Return whether a new account can be registered on this protocol, by setting
1179  * the special "register" parameter to %TRUE.
1180  *
1181  * Returns: %TRUE if @protocol supports the parameter "register"
1182  *
1183  * Since: 0.11.11
1184  */
1185 gboolean
tp_protocol_can_register(TpProtocol * self)1186 tp_protocol_can_register (TpProtocol *self)
1187 {
1188   return tp_protocol_has_param (self, "register");
1189 }
1190 
1191 /**
1192  * tp_protocol_dup_param_names:
1193  * @self: a protocol
1194  *
1195  * Returns a list of parameter names supported by this connection manager
1196  * for this protocol.
1197  *
1198  * The result is copied and must be freed by the caller with g_strfreev().
1199  *
1200  * Returns: (array zero-terminated=1) (transfer full): a copy of
1201  *  #TpProtocol:param-names
1202  *
1203  * Since: 0.11.11
1204  */
1205 GStrv
tp_protocol_dup_param_names(TpProtocol * self)1206 tp_protocol_dup_param_names (TpProtocol *self)
1207 {
1208   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1209 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1210   return tp_connection_manager_protocol_dup_param_names (
1211       &self->priv->protocol_struct);
1212 G_GNUC_END_IGNORE_DEPRECATIONS
1213 }
1214 
1215 /**
1216  * tp_protocol_borrow_params: (skip)
1217  * @self: a protocol
1218  *
1219  * Returns an array of parameters supported by this connection manager,
1220  * without additional memory allocations. The returned array is owned by
1221  * @self, and must not be used after @self has been freed.
1222  *
1223  * Returns: (transfer none): an array of #TpConnectionManagerParam structures,
1224  *  terminated by one whose @name is %NULL
1225  *
1226  * Since: 0.17.6
1227  * Deprecated: Since 0.19.9. New code should use tp_protocol_dup_params()
1228  *  instead.
1229  */
1230 const TpConnectionManagerParam *
tp_protocol_borrow_params(TpProtocol * self)1231 tp_protocol_borrow_params (TpProtocol *self)
1232 {
1233   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1234 
1235   return self->priv->protocol_struct.params;
1236 }
1237 
1238 /**
1239  * tp_protocol_dup_params:
1240  * @self: a protocol
1241  *
1242  * Returns a list of parameters supported by this connection manager.
1243  *
1244  * The returned list must be freed by the caller, for instance with
1245  * <literal>g_list_free_full (l,
1246  * (GDestroyNotify) tp_connection_manager_param_free)</literal>.
1247  *
1248  * Returns: (transfer full) (element-type TelepathyGLib.ConnectionManagerParam):
1249  *  a list of #TpConnectionManagerParam structures, owned by the caller
1250  *
1251  * Since: 0.17.6
1252  */
1253 GList *
tp_protocol_dup_params(TpProtocol * self)1254 tp_protocol_dup_params (TpProtocol *self)
1255 {
1256   guint i;
1257   GList *ret = NULL;
1258 
1259   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1260 
1261   for (i = 0; self->priv->protocol_struct.params[i].name != NULL; i++)
1262     {
1263       ret = g_list_prepend (ret,
1264           tp_connection_manager_param_copy (
1265             &(self->priv->protocol_struct.params[i])));
1266     }
1267 
1268   return g_list_reverse (ret);
1269 }
1270 
1271 /**
1272  * tp_protocol_get_vcard_field:
1273  * @self: a protocol object
1274  *
1275  * <!-- -->
1276  *
1277  * Returns: the value of #TpProtocol:vcard-field
1278  *
1279  * Since: 0.11.11
1280  */
1281 const gchar *
tp_protocol_get_vcard_field(TpProtocol * self)1282 tp_protocol_get_vcard_field (TpProtocol *self)
1283 {
1284   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1285   return self->priv->vcard_field;
1286 }
1287 
1288 /**
1289  * tp_protocol_get_english_name:
1290  * @self: a protocol object
1291  *
1292  * <!-- -->
1293  *
1294  * Returns: the non-%NULL, non-empty value of #TpProtocol:english-name
1295  *
1296  * Since: 0.11.11
1297  */
1298 const gchar *
tp_protocol_get_english_name(TpProtocol * self)1299 tp_protocol_get_english_name (TpProtocol *self)
1300 {
1301   g_return_val_if_fail (TP_IS_PROTOCOL (self), "");
1302   return self->priv->english_name;
1303 }
1304 
1305 /**
1306  * tp_protocol_get_icon_name:
1307  * @self: a protocol object
1308  *
1309  * <!-- -->
1310  *
1311  * Returns: the non-%NULL, non-empty value of #TpProtocol:icon-name
1312  *
1313  * Since: 0.11.11
1314  */
1315 const gchar *
tp_protocol_get_icon_name(TpProtocol * self)1316 tp_protocol_get_icon_name (TpProtocol *self)
1317 {
1318   g_return_val_if_fail (TP_IS_PROTOCOL (self), "dialog-error");
1319   return self->priv->icon_name;
1320 }
1321 
1322 /**
1323  * tp_protocol_get_authentication_types:
1324  * @self: a protocol object
1325  *
1326  *
1327  <!-- -->
1328  *
1329  * Returns: (transfer none): the value of #TpProtocol:authentication-types
1330  *
1331  * Since: 0.13.9
1332  */
1333 const gchar * const *
tp_protocol_get_authentication_types(TpProtocol * self)1334 tp_protocol_get_authentication_types (TpProtocol *self)
1335 {
1336   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1337   return (const gchar * const *) self->priv->authentication_types;
1338 }
1339 
1340 /**
1341  * tp_protocol_get_capabilities:
1342  * @self: a protocol object
1343  *
1344  * <!-- -->
1345  *
1346  * Returns: (transfer none): #TpProtocol:capabilities, which must be referenced
1347  *  (if non-%NULL) if it will be kept
1348  *
1349  * Since: 0.11.11
1350  */
1351 TpCapabilities *
tp_protocol_get_capabilities(TpProtocol * self)1352 tp_protocol_get_capabilities (TpProtocol *self)
1353 {
1354   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
1355   return self->priv->capabilities;
1356 }
1357 
1358 static gboolean
init_gvalue_from_dbus_sig(const gchar * sig,GValue * value)1359 init_gvalue_from_dbus_sig (const gchar *sig,
1360                            GValue *value)
1361 {
1362   g_assert (!G_IS_VALUE (value));
1363 
1364   switch (sig[0])
1365     {
1366     case 'b':
1367       g_value_init (value, G_TYPE_BOOLEAN);
1368       return TRUE;
1369 
1370     case 's':
1371       g_value_init (value, G_TYPE_STRING);
1372       return TRUE;
1373 
1374     case 'q':
1375     case 'u':
1376       g_value_init (value, G_TYPE_UINT);
1377       return TRUE;
1378 
1379     case 'y':
1380       g_value_init (value, G_TYPE_UCHAR);
1381       return TRUE;
1382 
1383     case 'n':
1384     case 'i':
1385       g_value_init (value, G_TYPE_INT);
1386       return TRUE;
1387 
1388     case 'x':
1389       g_value_init (value, G_TYPE_INT64);
1390       return TRUE;
1391 
1392     case 't':
1393       g_value_init (value, G_TYPE_UINT64);
1394       return TRUE;
1395 
1396     case 'o':
1397       g_value_init (value, DBUS_TYPE_G_OBJECT_PATH);
1398       g_value_set_static_boxed (value, "/");
1399       return TRUE;
1400 
1401     case 'd':
1402       g_value_init (value, G_TYPE_DOUBLE);
1403       return TRUE;
1404 
1405     case 'v':
1406       g_value_init (value, G_TYPE_VALUE);
1407       return TRUE;
1408 
1409     case 'a':
1410       switch (sig[1])
1411         {
1412         case 's':
1413           g_value_init (value, G_TYPE_STRV);
1414           return TRUE;
1415 
1416         case 'o':
1417           g_value_init (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST);
1418           return TRUE;
1419 
1420         case 'y':
1421           g_value_init (value, DBUS_TYPE_G_UCHAR_ARRAY);
1422           return TRUE;
1423         }
1424     }
1425 
1426   return FALSE;
1427 }
1428 
1429 static gboolean
parse_default_value(GValue * value,const gchar * sig,gchar * raw_value,GKeyFile * file,const gchar * group,const gchar * key)1430 parse_default_value (GValue *value,
1431                      const gchar *sig,
1432                      gchar *raw_value,
1433                      GKeyFile *file,
1434                      const gchar *group,
1435                      const gchar *key)
1436 {
1437   GError *error = NULL;
1438   gchar *s, *p;
1439 
1440   switch (sig[0])
1441     {
1442     case 'b':
1443       g_value_set_boolean (value, g_key_file_get_boolean (file, group, key,
1444             &error));
1445 
1446       if (error == NULL)
1447         return TRUE;
1448 
1449       /* In telepathy-glib < 0.7.26 we accepted true and false in
1450        * any case combination, 0, and 1. The desktop file spec specifies
1451        * "true" and "false" only, while GKeyFile currently accepts 0 and 1 too.
1452        * So, on error, let's fall back to more lenient parsing that explicitly
1453        * allows everything we historically allowed. */
1454       g_error_free (error);
1455 
1456       if (raw_value == NULL)
1457         return FALSE;
1458 
1459       for (p = raw_value; *p != '\0'; p++)
1460         {
1461           *p = g_ascii_tolower (*p);
1462         }
1463 
1464       if (!tp_strdiff (raw_value, "1") || !tp_strdiff (raw_value, "true"))
1465         {
1466           g_value_set_boolean (value, TRUE);
1467         }
1468       else if (!tp_strdiff (raw_value, "0") || !tp_strdiff (raw_value, "false"))
1469         {
1470           g_value_set_boolean (value, TRUE);
1471         }
1472       else
1473         {
1474           return FALSE;
1475         }
1476 
1477       return TRUE;
1478 
1479     case 's':
1480       s = g_key_file_get_string (file, group, key, NULL);
1481 
1482       g_value_take_string (value, s);
1483       return (s != NULL);
1484 
1485     case 'y':
1486     case 'q':
1487     case 'u':
1488     case 't':
1489         {
1490           guint64 v = g_key_file_get_uint64 (file, group, key, &error);
1491 
1492           if (error != NULL)
1493             {
1494               g_error_free (error);
1495               return FALSE;
1496             }
1497 
1498           if (sig[0] == 't')
1499             {
1500               g_value_set_uint64 (value, v);
1501               return TRUE;
1502             }
1503 
1504           if (sig[0] == 'y')
1505             {
1506               if (v > G_MAXUINT8)
1507                 {
1508                   return FALSE;
1509                 }
1510 
1511               g_value_set_uchar (value, v);
1512               return TRUE;
1513             }
1514 
1515           if (v > G_MAXUINT32 || (sig[0] == 'q' && v > G_MAXUINT16))
1516             return FALSE;
1517 
1518           g_value_set_uint (value, v);
1519           return TRUE;
1520         }
1521 
1522     case 'n':
1523     case 'i':
1524     case 'x':
1525       if (raw_value[0] == '\0')
1526         {
1527           return FALSE;
1528         }
1529       else
1530         {
1531           gint64 v = g_key_file_get_int64 (file, group, key, &error);
1532 
1533           if (error != NULL)
1534             {
1535               g_error_free (error);
1536               return FALSE;
1537             }
1538 
1539           if (sig[0] == 'x')
1540             {
1541               g_value_set_int64 (value, v);
1542               return TRUE;
1543             }
1544 
1545           if (v > G_MAXINT32 || (sig[0] == 'q' && v > G_MAXINT16))
1546             return FALSE;
1547 
1548           if (v < G_MININT32 || (sig[0] == 'n' && v < G_MININT16))
1549             return FALSE;
1550 
1551           g_value_set_int (value, v);
1552           return TRUE;
1553         }
1554 
1555     case 'o':
1556       s = g_key_file_get_string (file, group, key, NULL);
1557 
1558       if (s == NULL || !tp_dbus_check_valid_object_path (s, NULL))
1559         {
1560           g_free (s);
1561           return FALSE;
1562         }
1563 
1564       g_value_take_boxed (value, s);
1565 
1566       return TRUE;
1567 
1568     case 'd':
1569       g_value_set_double (value, g_key_file_get_double (file, group, key,
1570             &error));
1571 
1572       if (error != NULL)
1573         {
1574           g_error_free (error);
1575           return FALSE;
1576         }
1577 
1578       return TRUE;
1579 
1580     case 'a':
1581       switch (sig[1])
1582         {
1583         case 's':
1584             {
1585               g_value_take_boxed (value,
1586                   g_key_file_get_string_list (file, group, key, NULL, &error));
1587 
1588               if (error != NULL)
1589                 {
1590                   g_error_free (error);
1591                   return FALSE;
1592                 }
1593 
1594               return TRUE;
1595             }
1596 
1597         case 'o':
1598             {
1599               gsize len = 0;
1600               GStrv strv = g_key_file_get_string_list (file, group, key, &len,
1601                   &error);
1602               gchar **iter;
1603               GPtrArray *arr;
1604 
1605               if (error != NULL)
1606                 {
1607                   g_error_free (error);
1608                   return FALSE;
1609                 }
1610 
1611               for (iter = strv; iter != NULL && *iter != NULL; iter++)
1612                 {
1613                   if (!g_variant_is_object_path (*iter))
1614                     {
1615                       g_strfreev (strv);
1616                       return FALSE;
1617                     }
1618                 }
1619 
1620               arr = g_ptr_array_sized_new (len);
1621 
1622               for (iter = strv; iter != NULL && *iter != NULL; iter++)
1623                 {
1624                   /* transfer ownership */
1625                   g_ptr_array_add (arr, *iter);
1626                 }
1627 
1628               g_free (strv);
1629               g_value_take_boxed (value, arr);
1630 
1631               return TRUE;
1632             }
1633         }
1634     }
1635 
1636   if (G_IS_VALUE (value))
1637     g_value_unset (value);
1638 
1639   return FALSE;
1640 }
1641 
1642 #define PROTOCOL_PREFIX "Protocol "
1643 #define PROTOCOL_PREFIX_LEN 9
1644 tp_verify (sizeof (PROTOCOL_PREFIX) == PROTOCOL_PREFIX_LEN + 1);
1645 
1646 static gchar *
replace_null_with_empty(gchar * in)1647 replace_null_with_empty (gchar *in)
1648 {
1649   return (in == NULL ? g_strdup ("") : in);
1650 }
1651 
1652 static GHashTable *
_tp_protocol_parse_channel_class(GKeyFile * file,const gchar * group)1653 _tp_protocol_parse_channel_class (GKeyFile *file,
1654     const gchar *group)
1655 {
1656   GHashTable *ret;
1657   gchar **keys, **key;
1658 
1659   ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1660       (GDestroyNotify) tp_g_value_slice_free);
1661 
1662   keys = g_key_file_get_keys (file, group, NULL, NULL);
1663 
1664   for (key = keys; key != NULL && *key != NULL; key++)
1665     {
1666       gchar *space = strchr (*key, ' ');
1667       gchar *value = NULL;
1668       gchar *property = NULL;
1669       const gchar *dbus_type;
1670       GValue *v = g_slice_new0 (GValue);
1671 
1672       value = g_key_file_get_value (file, group, *key, NULL);
1673 
1674       /* keys without a space are reserved */
1675       if (space == NULL)
1676         {
1677           DEBUG ("\t'%s' isn't a fixed property", *key);
1678           goto cleanup;
1679         }
1680 
1681       property = g_strndup (*key, space - *key);
1682       dbus_type = space + 1;
1683 
1684       if (!init_gvalue_from_dbus_sig (dbus_type, v))
1685         {
1686           DEBUG ("\tunable to parse D-Bus type '%s' for '%s' in a "
1687               ".manager file", dbus_type, property);
1688           goto cleanup;
1689         }
1690 
1691       if (!parse_default_value (v, dbus_type, value, file, group, *key))
1692         {
1693           DEBUG ("\tunable to parse '%s' as a value of type '%s' for '%s'",
1694               value, dbus_type, property);
1695           goto cleanup;
1696         }
1697 
1698       DEBUG ("\tfixed: '%s' of type '%s' = '%s'",
1699           property, dbus_type, value);
1700 
1701       /* transfer ownership to @ret */
1702       g_hash_table_insert (ret, property, v);
1703       property = NULL;
1704       v = NULL;
1705 
1706 cleanup:
1707       if (v != NULL)
1708         {
1709           if (G_IS_VALUE (v))
1710             tp_g_value_slice_free (v);
1711           else
1712             g_slice_free (GValue, v);
1713         }
1714 
1715       g_free (property);
1716       g_free (value);
1717     }
1718 
1719   g_strfreev (keys);
1720 
1721   return ret;
1722 }
1723 
1724 static GValueArray *
_tp_protocol_parse_rcc(const gchar * cm_debug_name,const gchar * protocol_debug_name,GKeyFile * file,const gchar * group)1725 _tp_protocol_parse_rcc (const gchar *cm_debug_name,
1726     const gchar *protocol_debug_name,
1727     GKeyFile *file,
1728     const gchar *group)
1729 {
1730   GHashTable *fixed;
1731   GStrv allowed;
1732   GValueArray *ret;
1733   guint i;
1734 
1735   DEBUG ("%s/%s: parsing requestable channel class '%s'", cm_debug_name,
1736       protocol_debug_name, group);
1737 
1738   fixed = _tp_protocol_parse_channel_class (file, group);
1739   allowed = g_key_file_get_string_list (file, group, "allowed", NULL, NULL);
1740 
1741   for (i = 0; allowed != NULL && allowed[i] != NULL; i++)
1742     {
1743       DEBUG ("\tallowed: '%s'", allowed[i]);
1744     }
1745 
1746   ret = tp_value_array_build (2,
1747       TP_HASH_TYPE_CHANNEL_CLASS, fixed,
1748       G_TYPE_STRV, allowed,
1749       NULL);
1750 
1751   g_hash_table_unref (fixed);
1752   g_strfreev (allowed);
1753 
1754   return ret;
1755 }
1756 
1757 GHashTable *
_tp_protocol_parse_manager_file(GKeyFile * file,const gchar * cm_debug_name,const gchar * group,gchar ** protocol_name)1758 _tp_protocol_parse_manager_file (GKeyFile *file,
1759     const gchar *cm_debug_name,
1760     const gchar *group,
1761     gchar **protocol_name)
1762 {
1763   GHashTable *immutables;
1764   GHashTable *status_specs;
1765   GPtrArray *param_specs, *rccs;
1766   const gchar *name;
1767   gchar **rcc_groups, **rcc_group;
1768   gchar **keys, **key;
1769   guint i;
1770 
1771   if (!g_str_has_prefix (group, PROTOCOL_PREFIX))
1772     return NULL;
1773 
1774   name = group + PROTOCOL_PREFIX_LEN;
1775 
1776   if (!tp_connection_manager_check_valid_protocol_name (name, NULL))
1777     {
1778       DEBUG ("%s: protocol '%s' has an invalid name", cm_debug_name, name);
1779       return NULL;
1780     }
1781 
1782   DEBUG ("%s: reading protocol '%s' from manager file", cm_debug_name, name);
1783 
1784   keys = g_key_file_get_keys (file, group, NULL, NULL);
1785 
1786   i = 0;
1787 
1788   for (key = keys; key != NULL && *key != NULL; key++)
1789     {
1790       if (g_str_has_prefix (*key, "param-"))
1791         i++;
1792     }
1793 
1794   param_specs = g_ptr_array_sized_new (i);
1795 
1796   for (key = keys; key != NULL && *key != NULL; key++)
1797     {
1798       if (g_str_has_prefix (*key, "param-"))
1799         {
1800           gchar **strv, **iter;
1801           gchar *value, *def;
1802           TpConnectionManagerParam param = { NULL };
1803 
1804           value = g_key_file_get_string (file, group, *key, NULL);
1805 
1806           if (value == NULL)
1807             continue;
1808 
1809           /* strlen ("param-") == 6 */
1810           param.name = *key + 6;
1811 
1812           strv = g_strsplit (value, " ", 0);
1813           g_free (value);
1814 
1815           param.dbus_signature = strv[0];
1816 
1817           param.flags = 0;
1818 
1819           for (iter = strv + 1; *iter != NULL; iter++)
1820             {
1821               if (!tp_strdiff (*iter, "required"))
1822                 param.flags |= TP_CONN_MGR_PARAM_FLAG_REQUIRED;
1823               if (!tp_strdiff (*iter, "register"))
1824                 param.flags |= TP_CONN_MGR_PARAM_FLAG_REGISTER;
1825               if (!tp_strdiff (*iter, "secret"))
1826                 param.flags |= TP_CONN_MGR_PARAM_FLAG_SECRET;
1827               if (!tp_strdiff (*iter, "dbus-property"))
1828                 param.flags |= TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY;
1829             }
1830 
1831           if ((!tp_strdiff (param.name, "password") ||
1832               g_str_has_suffix (param.name, "-password")) &&
1833               (param.flags & TP_CONN_MGR_PARAM_FLAG_SECRET) == 0)
1834             {
1835               DEBUG ("\tTreating %s as secret due to its name (please "
1836                   "fix %s.manager)", param.name, cm_debug_name);
1837               param.flags |= TP_CONN_MGR_PARAM_FLAG_SECRET;
1838             }
1839 
1840           def = g_strdup_printf ("default-%s", param.name);
1841           value = g_key_file_get_value (file, group, def, NULL);
1842 
1843           init_gvalue_from_dbus_sig (param.dbus_signature,
1844               &param.default_value);
1845 
1846           if (value != NULL && parse_default_value (&param.default_value,
1847                 param.dbus_signature, value, file, group, def))
1848             param.flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
1849 
1850           DEBUG ("\tParam name: %s", param.name);
1851           DEBUG ("\tParam flags: 0x%x", param.flags);
1852           DEBUG ("\tParam sig: %s", param.dbus_signature);
1853 
1854 #ifdef ENABLE_DEBUG
1855           if (G_IS_VALUE (&param.default_value))
1856             {
1857               gchar *repr = g_strdup_value_contents (&(param.default_value));
1858 
1859               DEBUG ("\tParam default value: %s of type %s", repr,
1860                   G_VALUE_TYPE_NAME (&(param.default_value)));
1861               g_free (repr);
1862             }
1863           else
1864             {
1865               DEBUG ("\tParam default value: not set");
1866             }
1867 #endif
1868 
1869           g_ptr_array_add (param_specs, tp_value_array_build (4,
1870                 G_TYPE_STRING, param.name,
1871                 G_TYPE_UINT, param.flags,
1872                 G_TYPE_STRING, param.dbus_signature,
1873                 G_TYPE_VALUE, &param.default_value,
1874                 G_TYPE_INVALID));
1875 
1876           if (G_IS_VALUE (&param.default_value))
1877             g_value_unset (&param.default_value);
1878 
1879           g_free (value);
1880           g_free (def);
1881           g_strfreev (strv);
1882         }
1883     }
1884 
1885   immutables = tp_asv_new (
1886       TP_PROP_PROTOCOL_PARAMETERS, TP_ARRAY_TYPE_PARAM_SPEC_LIST, param_specs,
1887       NULL);
1888 
1889   tp_asv_take_boxed (immutables, TP_PROP_PROTOCOL_INTERFACES, G_TYPE_STRV,
1890       g_key_file_get_string_list (file, group, "Interfaces", NULL, NULL));
1891   tp_asv_take_boxed (immutables, TP_PROP_PROTOCOL_CONNECTION_INTERFACES,
1892       G_TYPE_STRV,
1893       g_key_file_get_string_list (file, group, "ConnectionInterfaces",
1894         NULL, NULL));
1895   tp_asv_take_string (immutables, TP_PROP_PROTOCOL_VCARD_FIELD,
1896       replace_null_with_empty (
1897         g_key_file_get_string (file, group, "VCardField", NULL)));
1898   tp_asv_take_string (immutables, TP_PROP_PROTOCOL_ENGLISH_NAME,
1899       replace_null_with_empty (
1900         g_key_file_get_string (file, group, "EnglishName", NULL)));
1901   tp_asv_take_string (immutables, TP_PROP_PROTOCOL_ICON,
1902       replace_null_with_empty (
1903         g_key_file_get_string (file, group, "Icon", NULL)));
1904   tp_asv_take_boxed (immutables, TP_PROP_PROTOCOL_AUTHENTICATION_TYPES,
1905       G_TYPE_STRV, g_key_file_get_string_list (file, group,
1906           "AuthenticationTypes", NULL, NULL));
1907 
1908   /* Avatars */
1909   tp_asv_take_boxed (immutables,
1910       TP_PROP_PROTOCOL_INTERFACE_AVATARS_SUPPORTED_AVATAR_MIME_TYPES,
1911       G_TYPE_STRV,
1912       g_key_file_get_string_list (file, group, "SupportedAvatarMIMETypes",
1913         NULL, NULL));
1914   tp_asv_set_uint32 (immutables,
1915       TP_PROP_PROTOCOL_INTERFACE_AVATARS_MINIMUM_AVATAR_HEIGHT,
1916       g_key_file_get_uint64 (file, group, "MinimumAvatarHeight", NULL));
1917   tp_asv_set_uint32 (immutables,
1918       TP_PROP_PROTOCOL_INTERFACE_AVATARS_MINIMUM_AVATAR_WIDTH,
1919       g_key_file_get_uint64 (file, group, "MinimumAvatarWidth", NULL));
1920   tp_asv_set_uint32 (immutables,
1921       TP_PROP_PROTOCOL_INTERFACE_AVATARS_RECOMMENDED_AVATAR_HEIGHT,
1922       g_key_file_get_uint64 (file, group, "RecommendedAvatarHeight", NULL));
1923   tp_asv_set_uint32 (immutables,
1924       TP_PROP_PROTOCOL_INTERFACE_AVATARS_RECOMMENDED_AVATAR_WIDTH,
1925       g_key_file_get_uint64 (file, group, "RecommendedAvatarWidth", NULL));
1926   tp_asv_set_uint32 (immutables,
1927       TP_PROP_PROTOCOL_INTERFACE_AVATARS_MAXIMUM_AVATAR_HEIGHT,
1928       g_key_file_get_uint64 (file, group, "MaximumAvatarHeight", NULL));
1929   tp_asv_set_uint32 (immutables,
1930       TP_PROP_PROTOCOL_INTERFACE_AVATARS_MAXIMUM_AVATAR_WIDTH,
1931       g_key_file_get_uint64 (file, group, "MaximumAvatarWidth", NULL));
1932   tp_asv_set_uint32 (immutables,
1933       TP_PROP_PROTOCOL_INTERFACE_AVATARS_MAXIMUM_AVATAR_BYTES,
1934       g_key_file_get_uint64 (file, group, "MaximumAvatarBytes", NULL));
1935 
1936   rccs = g_ptr_array_new ();
1937 
1938   rcc_groups = g_key_file_get_string_list (file, group,
1939       "RequestableChannelClasses", NULL, NULL);
1940 
1941   if (rcc_groups != NULL)
1942     {
1943       for (rcc_group = rcc_groups; *rcc_group != NULL; rcc_group++)
1944         g_ptr_array_add (rccs,
1945             _tp_protocol_parse_rcc (cm_debug_name, name, file, *rcc_group));
1946     }
1947 
1948   g_strfreev (rcc_groups);
1949 
1950   /* Statuses */
1951   status_specs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1952       (GDestroyNotify) tp_value_array_free);
1953 
1954   for (key = keys; key != NULL && *key != NULL; key++)
1955     {
1956       if (g_str_has_prefix (*key, "status-"))
1957         {
1958           GValueArray *ubb;
1959           gint64 type;
1960           gboolean on_self = FALSE, has_message = FALSE;
1961           gchar *value, *endptr;
1962           gchar **strv, **iter;
1963 
1964           if (!tp_strdiff (*key, "status-"))
1965             {
1966               DEBUG ("'status-' is not a valid status");
1967               continue;
1968             }
1969 
1970           value = g_key_file_get_value (file, group, *key, NULL);
1971           strv = g_strsplit (value, " ", 0);
1972           g_free (value);
1973 
1974           type = g_ascii_strtoll (strv[0], &endptr, 10);
1975 
1976           if (endptr <= strv[0] || *endptr != '\0')
1977             {
1978               DEBUG ("invalid (non-numeric?) status type %s", strv[0]);
1979               goto next_status;
1980             }
1981 
1982           if (type == TP_CONNECTION_PRESENCE_TYPE_UNSET ||
1983               type < 0 || type >= TP_NUM_CONNECTION_PRESENCE_TYPES)
1984             {
1985               DEBUG ("presence type out of range: %" G_GINT64_FORMAT,
1986                   type);
1987               goto next_status;
1988             }
1989 
1990           for (iter = strv + 1; *iter != NULL; iter++)
1991             {
1992               if (!tp_strdiff (*iter, "settable"))
1993                 on_self = TRUE;
1994               else if (!tp_strdiff (*iter, "message"))
1995                 has_message = TRUE;
1996               else
1997                 DEBUG ("unknown status modifier '%s'", *iter);
1998             }
1999 
2000           ubb = tp_value_array_build (3,
2001               G_TYPE_UINT, (guint) type,
2002               G_TYPE_BOOLEAN, on_self,
2003               G_TYPE_BOOLEAN, has_message,
2004               G_TYPE_INVALID);
2005 
2006           /* strlen ("status-") == 7 */
2007           g_hash_table_insert (status_specs, g_strdup (*key + 7),
2008               ubb);
2009           DEBUG ("Status '%s': type %u%s%s", *key + 7, (guint) type,
2010               on_self ? ", can set on self" : "",
2011               has_message ? ", has message" : "");
2012 
2013 next_status:
2014           g_strfreev (strv);
2015         }
2016     }
2017 
2018   if (g_hash_table_size (status_specs) > 0)
2019     tp_asv_take_boxed (immutables,
2020         TP_PROP_PROTOCOL_INTERFACE_PRESENCE_STATUSES,
2021         TP_HASH_TYPE_SIMPLE_STATUS_SPEC_MAP, status_specs);
2022   else
2023     g_hash_table_unref (status_specs);
2024 
2025   g_strfreev (keys);
2026 
2027   tp_asv_take_boxed (immutables, TP_PROP_PROTOCOL_REQUESTABLE_CHANNEL_CLASSES,
2028       TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs);
2029 
2030   if (protocol_name != NULL)
2031     *protocol_name = g_strdup (name);
2032 
2033   return immutables;
2034 }
2035 
2036 /**
2037  * tp_protocol_get_avatar_requirements:
2038  * @self: a #TpProtocol
2039  *
2040  * Return the #TpProtocol:avatar-requirements property
2041  *
2042  * Returns: (transfer none): the value of #TpProtocol:avatar-requirements
2043  *
2044  * Since: 0.15.6
2045  */
2046 TpAvatarRequirements *
tp_protocol_get_avatar_requirements(TpProtocol * self)2047 tp_protocol_get_avatar_requirements (TpProtocol *self)
2048 {
2049   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
2050 
2051   return self->priv->avatar_req;
2052 }
2053 
2054 /**
2055  * tp_protocol_get_cm_name:
2056  * @self: a #TpProtocol
2057  *
2058  * Return the #TpProtocol:cm-name property.
2059  *
2060  * Returns: the value of #TpProtocol:cm-name
2061  *
2062  * Since: 0.19.1
2063  */
2064 const gchar *
tp_protocol_get_cm_name(TpProtocol * self)2065 tp_protocol_get_cm_name (TpProtocol *self)
2066 {
2067   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
2068 
2069   return self->priv->cm_name;
2070 }
2071 
2072 /*
2073  * Handle the result from a tp_cli_protocol_* function that
2074  * returns one string. user_data is a #GTask.
2075  */
2076 static void
tp_protocol_async_string_cb(TpProxy * proxy,const gchar * normalized,const GError * error,gpointer user_data,GObject * weak_object G_GNUC_UNUSED)2077 tp_protocol_async_string_cb (TpProxy *proxy,
2078     const gchar *normalized,
2079     const GError *error,
2080     gpointer user_data,
2081     GObject *weak_object G_GNUC_UNUSED)
2082 {
2083   if (error == NULL)
2084     g_task_return_pointer (user_data, g_strdup (normalized), g_free);
2085   else
2086     g_task_return_error (user_data, g_error_copy (error));
2087 }
2088 
2089 /**
2090  * tp_protocol_normalize_contact_async:
2091  * @self: a protocol
2092  * @contact: a contact identifier, possibly invalid
2093  * @cancellable: (allow-none): may be used to cancel the async request
2094  * @callback: (scope async): a callback to call when
2095  *  the request is satisfied
2096  * @user_data: (closure) (allow-none): data to pass to @callback
2097  *
2098  * Perform best-effort offline contact normalization. This does syntactic
2099  * normalization (e.g. transforming case-insensitive text to lower-case),
2100  * but does not query servers or anything similar.
2101  *
2102  * Since: 0.23.1
2103  */
2104 void
tp_protocol_normalize_contact_async(TpProtocol * self,const gchar * contact,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2105 tp_protocol_normalize_contact_async (TpProtocol *self,
2106     const gchar *contact,
2107     GCancellable *cancellable,
2108     GAsyncReadyCallback callback,
2109     gpointer user_data)
2110 {
2111   GTask *task;
2112 
2113   g_return_if_fail (TP_IS_PROTOCOL (self));
2114   g_return_if_fail (contact != NULL);
2115   /* this makes no sense to call for its side-effects */
2116   g_return_if_fail (callback != NULL);
2117 
2118   task = g_task_new (self, cancellable, callback, user_data);
2119   g_task_set_source_tag (task, tp_protocol_normalize_contact_async);
2120 
2121   tp_cli_protocol_call_normalize_contact (self, -1, contact,
2122       tp_protocol_async_string_cb, task, g_object_unref, NULL);
2123 }
2124 
2125 /**
2126  * tp_protocol_normalize_contact_finish:
2127  * @self: a protocol
2128  * @result: a #GAsyncResult
2129  * @error: a #GError to fill
2130  *
2131  * Interpret the result of tp_protocol_normalize_contact_async().
2132  *
2133  * Returns: (transfer full): the normalized form of @contact,
2134  *  or %NULL on error
2135  * Since: 0.23.1
2136  */
2137 gchar *
tp_protocol_normalize_contact_finish(TpProtocol * self,GAsyncResult * result,GError ** error)2138 tp_protocol_normalize_contact_finish (TpProtocol *self,
2139     GAsyncResult *result,
2140     GError **error)
2141 {
2142   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
2143   g_return_val_if_fail (g_async_result_is_tagged (result,
2144         tp_protocol_normalize_contact_async), NULL);
2145 
2146   return g_task_propagate_pointer (G_TASK (result), error);
2147 }
2148 
2149 /**
2150  * tp_protocol_identify_account_async:
2151  * @self: a protocol
2152  * @vardict: the account parameters as a #GVariant of
2153  *  type %G_VARIANT_TYPE_VARDICT. If it is floating, ownership will
2154  *  be taken, as if via g_variant_ref_sink().
2155  * @cancellable: (allow-none): may be used to cancel the async request
2156  * @callback: (scope async): a callback to call when
2157  *  the request is satisfied
2158  * @user_data: (closure) (allow-none): data to pass to @callback
2159  *
2160  * Return a string that could identify the account with the given
2161  * parameters. In most protocols that string is a normalized 'account'
2162  * parameter, but some protocols have more complex requirements;
2163  * for instance, on IRC, the 'account' (nickname) is insufficient,
2164  * and must be combined with a server or network name.
2165  *
2166  * Since: 0.23.1
2167  */
2168 void
tp_protocol_identify_account_async(TpProtocol * self,GVariant * vardict,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2169 tp_protocol_identify_account_async (TpProtocol *self,
2170     GVariant *vardict,
2171     GCancellable *cancellable,
2172     GAsyncReadyCallback callback,
2173     gpointer user_data)
2174 {
2175   GTask *task;
2176   GHashTable *asv;
2177 
2178   g_return_if_fail (TP_IS_PROTOCOL (self));
2179   g_return_if_fail (vardict != NULL);
2180   g_return_if_fail (g_variant_is_of_type (vardict, G_VARIANT_TYPE_VARDICT));
2181   /* this makes no sense to call for its side-effects */
2182   g_return_if_fail (callback != NULL);
2183 
2184   task = g_task_new (self, cancellable, callback, user_data);
2185   g_task_set_source_tag (task, tp_protocol_identify_account_async);
2186   g_variant_ref_sink (vardict);
2187   asv = _tp_asv_from_vardict (vardict);
2188   tp_cli_protocol_call_identify_account (self, -1, asv,
2189       tp_protocol_async_string_cb, task, g_object_unref, NULL);
2190   g_hash_table_unref (asv);
2191   g_variant_unref (vardict);
2192 }
2193 
2194 /**
2195  * tp_protocol_identify_account_finish:
2196  * @self: a protocol
2197  * @result: a #GAsyncResult
2198  * @error: a #GError to fill
2199  *
2200  * Interpret the result of tp_protocol_identify_account_async().
2201  *
2202  * Returns: (transfer full): a string identifying the account,
2203  *  or %NULL on error
2204  * Since: 0.23.1
2205  */
2206 gchar *
tp_protocol_identify_account_finish(TpProtocol * self,GAsyncResult * result,GError ** error)2207 tp_protocol_identify_account_finish (TpProtocol *self,
2208     GAsyncResult *result,
2209     GError **error)
2210 {
2211   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
2212   g_return_val_if_fail (g_async_result_is_tagged (result,
2213         tp_protocol_identify_account_async), NULL);
2214 
2215   return g_task_propagate_pointer (G_TASK (result), error);
2216 }
2217 
2218 /**
2219  * tp_protocol_normalize_contact_uri_async:
2220  * @self: a protocol
2221  * @uri: a contact URI, possibly invalid
2222  * @cancellable: (allow-none): may be used to cancel the async request
2223  * @callback: (scope async): a callback to call when the request is satisfied
2224  * @user_data: (closure) (allow-none): data to pass to @callback
2225  *
2226  * Perform best-effort offline contact normalization, for a contact in
2227  * the form of a URI. This method will fail if the URI is not in a
2228  * scheme supported by this protocol or connection manager.
2229  *
2230  * Since: 0.23.1
2231  */
2232 void
tp_protocol_normalize_contact_uri_async(TpProtocol * self,const gchar * uri,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2233 tp_protocol_normalize_contact_uri_async (TpProtocol *self,
2234     const gchar *uri,
2235     GCancellable *cancellable,
2236     GAsyncReadyCallback callback,
2237     gpointer user_data)
2238 {
2239   GTask *task;
2240 
2241   g_return_if_fail (TP_IS_PROTOCOL (self));
2242   g_return_if_fail (uri != NULL);
2243   /* this makes no sense to call for its side-effects */
2244   g_return_if_fail (callback != NULL);
2245 
2246   task = g_task_new (self, cancellable, callback, user_data);
2247   g_task_set_source_tag (task, tp_protocol_normalize_contact_uri_async);
2248 
2249   tp_cli_protocol_interface_addressing_call_normalize_contact_uri (self, -1,
2250       uri, tp_protocol_async_string_cb, task, g_object_unref, NULL);
2251 }
2252 
2253 /**
2254  * tp_protocol_normalize_contact_uri_finish:
2255  * @self: a protocol
2256  * @result: a #GAsyncResult
2257  * @error: a #GError to fill
2258  *
2259  * Interpret the result of tp_protocol_normalize_contact_uri_async().
2260  *
2261  * Returns: (transfer full): the normalized form of @uri,
2262  *  or %NULL on error
2263  * Since: 0.23.1
2264  */
2265 gchar *
tp_protocol_normalize_contact_uri_finish(TpProtocol * self,GAsyncResult * result,GError ** error)2266 tp_protocol_normalize_contact_uri_finish (TpProtocol *self,
2267     GAsyncResult *result,
2268     GError **error)
2269 {
2270   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
2271   g_return_val_if_fail (g_async_result_is_tagged (result,
2272         tp_protocol_normalize_contact_uri_async), NULL);
2273 
2274   return g_task_propagate_pointer (G_TASK (result), error);
2275 }
2276 
2277 /**
2278  * tp_protocol_normalize_vcard_address_async:
2279  * @self: a protocol
2280  * @field: a vCard field
2281  * @value: an address that is a value of @field
2282  * @cancellable: (allow-none): may be used to cancel the async request
2283  * @callback: (scope async): a callback to call when the request is satisfied
2284  * @user_data: (closure) (allow-none): data to pass to @callback
2285  *
2286  * Perform best-effort offline contact normalization, for a contact in
2287  * the form of a vCard field. This method will fail if the vCard field
2288  * is not supported by this protocol or connection manager.
2289  *
2290  * Since: 0.23.1
2291  */
2292 void
tp_protocol_normalize_vcard_address_async(TpProtocol * self,const gchar * field,const gchar * value,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2293 tp_protocol_normalize_vcard_address_async (TpProtocol *self,
2294     const gchar *field,
2295     const gchar *value,
2296     GCancellable *cancellable,
2297     GAsyncReadyCallback callback,
2298     gpointer user_data)
2299 {
2300   GTask *task;
2301 
2302   g_return_if_fail (TP_IS_PROTOCOL (self));
2303   g_return_if_fail (!tp_str_empty (field));
2304   g_return_if_fail (value != NULL);
2305   /* this makes no sense to call for its side-effects */
2306   g_return_if_fail (callback != NULL);
2307 
2308   task = g_task_new (self, cancellable, callback, user_data);
2309   g_task_set_source_tag (task, tp_protocol_normalize_vcard_address_async);
2310 
2311   tp_cli_protocol_interface_addressing_call_normalize_vcard_address (self, -1,
2312       field, value, tp_protocol_async_string_cb, task, g_object_unref, NULL);
2313 }
2314 
2315 /**
2316  * tp_protocol_normalize_vcard_address_finish:
2317  * @self: a protocol
2318  * @result: a #GAsyncResult
2319  * @error: a #GError to fill
2320  *
2321  * Interpret the result of tp_protocol_normalize_vcard_address_async().
2322  *
2323  * Returns: (transfer full): the normalized form of @value,
2324  *  or %NULL on error
2325  * Since: 0.23.1
2326  */
2327 gchar *
tp_protocol_normalize_vcard_address_finish(TpProtocol * self,GAsyncResult * result,GError ** error)2328 tp_protocol_normalize_vcard_address_finish (TpProtocol *self,
2329     GAsyncResult *result,
2330     GError **error)
2331 {
2332   g_return_val_if_fail (g_task_is_valid (result, self), NULL);
2333   g_return_val_if_fail (g_async_result_is_tagged (result,
2334         tp_protocol_normalize_vcard_address_async), NULL);
2335 
2336   return g_task_propagate_pointer (G_TASK (result), error);
2337 }
2338 
2339 /**
2340  * tp_protocol_get_addressable_vcard_fields:
2341  * @self: a protocol object
2342  *
2343  * <!-- -->
2344  *
2345  * Returns: (transfer none): the value of #TpProtocol:addressable-vcard-fields
2346  * Since: 0.23.1
2347  */
2348 const gchar * const *
tp_protocol_get_addressable_vcard_fields(TpProtocol * self)2349 tp_protocol_get_addressable_vcard_fields (TpProtocol *self)
2350 {
2351   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
2352   return (const gchar * const *) self->priv->addressable_vcard_fields;
2353 }
2354 
2355 /**
2356  * tp_protocol_get_addressable_uri_schemes:
2357  * @self: a protocol object
2358  *
2359  * <!-- -->
2360  *
2361  * Returns: (transfer none): the value of #TpProtocol:addressable-uri-schemes
2362  * Since: 0.23.1
2363  */
2364 const gchar * const *
tp_protocol_get_addressable_uri_schemes(TpProtocol * self)2365 tp_protocol_get_addressable_uri_schemes (TpProtocol *self)
2366 {
2367   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
2368   return (const gchar * const *) self->priv->addressable_uri_schemes;
2369 }
2370 
2371 /**
2372  * tp_protocol_dup_presence_statuses:
2373  * @self: a protocol object
2374  *
2375  * Return the presence statuses that might be supported by connections
2376  * to this protocol.
2377  *
2378  * It is possible that some of these statuses will not actually be supported
2379  * by a connection: for instance, an XMPP connection manager would
2380  * include "hidden" in this list, even though not all XMPP servers allow
2381  * users to be online-but-hidden.
2382  *
2383  * Returns: (transfer full) (element-type TelepathyGLib.PresenceStatusSpec): a
2384  *  list of statuses, or %NULL if unknown
2385  */
2386 GList *
tp_protocol_dup_presence_statuses(TpProtocol * self)2387 tp_protocol_dup_presence_statuses (TpProtocol *self)
2388 {
2389   GHashTableIter iter;
2390   gpointer k, v;
2391   GList *l = NULL;
2392 
2393   g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL);
2394 
2395   if (self->priv->presence_statuses == NULL)
2396     return NULL;
2397 
2398   g_hash_table_iter_init (&iter, self->priv->presence_statuses);
2399 
2400   while (g_hash_table_iter_next (&iter, &k, &v))
2401     {
2402       guint type;
2403       gboolean on_self, message;
2404 
2405       tp_value_array_unpack (v, 3,
2406           &type,
2407           &on_self,
2408           &message);
2409 
2410       l = g_list_prepend (l, tp_presence_status_spec_new (k, type,
2411             on_self, message));
2412     }
2413 
2414   return g_list_reverse (l);
2415 }
2416 
2417 /**
2418  * tp_protocol_dup_immutable_properties:
2419  * @self: a #TpProtocol object
2420  *
2421  * Return the #TpProtocol:protocol-properties-vardict property.
2422  *
2423  * Returns: (transfer full): the value of
2424  * #TpProtocol:protocol-properties-vardict
2425  * Since: 0.23.3
2426  */
2427 GVariant *
tp_protocol_dup_immutable_properties(TpProtocol * self)2428 tp_protocol_dup_immutable_properties (TpProtocol *self)
2429 {
2430   return _tp_asv_to_vardict (self->priv->protocol_properties);
2431 }
2432