1 /*
2 * connection-contact-info.c - ContactInfo implementation
3 * Copyright © 2011 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 #include "connection-contact-info.h"
21
22 #include <telepathy-glib/interfaces.h>
23 /* Slightly sketchy; included for TpContactInfoFieldSpec. */
24 #include <telepathy-glib/connection.h>
25 #include <telepathy-glib/gtypes.h>
26
27 #include "contact-manager.h"
28
29 enum {
30 PROP_CONTACT_INFO_FLAGS,
31 PROP_SUPPORTED_FIELDS
32 };
33
34 static gchar *i_heart_the_internet[] = { "type=internet", NULL };
35
36 static GPtrArray *
get_supported_fields(void)37 get_supported_fields (void)
38 {
39 static TpContactInfoFieldSpec supported_fields[] = {
40 /* We omit 'nickname' because it shows up, unmodifiably, as the alias. */
41 { "n", NULL,
42 TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT, 1 },
43 /* It's a little bit sketchy to expose 1st + ' ' + last as FN. But such
44 * are the limitations of the protocol.
45 */
46 { "fn", NULL,
47 TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT, 1 },
48 { "email", i_heart_the_internet,
49 TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT, 1 },
50 /* x-jabber is used for compatibility with Gabble */
51 { "x-jabber", NULL,
52 TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT, 1 },
53 /* Heh, we could also include the contact's IP address(es) here. */
54 { NULL }
55 };
56 static gsize supported_fields_ptr_array = 0;
57
58 if (g_once_init_enter (&supported_fields_ptr_array))
59 {
60 GPtrArray *fields = dbus_g_type_specialized_construct (
61 TP_ARRAY_TYPE_FIELD_SPECS);
62 TpContactInfoFieldSpec *spec;
63
64 for (spec = supported_fields; spec->name != NULL; spec++)
65 g_ptr_array_add (fields,
66 tp_value_array_build (4,
67 G_TYPE_STRING, spec->name,
68 G_TYPE_STRV, spec->parameters,
69 G_TYPE_UINT, spec->flags,
70 G_TYPE_UINT, spec->max,
71 G_TYPE_INVALID));
72
73 g_once_init_leave (&supported_fields_ptr_array, (gsize) fields);
74 }
75
76 return (GPtrArray *) supported_fields_ptr_array;
77 }
78
79 static void
salut_conn_contact_info_get_property(GObject * object,GQuark iface,GQuark name,GValue * value,gpointer getter_data)80 salut_conn_contact_info_get_property (
81 GObject *object,
82 GQuark iface,
83 GQuark name,
84 GValue *value,
85 gpointer getter_data)
86 {
87 switch (GPOINTER_TO_UINT (getter_data))
88 {
89 case PROP_CONTACT_INFO_FLAGS:
90 g_value_set_uint (value, TP_CONTACT_INFO_FLAG_PUSH);
91 break;
92 case PROP_SUPPORTED_FIELDS:
93 g_value_set_boxed (value, get_supported_fields ());
94 break;
95 default:
96 g_assert_not_reached ();
97 }
98 }
99
100 void
salut_conn_contact_info_class_init(SalutConnectionClass * klass)101 salut_conn_contact_info_class_init (
102 SalutConnectionClass *klass)
103 {
104 static TpDBusPropertiesMixinPropImpl props[] = {
105 { "ContactInfoFlags", GUINT_TO_POINTER (PROP_CONTACT_INFO_FLAGS), NULL },
106 { "SupportedFields", GUINT_TO_POINTER (PROP_SUPPORTED_FIELDS), NULL },
107 { NULL }
108 };
109
110 tp_dbus_properties_mixin_implement_interface (
111 G_OBJECT_CLASS (klass),
112 TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO,
113 salut_conn_contact_info_get_property,
114 NULL,
115 props);
116 }
117
118 static void
add_singleton_field(GPtrArray * contact_info,const gchar * field_name,gchar ** parameters,const gchar * value)119 add_singleton_field (
120 GPtrArray *contact_info,
121 const gchar *field_name,
122 gchar **parameters,
123 const gchar *value)
124 {
125 const gchar *field_value[] = { value, NULL };
126
127 g_ptr_array_add (contact_info,
128 tp_value_array_build (3,
129 G_TYPE_STRING, field_name,
130 G_TYPE_STRV, parameters,
131 G_TYPE_STRV, field_value,
132 G_TYPE_INVALID));
133 }
134
135 static GPtrArray *
build_contact_info(const gchar * first,const gchar * last,const gchar * full_name,const gchar * email,const gchar * jid)136 build_contact_info (
137 const gchar *first,
138 const gchar *last,
139 const gchar *full_name,
140 const gchar *email,
141 const gchar *jid)
142 {
143 GPtrArray *contact_info = dbus_g_type_specialized_construct (
144 TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
145
146 if (first != NULL || last != NULL)
147 {
148 const gchar *field_value[] = {
149 last != NULL ? last : "",
150 first != NULL ? first : "",
151 "",
152 "",
153 "",
154 NULL
155 };
156
157 g_ptr_array_add (contact_info,
158 tp_value_array_build (3,
159 G_TYPE_STRING, "n",
160 G_TYPE_STRV, NULL,
161 G_TYPE_STRV, field_value,
162 G_TYPE_INVALID));
163
164 g_warn_if_fail (full_name != NULL);
165 add_singleton_field (contact_info, "fn", NULL, full_name);
166 }
167
168 if (email != NULL)
169 add_singleton_field (contact_info, "email", i_heart_the_internet, email);
170
171 if (jid != NULL)
172 add_singleton_field (contact_info, "x-jabber", NULL, jid);
173
174 return contact_info;
175 }
176
177 static GPtrArray *
build_contact_info_for_contact(SalutContact * contact)178 build_contact_info_for_contact (
179 SalutContact *contact)
180 {
181 g_return_val_if_fail (contact != NULL, NULL);
182
183 return build_contact_info (contact->first, contact->last, contact->full_name,
184 contact->email, contact->jid);
185 }
186
187 static void
salut_conn_contact_info_fill_contact_attributes(GObject * obj,const GArray * contacts,GHashTable * attributes_hash)188 salut_conn_contact_info_fill_contact_attributes (
189 GObject *obj,
190 const GArray *contacts,
191 GHashTable *attributes_hash)
192 {
193 guint i;
194 SalutConnection *self = SALUT_CONNECTION (obj);
195 TpBaseConnection *base = TP_BASE_CONNECTION (self);
196 SalutContactManager *contact_manager;
197
198 g_object_get (self, "contact-manager", &contact_manager, NULL);
199
200 for (i = 0; i < contacts->len; i++)
201 {
202 TpHandle handle = g_array_index (contacts, TpHandle, i);
203 GPtrArray *contact_info = NULL;
204
205 if (base->self_handle == handle)
206 {
207 /* TODO: dig contact info out of SalutSelf. There's overlap with
208 * connection parameters here … should they be DBus_Property
209 * parameters? Should we have a new flag which means “you set this on
210 * ContactInfo”? What?
211 */
212 }
213 else
214 {
215 SalutContact *contact = salut_contact_manager_get_contact (
216 contact_manager, handle);
217 if (contact != NULL)
218 {
219 contact_info = build_contact_info_for_contact (contact);
220 g_object_unref (contact);
221 }
222 }
223
224 if (contact_info != NULL)
225 tp_contacts_mixin_set_contact_attribute (attributes_hash,
226 handle, TP_TOKEN_CONNECTION_INTERFACE_CONTACT_INFO_INFO,
227 tp_g_value_slice_new_take_boxed (
228 TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info));
229 }
230
231 g_object_unref (contact_manager);
232 }
233
salut_conn_contact_info_init(SalutConnection * self)234 void salut_conn_contact_info_init (
235 SalutConnection *self)
236 {
237 tp_contacts_mixin_add_contact_attributes_iface (
238 G_OBJECT (self),
239 TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
240 salut_conn_contact_info_fill_contact_attributes);
241 }
242
243 void
salut_conn_contact_info_changed(SalutConnection * self,SalutContact * contact,TpHandle handle)244 salut_conn_contact_info_changed (
245 SalutConnection *self,
246 SalutContact *contact,
247 TpHandle handle)
248 {
249 GPtrArray *contact_info = build_contact_info_for_contact (contact);
250
251 tp_svc_connection_interface_contact_info_emit_contact_info_changed (self,
252 handle, contact_info);
253 g_boxed_free (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info);
254 }
255
256 static void
salut_conn_contact_info_get_contact_info(TpSvcConnectionInterfaceContactInfo * iface,const GArray * contacts,DBusGMethodInvocation * context)257 salut_conn_contact_info_get_contact_info (
258 TpSvcConnectionInterfaceContactInfo *iface,
259 const GArray *contacts,
260 DBusGMethodInvocation *context)
261 {
262 SalutConnection *self = SALUT_CONNECTION (iface);
263 TpBaseConnection *base = (TpBaseConnection *) self;
264 TpHandleRepoIface *contacts_repo =
265 tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
266 SalutContactManager *contact_manager;
267 guint i;
268 GHashTable *ret;
269 GError *error = NULL;
270
271 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface),
272 context);
273
274 if (!tp_handles_are_valid (contacts_repo, contacts, FALSE, &error))
275 {
276 dbus_g_method_return_error (context, error);
277 g_error_free (error);
278 return;
279 }
280
281 g_object_get (self, "contact-manager", &contact_manager, NULL);
282 ret = dbus_g_type_specialized_construct (TP_HASH_TYPE_CONTACT_INFO_MAP);
283
284 for (i = 0; i < contacts->len; i++)
285 {
286 TpHandle handle = g_array_index (contacts, TpHandle, i);
287 SalutContact *contact = salut_contact_manager_get_contact (
288 contact_manager, handle);
289
290 if (contact != NULL)
291 {
292 g_hash_table_insert (ret, GUINT_TO_POINTER (handle),
293 build_contact_info_for_contact (contact));
294 g_object_unref (contact);
295 }
296 }
297
298 tp_svc_connection_interface_contact_info_return_from_get_contact_info (
299 context, ret);
300 g_boxed_free (TP_HASH_TYPE_CONTACT_INFO_MAP, ret);
301 g_object_unref (contact_manager);
302 }
303
304 static void
salut_conn_contact_info_request_contact_info(TpSvcConnectionInterfaceContactInfo * iface,guint handle,DBusGMethodInvocation * context)305 salut_conn_contact_info_request_contact_info (
306 TpSvcConnectionInterfaceContactInfo *iface,
307 guint handle,
308 DBusGMethodInvocation *context)
309 {
310 SalutConnection *self = SALUT_CONNECTION (iface);
311 TpBaseConnection *base = (TpBaseConnection *) self;
312 TpHandleRepoIface *contacts_repo =
313 tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
314 GError *error = NULL;
315
316 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface),
317 context);
318
319 if (!tp_handle_is_valid (contacts_repo, handle, &error))
320 {
321 dbus_g_method_return_error (context, error);
322 g_error_free (error);
323 }
324 else
325 {
326 SalutContactManager *contact_manager;
327 SalutContact *contact;
328
329 g_object_get (self, "contact-manager", &contact_manager, NULL);
330 contact = salut_contact_manager_get_contact (contact_manager, handle);
331 g_object_unref (contact_manager);
332
333 if (contact != NULL)
334 {
335 GPtrArray *contact_info = build_contact_info_for_contact (contact);
336
337 tp_svc_connection_interface_contact_info_return_from_request_contact_info (
338 context, contact_info);
339 g_boxed_free (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info);
340 }
341 else
342 {
343 error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
344 "No information available for '%s'",
345 tp_handle_inspect (contacts_repo, handle));
346 dbus_g_method_return_error (context, error);
347 g_error_free (error);
348 }
349 }
350 }
351
352 static void
salut_conn_contact_info_refresh_contact_info(TpSvcConnectionInterfaceContactInfo * iface,const GArray * contacts,DBusGMethodInvocation * context)353 salut_conn_contact_info_refresh_contact_info (
354 TpSvcConnectionInterfaceContactInfo *iface,
355 const GArray *contacts,
356 DBusGMethodInvocation *context)
357 {
358 /* This is a no-op on link-local XMPP: everything's always pushed to us. */
359 tp_svc_connection_interface_contact_info_return_from_refresh_contact_info (context);
360 }
361
362 void
salut_conn_contact_info_iface_init(gpointer g_iface,gpointer iface_data)363 salut_conn_contact_info_iface_init (
364 gpointer g_iface,
365 gpointer iface_data)
366 {
367 TpSvcConnectionInterfaceContactInfoClass *klass = g_iface;
368
369 #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x \
370 (klass, salut_conn_contact_info_##x)
371 IMPLEMENT (get_contact_info);
372 IMPLEMENT (request_contact_info);
373 IMPLEMENT (refresh_contact_info);
374 #undef IMPLEMENT
375 }
376