2 * @file connection.c Connection API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #define _PURPLE_CONNECTION_C_
31 #include "connection.h"
32 #include "dbus-maybe.h"
43 #define KEEPALIVE_INTERVAL 30
45 static GList
*connections
= NULL
;
46 static GList
*connections_connecting
= NULL
;
47 static PurpleConnectionUiOps
*connection_ui_ops
= NULL
;
49 static int connections_handle
;
52 send_keepalive(gpointer data
)
54 PurpleConnection
*gc
= data
;
55 PurplePluginProtocolInfo
*prpl_info
= NULL
;
60 /* Only send keep-alives if we haven't heard from the
63 if ((time(NULL
) - gc
->last_received
) < KEEPALIVE_INTERVAL
)
69 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
71 if (prpl_info
&& prpl_info
->keepalive
)
72 prpl_info
->keepalive(gc
);
78 update_keepalive(PurpleConnection
*gc
, gboolean on
)
80 PurplePluginProtocolInfo
*prpl_info
= NULL
;
82 if (gc
!= NULL
&& gc
->prpl
!= NULL
)
83 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
85 if (!prpl_info
|| !prpl_info
->keepalive
)
88 if (on
&& !gc
->keepalive
)
90 purple_debug_info("connection", "Activating keepalive.\n");
91 gc
->keepalive
= purple_timeout_add_seconds(KEEPALIVE_INTERVAL
, send_keepalive
, gc
);
93 else if (!on
&& gc
->keepalive
> 0)
95 purple_debug_info("connection", "Deactivating keepalive.\n");
96 purple_timeout_remove(gc
->keepalive
);
102 purple_connection_new(PurpleAccount
*account
, gboolean regist
, const char *password
)
104 _purple_connection_new(account
, regist
, password
);
108 _purple_connection_new(PurpleAccount
*account
, gboolean regist
, const char *password
)
110 PurpleConnection
*gc
;
112 PurplePluginProtocolInfo
*prpl_info
;
114 g_return_if_fail(account
!= NULL
);
116 if (!purple_account_is_disconnected(account
))
119 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
122 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
126 message
= g_strdup_printf(_("Missing protocol plugin for %s"),
127 purple_account_get_username(account
));
128 purple_notify_error(NULL
, regist
? _("Registration Error") :
129 _("Connection Error"), message
, NULL
);
136 if (prpl_info
->register_user
== NULL
)
141 if (((password
== NULL
) || (*password
== '\0')) &&
142 !(prpl_info
->options
& OPT_PROTO_NO_PASSWORD
) &&
143 !(prpl_info
->options
& OPT_PROTO_PASSWORD_OPTIONAL
))
145 purple_debug_error("connection", "Can not connect to account %s without "
146 "a password.\n", purple_account_get_username(account
));
151 gc
= g_new0(PurpleConnection
, 1);
152 PURPLE_DBUS_REGISTER_POINTER(gc
, PurpleConnection
);
155 if ((password
!= NULL
) && (*password
!= '\0'))
156 gc
->password
= g_strdup(password
);
157 purple_connection_set_account(gc
, account
);
158 purple_connection_set_state(gc
, PURPLE_CONNECTING
);
159 connections
= g_list_append(connections
, gc
);
160 purple_account_set_connection(account
, gc
);
162 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc
);
166 purple_debug_info("connection", "Registering. gc = %p\n", gc
);
168 /* set this so we don't auto-reconnect after registering */
169 gc
->wants_to_die
= TRUE
;
171 prpl_info
->register_user(account
);
175 purple_debug_info("connection", "Connecting. gc = %p\n", gc
);
177 purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account
);
178 prpl_info
->login(account
);
182 purple_connection_new_unregister(PurpleAccount
*account
, const char *password
, PurpleAccountUnregistrationCb cb
, void *user_data
)
184 _purple_connection_new_unregister(account
, password
, cb
, user_data
);
188 _purple_connection_new_unregister(PurpleAccount
*account
, const char *password
, PurpleAccountUnregistrationCb cb
, void *user_data
)
190 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
191 PurpleConnection
*gc
;
193 PurplePluginProtocolInfo
*prpl_info
;
195 g_return_if_fail(account
!= NULL
);
197 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
200 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
204 message
= g_strdup_printf(_("Missing protocol plugin for %s"),
205 purple_account_get_username(account
));
206 purple_notify_error(NULL
, _("Unregistration Error"), message
, NULL
);
211 if (!purple_account_is_disconnected(account
)) {
212 prpl_info
->unregister_user(account
, cb
, user_data
);
216 if (((password
== NULL
) || (*password
== '\0')) &&
217 !(prpl_info
->options
& OPT_PROTO_NO_PASSWORD
) &&
218 !(prpl_info
->options
& OPT_PROTO_PASSWORD_OPTIONAL
))
220 purple_debug_error("connection", "Can not connect to account %s without "
221 "a password.\n", purple_account_get_username(account
));
225 gc
= g_new0(PurpleConnection
, 1);
226 PURPLE_DBUS_REGISTER_POINTER(gc
, PurpleConnection
);
229 if ((password
!= NULL
) && (*password
!= '\0'))
230 gc
->password
= g_strdup(password
);
231 purple_connection_set_account(gc
, account
);
232 purple_connection_set_state(gc
, PURPLE_CONNECTING
);
233 connections
= g_list_append(connections
, gc
);
234 purple_account_set_connection(account
, gc
);
236 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc
);
238 purple_debug_info("connection", "Unregistering. gc = %p\n", gc
);
240 prpl_info
->unregister_user(account
, cb
, user_data
);
244 purple_connection_destroy(PurpleConnection
*gc
)
246 _purple_connection_destroy(gc
);
250 _purple_connection_destroy(PurpleConnection
*gc
)
252 PurpleAccount
*account
;
254 PurplePluginProtocolInfo
*prpl_info
= NULL
;
255 gboolean remove
= FALSE
;
257 g_return_if_fail(gc
!= NULL
);
259 account
= purple_connection_get_account(gc
);
261 purple_debug_info("connection", "Disconnecting connection %p\n", gc
);
263 if (purple_connection_get_state(gc
) != PURPLE_CONNECTING
)
266 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc
);
268 while (gc
->buddy_chats
)
270 PurpleConversation
*b
= gc
->buddy_chats
->data
;
272 gc
->buddy_chats
= g_slist_remove(gc
->buddy_chats
, b
);
273 purple_conv_chat_left(PURPLE_CONV_CHAT(b
));
276 update_keepalive(gc
, FALSE
);
278 purple_proxy_connect_cancel_with_handle(gc
);
280 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
281 if (prpl_info
->close
)
282 (prpl_info
->close
)(gc
);
284 /* Clear out the proto data that was freed in the prpl close method*/
285 buddies
= purple_find_buddies(account
, NULL
);
286 while (buddies
!= NULL
) {
287 PurpleBuddy
*buddy
= buddies
->data
;
288 buddy
->proto_data
= NULL
;
289 buddies
= g_slist_delete_link(buddies
, buddies
);
292 connections
= g_list_remove(connections
, gc
);
294 purple_connection_set_state(gc
, PURPLE_DISCONNECTED
);
297 purple_blist_remove_account(account
);
299 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc
);
301 purple_account_request_close_with_account(account
);
302 purple_request_close_with_handle(gc
);
303 purple_notify_close_with_handle(gc
);
305 purple_debug_info("connection", "Destroying connection %p\n", gc
);
307 purple_account_set_connection(account
, NULL
);
309 g_free(gc
->password
);
310 g_free(gc
->display_name
);
312 if (gc
->disconnect_timeout
)
313 purple_timeout_remove(gc
->disconnect_timeout
);
315 PURPLE_DBUS_UNREGISTER_POINTER(gc
);
330 purple_connection_set_state(PurpleConnection
*gc
, PurpleConnectionState state
)
332 PurpleConnectionUiOps
*ops
;
334 g_return_if_fail(gc
!= NULL
);
336 if (gc
->state
== state
)
341 ops
= purple_connections_get_ui_ops();
343 if (gc
->state
== PURPLE_CONNECTING
) {
344 connections_connecting
= g_list_append(connections_connecting
, gc
);
347 connections_connecting
= g_list_remove(connections_connecting
, gc
);
350 if (gc
->state
== PURPLE_CONNECTED
) {
351 PurpleAccount
*account
;
352 PurplePresence
*presence
;
354 account
= purple_connection_get_account(gc
);
355 presence
= purple_account_get_presence(account
);
357 /* Set the time the account came online */
358 purple_presence_set_login_time(presence
, time(NULL
));
360 if (purple_prefs_get_bool("/purple/logging/log_system"))
362 PurpleLog
*log
= purple_account_get_log(account
, TRUE
);
366 char *msg
= g_strdup_printf(_("+++ %s signed on"),
367 purple_account_get_username(account
));
368 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
369 purple_account_get_username(account
),
370 purple_presence_get_login_time(presence
),
376 if (ops
!= NULL
&& ops
->connected
!= NULL
)
379 purple_blist_add_account(account
);
381 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc
);
383 serv_set_permit_deny(gc
);
385 update_keepalive(gc
, TRUE
);
387 else if (gc
->state
== PURPLE_DISCONNECTED
) {
388 PurpleAccount
*account
= purple_connection_get_account(gc
);
390 if (purple_prefs_get_bool("/purple/logging/log_system"))
392 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
396 char *msg
= g_strdup_printf(_("+++ %s signed off"),
397 purple_account_get_username(account
));
398 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
399 purple_account_get_username(account
), time(NULL
),
405 purple_account_destroy_log(account
);
407 if (ops
!= NULL
&& ops
->disconnected
!= NULL
)
408 ops
->disconnected(gc
);
413 purple_connection_set_account(PurpleConnection
*gc
, PurpleAccount
*account
)
415 g_return_if_fail(gc
!= NULL
);
416 g_return_if_fail(account
!= NULL
);
418 gc
->account
= account
;
422 purple_connection_set_display_name(PurpleConnection
*gc
, const char *name
)
424 g_return_if_fail(gc
!= NULL
);
426 g_free(gc
->display_name
);
427 gc
->display_name
= g_strdup(name
);
430 PurpleConnectionState
431 purple_connection_get_state(const PurpleConnection
*gc
)
433 g_return_val_if_fail(gc
!= NULL
, PURPLE_DISCONNECTED
);
439 purple_connection_get_account(const PurpleConnection
*gc
)
441 g_return_val_if_fail(gc
!= NULL
, NULL
);
447 purple_connection_get_prpl(const PurpleConnection
*gc
)
449 g_return_val_if_fail(gc
!= NULL
, NULL
);
455 purple_connection_get_password(const PurpleConnection
*gc
)
457 g_return_val_if_fail(gc
!= NULL
, NULL
);
459 return gc
->password
? gc
->password
: gc
->account
->password
;
463 purple_connection_get_display_name(const PurpleConnection
*gc
)
465 g_return_val_if_fail(gc
!= NULL
, NULL
);
467 return gc
->display_name
;
471 purple_connection_update_progress(PurpleConnection
*gc
, const char *text
,
472 size_t step
, size_t count
)
474 PurpleConnectionUiOps
*ops
;
476 g_return_if_fail(gc
!= NULL
);
477 g_return_if_fail(text
!= NULL
);
478 g_return_if_fail(step
< count
);
479 g_return_if_fail(count
> 1);
481 ops
= purple_connections_get_ui_ops();
483 if (ops
!= NULL
&& ops
->connect_progress
!= NULL
)
484 ops
->connect_progress(gc
, text
, step
, count
);
488 purple_connection_notice(PurpleConnection
*gc
, const char *text
)
490 PurpleConnectionUiOps
*ops
;
492 g_return_if_fail(gc
!= NULL
);
493 g_return_if_fail(text
!= NULL
);
495 ops
= purple_connections_get_ui_ops();
497 if (ops
!= NULL
&& ops
->notice
!= NULL
)
498 ops
->notice(gc
, text
);
502 purple_connection_disconnect_cb(gpointer data
)
504 PurpleAccount
*account
= data
;
505 char *password
= g_strdup(purple_account_get_password(account
));
506 purple_account_disconnect(account
);
507 purple_account_set_password(account
, password
);
513 purple_connection_error(PurpleConnection
*gc
, const char *text
)
515 /* prpls that have not been updated to use disconnection reasons will
516 * be setting wants_to_die before calling this function, so choose
517 * PURPLE_CONNECTION_ERROR_OTHER_ERROR (which is fatal) if it's true,
518 * and PURPLE_CONNECTION_ERROR_NETWORK_ERROR (which isn't) if not. See
519 * the documentation in connection.h.
521 PurpleConnectionError reason
= gc
->wants_to_die
522 ? PURPLE_CONNECTION_ERROR_OTHER_ERROR
523 : PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
524 purple_connection_error_reason (gc
, reason
, text
);
528 purple_connection_error_reason (PurpleConnection
*gc
,
529 PurpleConnectionError reason
,
530 const char *description
)
532 PurpleConnectionUiOps
*ops
;
534 g_return_if_fail(gc
!= NULL
);
535 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
536 * being the last member of the PurpleConnectionError enum in
537 * connection.h; if other reasons are added after it, this check should
540 if (reason
> PURPLE_CONNECTION_ERROR_OTHER_ERROR
) {
541 purple_debug_error("connection",
542 "purple_connection_error_reason: reason %u isn't a "
543 "valid reason\n", reason
);
544 reason
= PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
547 if (description
== NULL
) {
548 purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n");
549 description
= _("Unknown error");
552 /* If we've already got one error, we don't need any more */
553 if (gc
->disconnect_timeout
)
556 gc
->wants_to_die
= purple_connection_error_is_fatal (reason
);
558 ops
= purple_connections_get_ui_ops();
562 if (ops
->report_disconnect_reason
!= NULL
)
563 ops
->report_disconnect_reason (gc
, reason
, description
);
564 if (ops
->report_disconnect
!= NULL
)
565 ops
->report_disconnect (gc
, description
);
568 purple_signal_emit(purple_connections_get_handle(), "connection-error",
569 gc
, reason
, description
);
571 gc
->disconnect_timeout
= purple_timeout_add(0, purple_connection_disconnect_cb
,
572 purple_connection_get_account(gc
));
576 purple_connection_ssl_error (PurpleConnection
*gc
,
577 PurpleSslErrorType ssl_error
)
579 PurpleConnectionError reason
;
582 case PURPLE_SSL_HANDSHAKE_FAILED
:
583 reason
= PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
;
585 case PURPLE_SSL_CONNECT_FAILED
:
586 reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
588 case PURPLE_SSL_CERTIFICATE_INVALID
:
589 /* TODO: maybe PURPLE_SSL_* should be more specific? */
590 reason
= PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
;
593 g_assert_not_reached ();
594 reason
= PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
;
597 purple_connection_error_reason (gc
, reason
,
598 purple_ssl_strerror(ssl_error
));
602 purple_connection_error_is_fatal (PurpleConnectionError reason
)
606 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR
:
607 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
:
609 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME
:
610 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
:
611 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
:
612 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
:
613 case PURPLE_CONNECTION_ERROR_NAME_IN_USE
:
614 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
:
615 case PURPLE_CONNECTION_ERROR_OTHER_ERROR
:
616 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED
:
617 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED
:
618 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED
:
619 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED
:
620 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH
:
621 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH
:
622 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED
:
623 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
:
626 g_return_val_if_reached(TRUE
);
631 purple_connections_disconnect_all(void)
634 PurpleConnection
*gc
;
636 while ((l
= purple_connections_get_all()) != NULL
) {
638 gc
->wants_to_die
= TRUE
;
639 purple_account_disconnect(gc
->account
);
644 purple_connections_get_all(void)
650 purple_connections_get_connecting(void)
652 return connections_connecting
;
656 purple_connections_set_ui_ops(PurpleConnectionUiOps
*ops
)
658 connection_ui_ops
= ops
;
661 PurpleConnectionUiOps
*
662 purple_connections_get_ui_ops(void)
664 return connection_ui_ops
;
668 purple_connections_init(void)
670 void *handle
= purple_connections_get_handle();
672 purple_signal_register(handle
, "signing-on",
673 purple_marshal_VOID__POINTER
, NULL
, 1,
674 purple_value_new(PURPLE_TYPE_SUBTYPE
,
675 PURPLE_SUBTYPE_CONNECTION
));
677 purple_signal_register(handle
, "signed-on",
678 purple_marshal_VOID__POINTER
, NULL
, 1,
679 purple_value_new(PURPLE_TYPE_SUBTYPE
,
680 PURPLE_SUBTYPE_CONNECTION
));
682 purple_signal_register(handle
, "signing-off",
683 purple_marshal_VOID__POINTER
, NULL
, 1,
684 purple_value_new(PURPLE_TYPE_SUBTYPE
,
685 PURPLE_SUBTYPE_CONNECTION
));
687 purple_signal_register(handle
, "signed-off",
688 purple_marshal_VOID__POINTER
, NULL
, 1,
689 purple_value_new(PURPLE_TYPE_SUBTYPE
,
690 PURPLE_SUBTYPE_CONNECTION
));
692 purple_signal_register(handle
, "connection-error",
693 purple_marshal_VOID__POINTER_INT_POINTER
, NULL
, 3,
694 purple_value_new(PURPLE_TYPE_SUBTYPE
,
695 PURPLE_SUBTYPE_CONNECTION
),
696 purple_value_new(PURPLE_TYPE_ENUM
),
697 purple_value_new(PURPLE_TYPE_STRING
));
702 purple_connections_uninit(void)
704 purple_signals_unregister_by_instance(purple_connections_get_handle());
708 purple_connections_get_handle(void)
710 return &connections_handle
;