Replace g_list_remove_link+g_list_free_1 with g_list_delete_link
[pidgin-git.git] / libpurple / connection.c
blobaba689d9fb1cb545fc27d1cbcef418f424ee481c
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 "log.h"
31 #include "notify.h"
32 #include "prefs.h"
33 #include "proxy.h"
34 #include "request.h"
35 #include "server.h"
36 #include "signals.h"
37 #include "util.h"
39 G_DEFINE_QUARK(purple-connection-error-quark, purple_connection_error);
41 /**
42 * PurpleConnection:
44 * Represents an active connection on an account.
46 struct _PurpleConnection
48 GObject gparent;
51 /* Private data for a connection */
52 typedef struct
54 PurpleProtocol *protocol; /* The protocol. */
55 PurpleConnectionFlags flags; /* Connection flags. */
57 PurpleConnectionState state; /* The connection state. */
59 PurpleAccount *account; /* The account being connected to. */
60 char *password; /* The password used. */
62 GSList *active_chats; /* A list of active chats
63 (#PurpleChatConversation structs). */
65 /* TODO Remove this and use protocol-specific subclasses. */
66 void *proto_data; /* Protocol-specific data. */
68 char *display_name; /* How you appear to other people. */
69 GSource *keepalive; /* Keep-alive. */
71 /* Wants to Die state. This is set when the user chooses to log out, or
72 * when the protocol is disconnected and should not be automatically
73 * reconnected (incorrect password, etc.). Protocols should rely on
74 * purple_connection_error() to set this for them rather than
75 * setting it themselves.
76 * See purple_connection_error_is_fatal()
78 gboolean wants_to_die;
80 gboolean is_finalizing; /* The object is being destroyed. */
82 /* The connection error and its description if an error occured */
83 PurpleConnectionErrorInfo *error_info;
85 guint disconnect_timeout; /* Timer used for nasty stack tricks */
86 } PurpleConnectionPrivate;
88 /* GObject property enums */
89 enum
91 PROP_0,
92 PROP_PROTOCOL,
93 PROP_FLAGS,
94 PROP_STATE,
95 PROP_ACCOUNT,
96 PROP_PASSWORD,
97 PROP_DISPLAY_NAME,
98 PROP_LAST
101 static GParamSpec *properties[PROP_LAST];
103 static GList *connections = NULL;
104 static GList *connections_connecting = NULL;
105 static PurpleConnectionUiOps *connection_ui_ops = NULL;
107 static int connections_handle;
109 static PurpleConnectionErrorInfo *
110 purple_connection_error_info_new(PurpleConnectionError type,
111 const gchar *description);
113 G_DEFINE_TYPE_WITH_PRIVATE(PurpleConnection, purple_connection, G_TYPE_OBJECT)
115 /**************************************************************************
116 * Connection API
117 **************************************************************************/
118 static gboolean
119 send_keepalive(gpointer data)
121 PurpleConnection *gc = data;
122 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
124 purple_protocol_server_iface_keepalive(priv->protocol, gc);
126 return TRUE;
129 static void
130 update_keepalive(PurpleConnection *gc, gboolean on)
132 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
134 if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER, keepalive))
135 return;
137 if (on && !priv->keepalive)
139 int interval = purple_protocol_server_iface_get_keepalive_interval(priv->protocol);
140 purple_debug_info("connection", "Activating keepalive to %d seconds.", interval);
141 priv->keepalive = g_main_context_find_source_by_id(NULL, g_timeout_add_seconds(interval, send_keepalive, gc));
143 else if (!on && priv->keepalive)
145 purple_debug_info("connection", "Deactivating keepalive.\n");
146 g_source_destroy(priv->keepalive);
147 priv->keepalive = NULL;
152 * d:)->-<
154 * d:O-\-<
156 * d:D-/-<
158 * d8D->-< DANCE!
161 void
162 purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
164 PurpleConnectionPrivate *priv = NULL;
165 PurpleConnectionUiOps *ops;
167 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
169 priv = purple_connection_get_instance_private(gc);
171 if (priv->state == state)
172 return;
174 priv->state = state;
176 ops = purple_connections_get_ui_ops();
178 if (priv->state == PURPLE_CONNECTION_CONNECTING) {
179 connections_connecting = g_list_append(connections_connecting, gc);
181 else {
182 connections_connecting = g_list_remove(connections_connecting, gc);
185 if (priv->state == PURPLE_CONNECTION_CONNECTED) {
186 PurpleAccount *account;
187 PurplePresence *presence;
189 account = purple_connection_get_account(gc);
190 presence = purple_account_get_presence(account);
192 /* Set the time the account came online */
193 purple_presence_set_login_time(presence, time(NULL));
195 if (purple_prefs_get_bool("/purple/logging/log_system"))
197 PurpleLog *log = purple_account_get_log(account, TRUE);
199 if (log != NULL)
201 char *msg = g_strdup_printf(_("+++ %s signed on"),
202 purple_account_get_username(account));
203 GDateTime *dt = g_date_time_new_from_unix_local(purple_presence_get_login_time(presence));
204 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
205 purple_account_get_username(account),
206 dt, msg);
207 g_date_time_unref(dt);
208 g_free(msg);
212 if (ops != NULL && ops->connected != NULL)
213 ops->connected(gc);
215 purple_blist_add_account(account);
217 purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
218 purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
220 purple_serv_set_permit_deny(gc);
222 update_keepalive(gc, TRUE);
224 else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
225 PurpleAccount *account = purple_connection_get_account(gc);
227 if (purple_prefs_get_bool("/purple/logging/log_system"))
229 PurpleLog *log = purple_account_get_log(account, FALSE);
231 if (log != NULL)
233 char *msg = g_strdup_printf(_("+++ %s signed off"),
234 purple_account_get_username(account));
235 GDateTime *dt = g_date_time_new_now_utc();
236 purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
237 purple_account_get_username(account),
238 dt, msg);
239 g_date_time_unref(dt);
240 g_free(msg);
244 purple_account_destroy_log(account);
246 if (ops != NULL && ops->disconnected != NULL)
247 ops->disconnected(gc);
250 if (!priv->is_finalizing)
251 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_STATE]);
254 void
255 purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
257 PurpleConnectionPrivate *priv = NULL;
259 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
261 priv = purple_connection_get_instance_private(gc);
262 priv->flags = flags;
264 if (!priv->is_finalizing)
265 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_FLAGS]);
268 void
269 purple_connection_set_display_name(PurpleConnection *gc, const char *name)
271 PurpleConnectionPrivate *priv = NULL;
273 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
275 priv = purple_connection_get_instance_private(gc);
276 g_free(priv->display_name);
277 priv->display_name = g_strdup(name);
279 g_object_notify_by_pspec(G_OBJECT(gc), properties[PROP_DISPLAY_NAME]);
282 void
283 purple_connection_set_protocol_data(PurpleConnection *gc, void *proto_data)
285 PurpleConnectionPrivate *priv = NULL;
287 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
289 priv = purple_connection_get_instance_private(gc);
290 priv->proto_data = proto_data;
293 PurpleConnectionState
294 purple_connection_get_state(PurpleConnection *gc)
296 PurpleConnectionPrivate *priv = NULL;
298 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), PURPLE_CONNECTION_DISCONNECTED);
300 priv = purple_connection_get_instance_private(gc);
301 return priv->state;
304 PurpleConnectionFlags
305 purple_connection_get_flags(PurpleConnection *gc)
307 PurpleConnectionPrivate *priv = NULL;
309 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), 0);
311 priv = purple_connection_get_instance_private(gc);
312 return priv->flags;
315 gboolean
316 purple_connection_is_disconnecting(PurpleConnection *gc)
318 PurpleConnectionPrivate *priv = NULL;
320 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), TRUE);
322 priv = purple_connection_get_instance_private(gc);
323 return priv->is_finalizing;
326 PurpleAccount *
327 purple_connection_get_account(PurpleConnection *gc)
329 PurpleConnectionPrivate *priv = NULL;
331 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
333 priv = purple_connection_get_instance_private(gc);
334 return priv->account;
337 PurpleProtocol *
338 purple_connection_get_protocol(PurpleConnection *gc)
340 PurpleConnectionPrivate *priv = NULL;
342 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
344 priv = purple_connection_get_instance_private(gc);
345 return priv->protocol;
348 const char *
349 purple_connection_get_password(PurpleConnection *gc)
351 PurpleConnectionPrivate *priv = NULL;
353 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
355 priv = purple_connection_get_instance_private(gc);
356 return priv->password;
359 GSList *
360 purple_connection_get_active_chats(PurpleConnection *gc)
362 PurpleConnectionPrivate *priv = NULL;
364 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
366 priv = purple_connection_get_instance_private(gc);
367 return priv->active_chats;
370 const char *
371 purple_connection_get_display_name(PurpleConnection *gc)
373 PurpleConnectionPrivate *priv = NULL;
375 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
377 priv = purple_connection_get_instance_private(gc);
378 return priv->display_name;
381 void *
382 purple_connection_get_protocol_data(PurpleConnection *gc)
384 PurpleConnectionPrivate *priv = NULL;
386 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
388 priv = purple_connection_get_instance_private(gc);
389 return priv->proto_data;
392 void
393 _purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
395 PurpleConnectionPrivate *priv = NULL;
397 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
399 priv = purple_connection_get_instance_private(gc);
400 priv->active_chats = g_slist_append(priv->active_chats, chat);
403 void
404 _purple_connection_remove_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
406 PurpleConnectionPrivate *priv = NULL;
408 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
410 priv = purple_connection_get_instance_private(gc);
411 priv->active_chats = g_slist_remove(priv->active_chats, chat);
414 gboolean
415 _purple_connection_wants_to_die(PurpleConnection *gc)
417 PurpleConnectionPrivate *priv = NULL;
419 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), FALSE);
421 priv = purple_connection_get_instance_private(gc);
422 return priv->wants_to_die;
425 void
426 purple_connection_update_progress(PurpleConnection *gc, const char *text,
427 size_t step, size_t count)
429 PurpleConnectionUiOps *ops;
431 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
432 g_return_if_fail(text != NULL);
433 g_return_if_fail(step < count);
434 g_return_if_fail(count > 1);
436 ops = purple_connections_get_ui_ops();
438 if (ops != NULL && ops->connect_progress != NULL)
439 ops->connect_progress(gc, text, step, count);
442 void
443 purple_connection_notice(PurpleConnection *gc, const char *text)
445 PurpleConnectionUiOps *ops;
447 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
448 g_return_if_fail(text != NULL);
450 ops = purple_connections_get_ui_ops();
452 if (ops != NULL && ops->notice != NULL)
453 ops->notice(gc, text);
456 static gboolean
457 purple_connection_disconnect_cb(gpointer data)
459 PurpleAccount *account;
460 PurpleConnection *gc;
462 account = data;
463 gc = purple_account_get_connection(account);
465 if (PURPLE_IS_CONNECTION(gc)) {
466 PurpleConnectionPrivate *priv;
467 priv = purple_connection_get_instance_private(gc);
468 priv->disconnect_timeout = 0;
471 purple_account_disconnect(account);
472 return FALSE;
475 void
476 purple_connection_error (PurpleConnection *gc,
477 PurpleConnectionError reason,
478 const char *description)
480 PurpleConnectionPrivate *priv = NULL;
481 PurpleConnectionUiOps *ops;
483 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
484 priv = purple_connection_get_instance_private(gc);
486 /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
487 * being the last member of the PurpleConnectionError enum in
488 * connection.h; if other reasons are added after it, this check should
489 * be updated.
491 if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
492 purple_debug_error("connection",
493 "purple_connection_error: reason %u isn't a "
494 "valid reason\n", reason);
495 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
498 if (description == NULL) {
499 purple_debug_error("connection", "purple_connection_error called with NULL description\n");
500 description = _("Unknown error");
503 /* If we've already got one error, we don't need any more */
504 if (purple_connection_get_error_info(gc))
505 return;
507 priv->wants_to_die = purple_connection_error_is_fatal (reason);
509 purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
510 gc, reason, description);
512 ops = purple_connections_get_ui_ops();
514 if (ops && ops->report_disconnect)
515 ops->report_disconnect(gc, reason, description);
517 priv->error_info = purple_connection_error_info_new(reason, description);
519 purple_signal_emit(purple_connections_get_handle(), "connection-error",
520 gc, reason, description);
522 priv->disconnect_timeout = g_timeout_add(0, purple_connection_disconnect_cb,
523 purple_connection_get_account(gc));
526 PurpleConnectionErrorInfo *
527 purple_connection_get_error_info(PurpleConnection *gc)
529 PurpleConnectionPrivate *priv = NULL;
531 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
533 priv = purple_connection_get_instance_private(gc);
534 return priv->error_info;
537 void
538 purple_connection_ssl_error (PurpleConnection *gc,
539 PurpleSslErrorType ssl_error)
541 PurpleConnectionError reason;
543 switch (ssl_error) {
544 case PURPLE_SSL_HANDSHAKE_FAILED:
545 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
546 break;
547 case PURPLE_SSL_CONNECT_FAILED:
548 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
549 break;
550 case PURPLE_SSL_CERTIFICATE_INVALID:
551 /* TODO: maybe PURPLE_SSL_* should be more specific? */
552 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
553 break;
554 default:
555 g_assert_not_reached ();
556 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
559 purple_connection_error (gc, reason,
560 purple_ssl_strerror(ssl_error));
563 void
564 purple_connection_g_error(PurpleConnection *pc, const GError *error)
566 PurpleConnectionError reason;
568 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
569 /* Not a connection error. Ignore. */
570 return;
573 if (error->domain == G_TLS_ERROR) {
574 switch (error->code) {
575 case G_TLS_ERROR_UNAVAILABLE:
576 reason = PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT;
577 break;
578 case G_TLS_ERROR_NOT_TLS:
579 case G_TLS_ERROR_HANDSHAKE:
580 reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR;
581 break;
582 case G_TLS_ERROR_BAD_CERTIFICATE:
583 case G_TLS_ERROR_CERTIFICATE_REQUIRED:
584 reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
585 break;
586 case G_TLS_ERROR_EOF:
587 case G_TLS_ERROR_MISC:
588 default:
589 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
591 } else if (error->domain == G_IO_ERROR) {
592 reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
593 } else if (error->domain == PURPLE_CONNECTION_ERROR) {
594 reason = error->code;
595 } else {
596 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
599 purple_connection_error(pc, reason, error->message);
602 void
603 purple_connection_take_error(PurpleConnection *pc, GError *error)
605 purple_connection_g_error(pc, error);
606 g_error_free(error);
609 gboolean
610 purple_connection_error_is_fatal (PurpleConnectionError reason)
612 switch (reason)
614 case PURPLE_CONNECTION_ERROR_NETWORK_ERROR:
615 case PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR:
616 return FALSE;
617 case PURPLE_CONNECTION_ERROR_INVALID_USERNAME:
618 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED:
619 case PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE:
620 case PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT:
621 case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
622 case PURPLE_CONNECTION_ERROR_INVALID_SETTINGS:
623 case PURPLE_CONNECTION_ERROR_OTHER_ERROR:
624 case PURPLE_CONNECTION_ERROR_CERT_NOT_PROVIDED:
625 case PURPLE_CONNECTION_ERROR_CERT_UNTRUSTED:
626 case PURPLE_CONNECTION_ERROR_CERT_EXPIRED:
627 case PURPLE_CONNECTION_ERROR_CERT_NOT_ACTIVATED:
628 case PURPLE_CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH:
629 case PURPLE_CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH:
630 case PURPLE_CONNECTION_ERROR_CERT_SELF_SIGNED:
631 case PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR:
632 return TRUE;
633 default:
634 g_return_val_if_reached(TRUE);
638 void purple_connection_update_last_received(PurpleConnection *gc)
640 PurpleConnectionPrivate *priv = NULL;
642 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
643 priv = purple_connection_get_instance_private(gc);
646 * For safety, actually this function shouldn't be called when the
647 * keepalive mechanism is inactive.
649 if (priv->keepalive) {
650 /* The #GTimeoutSource API doesn't expose a function to reset when a
651 * #GTimeoutSource will dispatch the next time, but because it works to
652 * directly call g_source_set_ready_time() on a #GTimeoutSource, and since
653 * it seems unlikely that the implementation will change, we just do that
654 * for now as a workaround for this API shortcoming.
656 gint64 seconds_from_now = purple_protocol_server_iface_get_keepalive_interval(priv->protocol);
658 g_source_set_ready_time(
659 priv->keepalive,
660 g_get_monotonic_time() + (seconds_from_now * G_USEC_PER_SEC)
665 static PurpleConnectionErrorInfo *
666 purple_connection_error_info_new(PurpleConnectionError type,
667 const gchar *description)
669 PurpleConnectionErrorInfo *err;
671 g_return_val_if_fail(description != NULL, NULL);
673 err = g_new(PurpleConnectionErrorInfo, 1);
675 err->type = type;
676 err->description = g_strdup(description);
678 return err;
681 /**************************************************************************
682 * GBoxed code
683 **************************************************************************/
684 static PurpleConnectionUiOps *
685 purple_connection_ui_ops_copy(PurpleConnectionUiOps *ops)
687 PurpleConnectionUiOps *ops_new;
689 g_return_val_if_fail(ops != NULL, NULL);
691 ops_new = g_new(PurpleConnectionUiOps, 1);
692 *ops_new = *ops;
694 return ops_new;
697 GType
698 purple_connection_ui_ops_get_type(void)
700 static GType type = 0;
702 if (type == 0) {
703 type = g_boxed_type_register_static("PurpleConnectionUiOps",
704 (GBoxedCopyFunc)purple_connection_ui_ops_copy,
705 (GBoxedFreeFunc)g_free);
708 return type;
711 static PurpleConnectionErrorInfo *
712 purple_connection_error_info_copy(PurpleConnectionErrorInfo *err)
714 g_return_val_if_fail(err != NULL, NULL);
716 return purple_connection_error_info_new(err->type, err->description);
719 static void
720 purple_connection_error_info_free(PurpleConnectionErrorInfo *err)
722 g_return_if_fail(err != NULL);
724 g_free(err->description);
725 g_free(err);
728 GType
729 purple_connection_error_info_get_type(void)
731 static GType type = 0;
733 if (type == 0) {
734 type = g_boxed_type_register_static("PurpleConnectionErrorInfo",
735 (GBoxedCopyFunc)purple_connection_error_info_copy,
736 (GBoxedFreeFunc)purple_connection_error_info_free);
739 return type;
742 /**************************************************************************
743 * GObject code
744 **************************************************************************/
746 /* Set method for GObject properties */
747 static void
748 purple_connection_set_property(GObject *obj, guint param_id, const GValue *value,
749 GParamSpec *pspec)
751 PurpleConnection *gc = PURPLE_CONNECTION(obj);
752 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
754 switch (param_id) {
755 case PROP_PROTOCOL:
756 priv->protocol = g_value_get_object(value);
757 break;
758 case PROP_FLAGS:
759 purple_connection_set_flags(gc, g_value_get_flags(value));
760 break;
761 case PROP_STATE:
762 purple_connection_set_state(gc, g_value_get_enum(value));
763 break;
764 case PROP_ACCOUNT:
765 priv->account = g_value_get_object(value);
766 break;
767 case PROP_PASSWORD:
768 g_free(priv->password);
769 priv->password = g_value_dup_string(value);
770 break;
771 case PROP_DISPLAY_NAME:
772 purple_connection_set_display_name(gc, g_value_get_string(value));
773 break;
774 default:
775 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
776 break;
780 /* Get method for GObject properties */
781 static void
782 purple_connection_get_property(GObject *obj, guint param_id, GValue *value,
783 GParamSpec *pspec)
785 PurpleConnection *gc = PURPLE_CONNECTION(obj);
787 switch (param_id) {
788 case PROP_PROTOCOL:
789 g_value_set_object(value, purple_connection_get_protocol(gc));
790 break;
791 case PROP_FLAGS:
792 g_value_set_flags(value, purple_connection_get_flags(gc));
793 break;
794 case PROP_STATE:
795 g_value_set_enum(value, purple_connection_get_state(gc));
796 break;
797 case PROP_ACCOUNT:
798 g_value_set_object(value, purple_connection_get_account(gc));
799 break;
800 case PROP_PASSWORD:
801 g_value_set_string(value, purple_connection_get_password(gc));
802 break;
803 case PROP_DISPLAY_NAME:
804 g_value_set_string(value, purple_connection_get_display_name(gc));
805 break;
806 default:
807 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
808 break;
812 /* GObject initialization function */
813 static void
814 purple_connection_init(PurpleConnection *gc)
816 purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTING);
817 connections = g_list_append(connections, gc);
820 /* Called when done constructing */
821 static void
822 purple_connection_constructed(GObject *object)
824 PurpleConnection *gc = PURPLE_CONNECTION(object);
825 PurpleAccount *account;
827 G_OBJECT_CLASS(purple_connection_parent_class)->constructed(object);
829 g_object_get(gc, "account", &account, NULL);
830 purple_account_set_connection(account, gc);
831 g_object_unref(account);
833 purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
836 /* GObject finalize function */
837 static void
838 purple_connection_finalize(GObject *object)
840 PurpleConnection *gc = PURPLE_CONNECTION(object);
841 PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
842 PurpleAccount *account;
843 GSList *buddies;
844 gboolean remove = FALSE;
846 priv->is_finalizing = TRUE;
848 account = purple_connection_get_account(gc);
850 purple_debug_info("connection", "Disconnecting connection %p\n", gc);
852 if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
853 remove = TRUE;
855 purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
857 g_slist_free_full(priv->active_chats, (GDestroyNotify)purple_chat_conversation_leave);
859 update_keepalive(gc, FALSE);
861 purple_protocol_class_close(priv->protocol, gc);
863 /* Clear out the proto data that was freed in the protocol's close method */
864 buddies = purple_blist_find_buddies(account, NULL);
865 while (buddies != NULL) {
866 PurpleBuddy *buddy = buddies->data;
867 purple_buddy_set_protocol_data(buddy, NULL);
868 buddies = g_slist_delete_link(buddies, buddies);
871 purple_proxy_connect_cancel_with_handle(gc);
873 connections = g_list_remove(connections, gc);
875 purple_connection_set_state(gc, PURPLE_CONNECTION_DISCONNECTED);
877 if (remove)
878 purple_blist_remove_account(account);
880 purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
882 purple_account_request_close_with_account(account);
883 purple_request_close_with_handle(gc);
884 purple_notify_close_with_handle(gc);
886 purple_debug_info("connection", "Destroying connection %p\n", gc);
888 purple_account_set_connection(account, NULL);
890 if (priv->error_info)
891 purple_connection_error_info_free(priv->error_info);
893 if (priv->disconnect_timeout > 0)
894 g_source_remove(priv->disconnect_timeout);
896 purple_str_wipe(priv->password);
897 g_free(priv->display_name);
899 G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
902 /* Class initializer function */
903 static void purple_connection_class_init(PurpleConnectionClass *klass)
905 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
907 obj_class->finalize = purple_connection_finalize;
908 obj_class->constructed = purple_connection_constructed;
910 /* Setup properties */
911 obj_class->get_property = purple_connection_get_property;
912 obj_class->set_property = purple_connection_set_property;
914 properties[PROP_PROTOCOL] = g_param_spec_object("protocol", "Protocol",
915 "The protocol that the connection is using.",
916 PURPLE_TYPE_PROTOCOL,
917 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
918 G_PARAM_STATIC_STRINGS);
920 properties[PROP_FLAGS] = g_param_spec_flags("flags", "Connection flags",
921 "The flags of the connection.",
922 PURPLE_TYPE_CONNECTION_FLAGS, 0,
923 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
925 properties[PROP_STATE] = g_param_spec_enum("state", "Connection state",
926 "The current state of the connection.",
927 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_DISCONNECTED,
928 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
930 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
931 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
932 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
933 G_PARAM_STATIC_STRINGS);
935 properties[PROP_PASSWORD] = g_param_spec_string("password", "Password",
936 "The password used for connection.", NULL,
937 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
938 G_PARAM_STATIC_STRINGS);
940 properties[PROP_DISPLAY_NAME] = g_param_spec_string("display-name",
941 "Display name",
942 "Your name that appears to other people.", NULL,
943 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
945 g_object_class_install_properties(obj_class, PROP_LAST, properties);
948 void
949 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
951 PurpleConnection *gc;
952 PurpleProtocol *protocol;
954 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
956 if (!purple_account_is_disconnected(account))
957 return;
959 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
961 if (protocol == NULL) {
962 gchar *message;
964 message = g_strdup_printf(_("Missing protocol for %s"),
965 purple_account_get_username(account));
966 purple_notify_error(NULL, regist ? _("Registration Error") :
967 _("Connection Error"), message, NULL,
968 purple_request_cpar_from_account(account));
969 g_free(message);
970 return;
973 if (regist)
975 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user))
976 return;
978 else
980 if (((password == NULL) || (*password == '\0')) &&
981 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
982 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
984 purple_debug_error("connection", "Cannot connect to account %s without "
985 "a password.\n", purple_account_get_username(account));
986 return;
990 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
991 gc = purple_protocol_factory_iface_connection_new(protocol, account,
992 password);
993 else
994 gc = g_object_new(PURPLE_TYPE_CONNECTION,
995 "protocol", protocol,
996 "account", account,
997 "password", password,
998 NULL);
1000 g_return_if_fail(gc != NULL);
1002 if (regist)
1004 PurpleConnectionPrivate *priv;
1005 purple_debug_info("connection", "Registering. gc = %p\n", gc);
1007 /* set this so we don't auto-reconnect after registering */
1008 priv = purple_connection_get_instance_private(gc);
1009 priv->wants_to_die = TRUE;
1011 purple_protocol_server_iface_register_user(protocol, account);
1013 else
1015 purple_debug_info("connection", "Connecting. gc = %p\n", gc);
1017 purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
1018 purple_protocol_class_login(protocol, account);
1022 void
1023 _purple_connection_new_unregister(PurpleAccount *account, const char *password,
1024 PurpleAccountUnregistrationCb cb, void *user_data)
1026 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when posssible. */
1027 PurpleConnection *gc;
1028 PurpleProtocol *protocol;
1030 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
1032 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
1034 if (protocol == NULL) {
1035 gchar *message;
1037 message = g_strdup_printf(_("Missing protocol for %s"),
1038 purple_account_get_username(account));
1039 purple_notify_error(NULL, _("Unregistration Error"), message,
1040 NULL, purple_request_cpar_from_account(account));
1041 g_free(message);
1042 return;
1045 if (!purple_account_is_disconnected(account)) {
1046 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1047 return;
1050 if (((password == NULL) || (*password == '\0')) &&
1051 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
1052 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
1054 purple_debug_error("connection", "Cannot connect to account %s without "
1055 "a password.\n", purple_account_get_username(account));
1056 return;
1059 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
1060 gc = purple_protocol_factory_iface_connection_new(protocol, account,
1061 password);
1062 else
1063 gc = g_object_new(PURPLE_TYPE_CONNECTION,
1064 "protocol", protocol,
1065 "account", account,
1066 "password", password,
1067 NULL);
1069 g_return_if_fail(gc != NULL);
1071 purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
1073 purple_protocol_server_iface_unregister_user(protocol, account, cb, user_data);
1076 /**************************************************************************
1077 * Connections API
1078 **************************************************************************/
1080 void
1081 _purple_assert_connection_is_valid(PurpleConnection *gc,
1082 const gchar *file, int line)
1084 if (gc && g_list_find(purple_connections_get_all(), gc))
1085 return;
1087 purple_debug_fatal("connection", "PURPLE_ASSERT_CONNECTION_IS_VALID(%p)"
1088 " failed at %s:%d", gc, file, line);
1089 exit(-1);
1092 void
1093 purple_connections_disconnect_all(void)
1095 GList *l;
1096 PurpleConnection *gc;
1097 PurpleConnectionPrivate *priv;
1099 while ((l = purple_connections_get_all()) != NULL) {
1100 gc = l->data;
1101 priv = purple_connection_get_instance_private(gc);
1102 priv->wants_to_die = TRUE;
1103 purple_account_disconnect(priv->account);
1107 GList *
1108 purple_connections_get_all(void)
1110 return connections;
1113 GList *
1114 purple_connections_get_connecting(void)
1116 return connections_connecting;
1119 void
1120 purple_connections_set_ui_ops(PurpleConnectionUiOps *ops)
1122 connection_ui_ops = ops;
1125 PurpleConnectionUiOps *
1126 purple_connections_get_ui_ops(void)
1128 return connection_ui_ops;
1131 void
1132 purple_connections_init(void)
1134 void *handle = purple_connections_get_handle();
1136 purple_signal_register(handle, "signing-on",
1137 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1138 PURPLE_TYPE_CONNECTION);
1140 purple_signal_register(handle, "signed-on",
1141 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1142 PURPLE_TYPE_CONNECTION);
1144 purple_signal_register(handle, "signing-off",
1145 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1146 PURPLE_TYPE_CONNECTION);
1148 purple_signal_register(handle, "signed-off",
1149 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
1150 PURPLE_TYPE_CONNECTION);
1152 purple_signal_register(handle, "connection-error",
1153 purple_marshal_VOID__POINTER_INT_POINTER,
1154 G_TYPE_NONE, 3, PURPLE_TYPE_CONNECTION,
1155 PURPLE_TYPE_CONNECTION_ERROR, G_TYPE_STRING);
1157 purple_signal_register(handle, "autojoin",
1158 purple_marshal_BOOLEAN__POINTER, G_TYPE_NONE, 1,
1159 PURPLE_TYPE_CONNECTION);
1163 void
1164 purple_connections_uninit(void)
1166 purple_signals_unregister_by_instance(purple_connections_get_handle());
1169 void *
1170 purple_connections_get_handle(void)
1172 return &connections_handle;