Fix crashes when filenames end up being NULL in some prpls.
[pidgin-git.git] / libpurple / connection.c
blob7e981f098ba75ad211aa600bbd6ef3d03989512b
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 = NULL;
57 if (gc == NULL)
58 return TRUE;
60 /* Only send keep-alives if we haven't heard from the
61 * server in a while.
63 if ((time(NULL) - gc->last_received) < KEEPALIVE_INTERVAL)
64 return TRUE;
66 if (gc->prpl == NULL)
67 return TRUE;
69 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
71 if (prpl_info && prpl_info->keepalive)
72 prpl_info->keepalive(gc);
74 return TRUE;
77 static void
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)
86 return;
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);
97 gc->keepalive = 0;
101 void
102 purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
104 _purple_connection_new(account, regist, password);
107 void
108 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
110 PurpleConnection *gc;
111 PurplePlugin *prpl;
112 PurplePluginProtocolInfo *prpl_info;
114 g_return_if_fail(account != NULL);
116 if (!purple_account_is_disconnected(account))
117 return;
119 prpl = purple_find_prpl(purple_account_get_protocol_id(account));
121 if (prpl != NULL)
122 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
123 else {
124 gchar *message;
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);
130 g_free(message);
131 return;
134 if (regist)
136 if (prpl_info->register_user == NULL)
137 return;
139 else
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));
147 return;
151 gc = g_new0(PurpleConnection, 1);
152 PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
154 gc->prpl = prpl;
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);
164 if (regist)
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);
173 else
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);
181 void
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);
187 void
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;
192 PurplePlugin *prpl;
193 PurplePluginProtocolInfo *prpl_info;
195 g_return_if_fail(account != NULL);
197 prpl = purple_find_prpl(purple_account_get_protocol_id(account));
199 if (prpl != NULL)
200 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
201 else {
202 gchar *message;
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);
207 g_free(message);
208 return;
211 if (!purple_account_is_disconnected(account)) {
212 prpl_info->unregister_user(account, cb, user_data);
213 return;
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));
222 return;
225 gc = g_new0(PurpleConnection, 1);
226 PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
228 gc->prpl = prpl;
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);
243 void
244 purple_connection_destroy(PurpleConnection *gc)
246 _purple_connection_destroy(gc);
249 void
250 _purple_connection_destroy(PurpleConnection *gc)
252 PurpleAccount *account;
253 GSList *buddies;
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)
264 remove = TRUE;
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);
296 if (remove)
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);
316 g_free(gc);
320 * d:)->-<
322 * d:O-\-<
324 * d:D-/-<
326 * d8D->-< DANCE!
329 void
330 purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
332 PurpleConnectionUiOps *ops;
334 g_return_if_fail(gc != NULL);
336 if (gc->state == state)
337 return;
339 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);
346 else {
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);
364 if (log != NULL)
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),
371 msg);
372 g_free(msg);
376 if (ops != NULL && ops->connected != NULL)
377 ops->connected(gc);
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);
394 if (log != NULL)
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),
400 msg);
401 g_free(msg);
405 purple_account_destroy_log(account);
407 if (ops != NULL && ops->disconnected != NULL)
408 ops->disconnected(gc);
412 void
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;
421 void
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);
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_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);
487 void
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);
501 static gboolean
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);
508 g_free(password);
509 return FALSE;
512 void
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);
527 void
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
538 * be updated.
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)
554 return;
556 gc->wants_to_die = purple_connection_error_is_fatal (reason);
558 ops = purple_connections_get_ui_ops();
560 if (ops != NULL)
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));
575 void
576 purple_connection_ssl_error (PurpleConnection *gc,
577 PurpleSslErrorType ssl_error)
579 PurpleConnectionError reason;
581 switch (ssl_error) {
582 case PURPLE_SSL_HANDSHAKE_FAILED:
583 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
584 break;
585 case PURPLE_SSL_CONNECT_FAILED:
586 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
587 break;
588 case PURPLE_SSL_CERTIFICATE_INVALID:
589 /* TODO: maybe PURPLE_SSL_* should be more specific? */
590 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
591 break;
592 default:
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));
601 gboolean
602 purple_connection_error_is_fatal (PurpleConnectionError reason)
604 switch (reason)
606 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
607 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
608 return FALSE;
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:
624 return TRUE;
625 default:
626 g_return_val_if_reached(TRUE);
630 void
631 purple_connections_disconnect_all(void)
633 GList *l;
634 PurpleConnection *gc;
636 while ((l = purple_connections_get_all()) != NULL) {
637 gc = l->data;
638 gc->wants_to_die = TRUE;
639 purple_account_disconnect(gc->account);
643 GList *
644 purple_connections_get_all(void)
646 return connections;
649 GList *
650 purple_connections_get_connecting(void)
652 return connections_connecting;
655 void
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;
667 void
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));
701 void
702 purple_connections_uninit(void)
704 purple_signals_unregister_by_instance(purple_connections_get_handle());
707 void *
708 purple_connections_get_handle(void)
710 return &connections_handle;