1 /*
2  * /dev/null as a text channel
3  *
4  * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
5  * Copyright (C) 2008 Nokia Corporation
6  *
7  * Copying and distribution of this file, with or without modification,
8  * are permitted in any medium without royalty provided the copyright
9  * notice and this notice are preserved.
10  */
11 
12 #include "config.h"
13 
14 #include "textchan-null.h"
15 
16 #include <telepathy-glib/base-connection.h>
17 #include <telepathy-glib/channel-iface.h>
18 #include <telepathy-glib/dbus.h>
19 #include <telepathy-glib/dbus-properties-mixin.h>
20 #include <telepathy-glib/interfaces.h>
21 #include <telepathy-glib/svc-channel.h>
22 #include <telepathy-glib/svc-generic.h>
23 
24 /* This is for text-mixin unit tests, others should be using ExampleEcho2Channel
25  * which uses newer TpMessageMixin */
26 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
27 
28 static void text_iface_init (gpointer iface, gpointer data);
29 static void channel_iface_init (gpointer iface, gpointer data);
30 
31 G_DEFINE_TYPE_WITH_CODE (TpTestsTextChannelNull,
32     tp_tests_text_channel_null,
33     G_TYPE_OBJECT,
34     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
35     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init);
36     G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL))
37 
38 G_DEFINE_TYPE_WITH_CODE (TpTestsPropsTextChannel,
39     tp_tests_props_text_channel,
40     TP_TESTS_TYPE_TEXT_CHANNEL_NULL,
41     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
42       tp_dbus_properties_mixin_iface_init))
43 
44 G_DEFINE_TYPE_WITH_CODE (TpTestsPropsGroupTextChannel,
45     tp_tests_props_group_text_channel,
46     TP_TESTS_TYPE_PROPS_TEXT_CHANNEL,
47     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
48         tp_group_mixin_iface_init))
49 
50 static const char *tp_tests_text_channel_null_interfaces[] = { NULL };
51 
52 /* type definition stuff */
53 
54 enum
55 {
56   PROP_OBJECT_PATH = 1,
57   PROP_CHANNEL_TYPE,
58   PROP_HANDLE_TYPE,
59   PROP_HANDLE,
60   PROP_TARGET_ID,
61   PROP_CONNECTION,
62   PROP_INTERFACES,
63   PROP_REQUESTED,
64   PROP_INITIATOR_HANDLE,
65   PROP_INITIATOR_ID,
66   N_PROPS
67 };
68 
69 struct _TpTestsTextChannelNullPrivate
70 {
71   TpBaseConnection *conn;
72   gchar *object_path;
73   TpHandle handle;
74 
75   unsigned closed:1;
76   unsigned disposed:1;
77 };
78 
79 static void
tp_tests_text_channel_null_init(TpTestsTextChannelNull * self)80 tp_tests_text_channel_null_init (TpTestsTextChannelNull *self)
81 {
82   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
83       TP_TESTS_TYPE_TEXT_CHANNEL_NULL, TpTestsTextChannelNullPrivate);
84 }
85 
86 static void
tp_tests_props_text_channel_init(TpTestsPropsTextChannel * self)87 tp_tests_props_text_channel_init (TpTestsPropsTextChannel *self)
88 {
89   self->dbus_property_interfaces_retrieved = g_hash_table_new (NULL, NULL);
90 }
91 
92 static GObject *
constructor(GType type,guint n_props,GObjectConstructParam * props)93 constructor (GType type,
94              guint n_props,
95              GObjectConstructParam *props)
96 {
97   GObject *object =
98       G_OBJECT_CLASS (tp_tests_text_channel_null_parent_class)->constructor (type,
99           n_props, props);
100   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object);
101   TpHandleRepoIface *contact_repo = tp_base_connection_get_handles
102       (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
103 
104   tp_handle_ref (contact_repo, self->priv->handle);
105 
106   tp_dbus_daemon_register_object (
107       tp_base_connection_get_dbus_daemon (self->priv->conn),
108       self->priv->object_path, self);
109 
110   tp_text_mixin_init (object, G_STRUCT_OFFSET (TpTestsTextChannelNull, text),
111       contact_repo);
112 
113   tp_text_mixin_set_message_types (object,
114       TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
115       TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
116       TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
117       G_MAXUINT);
118 
119   return object;
120 }
121 
122 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)123 get_property (GObject *object,
124               guint property_id,
125               GValue *value,
126               GParamSpec *pspec)
127 {
128   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object);
129 
130   switch (property_id)
131     {
132     case PROP_OBJECT_PATH:
133       g_value_set_string (value, self->priv->object_path);
134       break;
135     case PROP_CHANNEL_TYPE:
136       g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT);
137       break;
138     case PROP_HANDLE_TYPE:
139       g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
140       break;
141     case PROP_HANDLE:
142       g_value_set_uint (value, self->priv->handle);
143       break;
144     case PROP_TARGET_ID:
145         {
146           TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
147               self->priv->conn, TP_HANDLE_TYPE_CONTACT);
148 
149           g_value_set_string (value,
150               tp_handle_inspect (contact_repo, self->priv->handle));
151         }
152       break;
153     case PROP_REQUESTED:
154       g_value_set_boolean (value, TRUE);
155       break;
156     case PROP_INITIATOR_HANDLE:
157       g_value_set_uint (value, tp_base_connection_get_self_handle (
158             self->priv->conn));
159       break;
160     case PROP_INITIATOR_ID:
161         {
162           TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
163               self->priv->conn, TP_HANDLE_TYPE_CONTACT);
164 
165           g_value_set_string (value,
166               tp_handle_inspect (contact_repo,
167                 tp_base_connection_get_self_handle (self->priv->conn)));
168         }
169       break;
170     case PROP_INTERFACES:
171       g_value_set_boxed (value, tp_tests_text_channel_null_interfaces);
172       break;
173     case PROP_CONNECTION:
174       g_value_set_object (value, self->priv->conn);
175       break;
176     default:
177       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
178       break;
179     }
180 }
181 
182 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)183 set_property (GObject *object,
184               guint property_id,
185               const GValue *value,
186               GParamSpec *pspec)
187 {
188   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object);
189 
190   switch (property_id)
191     {
192     case PROP_OBJECT_PATH:
193       g_free (self->priv->object_path);
194       self->priv->object_path = g_value_dup_string (value);
195       break;
196     case PROP_HANDLE:
197       /* we don't ref it here because we don't necessarily have access to the
198        * contact repo yet - instead we ref it in the constructor.
199        */
200       self->priv->handle = g_value_get_uint (value);
201       break;
202     case PROP_HANDLE_TYPE:
203     case PROP_CHANNEL_TYPE:
204       /* these properties are writable in the interface, but not actually
205        * meaningfully changeable on this channel, so we do nothing */
206       break;
207     case PROP_CONNECTION:
208       self->priv->conn = g_value_get_object (value);
209       break;
210     default:
211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
212       break;
213     }
214 }
215 
216 void
tp_tests_text_channel_null_close(TpTestsTextChannelNull * self)217 tp_tests_text_channel_null_close (TpTestsTextChannelNull *self)
218 {
219   if (!self->priv->closed)
220     {
221       self->priv->closed = TRUE;
222       tp_svc_channel_emit_closed (self);
223       tp_dbus_daemon_unregister_object (
224           tp_base_connection_get_dbus_daemon (self->priv->conn), self);
225     }
226 }
227 
228 static void
dispose(GObject * object)229 dispose (GObject *object)
230 {
231   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object);
232 
233   if (self->priv->disposed)
234     return;
235 
236   self->priv->disposed = TRUE;
237   tp_tests_text_channel_null_close (self);
238 
239   ((GObjectClass *) tp_tests_text_channel_null_parent_class)->dispose (object);
240 }
241 
242 static void
finalize(GObject * object)243 finalize (GObject *object)
244 {
245   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object);
246   TpHandleRepoIface *contact_handles = tp_base_connection_get_handles
247       (self->priv->conn, TP_HANDLE_TYPE_CONTACT);
248 
249   tp_handle_unref (contact_handles, self->priv->handle);
250   g_free (self->priv->object_path);
251 
252   tp_text_mixin_finalize (object);
253 
254   ((GObjectClass *) tp_tests_text_channel_null_parent_class)->finalize (object);
255 }
256 
257 static void
tp_tests_text_channel_null_class_init(TpTestsTextChannelNullClass * klass)258 tp_tests_text_channel_null_class_init (TpTestsTextChannelNullClass *klass)
259 {
260   GObjectClass *object_class = (GObjectClass *) klass;
261   GParamSpec *param_spec;
262 
263   g_type_class_add_private (klass, sizeof (TpTestsTextChannelNullPrivate));
264 
265   object_class->constructor = constructor;
266   object_class->set_property = set_property;
267   object_class->get_property = get_property;
268   object_class->dispose = dispose;
269   object_class->finalize = finalize;
270 
271   g_object_class_override_property (object_class, PROP_OBJECT_PATH,
272       "object-path");
273   g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
274       "channel-type");
275   g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
276       "handle-type");
277   g_object_class_override_property (object_class, PROP_HANDLE, "handle");
278 
279   param_spec = g_param_spec_object ("connection", "TpBaseConnection object",
280       "Connection object that owns this channel",
281       TP_TYPE_BASE_CONNECTION,
282       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
283   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
284 
285   param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
286       "Additional Channel.Interface.* interfaces",
287       G_TYPE_STRV,
288       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
289   g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
290 
291   param_spec = g_param_spec_string ("target-id", "Peer's ID",
292       "The string obtained by inspecting the target handle",
293       NULL,
294       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
295   g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
296 
297   param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle",
298       "The contact who initiated the channel",
299       0, G_MAXUINT32, 0,
300       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
301   g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE,
302       param_spec);
303 
304   param_spec = g_param_spec_string ("initiator-id", "Initiator's ID",
305       "The string obtained by inspecting the initiator-handle",
306       NULL,
307       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
308   g_object_class_install_property (object_class, PROP_INITIATOR_ID,
309       param_spec);
310 
311   param_spec = g_param_spec_boolean ("requested", "Requested?",
312       "True if this channel was requested by the local user",
313       FALSE,
314       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
315   g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
316 
317   tp_text_mixin_class_init (object_class,
318       G_STRUCT_OFFSET (TpTestsTextChannelNullClass, text_class));
319 }
320 
321 static void
tp_tests_props_text_channel_getter_gobject_properties(GObject * object,GQuark interface,GQuark name,GValue * value,gpointer getter_data)322 tp_tests_props_text_channel_getter_gobject_properties (GObject *object,
323     GQuark interface,
324     GQuark name,
325     GValue *value,
326     gpointer getter_data)
327 {
328   TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object);
329 
330   g_hash_table_insert (self->dbus_property_interfaces_retrieved,
331       GUINT_TO_POINTER (interface), GUINT_TO_POINTER (interface));
332 
333   tp_dbus_properties_mixin_getter_gobject_properties (object, interface, name,
334       value, getter_data);
335 }
336 
337 static void
props_finalize(GObject * object)338 props_finalize (GObject *object)
339 {
340   TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object);
341 
342   g_hash_table_unref (self->dbus_property_interfaces_retrieved);
343 
344   ((GObjectClass *) tp_tests_props_text_channel_parent_class)->finalize (object);
345 }
346 
347 static void
tp_tests_props_text_channel_class_init(TpTestsPropsTextChannelClass * klass)348 tp_tests_props_text_channel_class_init (TpTestsPropsTextChannelClass *klass)
349 {
350   GObjectClass *object_class = (GObjectClass *) klass;
351   static TpDBusPropertiesMixinPropImpl channel_props[] = {
352       { "TargetHandleType", "handle-type", NULL },
353       { "TargetHandle", "handle", NULL },
354       { "ChannelType", "channel-type", NULL },
355       { "Interfaces", "interfaces", NULL },
356       { "TargetID", "target-id", NULL },
357       { "Requested", "requested", NULL },
358       { "InitiatorHandle", "initiator-handle", NULL },
359       { "InitiatorID", "initiator-id", NULL },
360       { NULL }
361   };
362   static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
363       { TP_IFACE_CHANNEL,
364         tp_tests_props_text_channel_getter_gobject_properties,
365         NULL,
366         channel_props,
367       },
368       { NULL }
369   };
370 
371   object_class->finalize = props_finalize;
372 
373   klass->dbus_properties_class.interfaces = prop_interfaces;
374   tp_dbus_properties_mixin_class_init (object_class,
375       G_STRUCT_OFFSET (TpTestsPropsTextChannelClass, dbus_properties_class));
376 }
377 
378 static void
tp_tests_props_group_text_channel_init(TpTestsPropsGroupTextChannel * self)379 tp_tests_props_group_text_channel_init (TpTestsPropsGroupTextChannel *self)
380 {
381 }
382 
383 static void
group_constructed(GObject * self)384 group_constructed (GObject *self)
385 {
386   TpBaseConnection *conn = TP_TESTS_TEXT_CHANNEL_NULL (self)->priv->conn;
387   void (*chain_up) (GObject *) =
388     ((GObjectClass *) tp_tests_props_group_text_channel_parent_class)->constructed;
389 
390   if (chain_up != NULL)
391     chain_up (self);
392 
393   tp_group_mixin_init (self,
394       G_STRUCT_OFFSET (TpTestsPropsGroupTextChannel, group),
395       tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT),
396       tp_base_connection_get_self_handle (conn));
397   tp_group_mixin_change_flags (self, TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0);
398 }
399 
400 static void
group_finalize(GObject * self)401 group_finalize (GObject *self)
402 {
403   tp_group_mixin_finalize (self);
404 
405   ((GObjectClass *) tp_tests_props_group_text_channel_parent_class)->finalize (self);
406 }
407 
408 static gboolean
dummy_add_remove_member(GObject * obj,TpHandle handle,const gchar * message,GError ** error)409 dummy_add_remove_member (GObject *obj,
410     TpHandle handle,
411     const gchar *message,
412     GError **error)
413 {
414   return TRUE;
415 }
416 
417 static void
group_iface_props_getter(GObject * object,GQuark interface,GQuark name,GValue * value,gpointer getter_data)418 group_iface_props_getter (GObject *object,
419     GQuark interface,
420     GQuark name,
421     GValue *value,
422     gpointer getter_data)
423 {
424   TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object);
425 
426   g_hash_table_insert (self->dbus_property_interfaces_retrieved,
427       GUINT_TO_POINTER (interface), GUINT_TO_POINTER (interface));
428 
429   tp_group_mixin_get_dbus_property (object, interface, name, value, getter_data);
430 }
431 
432 static void
tp_tests_props_group_text_channel_class_init(TpTestsPropsGroupTextChannelClass * klass)433 tp_tests_props_group_text_channel_class_init (TpTestsPropsGroupTextChannelClass *klass)
434 {
435   GObjectClass *object_class = (GObjectClass *) klass;
436   static TpDBusPropertiesMixinPropImpl group_props[] = {
437       { "GroupFlags", NULL, NULL },
438       { "HandleOwners", NULL, NULL },
439       { "LocalPendingMembers", NULL, NULL },
440       { "Members", NULL, NULL },
441       { "RemotePendingMembers", NULL, NULL },
442       { "SelfHandle", NULL, NULL },
443       { NULL }
444   };
445 
446   object_class->constructed = group_constructed;
447   object_class->finalize = group_finalize;
448 
449   tp_group_mixin_class_init (object_class,
450       G_STRUCT_OFFSET (TpTestsPropsGroupTextChannelClass, group_class),
451       dummy_add_remove_member,
452       dummy_add_remove_member);
453   tp_dbus_properties_mixin_implement_interface (object_class,
454       TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP, group_iface_props_getter, NULL,
455       group_props);
456 }
457 
458 static void
channel_close(TpSvcChannel * iface,DBusGMethodInvocation * context)459 channel_close (TpSvcChannel *iface,
460                DBusGMethodInvocation *context)
461 {
462   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface);
463 
464   tp_tests_text_channel_null_close (self);
465   tp_svc_channel_return_from_close (context);
466 }
467 
468 static void
channel_get_channel_type(TpSvcChannel * iface,DBusGMethodInvocation * context)469 channel_get_channel_type (TpSvcChannel *iface,
470                           DBusGMethodInvocation *context)
471 {
472   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface);
473 
474   self->get_channel_type_called++;
475 
476   tp_svc_channel_return_from_get_channel_type (context,
477       TP_IFACE_CHANNEL_TYPE_TEXT);
478 }
479 
480 static void
channel_get_handle(TpSvcChannel * iface,DBusGMethodInvocation * context)481 channel_get_handle (TpSvcChannel *iface,
482                     DBusGMethodInvocation *context)
483 {
484   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface);
485 
486   self->get_handle_called++;
487 
488   tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
489       self->priv->handle);
490 }
491 
492 static void
channel_get_interfaces(TpSvcChannel * iface,DBusGMethodInvocation * context)493 channel_get_interfaces (TpSvcChannel *iface,
494                         DBusGMethodInvocation *context)
495 {
496   TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface);
497 
498   self->get_interfaces_called++;
499 
500   tp_svc_channel_return_from_get_interfaces (context,
501       tp_tests_text_channel_null_interfaces);
502 }
503 
504 static void
channel_iface_init(gpointer iface,gpointer data)505 channel_iface_init (gpointer iface,
506                     gpointer data)
507 {
508   TpSvcChannelClass *klass = iface;
509 
510 #define IMPLEMENT(x) tp_svc_channel_implement_##x (klass, channel_##x)
511   IMPLEMENT (close);
512   IMPLEMENT (get_channel_type);
513   IMPLEMENT (get_handle);
514   IMPLEMENT (get_interfaces);
515 #undef IMPLEMENT
516 }
517 
518 static void
text_send(TpSvcChannelTypeText * iface,guint type,const gchar * text,DBusGMethodInvocation * context)519 text_send (TpSvcChannelTypeText *iface,
520            guint type,
521            const gchar *text,
522            DBusGMethodInvocation *context)
523 {
524   /* silently swallow the message */
525   tp_svc_channel_type_text_return_from_send (context);
526 }
527 
528 static void
text_iface_init(gpointer iface,gpointer data)529 text_iface_init (gpointer iface,
530                  gpointer data)
531 {
532   TpSvcChannelTypeTextClass *klass = iface;
533 
534   tp_text_mixin_iface_init (iface, data);
535 #define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (klass, text_##x)
536   IMPLEMENT (send);
537 #undef IMPLEMENT
538 }
539 
540 GHashTable *
tp_tests_text_channel_get_props(TpTestsTextChannelNull * self)541 tp_tests_text_channel_get_props (TpTestsTextChannelNull *self)
542 {
543   GHashTable *props;
544   TpHandleType handle_type;
545   TpHandle handle;
546   gchar *target_id;
547   gboolean requested;
548   TpHandle initiator_handle;
549   gchar *initiator_id;
550   GStrv interfaces;
551 
552   g_object_get (self,
553       "handle-type", &handle_type,
554       "handle", &handle,
555       "target-id", &target_id,
556       "requested", &requested,
557       "initiator-handle", &initiator_handle,
558       "initiator-id", &initiator_id,
559       "interfaces", &interfaces,
560       NULL);
561 
562   props = tp_asv_new (
563       TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
564       TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, handle_type,
565       TP_PROP_CHANNEL_TARGET_HANDLE, G_TYPE_UINT, handle,
566       TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, target_id,
567       TP_PROP_CHANNEL_REQUESTED, G_TYPE_BOOLEAN, requested,
568       TP_PROP_CHANNEL_INITIATOR_HANDLE, G_TYPE_UINT, initiator_handle,
569       TP_PROP_CHANNEL_INITIATOR_ID, G_TYPE_STRING, initiator_id,
570       TP_PROP_CHANNEL_INTERFACES, G_TYPE_STRV, interfaces,
571       NULL);
572 
573   g_free (target_id);
574   g_free (initiator_id);
575   g_strfreev (interfaces);
576   return props;
577 }
578 
579 G_GNUC_END_IGNORE_DEPRECATIONS
580