I think this was accidentally changed in revision
[pidgin-git.git] / libpurple / connection.c
blob9e0055fb17c2ea3833eba9d5b35327fbc2533720
1 /**
2 * @file connection.c Connection API
3 * @ingroup core
4 */
6 /* purple
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_
28 #include "internal.h"
29 #include "account.h"
30 #include "blist.h"
31 #include "connection.h"
32 #include "dbus-maybe.h"
33 #include "debug.h"
34 #include "log.h"
35 #include "notify.h"
36 #include "prefs.h"
37 #include "proxy.h"
38 #include "request.h"
39 #include "server.h"
40 #include "signals.h"
41 #include "util.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;
51 static gboolean
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
58 * server in a while.
60 if ((time(NULL) - gc->last_received) < KEEPALIVE_INTERVAL)
61 return TRUE;
63 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
64 if (prpl_info->keepalive)
65 prpl_info->keepalive(gc);
67 return TRUE;
70 static void
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)
79 return;
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);
90 gc->keepalive = 0;
94 void
95 purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
97 _purple_connection_new(account, regist, password);
100 void
101 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
103 PurpleConnection *gc;
104 PurplePlugin *prpl;
105 PurplePluginProtocolInfo *prpl_info;
107 g_return_if_fail(account != NULL);
109 if (!purple_account_is_disconnected(account))
110 return;
112 prpl = purple_find_prpl(purple_account_get_protocol_id(account));
114 if (prpl != NULL)
115 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
116 else {
117 gchar *message;
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);
123 g_free(message);
124 return;
127 if (regist)
129 if (prpl_info->register_user == NULL)
130 return;
132 else
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));
140 return;
144 gc = g_new0(PurpleConnection, 1);
145 PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
147 gc->prpl = prpl;
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);
157 if (regist)
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);
166 else
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);
174 void
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);
180 void
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;
185 PurplePlugin *prpl;
186 PurplePluginProtocolInfo *prpl_info;
188 g_return_if_fail(account != NULL);
190 prpl = purple_find_prpl(purple_account_get_protocol_id(account));
192 if (prpl != NULL)
193 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
194 else {
195 gchar *message;
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);
200 g_free(message);
201 return;
204 if (!purple_account_is_disconnected(account)) {
205 prpl_info->unregister_user(account, cb, user_data);
206 return;
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));
215 return;
218 gc = g_new0(PurpleConnection, 1);
219 PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
221 gc->prpl = prpl;
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);
236 void
237 purple_connection_destroy(PurpleConnection *gc)
239 _purple_connection_destroy(gc);
242 void
243 _purple_connection_destroy(PurpleConnection *gc)
245 PurpleAccount *account;
246 GSList *buddies;
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)
257 remove = TRUE;
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);
289 if (remove)
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);
309 g_free(gc);
313 * d:)->-<
315 * d:O-\-<
317 * d:D-/-<
319 * d8D->-< DANCE!
322 void
323 purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
325 PurpleConnectionUiOps *ops;
327 g_return_if_fail(gc != NULL);
329 if (gc->state == state)
330 return;
332 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);
339 else {
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);
357 if (log != NULL)
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),
364 msg);
365 g_free(msg);
369 if (ops != NULL && ops->connected != NULL)
370 ops->connected(gc);
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);
388 if (log != NULL)
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),
394 msg);
395 g_free(msg);
399 purple_account_destroy_log(account);
401 if (ops != NULL && ops->disconnected != NULL)
402 ops->disconnected(gc);
406 void
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;
415 void
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);
424 void
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);
436 return gc->state;
439 PurpleAccount *
440 purple_connection_get_account(const PurpleConnection *gc)
442 g_return_val_if_fail(gc != NULL, NULL);
444 return gc->account;
447 PurplePlugin *
448 purple_connection_get_prpl(const PurpleConnection *gc)
450 g_return_val_if_fail(gc != NULL, NULL);
452 return gc->prpl;
455 const char *
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;
463 const char *
464 purple_connection_get_display_name(const PurpleConnection *gc)
466 g_return_val_if_fail(gc != NULL, NULL);
468 return gc->display_name;
471 void *
472 purple_connection_get_protocol_data(const PurpleConnection *connection) {
473 g_return_val_if_fail(connection != NULL, NULL);
475 return connection->proto_data;
478 void
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);
495 void
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);
509 static gboolean
510 purple_connection_disconnect_cb(gpointer data)
512 PurpleAccount *account;
513 PurpleConnection *gc;
514 char *password;
516 account = data;
517 gc = purple_account_get_connection(account);
519 if (gc != NULL)
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);
525 g_free(password);
527 return FALSE;
530 void
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);
545 void
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
556 * be updated.
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)
572 return;
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();
581 if (ops != NULL)
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));
596 void
597 purple_connection_ssl_error (PurpleConnection *gc,
598 PurpleSslErrorType ssl_error)
600 PurpleConnectionError reason;
602 switch (ssl_error) {
603 case PURPLE_SSL_HANDSHAKE_FAILED:
604 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
605 break;
606 case PURPLE_SSL_CONNECT_FAILED:
607 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
608 break;
609 case PURPLE_SSL_CERTIFICATE_INVALID:
610 /* TODO: maybe PURPLE_SSL_* should be more specific? */
611 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
612 break;
613 default:
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));
622 gboolean
623 purple_connection_error_is_fatal (PurpleConnectionError reason)
625 switch (reason)
627 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
628 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
629 return FALSE;
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:
645 return TRUE;
646 default:
647 g_return_val_if_reached(TRUE);
651 void
652 purple_connections_disconnect_all(void)
654 GList *l;
655 PurpleConnection *gc;
657 while ((l = purple_connections_get_all()) != NULL) {
658 gc = l->data;
659 gc->wants_to_die = TRUE;
660 purple_account_disconnect(gc->account);
664 GList *
665 purple_connections_get_all(void)
667 return connections;
670 GList *
671 purple_connections_get_connecting(void)
673 return connections_connecting;
676 void
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;
688 void
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));
727 void
728 purple_connections_uninit(void)
730 purple_signals_unregister_by_instance(purple_connections_get_handle());
733 void *
734 purple_connections_get_handle(void)
736 return &connections_handle;