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", "Can not 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", "Can not 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
);
376 serv_set_permit_deny(gc
);
378 update_keepalive(gc
, TRUE
);
380 else if (gc
->state
== PURPLE_DISCONNECTED
) {
381 PurpleAccount
*account
= purple_connection_get_account(gc
);
383 if (purple_prefs_get_bool("/purple/logging/log_system"))
385 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
389 char *msg
= g_strdup_printf(_("+++ %s signed off"),
390 purple_account_get_username(account
));
391 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
392 purple_account_get_username(account
), time(NULL
),
398 purple_account_destroy_log(account
);
400 if (ops
!= NULL
&& ops
->disconnected
!= NULL
)
401 ops
->disconnected(gc
);
406 purple_connection_set_account(PurpleConnection
*gc
, PurpleAccount
*account
)
408 g_return_if_fail(gc
!= NULL
);
409 g_return_if_fail(account
!= NULL
);
411 gc
->account
= account
;
415 purple_connection_set_display_name(PurpleConnection
*gc
, const char *name
)
417 g_return_if_fail(gc
!= NULL
);
419 g_free(gc
->display_name
);
420 gc
->display_name
= g_strdup(name
);
424 purple_connection_set_protocol_data(PurpleConnection
*connection
, void *proto_data
) {
425 g_return_if_fail(connection
!= NULL
);
427 connection
->proto_data
= proto_data
;
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_get_protocol_data(const PurpleConnection
*connection
) {
472 g_return_val_if_fail(connection
!= NULL
, NULL
);
474 return connection
->proto_data
;
478 purple_connection_update_progress(PurpleConnection
*gc
, const char *text
,
479 size_t step
, size_t count
)
481 PurpleConnectionUiOps
*ops
;
483 g_return_if_fail(gc
!= NULL
);
484 g_return_if_fail(text
!= NULL
);
485 g_return_if_fail(step
< count
);
486 g_return_if_fail(count
> 1);
488 ops
= purple_connections_get_ui_ops();
490 if (ops
!= NULL
&& ops
->connect_progress
!= NULL
)
491 ops
->connect_progress(gc
, text
, step
, count
);
495 purple_connection_notice(PurpleConnection
*gc
, const char *text
)
497 PurpleConnectionUiOps
*ops
;
499 g_return_if_fail(gc
!= NULL
);
500 g_return_if_fail(text
!= NULL
);
502 ops
= purple_connections_get_ui_ops();
504 if (ops
!= NULL
&& ops
->notice
!= NULL
)
505 ops
->notice(gc
, text
);
509 purple_connection_disconnect_cb(gpointer data
)
511 PurpleAccount
*account
;
512 PurpleConnection
*gc
;
516 gc
= purple_account_get_connection(account
);
518 gc
->disconnect_timeout
= 0;
520 password
= g_strdup(purple_account_get_password(account
));
521 purple_account_disconnect(account
);
522 purple_account_set_password(account
, password
);
529 purple_connection_error(PurpleConnection
*gc
, const char *text
)
531 /* prpls that have not been updated to use disconnection reasons will
532 * be setting wants_to_die before calling this function, so choose
533 * PURPLE_CONNECTION_ERROR_OTHER_ERROR (which is fatal) if it's true,
534 * and PURPLE_CONNECTION_ERROR_NETWORK_ERROR (which isn't) if not. See
535 * the documentation in connection.h.
537 PurpleConnectionError reason
= gc
->wants_to_die
538 ? PURPLE_CONNECTION_ERROR_OTHER_ERROR
539 : PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
540 purple_connection_error_reason (gc
, reason
, text
);
544 purple_connection_error_reason (PurpleConnection
*gc
,
545 PurpleConnectionError reason
,
546 const char *description
)
548 PurpleConnectionUiOps
*ops
;
550 g_return_if_fail(gc
!= NULL
);
551 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
552 * being the last member of the PurpleConnectionError enum in
553 * connection.h; if other reasons are added after it, this check should
556 if (reason
> PURPLE_CONNECTION_ERROR_OTHER_ERROR
) {
557 purple_debug_error("connection",
558 "purple_connection_error_reason: reason %u isn't a "
559 "valid reason\n", reason
);
560 reason
= PURPLE_CONNECTION_ERROR_OTHER_ERROR
;
563 if (description
== NULL
) {
564 purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n");
565 description
= _("Unknown error");
568 /* If we've already got one error, we don't need any more */
569 if (gc
->disconnect_timeout
> 0)
572 gc
->wants_to_die
= purple_connection_error_is_fatal (reason
);
574 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
575 gc
, reason
, description
);
577 ops
= purple_connections_get_ui_ops();
581 if (ops
->report_disconnect_reason
!= NULL
)
582 ops
->report_disconnect_reason (gc
, reason
, description
);
583 if (ops
->report_disconnect
!= NULL
)
584 ops
->report_disconnect (gc
, description
);
587 purple_signal_emit(purple_connections_get_handle(), "connection-error",
588 gc
, reason
, description
);
590 gc
->disconnect_timeout
= purple_timeout_add(0, purple_connection_disconnect_cb
,
591 purple_connection_get_account(gc
));
595 purple_connection_ssl_error (PurpleConnection
*gc
,
596 PurpleSslErrorType ssl_error
)
598 PurpleConnectionError reason
;
601 case PURPLE_SSL_HANDSHAKE_FAILED
:
602 reason
= PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
;
604 case PURPLE_SSL_CONNECT_FAILED
:
605 reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
607 case PURPLE_SSL_CERTIFICATE_INVALID
:
608 /* TODO: maybe PURPLE_SSL_* should be more specific? */
609 reason
= PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
;
612 g_assert_not_reached ();
613 reason
= PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
;
616 purple_connection_error_reason (gc
, reason
,
617 purple_ssl_strerror(ssl_error
));
621 purple_connection_error_is_fatal (PurpleConnectionError reason
)
625 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR
:
626 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR
:
628 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME
:
629 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
:
630 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
:
631 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
:
632 case PURPLE_CONNECTION_ERROR_NAME_IN_USE
:
633 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
:
634 case PURPLE_CONNECTION_ERROR_OTHER_ERROR
:
635 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED
:
636 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED
:
637 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED
:
638 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED
:
639 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH
:
640 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH
:
641 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED
:
642 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR
:
645 g_return_val_if_reached(TRUE
);
650 purple_connections_disconnect_all(void)
653 PurpleConnection
*gc
;
655 while ((l
= purple_connections_get_all()) != NULL
) {
657 gc
->wants_to_die
= TRUE
;
658 purple_account_disconnect(gc
->account
);
663 purple_connections_get_all(void)
669 purple_connections_get_connecting(void)
671 return connections_connecting
;
675 purple_connections_set_ui_ops(PurpleConnectionUiOps
*ops
)
677 connection_ui_ops
= ops
;
680 PurpleConnectionUiOps
*
681 purple_connections_get_ui_ops(void)
683 return connection_ui_ops
;
687 purple_connections_init(void)
689 void *handle
= purple_connections_get_handle();
691 purple_signal_register(handle
, "signing-on",
692 purple_marshal_VOID__POINTER
, NULL
, 1,
693 purple_value_new(PURPLE_TYPE_SUBTYPE
,
694 PURPLE_SUBTYPE_CONNECTION
));
696 purple_signal_register(handle
, "signed-on",
697 purple_marshal_VOID__POINTER
, NULL
, 1,
698 purple_value_new(PURPLE_TYPE_SUBTYPE
,
699 PURPLE_SUBTYPE_CONNECTION
));
701 purple_signal_register(handle
, "signing-off",
702 purple_marshal_VOID__POINTER
, NULL
, 1,
703 purple_value_new(PURPLE_TYPE_SUBTYPE
,
704 PURPLE_SUBTYPE_CONNECTION
));
706 purple_signal_register(handle
, "signed-off",
707 purple_marshal_VOID__POINTER
, NULL
, 1,
708 purple_value_new(PURPLE_TYPE_SUBTYPE
,
709 PURPLE_SUBTYPE_CONNECTION
));
711 purple_signal_register(handle
, "connection-error",
712 purple_marshal_VOID__POINTER_INT_POINTER
, NULL
, 3,
713 purple_value_new(PURPLE_TYPE_SUBTYPE
,
714 PURPLE_SUBTYPE_CONNECTION
),
715 purple_value_new(PURPLE_TYPE_ENUM
),
716 purple_value_new(PURPLE_TYPE_STRING
));
721 purple_connections_uninit(void)
723 purple_signals_unregister_by_instance(purple_connections_get_handle());
727 purple_connections_get_handle(void)
729 return &connections_handle
;