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
;
57 /* Only send keep-alives if we haven't heard from the
60 if ((time(NULL
) - gc
->last_received
) < KEEPALIVE_INTERVAL
)
63 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
64 if (prpl_info
->keepalive
)
65 prpl_info
->keepalive(gc
);
71 update_keepalive(PurpleConnection
*gc
, gboolean on
)
73 PurplePluginProtocolInfo
*prpl_info
= NULL
;
75 if (gc
!= NULL
&& gc
->prpl
!= NULL
)
76 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
78 if (!prpl_info
|| !prpl_info
->keepalive
)
81 if (on
&& !gc
->keepalive
)
83 purple_debug_info("connection", "Activating keepalive.\n");
84 gc
->keepalive
= purple_timeout_add_seconds(KEEPALIVE_INTERVAL
, send_keepalive
, gc
);
86 else if (!on
&& gc
->keepalive
> 0)
88 purple_debug_info("connection", "Deactivating keepalive.\n");
89 purple_timeout_remove(gc
->keepalive
);
95 purple_connection_new(PurpleAccount
*account
, gboolean regist
, const char *password
)
97 _purple_connection_new(account
, regist
, password
);
101 _purple_connection_new(PurpleAccount
*account
, gboolean regist
, const char *password
)
103 PurpleConnection
*gc
;
105 PurplePluginProtocolInfo
*prpl_info
;
107 g_return_if_fail(account
!= NULL
);
109 if (!purple_account_is_disconnected(account
))
112 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
115 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
119 message
= g_strdup_printf(_("Missing protocol plugin for %s"),
120 purple_account_get_username(account
));
121 purple_notify_error(NULL
, regist
? _("Registration Error") :
122 _("Connection Error"), message
, NULL
);
129 if (prpl_info
->register_user
== NULL
)
134 if (((password
== NULL
) || (*password
== '\0')) &&
135 !(prpl_info
->options
& OPT_PROTO_NO_PASSWORD
) &&
136 !(prpl_info
->options
& OPT_PROTO_PASSWORD_OPTIONAL
))
138 purple_debug_error("connection", "Cannot connect to account %s without "
139 "a password.\n", purple_account_get_username(account
));
144 gc
= g_new0(PurpleConnection
, 1);
145 PURPLE_DBUS_REGISTER_POINTER(gc
, PurpleConnection
);
148 if ((password
!= NULL
) && (*password
!= '\0'))
149 gc
->password
= g_strdup(password
);
150 purple_connection_set_account(gc
, account
);
151 purple_connection_set_state(gc
, PURPLE_CONNECTING
);
152 connections
= g_list_append(connections
, gc
);
153 purple_account_set_connection(account
, gc
);
155 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc
);
159 purple_debug_info("connection", "Registering. gc = %p\n", gc
);
161 /* set this so we don't auto-reconnect after registering */
162 gc
->wants_to_die
= TRUE
;
164 prpl_info
->register_user(account
);
168 purple_debug_info("connection", "Connecting. gc = %p\n", gc
);
170 purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account
);
171 prpl_info
->login(account
);
175 purple_connection_new_unregister(PurpleAccount
*account
, const char *password
, PurpleAccountUnregistrationCb cb
, void *user_data
)
177 _purple_connection_new_unregister(account
, password
, cb
, user_data
);
181 _purple_connection_new_unregister(PurpleAccount
*account
, const char *password
, PurpleAccountUnregistrationCb cb
, void *user_data
)
183 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
184 PurpleConnection
*gc
;
186 PurplePluginProtocolInfo
*prpl_info
;
188 g_return_if_fail(account
!= NULL
);
190 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
193 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
197 message
= g_strdup_printf(_("Missing protocol plugin for %s"),
198 purple_account_get_username(account
));
199 purple_notify_error(NULL
, _("Unregistration Error"), message
, NULL
);
204 if (!purple_account_is_disconnected(account
)) {
205 prpl_info
->unregister_user(account
, cb
, user_data
);
209 if (((password
== NULL
) || (*password
== '\0')) &&
210 !(prpl_info
->options
& OPT_PROTO_NO_PASSWORD
) &&
211 !(prpl_info
->options
& OPT_PROTO_PASSWORD_OPTIONAL
))
213 purple_debug_error("connection", "Cannot connect to account %s without "
214 "a password.\n", purple_account_get_username(account
));
218 gc
= g_new0(PurpleConnection
, 1);
219 PURPLE_DBUS_REGISTER_POINTER(gc
, PurpleConnection
);
222 if ((password
!= NULL
) && (*password
!= '\0'))
223 gc
->password
= g_strdup(password
);
224 purple_connection_set_account(gc
, account
);
225 purple_connection_set_state(gc
, PURPLE_CONNECTING
);
226 connections
= g_list_append(connections
, gc
);
227 purple_account_set_connection(account
, gc
);
229 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc
);
231 purple_debug_info("connection", "Unregistering. gc = %p\n", gc
);
233 prpl_info
->unregister_user(account
, cb
, user_data
);
237 purple_connection_destroy(PurpleConnection
*gc
)
239 _purple_connection_destroy(gc
);
243 _purple_connection_destroy(PurpleConnection
*gc
)
245 PurpleAccount
*account
;
247 PurplePluginProtocolInfo
*prpl_info
= NULL
;
248 gboolean remove
= FALSE
;
250 g_return_if_fail(gc
!= NULL
);
252 account
= purple_connection_get_account(gc
);
254 purple_debug_info("connection", "Disconnecting connection %p\n", gc
);
256 if (purple_connection_get_state(gc
) != PURPLE_CONNECTING
)
259 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc
);
261 while (gc
->buddy_chats
)
263 PurpleConversation
*b
= gc
->buddy_chats
->data
;
265 gc
->buddy_chats
= g_slist_remove(gc
->buddy_chats
, b
);
266 purple_conv_chat_left(PURPLE_CONV_CHAT(b
));
269 update_keepalive(gc
, FALSE
);
271 purple_proxy_connect_cancel_with_handle(gc
);
273 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
274 if (prpl_info
->close
)
275 (prpl_info
->close
)(gc
);
277 /* Clear out the proto data that was freed in the prpl close method*/
278 buddies
= purple_find_buddies(account
, NULL
);
279 while (buddies
!= NULL
) {
280 PurpleBuddy
*buddy
= buddies
->data
;
281 purple_buddy_set_protocol_data(buddy
, NULL
);
282 buddies
= g_slist_delete_link(buddies
, buddies
);
285 connections
= g_list_remove(connections
, gc
);
287 purple_connection_set_state(gc
, PURPLE_DISCONNECTED
);
290 purple_blist_remove_account(account
);
292 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc
);
294 purple_account_request_close_with_account(account
);
295 purple_request_close_with_handle(gc
);
296 purple_notify_close_with_handle(gc
);
298 purple_debug_info("connection", "Destroying connection %p\n", gc
);
300 purple_account_set_connection(account
, NULL
);
302 g_free(gc
->password
);
303 g_free(gc
->display_name
);
305 if (gc
->disconnect_timeout
> 0)
306 purple_timeout_remove(gc
->disconnect_timeout
);
308 PURPLE_DBUS_UNREGISTER_POINTER(gc
);
323 purple_connection_set_state(PurpleConnection
*gc
, PurpleConnectionState state
)
325 PurpleConnectionUiOps
*ops
;
327 g_return_if_fail(gc
!= NULL
);
329 if (gc
->state
== state
)
334 ops
= purple_connections_get_ui_ops();
336 if (gc
->state
== PURPLE_CONNECTING
) {
337 connections_connecting
= g_list_append(connections_connecting
, gc
);
340 connections_connecting
= g_list_remove(connections_connecting
, gc
);
343 if (gc
->state
== PURPLE_CONNECTED
) {
344 PurpleAccount
*account
;
345 PurplePresence
*presence
;
347 account
= purple_connection_get_account(gc
);
348 presence
= purple_account_get_presence(account
);
350 /* Set the time the account came online */
351 purple_presence_set_login_time(presence
, time(NULL
));
353 if (purple_prefs_get_bool("/purple/logging/log_system"))
355 PurpleLog
*log
= purple_account_get_log(account
, TRUE
);
359 char *msg
= g_strdup_printf(_("+++ %s signed on"),
360 purple_account_get_username(account
));
361 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
362 purple_account_get_username(account
),
363 purple_presence_get_login_time(presence
),
369 if (ops
!= NULL
&& ops
->connected
!= NULL
)
372 purple_blist_add_account(account
);
374 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc
);
375 purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc
);
377 serv_set_permit_deny(gc
);
379 update_keepalive(gc
, TRUE
);
381 else if (gc
->state
== PURPLE_DISCONNECTED
) {
382 PurpleAccount
*account
= purple_connection_get_account(gc
);
384 if (purple_prefs_get_bool("/purple/logging/log_system"))
386 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
390 char *msg
= g_strdup_printf(_("+++ %s signed off"),
391 purple_account_get_username(account
));
392 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
393 purple_account_get_username(account
), time(NULL
),
399 purple_account_destroy_log(account
);
401 if (ops
!= NULL
&& ops
->disconnected
!= NULL
)
402 ops
->disconnected(gc
);
407 purple_connection_set_account(PurpleConnection
*gc
, PurpleAccount
*account
)
409 g_return_if_fail(gc
!= NULL
);
410 g_return_if_fail(account
!= NULL
);
412 gc
->account
= account
;
416 purple_connection_set_display_name(PurpleConnection
*gc
, const char *name
)
418 g_return_if_fail(gc
!= NULL
);
420 g_free(gc
->display_name
);
421 gc
->display_name
= g_strdup(name
);
425 purple_connection_set_protocol_data(PurpleConnection
*connection
, void *proto_data
) {
426 g_return_if_fail(connection
!= NULL
);
428 connection
->proto_data
= proto_data
;
431 PurpleConnectionState
432 purple_connection_get_state(const PurpleConnection
*gc
)
434 g_return_val_if_fail(gc
!= NULL
, PURPLE_DISCONNECTED
);
440 purple_connection_get_account(const PurpleConnection
*gc
)
442 g_return_val_if_fail(gc
!= NULL
, NULL
);
448 purple_connection_get_prpl(const PurpleConnection
*gc
)
450 g_return_val_if_fail(gc
!= NULL
, NULL
);
456 purple_connection_get_password(const PurpleConnection
*gc
)
458 g_return_val_if_fail(gc
!= NULL
, NULL
);
460 return gc
->password
? gc
->password
: gc
->account
->password
;
464 purple_connection_get_display_name(const PurpleConnection
*gc
)
466 g_return_val_if_fail(gc
!= NULL
, NULL
);
468 return gc
->display_name
;
472 purple_connection_get_protocol_data(const PurpleConnection
*connection
) {
473 g_return_val_if_fail(connection
!= NULL
, NULL
);
475 return connection
->proto_data
;
479 purple_connection_update_progress(PurpleConnection
*gc
, const char *text
,
480 size_t step
, size_t count
)
482 PurpleConnectionUiOps
*ops
;
484 g_return_if_fail(gc
!= NULL
);
485 g_return_if_fail(text
!= NULL
);
486 g_return_if_fail(step
< count
);
487 g_return_if_fail(count
> 1);
489 ops
= purple_connections_get_ui_ops();
491 if (ops
!= NULL
&& ops
->connect_progress
!= NULL
)
492 ops
->connect_progress(gc
, text
, step
, count
);
496 purple_connection_notice(PurpleConnection
*gc
, const char *text
)
498 PurpleConnectionUiOps
*ops
;
500 g_return_if_fail(gc
!= NULL
);
501 g_return_if_fail(text
!= NULL
);
503 ops
= purple_connections_get_ui_ops();
505 if (ops
!= NULL
&& ops
->notice
!= NULL
)
506 ops
->notice(gc
, text
);
510 purple_connection_disconnect_cb(gpointer data
)
512 PurpleAccount
*account
;
513 PurpleConnection
*gc
;
517 gc
= purple_account_get_connection(account
);
520 gc
->disconnect_timeout
= 0;
522 password
= g_strdup(purple_account_get_password(account
));
523 purple_account_disconnect(account
);
524 purple_account_set_password(account
, password
);
531 purple_connection_error(PurpleConnection
*gc
, const char *text
)
533 /* prpls that have not been updated to use disconnection reasons will
534 * be setting wants_to_die before calling this function, so choose
535 * PURPLE_CONNECTION_ERROR_OTHER_ERROR (which is fatal) if it's true,
536 * and PURPLE_CONNECTION_ERROR_NETWORK_ERROR (which isn't) if not. See
537 * the documentation in connection.h.
539 PurpleConnectionError reason
= gc
->wants_to_die
540 ? PURPLE_CONNECTION_ERROR_OTHER_ERROR
541 : PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
542 purple_connection_error_reason (gc
, reason
, text
);
546 purple_connection_error_reason (PurpleConnection
*gc
,
547 PurpleConnectionError reason
,
548 const char *description
)
550 PurpleConnectionUiOps
*ops
;
552 g_return_if_fail(gc
!= NULL
);
553 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
554 * being the last member of the PurpleConnectionError enum in
555 * connection.h; if other reasons are added after it, this check should
558 if (reason
> PURPLE_CONNECTION_ERROR_OTHER_ERROR
) {
559 purple_debug_error("connection",
560 "purple_connection_error_reason: reason %u isn't a "
561 "valid reason\n", reason
);
562 reason
= PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
565 if (description
== NULL
) {
566 purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n");
567 description
= _("Unknown error");
570 /* If we've already got one error, we don't need any more */
571 if (gc
->disconnect_timeout
> 0)
574 gc
->wants_to_die
= purple_connection_error_is_fatal (reason
);
576 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
577 gc
, reason
, description
);
579 ops
= purple_connections_get_ui_ops();
583 if (ops
->report_disconnect_reason
!= NULL
)
584 ops
->report_disconnect_reason (gc
, reason
, description
);
585 if (ops
->report_disconnect
!= NULL
)
586 ops
->report_disconnect (gc
, description
);
589 purple_signal_emit(purple_connections_get_handle(), "connection-error",
590 gc
, reason
, description
);
592 gc
->disconnect_timeout
= purple_timeout_add(0, purple_connection_disconnect_cb
,
593 purple_connection_get_account(gc
));
597 purple_connection_ssl_error (PurpleConnection
*gc
,
598 PurpleSslErrorType ssl_error
)
600 PurpleConnectionError reason
;
603 case PURPLE_SSL_HANDSHAKE_FAILED
:
604 reason
= PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
;
606 case PURPLE_SSL_CONNECT_FAILED
:
607 reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
609 case PURPLE_SSL_CERTIFICATE_INVALID
:
610 /* TODO: maybe PURPLE_SSL_* should be more specific? */
611 reason
= PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
;
614 g_assert_not_reached ();
615 reason
= PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
;
618 purple_connection_error_reason (gc
, reason
,
619 purple_ssl_strerror(ssl_error
));
623 purple_connection_error_is_fatal (PurpleConnectionError reason
)
627 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR
:
628 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
:
630 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME
:
631 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
:
632 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
:
633 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
:
634 case PURPLE_CONNECTION_ERROR_NAME_IN_USE
:
635 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
:
636 case PURPLE_CONNECTION_ERROR_OTHER_ERROR
:
637 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED
:
638 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED
:
639 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED
:
640 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED
:
641 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH
:
642 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH
:
643 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED
:
644 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
:
647 g_return_val_if_reached(TRUE
);
652 purple_connections_disconnect_all(void)
655 PurpleConnection
*gc
;
657 while ((l
= purple_connections_get_all()) != NULL
) {
659 gc
->wants_to_die
= TRUE
;
660 purple_account_disconnect(gc
->account
);
665 purple_connections_get_all(void)
671 purple_connections_get_connecting(void)
673 return connections_connecting
;
677 purple_connections_set_ui_ops(PurpleConnectionUiOps
*ops
)
679 connection_ui_ops
= ops
;
682 PurpleConnectionUiOps
*
683 purple_connections_get_ui_ops(void)
685 return connection_ui_ops
;
689 purple_connections_init(void)
691 void *handle
= purple_connections_get_handle();
693 purple_signal_register(handle
, "signing-on",
694 purple_marshal_VOID__POINTER
, NULL
, 1,
695 purple_value_new(PURPLE_TYPE_SUBTYPE
,
696 PURPLE_SUBTYPE_CONNECTION
));
698 purple_signal_register(handle
, "signed-on",
699 purple_marshal_VOID__POINTER
, NULL
, 1,
700 purple_value_new(PURPLE_TYPE_SUBTYPE
,
701 PURPLE_SUBTYPE_CONNECTION
));
703 purple_signal_register(handle
, "signing-off",
704 purple_marshal_VOID__POINTER
, NULL
, 1,
705 purple_value_new(PURPLE_TYPE_SUBTYPE
,
706 PURPLE_SUBTYPE_CONNECTION
));
708 purple_signal_register(handle
, "signed-off",
709 purple_marshal_VOID__POINTER
, NULL
, 1,
710 purple_value_new(PURPLE_TYPE_SUBTYPE
,
711 PURPLE_SUBTYPE_CONNECTION
));
713 purple_signal_register(handle
, "connection-error",
714 purple_marshal_VOID__POINTER_INT_POINTER
, NULL
, 3,
715 purple_value_new(PURPLE_TYPE_SUBTYPE
,
716 PURPLE_SUBTYPE_CONNECTION
),
717 purple_value_new(PURPLE_TYPE_ENUM
),
718 purple_value_new(PURPLE_TYPE_STRING
));
720 purple_signal_register(handle
, "autojoin",
721 purple_marshal_BOOLEAN__POINTER
, NULL
, 1,
722 purple_value_new(PURPLE_TYPE_SUBTYPE
,
723 PURPLE_SUBTYPE_CONNECTION
));
728 purple_connections_uninit(void)
730 purple_signals_unregister_by_instance(purple_connections_get_handle());
734 purple_connections_get_handle(void)
736 return &connections_handle
;