1 /**
2  * @file telepathy-connection.c
3  *
4  * pidgin-sipe
5  *
6  * Copyright (C) 2012-2017 SIPE Project <http://sipe.sourceforge.net/>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <string.h>
28 #include <sys/stat.h>
29 
30 #include <glib-object.h>
31 #include <glib/gstdio.h>
32 #include <telepathy-glib/base-connection.h>
33 #include <telepathy-glib/base-protocol.h>
34 #include <telepathy-glib/contacts-mixin.h>
35 #include <telepathy-glib/handle-repo-dynamic.h>
36 #include <telepathy-glib/presence-mixin.h>
37 #include <telepathy-glib/simple-password-manager.h>
38 #include <telepathy-glib/telepathy-glib.h>
39 
40 #include "sipe-backend.h"
41 #include "sipe-common.h"
42 #include "sipe-core.h"
43 
44 #include "telepathy-private.h"
45 
46 G_BEGIN_DECLS
47 /*
48  * Connection class - data structures
49  */
50 typedef struct _SipeConnectionClass {
51 	TpBaseConnectionClass parent_class;
52 	TpDBusPropertiesMixinClass properties_mixin;
53 	TpContactsMixinClass contacts_mixin;
54 	TpPresenceMixinClass presence_mixin;
55 } SipeConnectionClass;
56 
57 typedef struct _SipeConnection {
58 	TpBaseConnection parent;
59 	TpContactsMixinClass contacts_mixin;
60 	TpPresenceMixin presence_mixin;
61 
62 	/* channel managers */
63 	TpSimplePasswordManager *password_manager;
64 	struct _SipeContactList *contact_list;
65 	struct _SipeTLSManager  *tls_manager;
66 
67 	struct sipe_backend_private private;
68 	gchar *account;
69 	gchar *login;
70 	gchar *password;
71 	gchar *server;
72 	gchar *port;
73 	guint  transport;
74 	guint  authentication_type;
75 	gchar *user_agent;
76 	gchar *authentication;
77 	gboolean sso;
78 	gboolean dont_publish;
79 	gboolean allow_web_photo;
80 	gboolean is_disconnecting;
81 
82 	GPtrArray *contact_info_fields;
83 } SipeConnection;
84 
85 #define SIPE_PUBLIC_TO_CONNECTION sipe_public->backend_private->connection
86 
87 /*
88  * Connection class - type macros
89  */
90 static GType sipe_connection_get_type(void) G_GNUC_CONST;
91 #define SIPE_TYPE_CONNECTION \
92 	(sipe_connection_get_type())
93 #define SIPE_CONNECTION(obj) \
94 	(G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONNECTION, \
95 				    SipeConnection))
96 G_END_DECLS
97 
98 /*
99  * Connection class - type definition
100  */
101 static void init_aliasing (gpointer, gpointer);
G_DEFINE_TYPE_WITH_CODE(SipeConnection,sipe_connection,TP_TYPE_BASE_CONNECTION,G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING,init_aliasing);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS,sipe_telepathy_avatars_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,tp_contacts_mixin_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS,tp_base_contact_list_mixin_groups_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO,sipe_telepathy_contact_info_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST,tp_base_contact_list_mixin_list_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,tp_presence_mixin_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,tp_presence_mixin_simple_presence_iface_init);G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,tp_dbus_properties_mixin_iface_init);)102 G_DEFINE_TYPE_WITH_CODE(SipeConnection,
103 			sipe_connection,
104 			TP_TYPE_BASE_CONNECTION,
105 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING,
106 					      init_aliasing);
107 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS,
108 					      sipe_telepathy_avatars_iface_init);
109 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
110 					      tp_contacts_mixin_iface_init);
111 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS,
112 					      tp_base_contact_list_mixin_groups_iface_init);
113 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO,
114 					      sipe_telepathy_contact_info_iface_init);
115 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST,
116 					      tp_base_contact_list_mixin_list_iface_init);
117 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
118 					      tp_presence_mixin_iface_init);
119 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
120 					      tp_presence_mixin_simple_presence_iface_init);
121 			G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES,
122 					      tp_dbus_properties_mixin_iface_init);
123 )
124 
125 
126 /*
127  * Connection class - instance methods
128  */
129 static gchar *normalize_contact(SIPE_UNUSED_PARAMETER TpHandleRepoIface *repo,
130 				const gchar *id,
131 				SIPE_UNUSED_PARAMETER gpointer context,
132 				GError **error)
133 {
134 	return(sipe_telepathy_protocol_normalize_contact(NULL, id, error));
135 }
136 
create_handle_repos(SIPE_UNUSED_PARAMETER TpBaseConnection * conn,TpHandleRepoIface * repos[NUM_TP_HANDLE_TYPES])137 static void create_handle_repos(SIPE_UNUSED_PARAMETER TpBaseConnection *conn,
138 				TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
139 {
140 	repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new(TP_HANDLE_TYPE_CONTACT,
141 								   normalize_contact,
142 								   NULL);
143 }
144 
connect_to_core(SipeConnection * self,GError ** error)145 static gboolean connect_to_core(SipeConnection *self,
146 				GError **error)
147 {
148 	struct sipe_core_public *sipe_public;
149 	const gchar *errmsg;
150 
151 	sipe_public = sipe_core_allocate(self->account,
152 					 self->sso,
153 					 self->login,
154 					 self->password,
155 					 NULL, /* @TODO: email     */
156 					 NULL, /* @TODO: email_url */
157 					 &errmsg);
158 
159 	SIPE_DEBUG_INFO("connect_to_core: created %p", sipe_public);
160 
161 	if (sipe_public) {
162 		struct sipe_backend_private *telepathy_private = &self->private;
163 
164 		/* initialize backend private data */
165 		sipe_public->backend_private    = telepathy_private;
166 		telepathy_private->public       = sipe_public;
167 		telepathy_private->contact_list = self->contact_list;
168 		telepathy_private->connection   = self;
169 		telepathy_private->activity     = SIPE_ACTIVITY_UNSET;
170 		telepathy_private->cache_dir    = g_build_path(G_DIR_SEPARATOR_S,
171 							       g_get_user_cache_dir(),
172 							       "telepathy",
173 							       "sipe",
174 							       self->account,
175 							       NULL);
176 		telepathy_private->message      = NULL;
177 		telepathy_private->tls_manager  = self->tls_manager;
178 		telepathy_private->transport    = NULL;
179 
180 		/* make sure cache directory exists */
181 		if (!g_file_test(telepathy_private->cache_dir,
182 				 G_FILE_TEST_IS_DIR) &&
183 		    (g_mkdir_with_parents(telepathy_private->cache_dir,
184 					  S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
185 		     == 0))
186 			SIPE_DEBUG_INFO("connect_to_core: created cache directory %s",
187 					telepathy_private->cache_dir);
188 
189 		SIPE_CORE_FLAG_UNSET(DONT_PUBLISH);
190 		if (self->dont_publish)
191 			SIPE_CORE_FLAG_SET(DONT_PUBLISH);
192 		SIPE_CORE_FLAG_UNSET(ALLOW_WEB_PHOTO);
193 		if (self->allow_web_photo)
194 			SIPE_CORE_FLAG_SET(ALLOW_WEB_PHOTO);
195 
196 		sipe_core_transport_sip_connect(sipe_public,
197 						self->transport,
198 						self->authentication_type,
199 						self->server,
200 						self->port);
201 
202 		return(TRUE);
203 	} else {
204 		g_set_error_literal(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
205 				    errmsg);
206 		return(FALSE);
207 	}
208 }
209 
password_manager_cb(GObject * source,GAsyncResult * result,gpointer data)210 static void password_manager_cb(GObject *source,
211 				GAsyncResult *result,
212 				gpointer data)
213 {
214 	SipeConnection   *self  = data;
215 	TpBaseConnection *base  = TP_BASE_CONNECTION(self);
216 	GError *error           = NULL;
217 	const GString *password = tp_simple_password_manager_prompt_finish(
218 		TP_SIMPLE_PASSWORD_MANAGER(source),
219 		result,
220 		&error);
221 
222 	if (password == NULL) {
223 		SIPE_DEBUG_ERROR("password_manager_cb: failed: %s",
224 				 error ? error->message : "UNKNOWN");
225 
226 		if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) {
227 			tp_base_connection_disconnect_with_dbus_error(base,
228 								      error ? tp_error_get_dbus_name(error->code) : "",
229 								      NULL,
230 								      TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
231 		}
232 		if (error)
233 			g_error_free(error);
234 	} else {
235 
236 		g_free(self->password);
237 		self->password = g_strdup(password->str);
238 
239 		if (!connect_to_core(self, &error)) {
240 			if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) {
241 				tp_base_connection_disconnect_with_dbus_error(base,
242 									      tp_error_get_dbus_name(error->code),
243 									      NULL,
244 									      TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
245 			}
246 			g_error_free(error);
247 		}
248 	}
249 }
250 
start_connecting(TpBaseConnection * base,GError ** error)251 static gboolean start_connecting(TpBaseConnection *base,
252 				 GError **error)
253 {
254 	SipeConnection *self = SIPE_CONNECTION(base);
255 	gboolean        rc   = TRUE;
256 	gchar          *uri  = sipe_telepathy_protocol_normalize_contact(NULL,
257 									 self->account,
258 									 error);
259 
260 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting");
261 
262 	/* set up mandatory self-handle */
263 	if (uri) {
264 		base->self_handle = tp_handle_ensure(tp_base_connection_get_handles(base,
265 										    TP_HANDLE_TYPE_CONTACT),
266 						     uri,
267 						     NULL,
268 						     error);
269 		g_free(uri);
270 		if (!base->self_handle) {
271 			SIPE_DEBUG_ERROR("SipeConnection::start_connecting: self handle creation failed: %s",
272 					 (*error)->message);
273 			return(FALSE);
274 		}
275 	} else {
276 		SIPE_DEBUG_ERROR("SipeConnection::start_connecting: %s",
277 				 (*error)->message);
278 		return(FALSE);
279 	}
280 
281 	tp_base_connection_change_status(base, TP_CONNECTION_STATUS_CONNECTING,
282 					 TP_CONNECTION_STATUS_REASON_REQUESTED);
283 
284 	/* map option list to flags - default is automatic */
285 	self->authentication_type = SIPE_AUTHENTICATION_TYPE_AUTOMATIC;
286 	if (sipe_strequal(self->authentication, "ntlm")) {
287 		SIPE_DEBUG_INFO_NOFORMAT("start_connecting: NTLM selected");
288 		self->authentication_type = SIPE_AUTHENTICATION_TYPE_NTLM;
289 	} else
290 #ifdef HAVE_GSSAPI_GSSAPI_H
291 	if (sipe_strequal(self->authentication, "krb5")) {
292 		SIPE_DEBUG_INFO_NOFORMAT("start_connecting: KRB5 selected");
293 		self->authentication_type = SIPE_AUTHENTICATION_TYPE_KERBEROS;
294 	} else
295 #endif
296 	if (sipe_strequal(self->authentication, "tls-dsk")) {
297 		SIPE_DEBUG_INFO_NOFORMAT("start_connecting: TLS-DSK selected");
298 		self->authentication_type = SIPE_AUTHENTICATION_TYPE_TLS_DSK;
299 	}
300 
301 	/* Only ask for a password when required */
302 	if (!sipe_core_transport_sip_requires_password(self->authentication_type,
303 						       self->sso) ||
304 	    (self->password && strlen(self->password)))
305 		rc = connect_to_core(self, error);
306 	else {
307 		SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting: requesting password from user");
308 		tp_simple_password_manager_prompt_async(self->password_manager,
309 							password_manager_cb,
310 							self);
311 	}
312 
313 	return(rc);
314 }
315 
disconnect_from_core(gpointer data)316 static gboolean disconnect_from_core(gpointer data)
317 {
318 	TpBaseConnection *base                         = data;
319 	SipeConnection *self                           = SIPE_CONNECTION(base);
320 	struct sipe_backend_private *telepathy_private = &self->private;
321 	struct sipe_core_public *sipe_public           = telepathy_private->public;
322 
323 	SIPE_DEBUG_INFO("disconnect_from_core: %p", sipe_public);
324 
325 	if (sipe_public)
326 		sipe_core_deallocate(sipe_public);
327 	telepathy_private->public    = NULL;
328 	telepathy_private->transport = NULL;
329 
330 	g_free(telepathy_private->message);
331 	telepathy_private->message   = NULL;
332 
333 	g_free(telepathy_private->cache_dir);
334 	telepathy_private->cache_dir = NULL;
335 
336 	SIPE_DEBUG_INFO_NOFORMAT("disconnect_from_core: core deallocated");
337 
338 	/* now it is OK to destroy the connection object */
339 	tp_base_connection_finish_shutdown(base);
340 
341 	return(FALSE);
342 }
343 
shut_down(TpBaseConnection * base)344 static void shut_down(TpBaseConnection *base)
345 {
346 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::shut_down");
347 
348 	/* this can be called synchronously, defer destruction */
349 	g_idle_add(disconnect_from_core, base);
350 }
351 
create_channel_managers(TpBaseConnection * base)352 static GPtrArray *create_channel_managers(TpBaseConnection *base)
353 {
354 	SipeConnection *self = SIPE_CONNECTION(base);
355 	GPtrArray *channel_managers = g_ptr_array_new();
356 
357 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::create_channel_managers");
358 
359 	self->contact_list = sipe_telepathy_contact_list_new(base);
360 	g_ptr_array_add(channel_managers, self->contact_list);
361 
362 	self->password_manager = tp_simple_password_manager_new(base);
363 	g_ptr_array_add(channel_managers, self->password_manager);
364 
365 	g_ptr_array_add(channel_managers, sipe_telepathy_search_new(base));
366 
367 	self->tls_manager = sipe_telepathy_tls_new(base);
368 	g_ptr_array_add(channel_managers, self->tls_manager);
369 
370 	return(channel_managers);
371 }
372 
aliasing_fill_contact_attributes(GObject * object,const GArray * contacts,GHashTable * attributes)373 static void aliasing_fill_contact_attributes(GObject *object,
374 					     const GArray *contacts,
375 					     GHashTable *attributes)
376 {
377 	SipeConnection *self = SIPE_CONNECTION(object);
378 	guint i;
379 
380 	for (i = 0; i < contacts->len; i++) {
381 		TpHandle contact = g_array_index(contacts, guint, i);
382 
383 		tp_contacts_mixin_set_contact_attribute(attributes,
384 							contact,
385 							TP_TOKEN_CONNECTION_INTERFACE_ALIASING_ALIAS,
386 							tp_g_value_slice_new_string(
387 								sipe_telepathy_buddy_get_alias(self->contact_list,
388 											       contact)));
389 	}
390 }
391 
avatars_fill_contact_attributes(GObject * object,const GArray * contacts,GHashTable * attributes)392 static void avatars_fill_contact_attributes(GObject *object,
393 					    const GArray *contacts,
394 					    GHashTable *attributes)
395 {
396 	SipeConnection *self = SIPE_CONNECTION(object);
397 	guint i;
398 
399 	for (i = 0; i < contacts->len; i++) {
400 		TpHandle contact = g_array_index(contacts, guint, i);
401 		const gchar *hash = sipe_telepathy_buddy_get_hash(self->contact_list,
402 								  contact);
403 
404 		if (!hash) hash = "";
405 		tp_contacts_mixin_set_contact_attribute(attributes,
406 							contact,
407 							TP_IFACE_CONNECTION_INTERFACE_AVATARS"/token",
408 							tp_g_value_slice_new_string(hash));
409 	}
410 }
411 
contact_info_properties_getter(GObject * object,SIPE_UNUSED_PARAMETER GQuark interface,GQuark name,GValue * value,gpointer getter_data)412 static void contact_info_properties_getter(GObject *object,
413 					   SIPE_UNUSED_PARAMETER GQuark interface,
414 					   GQuark name,
415 					   GValue *value,
416 					   gpointer getter_data)
417 {
418 	GQuark fields = g_quark_from_static_string("SupportedFields");
419 
420 	if (name == fields)
421 		g_value_set_boxed(value,
422 				  SIPE_CONNECTION(object)->contact_info_fields);
423 	else
424 		g_value_set_uint(value,
425 				 GPOINTER_TO_UINT(getter_data));
426 }
427 
sipe_connection_constructed(GObject * object)428 static void sipe_connection_constructed(GObject *object)
429 {
430 	SipeConnection *self   = SIPE_CONNECTION(object);
431 	TpBaseConnection *base = TP_BASE_CONNECTION(object);
432 	void (*chain_up)(GObject *) = G_OBJECT_CLASS(sipe_connection_parent_class)->constructed;
433 
434 	if (chain_up)
435 		chain_up(object);
436 
437 	tp_contacts_mixin_init(object,
438 			       G_STRUCT_OFFSET(SipeConnection, contacts_mixin));
439 	tp_base_connection_register_with_contacts_mixin(base);
440 
441 	tp_base_contact_list_mixin_register_with_contacts_mixin(base);
442 
443 	tp_contacts_mixin_add_contact_attributes_iface(object,
444 						       TP_IFACE_CONNECTION_INTERFACE_ALIASING,
445 						       aliasing_fill_contact_attributes);
446 	tp_contacts_mixin_add_contact_attributes_iface(object,
447 						       TP_IFACE_CONNECTION_INTERFACE_AVATARS,
448 						       avatars_fill_contact_attributes);
449 
450 	tp_presence_mixin_init(object,
451 			       G_STRUCT_OFFSET(SipeConnection,
452 					       presence_mixin));
453 	tp_presence_mixin_simple_presence_register_with_contacts_mixin(object);
454 
455 	self->contact_info_fields = sipe_telepathy_contact_info_fields();
456 }
457 
sipe_connection_finalize(GObject * object)458 static void sipe_connection_finalize(GObject *object)
459 {
460 	SipeConnection *self = SIPE_CONNECTION(object);
461 
462 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::finalize");
463 
464 	tp_contacts_mixin_finalize(object);
465 	tp_presence_mixin_finalize(object);
466 	g_boxed_free(TP_ARRAY_TYPE_FIELD_SPECS, self->contact_info_fields);
467 
468 	g_free(self->authentication);
469 	g_free(self->user_agent);
470 	g_free(self->port);
471 	g_free(self->server);
472 	g_free(self->password);
473 	g_free(self->login);
474 	g_free(self->account);
475 
476 	G_OBJECT_CLASS(sipe_connection_parent_class)->finalize(object);
477 }
478 
479 /*
480  * Connection class - type implementation
481  */
482 static const gchar *interfaces_always_present[] = {
483 	/* @TODO */
484 	TP_IFACE_CONNECTION_INTERFACE_ALIASING,
485 	TP_IFACE_CONNECTION_INTERFACE_AVATARS,
486 	TP_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS,
487 	TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
488 	TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST,
489 	TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
490 	TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
491 	TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
492 	TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
493 	NULL
494 };
495 
sipe_connection_class_init(SipeConnectionClass * klass)496 static void sipe_connection_class_init(SipeConnectionClass *klass)
497 {
498 	GObjectClass *object_class = G_OBJECT_CLASS(klass);
499 	TpBaseConnectionClass *base_class = TP_BASE_CONNECTION_CLASS(klass);
500 	static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
501 		{
502 			/* 0 */
503 			.name   = TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO,
504 			.getter = contact_info_properties_getter,
505 			.setter = NULL,
506 		},
507 		{
508 			/* LAST! */
509 			.name   = NULL,
510 		}
511 	};
512 
513 	/* initalize non-constant fields */
514 	prop_interfaces[0].props = sipe_telepathy_contact_info_props();
515 
516 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::class_init");
517 
518 	object_class->constructed = sipe_connection_constructed;
519 	object_class->finalize    = sipe_connection_finalize;
520 
521 	base_class->create_handle_repos     = create_handle_repos;
522 	base_class->start_connecting        = start_connecting;
523 	base_class->shut_down               = shut_down;
524 	base_class->create_channel_managers = create_channel_managers;
525 
526 	base_class->interfaces_always_present = interfaces_always_present;
527 
528 	klass->properties_mixin.interfaces = prop_interfaces;
529 	tp_dbus_properties_mixin_class_init(object_class,
530 					    G_STRUCT_OFFSET(SipeConnectionClass,
531 							    properties_mixin));
532 	tp_contacts_mixin_class_init(object_class,
533 				     G_STRUCT_OFFSET(SipeConnectionClass,
534 						     contacts_mixin));
535 	sipe_telepathy_status_init(object_class,
536 				   G_STRUCT_OFFSET(SipeConnectionClass,
537 						   presence_mixin));
538 	tp_presence_mixin_simple_presence_init_dbus_properties(object_class);
539 	tp_base_contact_list_mixin_class_init(base_class);
540 }
541 
sipe_connection_init(SIPE_UNUSED_PARAMETER SipeConnection * self)542 static void sipe_connection_init(SIPE_UNUSED_PARAMETER SipeConnection *self)
543 {
544 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::init");
545 }
546 
547 /*
548  * Connection class - interface implementation
549  *
550  * Contact aliases
551  */
get_alias_flags(TpSvcConnectionInterfaceAliasing * aliasing,DBusGMethodInvocation * context)552 static void get_alias_flags(TpSvcConnectionInterfaceAliasing *aliasing,
553 			    DBusGMethodInvocation *context)
554 {
555 	TpBaseConnection *base = TP_BASE_CONNECTION(aliasing);
556 
557 	TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
558 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::get_alias_flags called");
559 
560 	tp_svc_connection_interface_aliasing_return_from_get_alias_flags(context,
561 									 TP_CONNECTION_ALIAS_FLAG_USER_SET);
562 }
563 
get_aliases(TpSvcConnectionInterfaceAliasing * aliasing,const GArray * contacts,DBusGMethodInvocation * context)564 static void get_aliases(TpSvcConnectionInterfaceAliasing *aliasing,
565 			const GArray *contacts,
566 			DBusGMethodInvocation *context)
567 {
568 	SipeConnection *self            = SIPE_CONNECTION(aliasing);
569 	TpBaseConnection *base          = TP_BASE_CONNECTION(aliasing);
570 	TpHandleRepoIface *contact_repo = tp_base_connection_get_handles(base,
571 									 TP_HANDLE_TYPE_CONTACT);
572 	GError *error                   = NULL;
573 	GHashTable *result;
574 	guint i;
575 
576 	TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
577 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::get_aliases called");
578 
579 	if (!tp_handles_are_valid(contact_repo, contacts, FALSE, &error)) {
580 		dbus_g_method_return_error(context, error);
581 		g_error_free(error);
582 		return;
583 	}
584 
585 	result = g_hash_table_new(g_direct_hash, g_direct_equal);
586 
587 	for (i = 0; i < contacts->len; i++) {
588 		TpHandle contact   = g_array_index(contacts, TpHandle, i);
589 		const gchar *alias = sipe_telepathy_buddy_get_alias(self->contact_list,
590 								    contact);
591 		g_hash_table_insert(result,
592 				    GUINT_TO_POINTER(contact),
593 				    (gchar *) alias);
594 	}
595 
596 	tp_svc_connection_interface_aliasing_return_from_get_aliases(context,
597 								     result);
598 	g_hash_table_unref(result);
599 }
600 
request_aliases(TpSvcConnectionInterfaceAliasing * aliasing,const GArray * contacts,DBusGMethodInvocation * context)601 static void request_aliases(TpSvcConnectionInterfaceAliasing *aliasing,
602 			    const GArray *contacts,
603 			    DBusGMethodInvocation *context)
604 {
605 	SipeConnection *self            = SIPE_CONNECTION(aliasing);
606 	TpBaseConnection *base          = TP_BASE_CONNECTION(aliasing);
607 	TpHandleRepoIface *contact_repo = tp_base_connection_get_handles(base,
608 									 TP_HANDLE_TYPE_CONTACT);
609 	GError *error                   = NULL;
610 	GPtrArray *result;
611 	gchar **strings;
612 	guint i;
613 
614 	TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context);
615 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::request_aliases called");
616 
617 	if (!tp_handles_are_valid(contact_repo, contacts, FALSE, &error)) {
618 		dbus_g_method_return_error(context, error);
619 		g_error_free(error);
620 		return;
621 	}
622 
623 	result = g_ptr_array_sized_new(contacts->len + 1);
624 
625 	for (i = 0; i < contacts->len; i++) {
626 		TpHandle contact   = g_array_index(contacts, TpHandle, i);
627 		const gchar *alias = sipe_telepathy_buddy_get_alias(self->contact_list,
628 								    contact);
629 		g_ptr_array_add(result, (gchar *) alias);
630 	}
631 
632 	g_ptr_array_add(result, NULL);
633 	strings = (gchar **) g_ptr_array_free(result, FALSE);
634 
635 	tp_svc_connection_interface_aliasing_return_from_request_aliases(context,
636 									 (const gchar **) strings);
637 	g_free(strings);
638 }
639 
set_aliases(TpSvcConnectionInterfaceAliasing * aliasing,GHashTable * aliases,DBusGMethodInvocation * context)640 static void set_aliases(TpSvcConnectionInterfaceAliasing *aliasing,
641 			GHashTable *aliases,
642 			DBusGMethodInvocation *context)
643 {
644 	SipeConnection *self            = SIPE_CONNECTION(aliasing);
645 	TpBaseConnection *base          = TP_BASE_CONNECTION(aliasing);
646 	TpHandleRepoIface *contact_repo = tp_base_connection_get_handles(base,
647 									 TP_HANDLE_TYPE_CONTACT);
648 	GHashTableIter iter;
649 	gpointer key, value;
650 
651 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::set_aliases called");
652 
653 	g_hash_table_iter_init(&iter, aliases);
654 
655 	while (g_hash_table_iter_next(&iter, &key, NULL)) {
656 		GError *error = NULL;
657 
658 		if (!tp_handle_is_valid(contact_repo,
659 					GPOINTER_TO_UINT(key),
660 					&error)) {
661 			dbus_g_method_return_error(context, error);
662 			g_error_free(error);
663 			return;
664 		}
665 	}
666 
667 	g_hash_table_iter_init(&iter, aliases);
668 
669 	while (g_hash_table_iter_next(&iter, &key, &value)) {
670 		sipe_telepathy_buddy_set_alias(self->contact_list,
671 					       GPOINTER_TO_UINT(key),
672 					       value);
673 	}
674 
675 	tp_svc_connection_interface_aliasing_return_from_set_aliases(context);
676 }
677 
init_aliasing(gpointer iface,SIPE_UNUSED_PARAMETER gpointer iface_data)678 static void init_aliasing(gpointer iface,
679 			  SIPE_UNUSED_PARAMETER gpointer iface_data)
680 {
681 	TpSvcConnectionInterfaceAliasingClass *klass = iface;
682 
683 	SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::init_aliasing called");
684 
685 	tp_svc_connection_interface_aliasing_implement_get_alias_flags(klass, get_alias_flags);
686 	tp_svc_connection_interface_aliasing_implement_request_aliases(klass, request_aliases);
687 	tp_svc_connection_interface_aliasing_implement_get_aliases(klass, get_aliases);
688 	tp_svc_connection_interface_aliasing_implement_set_aliases(klass, set_aliases);
689 }
690 
691 /* create new connection object */
sipe_telepathy_connection_new(TpBaseProtocol * protocol,GHashTable * params,SIPE_UNUSED_PARAMETER GError ** error)692 TpBaseConnection *sipe_telepathy_connection_new(TpBaseProtocol *protocol,
693 						GHashTable *params,
694 						SIPE_UNUSED_PARAMETER GError **error)
695 {
696 	SipeConnection *conn = g_object_new(SIPE_TYPE_CONNECTION,
697 					    "protocol", tp_base_protocol_get_name(protocol),
698 					    NULL);
699 	const gchar *value;
700 	guint port;
701 	gboolean boolean_value;
702 	gboolean valid;
703 
704 	SIPE_DEBUG_INFO_NOFORMAT("sipe_telepathy_connection_new");
705 
706 	/* initialize private fields */
707 	conn->is_disconnecting = FALSE;
708 
709 	/* account is required field */
710 	conn->account = g_strdup(tp_asv_get_string(params, "account"));
711 
712 	/* if login is not specified, account value will be used in connect_to_core */
713 	value = tp_asv_get_string(params, "login");
714 	if (value && strlen(value))
715 		conn->login = g_strdup(value);
716 	else
717 		conn->login = NULL;
718 
719 	/* password */
720 	value = tp_asv_get_string(params, "password");
721 	if (value && strlen(value))
722 		conn->password = g_strdup(value);
723 	else
724 		conn->password = NULL;
725 
726 	/* server name */
727 	value = tp_asv_get_string(params, "server");
728 	if (value && strlen(value))
729 		conn->server = g_strdup(value);
730 	else
731 		conn->server = NULL;
732 
733 	/* server port: core expects a string */
734 	port = tp_asv_get_uint32(params, "port", &valid);
735 	if (valid)
736 		conn->port = g_strdup_printf("%d", port);
737 	else
738 		conn->port = NULL;
739 
740 	/* transport type */
741 	value = tp_asv_get_string(params, "transport");
742 	if (sipe_strequal(value, "auto")) {
743 		conn->transport = conn->server ?
744 			SIPE_TRANSPORT_TLS : SIPE_TRANSPORT_AUTO;
745 	} else if (sipe_strequal(value, "tls")) {
746 		conn->transport = SIPE_TRANSPORT_TLS;
747 	} else {
748 		conn->transport = SIPE_TRANSPORT_TCP;
749 	}
750 
751 	/* User-Agent: override */
752 	value = tp_asv_get_string(params, "useragent");
753 	if (value && strlen(value))
754 		conn->user_agent = g_strdup(value);
755 	else
756 		conn->user_agent = NULL;
757 
758 	/* authentication type */
759 	value = tp_asv_get_string(params, "authentication");
760 	if (value && strlen(value) && strcmp(value, "ntlm"))
761 		conn->authentication = g_strdup(value);
762 	else
763 		conn->authentication = NULL; /* NTLM is default */
764 
765 	/* Single Sign-On */
766 	boolean_value = tp_asv_get_boolean(params, "single-sign-on", &valid);
767 	if (valid)
768 		conn->sso = boolean_value;
769 	else
770 		conn->sso = FALSE;
771 
772 	/* Don't publish my calendar information */
773 	boolean_value = tp_asv_get_boolean(params, "don't-publish-calendar", &valid);
774 	if (valid)
775 		conn->dont_publish = boolean_value;
776 	else
777 		conn->dont_publish = FALSE;
778 
779 	/* Allow insecure download of buddy icons from web */
780 	boolean_value = tp_asv_get_boolean(params, "allow-web-photo", &valid);
781 	if (valid)
782 		conn->allow_web_photo = boolean_value;
783 	else
784 		conn->allow_web_photo = FALSE;
785 
786 	return(TP_BASE_CONNECTION(conn));
787 }
788 
sipe_telepathy_connection_alias_updated(TpBaseConnection * connection,guint contact,const gchar * alias)789 void sipe_telepathy_connection_alias_updated(TpBaseConnection *connection,
790 					     guint contact,
791 					     const gchar *alias)
792 {
793 	GPtrArray *aliases = g_ptr_array_sized_new(1);
794 	GValueArray *pair  = g_value_array_new(2);
795 
796 	g_value_array_append(pair, NULL);
797 	g_value_array_append(pair, NULL);
798 	g_value_init(pair->values + 0, G_TYPE_UINT);
799 	g_value_init(pair->values + 1, G_TYPE_STRING);
800 	g_value_set_uint(pair->values + 0, contact);
801 	g_value_set_string(pair->values + 1, alias);
802 	g_ptr_array_add(aliases, pair);
803 
804 	tp_svc_connection_interface_aliasing_emit_aliases_changed(SIPE_CONNECTION(connection),
805 								  aliases);
806 
807 	g_ptr_array_unref(aliases);
808 	g_value_array_free(pair);
809 }
810 
sipe_telepathy_connection_private(GObject * object)811 struct sipe_backend_private *sipe_telepathy_connection_private(GObject *object)
812 {
813 	SipeConnection *self = SIPE_CONNECTION(object);
814 	/* connected to core already? */
815 	if (self->private.public)
816 		return(&self->private);
817 	else
818 		return(NULL);
819 }
820 
821 /*
822  * Backend adaptor functions
823  */
sipe_backend_connection_completed(struct sipe_core_public * sipe_public)824 void sipe_backend_connection_completed(struct sipe_core_public *sipe_public)
825 {
826 	SipeConnection *self   = SIPE_PUBLIC_TO_CONNECTION;
827 	TpBaseConnection *base = TP_BASE_CONNECTION(self);
828 
829 	/* we are only allowed to do this once */
830 	if (base->status != TP_CONNECTION_STATUS_CONNECTED)
831 		tp_base_connection_change_status(base,
832 						 TP_CONNECTION_STATUS_CONNECTED,
833 						 TP_CONNECTION_STATUS_REASON_REQUESTED);
834 }
835 
sipe_backend_connection_error(struct sipe_core_public * sipe_public,sipe_connection_error error,const gchar * msg)836 void sipe_backend_connection_error(struct sipe_core_public *sipe_public,
837 				   sipe_connection_error error,
838 				   const gchar *msg)
839 {
840 	SipeConnection *self   = SIPE_PUBLIC_TO_CONNECTION;
841 	TpBaseConnection *base = TP_BASE_CONNECTION(self);
842 	GHashTable *details    = tp_asv_new("server-message", G_TYPE_STRING, msg,
843 					    NULL);
844 	TpConnectionStatusReason reason;
845 	const gchar *name;
846 
847 	self->is_disconnecting = TRUE;
848 
849 	switch (error) {
850 	case SIPE_CONNECTION_ERROR_NETWORK:
851 		reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR;
852 		if (base->status == TP_CONNECTION_STATUS_CONNECTING)
853 			name = TP_ERROR_STR_CONNECTION_FAILED;
854 		else
855 			name = TP_ERROR_STR_CONNECTION_LOST;
856 		break;
857 
858 	case SIPE_CONNECTION_ERROR_INVALID_USERNAME:
859 	case SIPE_CONNECTION_ERROR_INVALID_SETTINGS:
860 	case SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
861 	case SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
862 		/* copied from haze code. I agree there should be better ones */
863 		reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED;
864 		name   = TP_ERROR_STR_AUTHENTICATION_FAILED;
865 		break;
866 
867 	default:
868 		reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED;
869 		name   = TP_ERROR_STR_DISCONNECTED;
870 		break;
871 	}
872 
873 	SIPE_DEBUG_ERROR("sipe_backend_connection_error: %s (%s)", name, msg);
874 	tp_base_connection_disconnect_with_dbus_error(base,
875 						      name,
876 						      details,
877 						      reason);
878 	g_hash_table_unref(details);
879 }
880 
sipe_backend_connection_is_disconnecting(struct sipe_core_public * sipe_public)881 gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public)
882 {
883 	SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION;
884 
885 	/* disconnect was requested or transport was already disconnected */
886 	return(self->is_disconnecting ||
887 	       self->private.transport == NULL);
888 }
889 
sipe_backend_connection_is_valid(struct sipe_core_public * sipe_public)890 gboolean sipe_backend_connection_is_valid(struct sipe_core_public *sipe_public)
891 {
892 	return(!sipe_backend_connection_is_disconnecting(sipe_public));
893 }
894 
sipe_backend_setting(struct sipe_core_public * sipe_public,sipe_setting type)895 const gchar *sipe_backend_setting(struct sipe_core_public *sipe_public,
896 				  sipe_setting type)
897 {
898 	SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION;
899 	const gchar *value;
900 
901 	switch (type) {
902 	case SIPE_SETTING_USER_AGENT:
903 		value = self->user_agent;
904 		break;
905 	default:
906 		/* @TODO: update when settings are implemented */
907 		value = NULL;
908 		break;
909 	}
910 
911 	return(value);
912 }
913 
914 
915 /*
916   Local Variables:
917   mode: c
918   c-file-style: "bsd"
919   indent-tabs-mode: t
920   tab-width: 8
921   End:
922 */
923