telepathy: connection must have a self-handle
[siplcs.git] / src / telepathy / telepathy-connection.c
blob653eb9af8ac258cdb8a38402f0e625bc5464adde
1 /**
2 * @file telepathy-connection.c
4 * pidgin-sipe
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
23 #include <string.h>
25 #include <glib-object.h>
26 #include <telepathy-glib/base-connection.h>
27 #include <telepathy-glib/base-protocol.h>
28 #include <telepathy-glib/handle-repo-dynamic.h>
29 #include <telepathy-glib/simple-password-manager.h>
30 #include <telepathy-glib/telepathy-glib.h>
32 #include "sipe-backend.h"
33 #include "sipe-common.h"
34 #include "sipe-core.h"
36 #include "telepathy-private.h"
38 G_BEGIN_DECLS
40 * Connection class - data structures
42 typedef struct _SipeConnectionClass {
43 TpBaseConnectionClass parent_class;
44 } SipeConnectionClass;
46 typedef struct _SipeConnection {
47 TpBaseConnection parent;
49 TpSimplePasswordManager *password_manager;
51 struct sipe_backend_private private;
52 gchar *account;
53 gchar *login;
54 gchar *password;
55 gchar *server;
56 gchar *port;
57 guint transport;
58 gboolean is_disconnecting;
59 } SipeConnection;
61 #define SIPE_PUBLIC_TO_CONNECTION sipe_public->backend_private->connection
64 * Connection class - type macros
66 static GType sipe_connection_get_type(void) G_GNUC_CONST;
67 #define SIPE_TYPE_CONNECTION \
68 (sipe_connection_get_type())
69 #define SIPE_CONNECTION(obj) \
70 (G_TYPE_CHECK_INSTANCE_CAST((obj), SIPE_TYPE_CONNECTION, \
71 SipeConnection))
72 G_END_DECLS
75 * Connection class - type definition
77 G_DEFINE_TYPE(SipeConnection,
78 sipe_connection,
79 TP_TYPE_BASE_CONNECTION)
82 * Connection class - instance methods
84 static gchar *normalize_contact(SIPE_UNUSED_PARAMETER TpHandleRepoIface *repo,
85 const gchar *id,
86 SIPE_UNUSED_PARAMETER gpointer context,
87 GError **error)
89 return(sipe_telepathy_protocol_normalize_contact(NULL, id, error));
92 static void create_handle_repos(SIPE_UNUSED_PARAMETER TpBaseConnection *conn,
93 TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
95 repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new(TP_HANDLE_TYPE_CONTACT,
96 normalize_contact,
97 NULL);
100 static gboolean connect_to_core(SipeConnection *self,
101 GError **error)
103 gchar *login_domain = NULL;
104 gchar *login_account = NULL;
105 struct sipe_core_public *sipe_public;
106 const gchar *errmsg;
108 /* login name specified? */
109 if (self->login && strlen(self->login)) {
110 /* Allowed domain-account separators are / or \ */
111 gchar **domain_user = g_strsplit_set(self->login, "/\\", 2);
112 gboolean has_domain = domain_user[1] != NULL;
113 SIPE_DEBUG_INFO("connect_to_core: login '%s'", self->login);
114 login_domain = has_domain ? g_strdup(domain_user[0]) : NULL;
115 login_account = g_strdup(domain_user[has_domain ? 1 : 0]);
116 SIPE_DEBUG_INFO("connect_to_core: auth domain '%s' user '%s'",
117 login_domain ? login_domain : "",
118 login_account);
119 g_strfreev(domain_user);
122 sipe_public = sipe_core_allocate(self->account,
123 login_domain, login_account,
124 self->password,
125 NULL, /* @TODO: email */
126 NULL, /* @TODO: email_url */
127 &errmsg);
128 g_free(login_domain);
129 g_free(login_account);
131 SIPE_DEBUG_INFO("connect_to_core: created %p", sipe_public);
133 if (sipe_public) {
134 struct sipe_backend_private *telepathy_private = &self->private;
136 /* initialize backend private data */
137 sipe_public->backend_private = telepathy_private;
138 telepathy_private->public = sipe_public;
139 telepathy_private->connection = self;
140 telepathy_private->transport = NULL;
141 telepathy_private->ipaddress = NULL;
143 /* map option list to flags - default is NTLM */
144 SIPE_CORE_FLAG_UNSET(KRB5);
145 SIPE_CORE_FLAG_UNSET(TLS_DSK);
146 SIPE_CORE_FLAG_UNSET(SSO);
147 /* @TODO: add parameters for these */
149 sipe_core_transport_sip_connect(sipe_public,
150 self->transport,
151 self->server,
152 self->port);
154 return(TRUE);
155 } else {
156 g_set_error_literal(error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
157 errmsg);
158 return(FALSE);
162 static void password_manager_cb(GObject *source,
163 GAsyncResult *result,
164 gpointer data)
166 SipeConnection *self = data;
167 TpBaseConnection *base = TP_BASE_CONNECTION(self);
168 GError *error = NULL;
169 const GString *password = tp_simple_password_manager_prompt_finish(
170 TP_SIMPLE_PASSWORD_MANAGER(source),
171 result,
172 &error);
174 if (error) {
175 SIPE_DEBUG_ERROR("password_manager_cb: failed: %s",
176 error->message);
178 if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) {
179 tp_base_connection_disconnect_with_dbus_error(base,
180 tp_error_get_dbus_name(error->code),
181 NULL,
182 TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
184 g_error_free(error);
185 } else {
187 g_free(self->password);
188 self->password = g_strdup(password->str);
190 if (!connect_to_core(self, &error)) {
191 if (base->status != TP_CONNECTION_STATUS_DISCONNECTED) {
192 tp_base_connection_disconnect_with_dbus_error(base,
193 tp_error_get_dbus_name(error->code),
194 NULL,
195 TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
197 g_error_free(error);
202 static gboolean start_connecting(TpBaseConnection *base,
203 GError **error)
205 SipeConnection *self = SIPE_CONNECTION(base);
206 gboolean rc = TRUE;
207 gchar *uri = sipe_telepathy_protocol_normalize_contact(NULL,
208 self->account,
209 error);
211 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting");
213 /* set up mandatory self-handle */
214 if (uri) {
215 base->self_handle = tp_handle_ensure(tp_base_connection_get_handles(base,
216 TP_HANDLE_TYPE_CONTACT),
217 uri,
218 NULL,
219 error);
220 g_free(uri);
221 if (!base->self_handle) {
222 SIPE_DEBUG_ERROR("SipeConnection::start_connecting: self handle creation failed: %s",
223 (*error)->message);
224 return(FALSE);
226 } else {
227 SIPE_DEBUG_ERROR("SipeConnection::start_connecting: %s",
228 (*error)->message);
229 return(FALSE);
232 tp_base_connection_change_status(base, TP_CONNECTION_STATUS_CONNECTING,
233 TP_CONNECTION_STATUS_REASON_REQUESTED);
235 /* we need a password */
236 if (self->password)
237 rc = connect_to_core(self, error);
238 else {
239 #if 0
240 /* this doesn't work. Nobody has a clue why */
241 SIPE_DEBUG_INFO("SipeConnection::start_connecting: requesting password from user: %p",
242 self->password_manager);
243 tp_simple_password_manager_prompt_async(self->password_manager,
244 password_manager_cb,
245 self);
246 #else
247 /* MAJOR HACK */
248 gchar *password_file = g_strdup_printf("%s/.sipe-password",
249 g_getenv("HOME"));
250 SIPE_DEBUG_INFO("SipeConnection::start_connecting - password file %s",
251 password_file);
252 rc = FALSE;
253 if (g_file_get_contents(password_file,
254 &self->password,
255 NULL, error)) {
256 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::start_connecting - got password");
257 g_strchomp(self->password);
258 rc = connect_to_core(self, error);
259 } else {
260 SIPE_DEBUG_ERROR("SipeConnection::start_connecting: %s",
261 (*error)->message);
263 g_free(password_file);
265 /* to make compiler happy */
266 (void)password_manager_cb;
267 #endif
270 return(rc);
273 static gboolean disconnect_from_core(gpointer data)
275 TpBaseConnection *base = data;
276 SipeConnection *self = SIPE_CONNECTION(base);
277 struct sipe_backend_private *telepathy_private = &self->private;
278 struct sipe_core_public *sipe_public = telepathy_private->public;
280 SIPE_DEBUG_INFO("disconnect_from_core: %p", sipe_public);
282 if (sipe_public)
283 sipe_core_deallocate(sipe_public);
284 telepathy_private->public = NULL;
285 telepathy_private->transport = NULL;
287 g_free(telepathy_private->ipaddress);
288 telepathy_private->ipaddress = NULL;
290 SIPE_DEBUG_INFO_NOFORMAT("disconnect_from_core: core deallocated");
292 /* now it is OK to destroy the connection object */
293 tp_base_connection_finish_shutdown(base);
295 return(FALSE);
298 static void shut_down(TpBaseConnection *base)
300 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::shut_down");
302 /* this can be called synchronously, defer destruction */
303 g_idle_add(disconnect_from_core, base);
306 static GPtrArray *create_channel_managers(TpBaseConnection *base)
308 SipeConnection *self = SIPE_CONNECTION(base);
309 GPtrArray *channel_managers = g_ptr_array_new();
311 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::create_channel_managers");
313 self->password_manager = tp_simple_password_manager_new(base);
314 g_ptr_array_add(channel_managers, self->password_manager);
315 SIPE_DEBUG_INFO("SipeConnection::create_channel_managers: password %p",
316 self->password_manager);
318 return(channel_managers);
321 static void sipe_connection_finalize(GObject *object)
323 SipeConnection *self = SIPE_CONNECTION(object);
325 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::finalize");
327 g_free(self->port);
328 g_free(self->server);
329 g_free(self->password);
330 g_free(self->login);
331 g_free(self->account);
333 G_OBJECT_CLASS(sipe_connection_parent_class)->finalize(object);
337 * Connection class - type implementation
339 static void sipe_connection_class_init(SipeConnectionClass *klass)
341 GObjectClass *object_class = G_OBJECT_CLASS(klass);
342 TpBaseConnectionClass *base_class = TP_BASE_CONNECTION_CLASS(klass);
344 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::class_init");
346 object_class->finalize = sipe_connection_finalize;
348 base_class->create_handle_repos = create_handle_repos;
349 base_class->start_connecting = start_connecting;
350 base_class->shut_down = shut_down;
351 base_class->create_channel_managers = create_channel_managers;
354 static void sipe_connection_init(SIPE_UNUSED_PARAMETER SipeConnection *self)
356 SIPE_DEBUG_INFO_NOFORMAT("SipeConnection::init");
359 /* create new connection object */
360 TpBaseConnection *sipe_telepathy_connection_new(TpBaseProtocol *protocol,
361 GHashTable *params,
362 SIPE_UNUSED_PARAMETER GError **error)
364 const gchar *password = tp_asv_get_string(params, "password");
365 const gchar *server = tp_asv_get_string(params, "server");
366 const gchar *transport = tp_asv_get_string(params, "transport");
367 SipeConnection *conn = g_object_new(SIPE_TYPE_CONNECTION,
368 "protocol", tp_base_protocol_get_name(protocol),
369 NULL);
370 guint port;
371 gboolean valid;
373 SIPE_DEBUG_INFO_NOFORMAT("sipe_telepathy_connection_new");
375 /* initialize private fields */
376 conn->is_disconnecting = FALSE;
378 /* account & login are required fields */
379 conn->account = g_strdup(tp_asv_get_string(params, "account"));
380 conn->login = g_strdup(tp_asv_get_string(params, "login"));
382 /* password */
383 if (password && strlen(password))
384 conn->password = g_strdup(password);
385 else
386 conn->password = NULL;
388 /* server name */
389 if (server && strlen(server))
390 conn->server = g_strdup(server);
391 else
392 conn->server = NULL;
394 /* server port: core expects a string */
395 port = tp_asv_get_uint32(params, "port", &valid);
396 if (valid)
397 conn->port = g_strdup_printf("%d", port);
398 else
399 conn->port = NULL;
401 /* transport type */
402 if (sipe_strequal(transport, "auto")) {
403 conn->transport = conn->server ?
404 SIPE_TRANSPORT_TLS : SIPE_TRANSPORT_AUTO;
405 } else if (sipe_strequal(transport, "tls")) {
406 conn->transport = SIPE_TRANSPORT_TLS;
407 } else {
408 conn->transport = SIPE_TRANSPORT_TCP;
411 return(TP_BASE_CONNECTION(conn));
416 * Backend adaptor functions
418 void sipe_backend_connection_completed(struct sipe_core_public *sipe_public)
420 SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION;
421 tp_base_connection_change_status(TP_BASE_CONNECTION(self),
422 TP_CONNECTION_STATUS_CONNECTED,
423 TP_CONNECTION_STATUS_REASON_REQUESTED);
426 void sipe_backend_connection_error(struct sipe_core_public *sipe_public,
427 sipe_connection_error error,
428 const gchar *msg)
430 SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION;
431 TpBaseConnection *base = TP_BASE_CONNECTION(self);
432 GHashTable *details = tp_asv_new("debug-message", G_TYPE_STRING, msg,
433 NULL);
434 TpConnectionStatusReason reason;
435 const gchar *name;
437 self->is_disconnecting = TRUE;
439 switch (error) {
440 case SIPE_CONNECTION_ERROR_NETWORK:
441 reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR;
442 if (base->status == TP_CONNECTION_STATUS_CONNECTING)
443 name = TP_ERROR_STR_CONNECTION_FAILED;
444 else
445 name = TP_ERROR_STR_CONNECTION_LOST;
446 break;
448 case SIPE_CONNECTION_ERROR_INVALID_USERNAME:
449 case SIPE_CONNECTION_ERROR_INVALID_SETTINGS:
450 case SIPE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
451 case SIPE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
452 /* copied from haze code. I agree there should be better ones */
453 reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED;
454 name = TP_ERROR_STR_AUTHENTICATION_FAILED;
455 break;
457 default:
458 reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED;
459 name = TP_ERROR_STR_DISCONNECTED;
460 break;
463 SIPE_DEBUG_ERROR("sipe_backend_connection_error: %s (%s)", name, msg);
464 tp_base_connection_disconnect_with_dbus_error(base,
465 name,
466 details,
467 reason);
468 g_hash_table_unref(details);
471 gboolean sipe_backend_connection_is_disconnecting(struct sipe_core_public *sipe_public)
473 SipeConnection *self = SIPE_PUBLIC_TO_CONNECTION;
475 /* disconnect was requested or transport was already disconnected */
476 return(self->is_disconnecting ||
477 self->private.transport == NULL);
480 gboolean sipe_backend_connection_is_valid(struct sipe_core_public *sipe_public)
482 return(!sipe_backend_connection_is_disconnecting(sipe_public));
486 Local Variables:
487 mode: c
488 c-file-style: "bsd"
489 indent-tabs-mode: t
490 tab-width: 8
491 End: