Convert XMPP console dialogs to popovers.
[pidgin-git.git] / libpurple / connection.c
blob93d2851640b135138f807757856ffa271f027e51
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "account.h"
26 #include "buddylist.h"
27 #include "connection.h"
28 #include "debug.h"
29 #include "enums.h"
30 #include "http.h"
31 #include "log.h"
32 #include "notify.h"
33 #include "prefs.h"
34 #include "proxy.h"
35 #include "request.h"
36 #include "server.h"
37 #include "signals.h"
38 #include "util.h"
40 G_DEFINE_QUARK(purple-connection-error-quark, purple_connection_error);
42 /**
43 * PurpleConnection:
45 * Represents an active connection on an account.
47 struct _PurpleConnection
49 GObject gparent;
52 /* Private data for a connection */
53 typedef struct
55 PurpleProtocol *protocol; /* The protocol. */
56 PurpleConnectionFlags flags; /* Connection flags. */
58 PurpleConnectionState state; /* The connection state. */
60 PurpleAccount *account; /* The account being connected to. */
61 char *password; /* The password used. */
63 GSList *active_chats; /* A list of active chats
64 (#PurpleChatConversation structs). */
66 /* TODO Remove this and use protocol-specific subclasses. */
67 void *proto_data; /* Protocol-specific data. */
69 char *display_name; /* How you appear to other people. */
70 GSource *keepalive; /* Keep-alive. */
72 /* Wants to Die state. This is set when the user chooses to log out, or
73 * when the protocol is disconnected and should not be automatically
74 * reconnected (incorrect password, etc.). Protocols should rely on
75 * purple_connection_error() to set this for them rather than
76 * setting it themselves.
77 * See purple_connection_error_is_fatal()
79 gboolean wants_to_die;
81 gboolean is_finalizing; /* The object is being destroyed. */
83 /* The connection error and its description if an error occured */
84 PurpleConnectionErrorInfo *error_info;
86 guint disconnect_timeout; /* Timer used for nasty stack tricks */
87 } PurpleConnectionPrivate;
89 /* GObject property enums */
90 enum
92 PROP_0,
93 PROP_PROTOCOL,
94 PROP_FLAGS,
95 PROP_STATE,
96 PROP_ACCOUNT,
97 PROP_PASSWORD,
98 PROP_DISPLAY_NAME,
99 PROP_LAST
102 static GParamSpec *properties[PROP_LAST];
104 static GList *connections = NULL;
105 static GList *connections_connecting = NULL;
106 static PurpleConnectionUiOps *connection_ui_ops = NULL;
108 static int connections_handle;
110 static PurpleConnectionErrorInfo *
111 purple_connection_error_info_new(PurpleConnectionError type,
112 const gchar *description);
114 G_DEFINE_TYPE_WITH_PRIVATE(PurpleConnection, purple_connection, G_TYPE_OBJECT)
116 /**************************************************************************
117 * Connection API
118 **************************************************************************/
119 static gboolean
120 send_keepalive(gpointer data)
122 PurpleConnection *gc = data;
123 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
125 purple_protocol_server_iface_keepalive(priv->protocol, gc);
127 return TRUE;
130 static void
131 update_keepalive(PurpleConnection *gc, gboolean on)
133 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
135 if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER, keepalive))
136 return;
138 if (on && !priv->keepalive)
140 int interval = purple_protocol_server_iface_get_keepalive_interval(priv->protocol);
141 purple_debug_info("connection", "Activating keepalive to %d seconds.", interval);
142 priv->keepalive = g_main_context_find_source_by_id(NULL, g_timeout_add_seconds(interval, send_keepalive, gc));
144 else if (!on && priv->keepalive)
146 purple_debug_info("connection", "Deactivating keepalive.\n");
147 g_source_destroy(priv->keepalive);
148 priv->keepalive = NULL;
153 * d:)->-<
155 * d:O-\-<
157 * d:D-/-<
159 * d8D->-< DANCE!
162 void
163 purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
165 PurpleConnectionPrivate *priv = NULL;
166 PurpleConnectionUiOps *ops;
168 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
170 priv = purple_connection_get_instance_private(gc);
172 if (priv->state == state)
173 return;
175 priv->state = state;
177 ops = purple_connections_get_ui_ops();
179 if (priv->state == PURPLE_CONNECTION_CONNECTING) {
180 connections_connecting = g_list_append(connections_connecting, gc);
182 else {
183 connections_connecting = g_list_remove(connections_connecting, gc);
186 if (priv->state == PURPLE_CONNECTION_CONNECTED) {
187 PurpleAccount *account;
188 PurplePresence *presence;
190 account = purple_connection_get_account(gc);
191 presence = purple_account_get_presence(account);
193 /* Set the time the account came online */
194 purple_presence_set_login_time(presence, time(NULL));
196 if (purple_prefs_get_bool("/purple/logging/log_system"))
198 PurpleLog *log = purple_account_get_log(account, TRUE);
200 if (log != NULL)
202 char *msg = g_strdup_printf(_("+++ %s signed on"),
203 purple_account_get_username(account));
204 GDateTime *dt = g_date_time_new_from_unix_local(purple_presence_get_login_time(presence));
205 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
206 purple_account_get_username(account),
207 dt, msg);
208 g_date_time_unref(dt);
209 g_free(msg);
213 if (ops != NULL && ops->connected != NULL)
214 ops->connected(gc);
216 purple_blist_add_account(account);
218 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
219 purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
221 purple_serv_set_permit_deny(gc);
223 update_keepalive(gc, TRUE);
225 else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
226 PurpleAccount *account = purple_connection_get_account(gc);
228 if (purple_prefs_get_bool("/purple/logging/log_system"))
230 PurpleLog *log = purple_account_get_log(account, FALSE);
232 if (log != NULL)
234 char *msg = g_strdup_printf(_("+++ %s signed off"),
235 purple_account_get_username(account));
236 GDateTime *dt = g_date_time_new_now_utc();
237 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
238 purple_account_get_username(account),
239 dt, msg);
240 g_date_time_unref(dt);
241 g_free(msg);
245 purple_account_destroy_log(account);
247 if (ops != NULL && ops->disconnected != NULL)
248 ops->disconnected(gc);
251 if (!priv->is_finalizing)
252 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_STATE]);
255 void
256 purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
258 PurpleConnectionPrivate *priv = NULL;
260 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
262 priv = purple_connection_get_instance_private(gc);
263 priv->flags = flags;
265 if (!priv->is_finalizing)
266 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_FLAGS]);
269 void
270 purple_connection_set_display_name(PurpleConnection *gc, const char *name)
272 PurpleConnectionPrivate *priv = NULL;
274 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
276 priv = purple_connection_get_instance_private(gc);
277 g_free(priv->display_name);
278 priv->display_name = g_strdup(name);
280 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_DISPLAY_NAME]);
283 void
284 purple_connection_set_protocol_data(PurpleConnection *gc, void *proto_data)
286 PurpleConnectionPrivate *priv = NULL;
288 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
290 priv = purple_connection_get_instance_private(gc);
291 priv->proto_data = proto_data;
294 PurpleConnectionState
295 purple_connection_get_state(PurpleConnection *gc)
297 PurpleConnectionPrivate *priv = NULL;
299 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), PURPLE_CONNECTION_DISCONNECTED);
301 priv = purple_connection_get_instance_private(gc);
302 return priv->state;
305 PurpleConnectionFlags
306 purple_connection_get_flags(PurpleConnection *gc)
308 PurpleConnectionPrivate *priv = NULL;
310 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), 0);
312 priv = purple_connection_get_instance_private(gc);
313 return priv->flags;
316 gboolean
317 purple_connection_is_disconnecting(PurpleConnection *gc)
319 PurpleConnectionPrivate *priv = NULL;
321 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), TRUE);
323 priv = purple_connection_get_instance_private(gc);
324 return priv->is_finalizing;
327 PurpleAccount *
328 purple_connection_get_account(PurpleConnection *gc)
330 PurpleConnectionPrivate *priv = NULL;
332 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
334 priv = purple_connection_get_instance_private(gc);
335 return priv->account;
338 PurpleProtocol *
339 purple_connection_get_protocol(PurpleConnection *gc)
341 PurpleConnectionPrivate *priv = NULL;
343 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
345 priv = purple_connection_get_instance_private(gc);
346 return priv->protocol;
349 const char *
350 purple_connection_get_password(PurpleConnection *gc)
352 PurpleConnectionPrivate *priv = NULL;
354 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
356 priv = purple_connection_get_instance_private(gc);
357 return priv->password;
360 GSList *
361 purple_connection_get_active_chats(PurpleConnection *gc)
363 PurpleConnectionPrivate *priv = NULL;
365 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
367 priv = purple_connection_get_instance_private(gc);
368 return priv->active_chats;
371 const char *
372 purple_connection_get_display_name(PurpleConnection *gc)
374 PurpleConnectionPrivate *priv = NULL;
376 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
378 priv = purple_connection_get_instance_private(gc);
379 return priv->display_name;
382 void *
383 purple_connection_get_protocol_data(PurpleConnection *gc)
385 PurpleConnectionPrivate *priv = NULL;
387 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
389 priv = purple_connection_get_instance_private(gc);
390 return priv->proto_data;
393 void
394 _purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
396 PurpleConnectionPrivate *priv = NULL;
398 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
400 priv = purple_connection_get_instance_private(gc);
401 priv->active_chats = g_slist_append(priv->active_chats, chat);
404 void
405 _purple_connection_remove_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
407 PurpleConnectionPrivate *priv = NULL;
409 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
411 priv = purple_connection_get_instance_private(gc);
412 priv->active_chats = g_slist_remove(priv->active_chats, chat);
415 gboolean
416 _purple_connection_wants_to_die(PurpleConnection *gc)
418 PurpleConnectionPrivate *priv = NULL;
420 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), FALSE);
422 priv = purple_connection_get_instance_private(gc);
423 return priv->wants_to_die;
426 void
427 purple_connection_update_progress(PurpleConnection *gc, const char *text,
428 size_t step, size_t count)
430 PurpleConnectionUiOps *ops;
432 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
433 g_return_if_fail(text != NULL);
434 g_return_if_fail(step < count);
435 g_return_if_fail(count > 1);
437 ops = purple_connections_get_ui_ops();
439 if (ops != NULL && ops->connect_progress != NULL)
440 ops->connect_progress(gc, text, step, count);
443 void
444 purple_connection_notice(PurpleConnection *gc, const char *text)
446 PurpleConnectionUiOps *ops;
448 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
449 g_return_if_fail(text != NULL);
451 ops = purple_connections_get_ui_ops();
453 if (ops != NULL && ops->notice != NULL)
454 ops->notice(gc, text);
457 static gboolean
458 purple_connection_disconnect_cb(gpointer data)
460 PurpleAccount *account;
461 PurpleConnection *gc;
463 account = data;
464 gc = purple_account_get_connection(account);
466 if (PURPLE_IS_CONNECTION(gc)) {
467 PurpleConnectionPrivate *priv;
468 priv = purple_connection_get_instance_private(gc);
469 priv->disconnect_timeout = 0;
472 purple_account_disconnect(account);
473 return FALSE;
476 void
477 purple_connection_error (PurpleConnection *gc,
478 PurpleConnectionError reason,
479 const char *description)
481 PurpleConnectionPrivate *priv = NULL;
482 PurpleConnectionUiOps *ops;
484 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
485 priv = purple_connection_get_instance_private(gc);
487 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
488 * being the last member of the PurpleConnectionError enum in
489 * connection.h; if other reasons are added after it, this check should
490 * be updated.
492 if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
493 purple_debug_error("connection",
494 "purple_connection_error: reason %u isn't a "
495 "valid reason\n", reason);
496 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
499 if (description == NULL) {
500 purple_debug_error("connection", "purple_connection_error called with NULL description\n");
501 description = _("Unknown error");
504 /* If we've already got one error, we don't need any more */
505 if (purple_connection_get_error_info(gc))
506 return;
508 priv->wants_to_die = purple_connection_error_is_fatal (reason);
510 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
511 gc, reason, description);
513 ops = purple_connections_get_ui_ops();
515 if (ops && ops->report_disconnect)
516 ops->report_disconnect(gc, reason, description);
518 priv->error_info = purple_connection_error_info_new(reason, description);
520 purple_signal_emit(purple_connections_get_handle(), "connection-error",
521 gc, reason, description);
523 priv->disconnect_timeout = g_timeout_add(0, purple_connection_disconnect_cb,
524 purple_connection_get_account(gc));
527 PurpleConnectionErrorInfo *
528 purple_connection_get_error_info(PurpleConnection *gc)
530 PurpleConnectionPrivate *priv = NULL;
532 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
534 priv = purple_connection_get_instance_private(gc);
535 return priv->error_info;
538 void
539 purple_connection_ssl_error (PurpleConnection *gc,
540 PurpleSslErrorType ssl_error)
542 PurpleConnectionError reason;
544 switch (ssl_error) {
545 case PURPLE_SSL_HANDSHAKE_FAILED:
546 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
547 break;
548 case PURPLE_SSL_CONNECT_FAILED:
549 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
550 break;
551 case PURPLE_SSL_CERTIFICATE_INVALID:
552 /* TODO: maybe PURPLE_SSL_* should be more specific? */
553 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
554 break;
555 default:
556 g_assert_not_reached ();
557 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
560 purple_connection_error (gc, reason,
561 purple_ssl_strerror(ssl_error));
564 void
565 purple_connection_g_error(PurpleConnection *pc, const GError *error)
567 PurpleConnectionError reason;
569 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
570 /* Not a connection error. Ignore. */
571 return;
574 if (error->domain == G_TLS_ERROR) {
575 switch (error->code) {
576 case G_TLS_ERROR_UNAVAILABLE:
577 reason = PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT;
578 break;
579 case G_TLS_ERROR_NOT_TLS:
580 case G_TLS_ERROR_HANDSHAKE:
581 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
582 break;
583 case G_TLS_ERROR_BAD_CERTIFICATE:
584 case G_TLS_ERROR_CERTIFICATE_REQUIRED:
585 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
586 break;
587 case G_TLS_ERROR_EOF:
588 case G_TLS_ERROR_MISC:
589 default:
590 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
592 } else if (error->domain == G_IO_ERROR) {
593 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
594 } else if (error->domain == PURPLE_CONNECTION_ERROR) {
595 reason = error->code;
596 } else {
597 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
600 purple_connection_error(pc, reason, error->message);
603 void
604 purple_connection_take_error(PurpleConnection *pc, GError *error)
606 purple_connection_g_error(pc, error);
607 g_error_free(error);
610 gboolean
611 purple_connection_error_is_fatal (PurpleConnectionError reason)
613 switch (reason)
615 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
616 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
617 return FALSE;
618 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME:
619 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
620 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
621 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
622 case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
623 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
624 case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
625 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
626 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
627 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
628 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
629 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
630 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
631 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
632 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
633 return TRUE;
634 default:
635 g_return_val_if_reached(TRUE);
639 void purple_connection_update_last_received(PurpleConnection *gc)
641 PurpleConnectionPrivate *priv = NULL;
643 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
644 priv = purple_connection_get_instance_private(gc);
647 * For safety, actually this function shouldn't be called when the
648 * keepalive mechanism is inactive.
650 if (priv->keepalive) {
651 purple_timeout_reset(priv->keepalive, purple_protocol_server_iface_get_keepalive_interval(priv->protocol));
655 static PurpleConnectionErrorInfo *
656 purple_connection_error_info_new(PurpleConnectionError type,
657 const gchar *description)
659 PurpleConnectionErrorInfo *err;
661 g_return_val_if_fail(description != NULL, NULL);
663 err = g_new(PurpleConnectionErrorInfo, 1);
665 err->type = type;
666 err->description = g_strdup(description);
668 return err;
671 /**************************************************************************
672 * GBoxed code
673 **************************************************************************/
674 static PurpleConnectionUiOps *
675 purple_connection_ui_ops_copy(PurpleConnectionUiOps *ops)
677 PurpleConnectionUiOps *ops_new;
679 g_return_val_if_fail(ops != NULL, NULL);
681 ops_new = g_new(PurpleConnectionUiOps, 1);
682 *ops_new = *ops;
684 return ops_new;
687 GType
688 purple_connection_ui_ops_get_type(void)
690 static GType type = 0;
692 if (type == 0) {
693 type = g_boxed_type_register_static("PurpleConnectionUiOps",
694 (GBoxedCopyFunc)purple_connection_ui_ops_copy,
695 (GBoxedFreeFunc)g_free);
698 return type;
701 static PurpleConnectionErrorInfo *
702 purple_connection_error_info_copy(PurpleConnectionErrorInfo *err)
704 g_return_val_if_fail(err != NULL, NULL);
706 return purple_connection_error_info_new(err->type, err->description);
709 static void
710 purple_connection_error_info_free(PurpleConnectionErrorInfo *err)
712 g_return_if_fail(err != NULL);
714 g_free(err->description);
715 g_free(err);
718 GType
719 purple_connection_error_info_get_type(void)
721 static GType type = 0;
723 if (type == 0) {
724 type = g_boxed_type_register_static("PurpleConnectionErrorInfo",
725 (GBoxedCopyFunc)purple_connection_error_info_copy,
726 (GBoxedFreeFunc)purple_connection_error_info_free);
729 return type;
732 /**************************************************************************
733 * GObject code
734 **************************************************************************/
736 /* Set method for GObject properties */
737 static void
738 purple_connection_set_property(GObject *obj, guint param_id, const GValue *value,
739 GParamSpec *pspec)
741 PurpleConnection *gc = PURPLE_CONNECTION(obj);
742 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
744 switch (param_id) {
745 case PROP_PROTOCOL:
746 priv->protocol = g_value_get_object(value);
747 break;
748 case PROP_FLAGS:
749 purple_connection_set_flags(gc, g_value_get_flags(value));
750 break;
751 case PROP_STATE:
752 purple_connection_set_state(gc, g_value_get_enum(value));
753 break;
754 case PROP_ACCOUNT:
755 priv->account = g_value_get_object(value);
756 break;
757 case PROP_PASSWORD:
758 g_free(priv->password);
759 priv->password = g_value_dup_string(value);
760 break;
761 case PROP_DISPLAY_NAME:
762 purple_connection_set_display_name(gc, g_value_get_string(value));
763 break;
764 default:
765 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
766 break;
770 /* Get method for GObject properties */
771 static void
772 purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
773 GParamSpec *pspec)
775 PurpleConnection *gc = PURPLE_CONNECTION(obj);
777 switch (param_id) {
778 case PROP_PROTOCOL:
779 g_value_set_object(value, purple_connection_get_protocol(gc));
780 break;
781 case PROP_FLAGS:
782 g_value_set_flags(value, purple_connection_get_flags(gc));
783 break;
784 case PROP_STATE:
785 g_value_set_enum(value, purple_connection_get_state(gc));
786 break;
787 case PROP_ACCOUNT:
788 g_value_set_object(value, purple_connection_get_account(gc));
789 break;
790 case PROP_PASSWORD:
791 g_value_set_string(value, purple_connection_get_password(gc));
792 break;
793 case PROP_DISPLAY_NAME:
794 g_value_set_string(value, purple_connection_get_display_name(gc));
795 break;
796 default:
797 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
798 break;
802 /* GObject initialization function */
803 static void
804 purple_connection_init(PurpleConnection *gc)
806 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTING);
807 connections = g_list_append(connections, gc);
810 /* Called when done constructing */
811 static void
812 purple_connection_constructed(GObject *object)
814 PurpleConnection *gc = PURPLE_CONNECTION(object);
815 PurpleAccount *account;
817 G_OBJECT_CLASS(purple_connection_parent_class)->constructed(object);
819 g_object_get(gc, "account", &account, NULL);
820 purple_account_set_connection(account, gc);
821 g_object_unref(account);
823 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
826 /* GObject finalize function */
827 static void
828 purple_connection_finalize(GObject *object)
830 PurpleConnection *gc = PURPLE_CONNECTION(object);
831 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
832 PurpleAccount *account;
833 GSList *buddies;
834 gboolean remove = FALSE;
836 priv->is_finalizing = TRUE;
838 account = purple_connection_get_account(gc);
840 purple_debug_info("connection", "Disconnecting connection %p\n", gc);
842 if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
843 remove = TRUE;
845 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
847 while (priv->active_chats)
849 PurpleChatConversation *b = priv->active_chats->data;
851 priv->active_chats = g_slist_remove(priv->active_chats, b);
852 purple_chat_conversation_leave(b);
855 update_keepalive(gc, FALSE);
857 purple_protocol_class_close(priv->protocol, gc);
859 /* Clear out the proto data that was freed in the protocol's close method */
860 buddies = purple_blist_find_buddies(account, NULL);
861 while (buddies != NULL) {
862 PurpleBuddy *buddy = buddies->data;
863 purple_buddy_set_protocol_data(buddy, NULL);
864 buddies = g_slist_delete_link(buddies, buddies);
867 purple_http_conn_cancel_all(gc);
868 purple_proxy_connect_cancel_with_handle(gc);
870 connections = g_list_remove(connections, gc);
872 purple_connection_set_state(gc, PURPLE_CONNECTION_DISCONNECTED);
874 if (remove)
875 purple_blist_remove_account(account);
877 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
879 purple_account_request_close_with_account(account);
880 purple_request_close_with_handle(gc);
881 purple_notify_close_with_handle(gc);
883 purple_debug_info("connection", "Destroying connection %p\n", gc);
885 purple_account_set_connection(account, NULL);
887 if (priv->error_info)
888 purple_connection_error_info_free(priv->error_info);
890 if (priv->disconnect_timeout > 0)
891 g_source_remove(priv->disconnect_timeout);
893 purple_str_wipe(priv->password);
894 g_free(priv->display_name);
896 G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
899 /* Class initializer function */
900 static void purple_connection_class_init(PurpleConnectionClass *klass)
902 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
904 obj_class->finalize = purple_connection_finalize;
905 obj_class->constructed = purple_connection_constructed;
907 /* Setup properties */
908 obj_class->get_property = purple_connection_get_property;
909 obj_class->set_property = purple_connection_set_property;
911 properties[PROP_PROTOCOL] = g_param_spec_object("protocol", "Protocol",
912 "The protocol that the connection is using.",
913 PURPLE_TYPE_PROTOCOL,
914 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
915 G_PARAM_STATIC_STRINGS);
917 properties[PROP_FLAGS] = g_param_spec_flags("flags", "Connection flags",
918 "The flags of the connection.",
919 PURPLE_TYPE_CONNECTION_FLAGS, 0,
920 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
922 properties[PROP_STATE] = g_param_spec_enum("state", "Connection state",
923 "The current state of the connection.",
924 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_DISCONNECTED,
925 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
927 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
928 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
929 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
930 G_PARAM_STATIC_STRINGS);
932 properties[PROP_PASSWORD] = g_param_spec_string("password", "Password",
933 "The password used for connection.", NULL,
934 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
935 G_PARAM_STATIC_STRINGS);
937 properties[PROP_DISPLAY_NAME] = g_param_spec_string("display-name",
938 "Display name",
939 "Your name that appears to other people.", NULL,
940 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
942 g_object_class_install_properties(obj_class, PROP_LAST, properties);
945 void
946 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
948 PurpleConnection *gc;
949 PurpleProtocol *protocol;
951 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
953 if (!purple_account_is_disconnected(account))
954 return;
956 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
958 if (protocol == NULL) {
959 gchar *message;
961 message = g_strdup_printf(_("Missing protocol for %s"),
962 purple_account_get_username(account));
963 purple_notify_error(NULL, regist ? _("Registration Error") :
964 _("Connection Error"), message, NULL,
965 purple_request_cpar_from_account(account));
966 g_free(message);
967 return;
970 if (regist)
972 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user))
973 return;
975 else
977 if (((password == NULL) || (*password == '\0')) &&
978 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
979 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
981 purple_debug_error("connection", "Cannot connect to account %s without "
982 "a password.\n", purple_account_get_username(account));
983 return;
987 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
988 gc = purple_protocol_factory_iface_connection_new(protocol, account,
989 password);
990 else
991 gc = g_object_new(PURPLE_TYPE_CONNECTION,
992 "protocol", protocol,
993 "account", account,
994 "password", password,
995 NULL);
997 g_return_if_fail(gc != NULL);
999 if (regist)
1001 PurpleConnectionPrivate *priv;
1002 purple_debug_info("connection", "Registering. gc = %p\n", gc);
1004 /* set this so we don't auto-reconnect after registering */
1005 priv = purple_connection_get_instance_private(gc);
1006 priv->wants_to_die = TRUE;
1008 purple_protocol_server_iface_register_user(protocol, account);
1010 else
1012 purple_debug_info("connection", "Connecting. gc = %p\n", gc);
1014 purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
1015 purple_protocol_class_login(protocol, account);
1019 void
1020 _purple_connection_new_unregister(PurpleAccount *account, const char *password,
1021 PurpleAccountUnregistrationCb cb, void *user_data)
1023 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
1024 PurpleConnection *gc;
1025 PurpleProtocol *protocol;
1027 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
1029 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
1031 if (protocol == NULL) {
1032 gchar *message;
1034 message = g_strdup_printf(_("Missing protocol for %s"),
1035 purple_account_get_username(account));
1036 purple_notify_error(NULL, _("Unregistration Error"), message,
1037 NULL, purple_request_cpar_from_account(account));
1038 g_free(message);
1039 return;
1042 if (!purple_account_is_disconnected(account)) {
1043 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1044 return;
1047 if (((password == NULL) || (*password == '\0')) &&
1048 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
1049 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
1051 purple_debug_error("connection", "Cannot connect to account %s without "
1052 "a password.\n", purple_account_get_username(account));
1053 return;
1056 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
1057 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1058 password);
1059 else
1060 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1061 "protocol", protocol,
1062 "account", account,
1063 "password", password,
1064 NULL);
1066 g_return_if_fail(gc != NULL);
1068 purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
1070 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1073 /**************************************************************************
1074 * Connections API
1075 **************************************************************************/
1077 void
1078 _purple_assert_connection_is_valid(PurpleConnection *gc,
1079 const gchar *file, int line)
1081 if (gc && g_list_find(purple_connections_get_all(), gc))
1082 return;
1084 purple_debug_fatal("connection", "PURPLE_ASSERT_CONNECTION_IS_VALID(%p)"
1085 " failed at %s:%d", gc, file, line);
1086 exit(-1);
1089 void
1090 purple_connections_disconnect_all(void)
1092 GList *l;
1093 PurpleConnection *gc;
1094 PurpleConnectionPrivate *priv;
1096 while ((l = purple_connections_get_all()) != NULL) {
1097 gc = l->data;
1098 priv = purple_connection_get_instance_private(gc);
1099 priv->wants_to_die = TRUE;
1100 purple_account_disconnect(priv->account);
1104 GList *
1105 purple_connections_get_all(void)
1107 return connections;
1110 GList *
1111 purple_connections_get_connecting(void)
1113 return connections_connecting;
1116 void
1117 purple_connections_set_ui_ops(PurpleConnectionUiOps *ops)
1119 connection_ui_ops = ops;
1122 PurpleConnectionUiOps *
1123 purple_connections_get_ui_ops(void)
1125 return connection_ui_ops;
1128 void
1129 purple_connections_init(void)
1131 void *handle = purple_connections_get_handle();
1133 purple_signal_register(handle, "signing-on",
1134 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1135 PURPLE_TYPE_CONNECTION);
1137 purple_signal_register(handle, "signed-on",
1138 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1139 PURPLE_TYPE_CONNECTION);
1141 purple_signal_register(handle, "signing-off",
1142 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1143 PURPLE_TYPE_CONNECTION);
1145 purple_signal_register(handle, "signed-off",
1146 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1147 PURPLE_TYPE_CONNECTION);
1149 purple_signal_register(handle, "connection-error",
1150 purple_marshal_VOID__POINTER_INT_POINTER,
1151 G_TYPE_NONE, 3, PURPLE_TYPE_CONNECTION,
1152 PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
1154 purple_signal_register(handle, "autojoin",
1155 purple_marshal_BOOLEAN__POINTER, G_TYPE_NONE, 1,
1156 PURPLE_TYPE_CONNECTION);
1160 void
1161 purple_connections_uninit(void)
1163 purple_signals_unregister_by_instance(purple_connections_get_handle());
1166 void *
1167 purple_connections_get_handle(void)
1169 return &connections_handle;