Move the "Change status to" menu to be beside the checkbox controlling it.
[pidgin-git.git] / libpurple / connection.c
blob3287e6876b577fea20b300d7eb4f2414a56ec9a1
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", "Can not 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", "Can not 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);
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);
387 if (log != NULL)
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),
393 msg);
394 g_free(msg);
398 purple_account_destroy_log(account);
400 if (ops != NULL && ops->disconnected != NULL)
401 ops->disconnected(gc);
405 void
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;
414 void
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);
423 void
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);
435 return gc->state;
438 PurpleAccount *
439 purple_connection_get_account(const PurpleConnection *gc)
441 g_return_val_if_fail(gc != NULL, NULL);
443 return gc->account;
446 PurplePlugin *
447 purple_connection_get_prpl(const PurpleConnection *gc)
449 g_return_val_if_fail(gc != NULL, NULL);
451 return gc->prpl;
454 const char *
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;
462 const char *
463 purple_connection_get_display_name(const PurpleConnection *gc)
465 g_return_val_if_fail(gc != NULL, NULL);
467 return gc->display_name;
470 void *
471 purple_connection_get_protocol_data(const PurpleConnection *connection) {
472 g_return_val_if_fail(connection != NULL, NULL);
474 return connection->proto_data;
477 void
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);
494 void
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);
508 static gboolean
509 purple_connection_disconnect_cb(gpointer data)
511 PurpleAccount *account;
512 PurpleConnection *gc;
513 char *password;
515 account = data;
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);
523 g_free(password);
525 return FALSE;
528 void
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);
543 void
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
554 * be updated.
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)
570 return;
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();
579 if (ops != NULL)
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));
594 void
595 purple_connection_ssl_error (PurpleConnection *gc,
596 PurpleSslErrorType ssl_error)
598 PurpleConnectionError reason;
600 switch (ssl_error) {
601 case PURPLE_SSL_HANDSHAKE_FAILED:
602 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
603 break;
604 case PURPLE_SSL_CONNECT_FAILED:
605 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
606 break;
607 case PURPLE_SSL_CERTIFICATE_INVALID:
608 /* TODO: maybe PURPLE_SSL_* should be more specific? */
609 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
610 break;
611 default:
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));
620 gboolean
621 purple_connection_error_is_fatal (PurpleConnectionError reason)
623 switch (reason)
625 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
626 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
627 return FALSE;
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:
643 return TRUE;
644 default:
645 g_return_val_if_reached(TRUE);
649 void
650 purple_connections_disconnect_all(void)
652 GList *l;
653 PurpleConnection *gc;
655 while ((l = purple_connections_get_all()) != NULL) {
656 gc = l->data;
657 gc->wants_to_die = TRUE;
658 purple_account_disconnect(gc->account);
662 GList *
663 purple_connections_get_all(void)
665 return connections;
668 GList *
669 purple_connections_get_connecting(void)
671 return connections_connecting;
674 void
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;
686 void
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));
720 void
721 purple_connections_uninit(void)
723 purple_signals_unregister_by_instance(purple_connections_get_handle());
726 void *
727 purple_connections_get_handle(void)
729 return &connections_handle;