2 * @file telepathy-connection.c
6 * Copyright (C) 2012 SIPE Project <http://sipe.sourceforge.net/>
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.
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.
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
29 #include <glib-object.h>
30 #include <telepathy-glib/base-connection.h>
31 #include <telepathy-glib/base-protocol.h>
32 #include <telepathy-glib/handle-repo-dynamic.h>
33 #include <telepathy-glib/simple-password-manager.h>
34 #include <telepathy-glib/telepathy-glib.h>
36 #include "sipe-backend.h"
37 #include "sipe-common.h"
38 #include "sipe-core.h"
40 #include "telepathy-private.h"
44 * Connection class - data structures
46 typedef struct _SipeConnectionClass
{
47 TpBaseConnectionClass parent_class
;
48 } SipeConnectionClass
;
50 typedef struct _SipeConnection
{
51 TpBaseConnection parent
;
53 TpSimplePasswordManager
*password_manager
;
55 struct sipe_backend_private
private;
63 gchar
*authentication
;
64 gboolean is_disconnecting
;
67 #define SIPE_PUBLIC_TO_CONNECTION sipe_public->backend_private->connection
70 * Connection class - type macros
72 static GType
sipe_connection_get_type(void) G_GNUC_CONST
;
73 #define SIPE_TYPE_CONNECTION \
74 (sipe_connection_get_type())
75 #define SIPE_CONNECTION(obj) \
76 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONNECTION, \
81 * Connection class - type definition
83 G_DEFINE_TYPE(SipeConnection
,
85 TP_TYPE_BASE_CONNECTION
)
88 * Connection class - instance methods
90 static gchar
*normalize_contact(SIPE_UNUSED_PARAMETER TpHandleRepoIface
*repo
,
92 SIPE_UNUSED_PARAMETER gpointer context
,
95 return(sipe_telepathy_protocol_normalize_contact(NULL
, id
, error
));
98 static void create_handle_repos(SIPE_UNUSED_PARAMETER TpBaseConnection
*conn
,
99 TpHandleRepoIface
*repos
[NUM_TP_HANDLE_TYPES
])
101 repos
[TP_HANDLE_TYPE_CONTACT
] = tp_dynamic_handle_repo_new(TP_HANDLE_TYPE_CONTACT
,
106 static gboolean
connect_to_core(SipeConnection
*self
,
109 gchar
*login_domain
= NULL
;
110 gchar
*login_account
= NULL
;
111 struct sipe_core_public
*sipe_public
;
114 /* login name specified? */
115 if (self
->login
&& strlen(self
->login
)) {
116 /* Allowed domain-account separators are / or \ */
117 gchar
**domain_user
= g_strsplit_set(self
->login
, "/\\", 2);
118 gboolean has_domain
= domain_user
[1] != NULL
;
119 SIPE_DEBUG_INFO("connect_to_core: login '%s'", self
->login
);
120 login_domain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
121 login_account
= g_strdup(domain_user
[has_domain
? 1 : 0]);
122 SIPE_DEBUG_INFO("connect_to_core: auth domain '%s' user '%s'",
123 login_domain
? login_domain
: "",
125 g_strfreev(domain_user
);
128 sipe_public
= sipe_core_allocate(self
->account
,
129 login_domain
, login_account
,
131 NULL
, /* @TODO: email */
132 NULL
, /* @TODO: email_url */
134 g_free(login_domain
);
135 g_free(login_account
);
137 SIPE_DEBUG_INFO("connect_to_core: created %p", sipe_public
);
140 struct sipe_backend_private
*telepathy_private
= &self
->private;
142 /* initialize backend private data */
143 sipe_public
->backend_private
= telepathy_private
;
144 telepathy_private
->public = sipe_public
;
145 telepathy_private
->connection
= self
;
146 telepathy_private
->transport
= NULL
;
147 telepathy_private
->ipaddress
= NULL
;
149 /* map option list to flags - default is NTLM */
150 SIPE_CORE_FLAG_UNSET(KRB5
);
151 SIPE_CORE_FLAG_UNSET(TLS_DSK
);
153 if (sipe_strequal(self
->authentication
, "krb5")) {
154 SIPE_DEBUG_INFO_NOFORMAT("connect_to_core: KRB5 selected");
155 SIPE_CORE_FLAG_SET(KRB5
);
158 if (sipe_strequal(self
->authentication
, "tls-dsk")) {
159 SIPE_DEBUG_INFO_NOFORMAT("connect_to_core: TLS-DSK selected");
160 SIPE_CORE_FLAG_SET(TLS_DSK
);
163 /* @TODO: add parameter for SSO */
164 SIPE_CORE_FLAG_UNSET(SSO
);
166 sipe_core_transport_sip_connect(sipe_public
,
173 g_set_error_literal(error
, TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
179 static void password_manager_cb(GObject
*source
,
180 GAsyncResult
*result
,
183 SipeConnection
*self
= data
;
184 TpBaseConnection
*base
= TP_BASE_CONNECTION(self
);
185 GError
*error
= NULL
;
186 const GString
*password
= tp_simple_password_manager_prompt_finish(
187 TP_SIMPLE_PASSWORD_MANAGER(source
),
192 SIPE_DEBUG_ERROR("password_manager_cb: failed: %s",
195 if (base
->status
!= TP_CONNECTION_STATUS_DISCONNECTED
) {
196 tp_base_connection_disconnect_with_dbus_error(base
,
197 tp_error_get_dbus_name(error
->code
),
199 TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED
);
204 g_free(self
->password
);
205 self
->password
= g_strdup(password
->str
);
207 if (!connect_to_core(self
, &error
)) {
208 if (base
->status
!= TP_CONNECTION_STATUS_DISCONNECTED
) {
209 tp_base_connection_disconnect_with_dbus_error(base
,
210 tp_error_get_dbus_name(error
->code
),
212 TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED
);
219 static gboolean
start_connecting(TpBaseConnection
*base
,
222 SipeConnection
*self
= SIPE_CONNECTION(base
);
224 gchar
*uri
= sipe_telepathy_protocol_normalize_contact(NULL
,
228 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting");
230 /* set up mandatory self-handle */
232 base
->self_handle
= tp_handle_ensure(tp_base_connection_get_handles(base
,
233 TP_HANDLE_TYPE_CONTACT
),
238 if (!base
->self_handle
) {
239 SIPE_DEBUG_ERROR("SipeConnection::start_connecting: self handle creation failed: %s",
244 SIPE_DEBUG_ERROR("SipeConnection::start_connecting: %s",
249 tp_base_connection_change_status(base
, TP_CONNECTION_STATUS_CONNECTING
,
250 TP_CONNECTION_STATUS_REASON_REQUESTED
);
252 /* we need a password */
254 rc
= connect_to_core(self
, error
);
257 /* this doesn't work. Nobody has a clue why */
258 SIPE_DEBUG_INFO("SipeConnection::start_connecting: requesting password from user: %p",
259 self
->password_manager
);
260 tp_simple_password_manager_prompt_async(self
->password_manager
,
265 gchar
*password_file
= g_strdup_printf("%s/.sipe-password",
267 SIPE_DEBUG_INFO("SipeConnection::start_connecting - password file %s",
270 if (g_file_get_contents(password_file
,
273 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting - got password");
274 g_strchomp(self
->password
);
275 rc
= connect_to_core(self
, error
);
277 SIPE_DEBUG_ERROR("SipeConnection::start_connecting: %s",
280 g_free(password_file
);
282 /* to make compiler happy */
283 (void)password_manager_cb
;
290 static gboolean
disconnect_from_core(gpointer data
)
292 TpBaseConnection
*base
= data
;
293 SipeConnection
*self
= SIPE_CONNECTION(base
);
294 struct sipe_backend_private
*telepathy_private
= &self
->private;
295 struct sipe_core_public
*sipe_public
= telepathy_private
->public;
297 SIPE_DEBUG_INFO("disconnect_from_core: %p", sipe_public
);
300 sipe_core_deallocate(sipe_public
);
301 telepathy_private
->public = NULL
;
302 telepathy_private
->transport
= NULL
;
304 g_free(telepathy_private
->ipaddress
);
305 telepathy_private
->ipaddress
= NULL
;
307 SIPE_DEBUG_INFO_NOFORMAT("disconnect_from_core: core deallocated");
309 /* now it is OK to destroy the connection object */
310 tp_base_connection_finish_shutdown(base
);
315 static void shut_down(TpBaseConnection
*base
)
317 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::shut_down");
319 /* this can be called synchronously, defer destruction */
320 g_idle_add(disconnect_from_core
, base
);
323 static GPtrArray
*create_channel_managers(TpBaseConnection
*base
)
325 SipeConnection
*self
= SIPE_CONNECTION(base
);
326 GPtrArray
*channel_managers
= g_ptr_array_new();
328 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::create_channel_managers");
330 self
->password_manager
= tp_simple_password_manager_new(base
);
331 g_ptr_array_add(channel_managers
, self
->password_manager
);
332 SIPE_DEBUG_INFO("SipeConnection::create_channel_managers: password %p",
333 self
->password_manager
);
335 return(channel_managers
);
338 static void sipe_connection_finalize(GObject
*object
)
340 SipeConnection
*self
= SIPE_CONNECTION(object
);
342 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::finalize");
344 g_free(self
->authentication
);
345 g_free(self
->user_agent
);
347 g_free(self
->server
);
348 g_free(self
->password
);
350 g_free(self
->account
);
352 G_OBJECT_CLASS(sipe_connection_parent_class
)->finalize(object
);
356 * Connection class - type implementation
358 static void sipe_connection_class_init(SipeConnectionClass
*klass
)
360 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
361 TpBaseConnectionClass
*base_class
= TP_BASE_CONNECTION_CLASS(klass
);
363 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::class_init");
365 object_class
->finalize
= sipe_connection_finalize
;
367 base_class
->create_handle_repos
= create_handle_repos
;
368 base_class
->start_connecting
= start_connecting
;
369 base_class
->shut_down
= shut_down
;
370 base_class
->create_channel_managers
= create_channel_managers
;
373 static void sipe_connection_init(SIPE_UNUSED_PARAMETER SipeConnection
*self
)
375 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::init");
378 /* create new connection object */
379 TpBaseConnection
*sipe_telepathy_connection_new(TpBaseProtocol
*protocol
,
381 SIPE_UNUSED_PARAMETER GError
**error
)
383 SipeConnection
*conn
= g_object_new(SIPE_TYPE_CONNECTION
,
384 "protocol", tp_base_protocol_get_name(protocol
),
390 SIPE_DEBUG_INFO_NOFORMAT("sipe_telepathy_connection_new");
392 /* initialize private fields */
393 conn
->is_disconnecting
= FALSE
;
395 /* account & login are required fields */
396 conn
->account
= g_strdup(tp_asv_get_string(params
, "account"));
397 conn
->login
= g_strdup(tp_asv_get_string(params
, "login"));
400 value
= tp_asv_get_string(params
, "password");
401 if (value
&& strlen(value
))
402 conn
->password
= g_strdup(value
);
404 conn
->password
= NULL
;
407 value
= tp_asv_get_string(params
, "server");
408 if (value
&& strlen(value
))
409 conn
->server
= g_strdup(value
);
413 /* server port: core expects a string */
414 port
= tp_asv_get_uint32(params
, "port", &valid
);
416 conn
->port
= g_strdup_printf("%d", port
);
421 value
= tp_asv_get_string(params
, "transport");
422 if (sipe_strequal(value
, "auto")) {
423 conn
->transport
= conn
->server
?
424 SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_AUTO
;
425 } else if (sipe_strequal(value
, "tls")) {
426 conn
->transport
= SIPE_TRANSPORT_TLS
;
428 conn
->transport
= SIPE_TRANSPORT_TCP
;
431 /* User-Agent: override */
432 value
= tp_asv_get_string(params
, "useragent");
433 if (value
&& strlen(value
))
434 conn
->user_agent
= g_strdup(value
);
436 conn
->user_agent
= NULL
;
438 /* authentication type */
439 value
= tp_asv_get_string(params
, "authentication");
440 if (value
&& strlen(value
) && strcmp(value
, "ntlm"))
441 conn
->authentication
= g_strdup(value
);
443 conn
->authentication
= NULL
; /* NTLM is default */
445 return(TP_BASE_CONNECTION(conn
));
450 * Backend adaptor functions
452 void sipe_backend_connection_completed(struct sipe_core_public
*sipe_public
)
454 SipeConnection
*self
= SIPE_PUBLIC_TO_CONNECTION
;
455 TpBaseConnection
*base
= TP_BASE_CONNECTION(self
);
457 /* we are only allowed to do this once */
458 if (base
->status
!= TP_CONNECTION_STATUS_CONNECTED
)
459 tp_base_connection_change_status(base
,
460 TP_CONNECTION_STATUS_CONNECTED
,
461 TP_CONNECTION_STATUS_REASON_REQUESTED
);
464 void sipe_backend_connection_error(struct sipe_core_public
*sipe_public
,
465 sipe_connection_error error
,
468 SipeConnection
*self
= SIPE_PUBLIC_TO_CONNECTION
;
469 TpBaseConnection
*base
= TP_BASE_CONNECTION(self
);
470 GHashTable
*details
= tp_asv_new("debug-message", G_TYPE_STRING
, msg
,
472 TpConnectionStatusReason reason
;
475 self
->is_disconnecting
= TRUE
;
478 case SIPE_CONNECTION_ERROR_NETWORK
:
479 reason
= TP_CONNECTION_STATUS_REASON_NETWORK_ERROR
;
480 if (base
->status
== TP_CONNECTION_STATUS_CONNECTING
)
481 name
= TP_ERROR_STR_CONNECTION_FAILED
;
483 name
= TP_ERROR_STR_CONNECTION_LOST
;
486 case SIPE_CONNECTION_ERROR_INVALID_USERNAME
:
487 case SIPE_CONNECTION_ERROR_INVALID_SETTINGS
:
488 case SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED
:
489 case SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
:
490 /* copied from haze code. I agree there should be better ones */
491 reason
= TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED
;
492 name
= TP_ERROR_STR_AUTHENTICATION_FAILED
;
496 reason
= TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED
;
497 name
= TP_ERROR_STR_DISCONNECTED
;
501 SIPE_DEBUG_ERROR("sipe_backend_connection_error: %s (%s)", name
, msg
);
502 tp_base_connection_disconnect_with_dbus_error(base
,
506 g_hash_table_unref(details
);
509 gboolean
sipe_backend_connection_is_disconnecting(struct sipe_core_public
*sipe_public
)
511 SipeConnection
*self
= SIPE_PUBLIC_TO_CONNECTION
;
513 /* disconnect was requested or transport was already disconnected */
514 return(self
->is_disconnecting
||
515 self
->private.transport
== NULL
);
518 gboolean
sipe_backend_connection_is_valid(struct sipe_core_public
*sipe_public
)
520 return(!sipe_backend_connection_is_disconnecting(sipe_public
));
523 const gchar
*sipe_backend_setting(struct sipe_core_public
*sipe_public
,
526 SipeConnection
*self
= SIPE_PUBLIC_TO_CONNECTION
;
530 case SIPE_SETTING_USER_AGENT
:
531 value
= self
->user_agent
;
534 /* @TODO: update when settings are implemented */