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, ¶m->name,
208 1, ¶m->flags,
209 2, ¶m->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 (¶m->default_value,
229 G_VALUE_TYPE (tmp));
230 g_value_copy (tmp, ¶m->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 (¶m->default_value))
371 g_value_unset (¶m->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@example.com, sips:bob@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 ¶m.default_value);
1845
1846 if (value != NULL && parse_default_value (¶m.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 (¶m.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, ¶m.default_value,
1874 G_TYPE_INVALID));
1875
1876 if (G_IS_VALUE (¶m.default_value))
1877 g_value_unset (¶m.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