libpurple/connection.c

changeset 41869
7bd5bff547b4
parent 41850
7d551859bd94
child 41903
27f355b8b497
equal deleted inserted replaced
41868:e69cbf7921ec 41869:7bd5bff547b4
71 * setting it themselves. 71 * setting it themselves.
72 * See purple_connection_error_is_fatal() 72 * See purple_connection_error_is_fatal()
73 */ 73 */
74 gboolean wants_to_die; 74 gboolean wants_to_die;
75 75
76 gboolean is_finalizing; /* The object is being destroyed. */
77
78 /* The connection error and its description if an error occurred. */ 76 /* The connection error and its description if an error occurred. */
79 PurpleConnectionErrorInfo *error_info; 77 PurpleConnectionErrorInfo *error_info;
80 78
81 guint disconnect_timeout; /* Timer used for nasty stack tricks. */ 79 guint disconnect_timeout; /* Timer used for nasty stack tricks. */
82 } PurpleConnectionPrivate; 80 } PurpleConnectionPrivate;
439 PurpleConnectionPrivate *priv = NULL; 437 PurpleConnectionPrivate *priv = NULL;
440 438
441 priv = purple_connection_get_instance_private(connection); 439 priv = purple_connection_get_instance_private(connection);
442 440
443 priv->disconnect_timeout = 0; 441 priv->disconnect_timeout = 0;
444 } 442
445 443 if(priv->state != PURPLE_CONNECTION_STATE_DISCONNECTED) {
446 purple_account_disconnect(account); 444 /* If the connection is not disconnected, disconnect it. */
447 445 purple_account_disconnect(account);
448 return FALSE; 446 } else {
447 /* Otherwise assume the connection was already disconnected or in
448 * the process of being disconnected and we just need to finish our
449 * cleanup.
450 */
451 GError *error = NULL;
452
453 if(!purple_connection_disconnect(connection, &error)) {
454 const char *message = "unknown error";
455
456 if(error != NULL) {
457 message = error->message;
458 }
459
460 purple_debug_warning("connections",
461 "failed to disconnect %p : %s",
462 connection, message);
463 }
464
465 g_clear_error(&error);
466
467 purple_account_set_connection(account, NULL);
468 }
469 }
470
471 return G_SOURCE_REMOVE;
449 } 472 }
450 473
451 void 474 void
452 purple_connection_error(PurpleConnection *connection, 475 purple_connection_error(PurpleConnection *connection,
453 PurpleConnectionError reason, 476 PurpleConnectionError reason,
657 priv->id = g_strdup(id); 680 priv->id = g_strdup(id);
658 681
659 g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_ID]); 682 g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_ID]);
660 } 683 }
661 684
685 static void
686 purple_connection_set_account(PurpleConnection *connection,
687 PurpleAccount *account)
688 {
689 PurpleConnectionPrivate *priv = NULL;
690
691 priv = purple_connection_get_instance_private(connection);
692
693 if(g_set_object(&priv->account, account)) {
694 g_object_notify_by_pspec(G_OBJECT(connection),
695 properties[PROP_ACCOUNT]);
696 }
697 }
698
662 /************************************************************************** 699 /**************************************************************************
663 * GObject code 700 * PurpleConnection Implementation
664 **************************************************************************/ 701 **************************************************************************/
665 702 static gboolean
703 purple_connection_default_connect(PurpleConnection *connection,
704 G_GNUC_UNUSED GError **error)
705 {
706 PurpleConnectionPrivate *priv = NULL;
707
708 priv = purple_connection_get_instance_private(connection);
709
710 purple_protocol_login(priv->protocol, priv->account);
711
712 return TRUE;
713 }
714
715 static gboolean
716 purple_connection_default_disconnect(PurpleConnection *connection,
717 G_GNUC_UNUSED GError **error)
718 {
719 PurpleConnectionPrivate *priv = NULL;
720
721 priv = purple_connection_get_instance_private(connection);
722
723 purple_protocol_close(priv->protocol, connection);
724
725 return TRUE;
726 }
727
728 /**************************************************************************
729 * GObject Implementation
730 **************************************************************************/
666 static void 731 static void
667 purple_connection_set_property(GObject *obj, guint param_id, 732 purple_connection_set_property(GObject *obj, guint param_id,
668 const GValue *value, GParamSpec *pspec) 733 const GValue *value, GParamSpec *pspec)
669 { 734 {
670 PurpleConnection *connection = PURPLE_CONNECTION(obj); 735 PurpleConnection *connection = PURPLE_CONNECTION(obj);
684 break; 749 break;
685 case PROP_STATE: 750 case PROP_STATE:
686 purple_connection_set_state(connection, g_value_get_enum(value)); 751 purple_connection_set_state(connection, g_value_get_enum(value));
687 break; 752 break;
688 case PROP_ACCOUNT: 753 case PROP_ACCOUNT:
689 priv->account = g_value_get_object(value); 754 purple_connection_set_account(connection,
755 g_value_get_object(value));
690 break; 756 break;
691 case PROP_PASSWORD: 757 case PROP_PASSWORD:
692 purple_connection_set_password(connection, 758 purple_connection_set_password(connection,
693 g_value_get_string(value)); 759 g_value_get_string(value));
694 break; 760 break;
761 purple_connection_set_id(connection, uuid); 827 purple_connection_set_id(connection, uuid);
762 828
763 g_free(uuid); 829 g_free(uuid);
764 } 830 }
765 831
766 purple_account_set_connection(priv->account, connection);
767
768 purple_signal_emit(purple_connections_get_handle(), "signing-on", 832 purple_signal_emit(purple_connections_get_handle(), "signing-on",
769 connection); 833 connection);
834 }
835
836 static void
837 purple_connection_dispose(GObject *obj) {
838 PurpleConnection *connection = PURPLE_CONNECTION(obj);
839 PurpleConnectionPrivate *priv = NULL;
840
841 priv = purple_connection_get_instance_private(connection);
842
843 g_clear_object(&priv->account);
844
845 if(priv->disconnect_timeout > 0) {
846 g_source_remove(priv->disconnect_timeout);
847 priv->disconnect_timeout = 0;
848 }
849
850 G_OBJECT_CLASS(purple_connection_parent_class)->dispose(obj);
770 } 851 }
771 852
772 static void 853 static void
773 purple_connection_finalize(GObject *object) { 854 purple_connection_finalize(GObject *object) {
774 PurpleConnection *connection = PURPLE_CONNECTION(object); 855 PurpleConnection *connection = PURPLE_CONNECTION(object);
775 PurpleConnectionPrivate *priv = NULL; 856 PurpleConnectionPrivate *priv = NULL;
776 GSList *buddies; 857
858 priv = purple_connection_get_instance_private(connection);
859
860 g_clear_pointer(&priv->error_info, purple_connection_error_info_free);
861
862 purple_str_wipe(priv->password);
863 g_free(priv->display_name);
864 g_free(priv->id);
865
866 G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
867 }
868
869 static void
870 purple_connection_class_init(PurpleConnectionClass *klass) {
871 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
872
873 obj_class->get_property = purple_connection_get_property;
874 obj_class->set_property = purple_connection_set_property;
875 obj_class->dispose = purple_connection_dispose;
876 obj_class->finalize = purple_connection_finalize;
877 obj_class->constructed = purple_connection_constructed;
878
879 klass->connect = purple_connection_default_connect;
880 klass->disconnect = purple_connection_default_disconnect;
881
882 properties[PROP_ID] = g_param_spec_string(
883 "id", "id",
884 "The identifier of the account",
885 NULL,
886 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
887
888 properties[PROP_PROTOCOL] = g_param_spec_object(
889 "protocol", "Protocol",
890 "The protocol that the connection is using.",
891 PURPLE_TYPE_PROTOCOL,
892 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
893
894 properties[PROP_FLAGS] = g_param_spec_flags(
895 "flags", "Connection flags",
896 "The flags of the connection.",
897 PURPLE_TYPE_CONNECTION_FLAGS, 0,
898 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
899
900 properties[PROP_STATE] = g_param_spec_enum(
901 "state", "Connection state",
902 "The current state of the connection.",
903 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_STATE_DISCONNECTED,
904 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
905
906 properties[PROP_ACCOUNT] = g_param_spec_object(
907 "account", "Account",
908 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
909 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
910
911 properties[PROP_PASSWORD] = g_param_spec_string(
912 "password", "Password",
913 "The password used for connection.", NULL,
914 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
915
916 properties[PROP_DISPLAY_NAME] = g_param_spec_string(
917 "display-name", "Display name",
918 "Your name that appears to other people.", NULL,
919 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
920
921 g_object_class_install_properties(obj_class, PROP_LAST, properties);
922 }
923
924 void
925 _purple_connection_new_unregister(PurpleAccount *account, const char *password,
926 PurpleAccountUnregistrationCb cb,
927 gpointer user_data)
928 {
929 PurpleConnection *gc;
930 PurpleProtocol *protocol;
931
932 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
933
934 protocol = purple_account_get_protocol(account);
935
936 if(protocol == NULL) {
937 gchar *message;
938
939 message = g_strdup_printf(_("Missing protocol for %s"),
940 purple_account_get_username(account));
941 purple_notify_error(NULL, _("Unregistration Error"), message,
942 NULL, purple_request_cpar_from_account(account));
943 g_free(message);
944 return;
945 }
946
947 if(!purple_account_is_disconnected(account)) {
948 purple_protocol_server_unregister_user(PURPLE_PROTOCOL_SERVER(protocol),
949 account, cb, user_data);
950 return;
951 }
952
953 if(((password == NULL) || (*password == '\0')) &&
954 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
955 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
956 {
957 purple_debug_error("connection", "Cannot connect to account %s "
958 "without a password.",
959 purple_account_get_username(account));
960
961 return;
962 }
963
964 gc = g_object_new(
965 PURPLE_TYPE_CONNECTION,
966 "protocol", protocol,
967 "account", account,
968 "password", password,
969 NULL);
970
971 g_return_if_fail(gc != NULL);
972
973 purple_debug_info("connection", "Unregistering. gc = %p", gc);
974
975 purple_protocol_server_unregister_user(PURPLE_PROTOCOL_SERVER(protocol),
976 account, cb, user_data);
977 }
978
979 gboolean
980 purple_connection_connect(PurpleConnection *connection, GError **error) {
981 PurpleConnectionClass *klass = NULL;
982 PurpleConnectionPrivate *priv = NULL;
983
984 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
985
986 priv = purple_connection_get_instance_private(connection);
987
988 if(!purple_account_is_disconnected(priv->account)) {
989 g_set_error(error, PURPLE_CONNECTION_ERROR, 0,
990 "account %s is not disconnected",
991 purple_account_get_username(priv->account));
992
993 return TRUE;
994 }
995
996 if(((priv->password == NULL) || (*priv->password == '\0')) &&
997 !(purple_protocol_get_options(priv->protocol) & OPT_PROTO_NO_PASSWORD) &&
998 !(purple_protocol_get_options(priv->protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
999 {
1000 g_set_error(error, PURPLE_CONNECTION_ERROR, 0,
1001 "Cannot connect to account %s without a password.",
1002 purple_account_get_username(priv->account));
1003
1004 return FALSE;
1005 }
1006
1007 purple_debug_info("connection", "Connecting. connection = %p",
1008 connection);
1009
1010 klass = PURPLE_CONNECTION_GET_CLASS(connection);
1011 if(klass != NULL && klass->connect != NULL) {
1012 return klass->connect(connection, error);
1013 }
1014
1015 g_set_error(error, PURPLE_CONNECTION_ERROR, 0,
1016 "The connection for %s did not implement the connect method",
1017 purple_account_get_username(priv->account));
1018
1019 return FALSE;
1020 }
1021
1022 gboolean
1023 purple_connection_disconnect(PurpleConnection *connection, GError **error) {
1024 PurpleConnectionClass *klass = NULL;
1025 PurpleConnectionPrivate *priv = NULL;
1026 GSList *buddies = NULL;
777 gboolean remove = FALSE; 1027 gboolean remove = FALSE;
778 gpointer handle; 1028 gboolean ret = TRUE;
779 1029 gpointer handle = NULL;
780 priv = purple_connection_get_instance_private(connection); 1030
781 1031 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
782 priv->is_finalizing = TRUE; 1032
783 1033 /* We don't check if the connection's state is connected as everything
784 handle = purple_connections_get_handle(); 1034 * should be idempotent when doing cleanup.
785 1035 */
786 purple_debug_info("connection", "Disconnecting connection %p", connection); 1036
787 1037 priv = purple_connection_get_instance_private(connection);
1038
1039 /* If we're not connecting, we'll need to remove stuff from our contacts
1040 * from the buddy list.
1041 */
788 if(priv->state != PURPLE_CONNECTION_STATE_CONNECTING) { 1042 if(priv->state != PURPLE_CONNECTION_STATE_CONNECTING) {
789 remove = TRUE; 1043 remove = TRUE;
790 } 1044 }
791 1045
1046 handle = purple_connections_get_handle();
1047
1048 purple_debug_info("connection", "Disconnecting connection %p", connection);
1049 purple_connection_set_state(connection,
1050 PURPLE_CONNECTION_STATE_DISCONNECTING);
792 purple_signal_emit(handle, "signing-off", connection); 1051 purple_signal_emit(handle, "signing-off", connection);
793 1052
794 g_slist_free_full(priv->active_chats, 1053 g_slist_free_full(priv->active_chats,
795 (GDestroyNotify)purple_chat_conversation_leave); 1054 (GDestroyNotify)purple_chat_conversation_leave);
796 1055
797 update_keepalive(connection, FALSE); 1056 update_keepalive(connection, FALSE);
798 1057
799 purple_protocol_close(priv->protocol, connection); 1058 /* Dispatch to the connection's disconnect method. */
1059 klass = PURPLE_CONNECTION_GET_CLASS(connection);
1060 if(klass != NULL && klass->disconnect != NULL) {
1061 ret = klass->disconnect(connection, error);
1062 }
800 1063
801 /* Clear out the proto data that was freed in the protocol's close method */ 1064 /* Clear out the proto data that was freed in the protocol's close method */
802 buddies = purple_blist_find_buddies(priv->account, NULL); 1065 buddies = purple_blist_find_buddies(priv->account, NULL);
803 while (buddies != NULL) { 1066 while (buddies != NULL) {
804 PurpleBuddy *buddy = buddies->data; 1067 PurpleBuddy *buddy = buddies->data;
805 purple_buddy_set_protocol_data(buddy, NULL); 1068 purple_buddy_set_protocol_data(buddy, NULL);
806 buddies = g_slist_delete_link(buddies, buddies); 1069 buddies = g_slist_delete_link(buddies, buddies);
807 } 1070 }
808 1071
1072 /* Do the rest of our cleanup. */
809 connections = g_list_remove(connections, connection); 1073 connections = g_list_remove(connections, connection);
810 1074
811 purple_connection_set_state(connection, PURPLE_CONNECTION_STATE_DISCONNECTED); 1075 purple_connection_set_state(connection,
1076 PURPLE_CONNECTION_STATE_DISCONNECTED);
812 1077
813 if(remove) { 1078 if(remove) {
814 purple_blist_remove_account(priv->account); 1079 purple_blist_remove_account(priv->account);
815 } 1080 }
816 1081
825 purple_signal_emit(handle, "offline"); 1090 purple_signal_emit(handle, "offline");
826 } 1091 }
827 1092
828 purple_debug_info("connection", "Destroying connection %p", connection); 1093 purple_debug_info("connection", "Destroying connection %p", connection);
829 1094
830 purple_account_set_connection(priv->account, NULL); 1095 return ret;
831
832 g_clear_pointer(&priv->error_info, purple_connection_error_info_free);
833
834 if(priv->disconnect_timeout > 0) {
835 g_source_remove(priv->disconnect_timeout);
836 }
837
838 purple_str_wipe(priv->password);
839 g_free(priv->display_name);
840 g_free(priv->id);
841
842 G_OBJECT_CLASS(purple_connection_parent_class)->finalize(object);
843 }
844
845 static void
846 purple_connection_class_init(PurpleConnectionClass *klass) {
847 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
848
849 obj_class->get_property = purple_connection_get_property;
850 obj_class->set_property = purple_connection_set_property;
851 obj_class->finalize = purple_connection_finalize;
852 obj_class->constructed = purple_connection_constructed;
853
854 properties[PROP_ID] = g_param_spec_string(
855 "id", "id",
856 "The identifier of the account",
857 NULL,
858 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
859
860 properties[PROP_PROTOCOL] = g_param_spec_object(
861 "protocol", "Protocol",
862 "The protocol that the connection is using.",
863 PURPLE_TYPE_PROTOCOL,
864 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
865
866 properties[PROP_FLAGS] = g_param_spec_flags(
867 "flags", "Connection flags",
868 "The flags of the connection.",
869 PURPLE_TYPE_CONNECTION_FLAGS, 0,
870 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
871
872 properties[PROP_STATE] = g_param_spec_enum(
873 "state", "Connection state",
874 "The current state of the connection.",
875 PURPLE_TYPE_CONNECTION_STATE, PURPLE_CONNECTION_STATE_DISCONNECTED,
876 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
877
878 properties[PROP_ACCOUNT] = g_param_spec_object(
879 "account", "Account",
880 "The account using the connection.", PURPLE_TYPE_ACCOUNT,
881 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
882
883 properties[PROP_PASSWORD] = g_param_spec_string(
884 "password", "Password",
885 "The password used for connection.", NULL,
886 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
887
888 properties[PROP_DISPLAY_NAME] = g_param_spec_string(
889 "display-name", "Display name",
890 "Your name that appears to other people.", NULL,
891 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
892
893 g_object_class_install_properties(obj_class, PROP_LAST, properties);
894 }
895
896 void
897 _purple_connection_new(PurpleAccount *account, gboolean is_registration,
898 const gchar *password)
899 {
900 PurpleConnection *connection = NULL;
901 PurpleConnectionPrivate *priv = NULL;
902 PurpleProtocol *protocol = NULL;
903
904 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
905
906 if(!purple_account_is_disconnected(account)) {
907 return;
908 }
909
910 protocol = purple_account_get_protocol(account);
911
912 if(protocol == NULL) {
913 gchar *message;
914
915 message = g_strdup_printf(_("Missing protocol for %s"),
916 purple_account_get_username(account));
917 purple_notify_error(NULL, is_registration ? _("Registration Error") :
918 _("Connection Error"), message, NULL,
919 purple_request_cpar_from_account(account));
920 g_free(message);
921
922 return;
923 }
924
925 if(is_registration) {
926 if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user)) {
927 return;
928 }
929 } else {
930 if(((password == NULL) || (*password == '\0')) &&
931 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
932 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
933 {
934 purple_debug_error("connection", "Cannot connect to account %s "
935 "without a password.",
936 purple_account_get_username(account));
937
938 return;
939 }
940 }
941
942 connection = g_object_new(
943 PURPLE_TYPE_CONNECTION,
944 "protocol", protocol,
945 "account", account,
946 "password", password,
947 NULL);
948
949 g_return_if_fail(connection != NULL);
950
951 priv = purple_connection_get_instance_private(connection);
952
953 if(is_registration) {
954 purple_debug_info("connection", "Registering. connection = %p",
955 connection);
956
957 /* set this so we don't auto-reconnect after registering */
958 priv->wants_to_die = TRUE;
959
960 purple_protocol_server_register_user(PURPLE_PROTOCOL_SERVER(protocol),
961 account);
962 } else {
963 purple_debug_info("connection", "Connecting. connection = %p",
964 connection);
965
966 purple_protocol_login(protocol, account);
967 }
968 }
969
970 void
971 _purple_connection_new_unregister(PurpleAccount *account, const char *password,
972 PurpleAccountUnregistrationCb cb,
973 gpointer user_data)
974 {
975 /* Lots of copy/pasted code to avoid API changes. You might want to integrate that into the previous function when possible. */
976 PurpleConnection *gc;
977 PurpleProtocol *protocol;
978
979 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
980
981 protocol = purple_account_get_protocol(account);
982
983 if(protocol == NULL) {
984 gchar *message;
985
986 message = g_strdup_printf(_("Missing protocol for %s"),
987 purple_account_get_username(account));
988 purple_notify_error(NULL, _("Unregistration Error"), message,
989 NULL, purple_request_cpar_from_account(account));
990 g_free(message);
991 return;
992 }
993
994 if(!purple_account_is_disconnected(account)) {
995 purple_protocol_server_unregister_user(PURPLE_PROTOCOL_SERVER(protocol),
996 account, cb, user_data);
997 return;
998 }
999
1000 if(((password == NULL) || (*password == '\0')) &&
1001 !(purple_protocol_get_options(protocol) & OPT_PROTO_NO_PASSWORD) &&
1002 !(purple_protocol_get_options(protocol) & OPT_PROTO_PASSWORD_OPTIONAL))
1003 {
1004 purple_debug_error("connection", "Cannot connect to account %s without "
1005 "a password.\n", purple_account_get_username(account));
1006
1007 return;
1008 }
1009
1010 gc = g_object_new(
1011 PURPLE_TYPE_CONNECTION,
1012 "protocol", protocol,
1013 "account", account,
1014 "password", password,
1015 NULL);
1016
1017 g_return_if_fail(gc != NULL);
1018
1019 purple_debug_info("connection", "Unregistering. gc = %p\n", gc);
1020
1021 purple_protocol_server_unregister_user(PURPLE_PROTOCOL_SERVER(protocol),
1022 account, cb, user_data);
1023 } 1096 }
1024 1097
1025 /************************************************************************** 1098 /**************************************************************************
1026 * Connections API 1099 * Connections API
1027 **************************************************************************/ 1100 **************************************************************************/

mercurial