From 6ee9436db4f9f1907353442af5937d89e29ce91d Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Mon, 7 Sep 2009 23:51:43 +0200 Subject: [PATCH] Save logins in a text file instead of a keyring This means GNOME keyring isn't required for saving logins. --- katze/katze-http-auth.c | 321 +++++++++++++++++++++++++++++++++------- midori/main.c | 20 +-- 2 files changed, 270 insertions(+), 71 deletions(-) diff --git a/katze/katze-http-auth.c b/katze/katze-http-auth.c index 59770e73..fb020822 100644 --- a/katze/katze-http-auth.c +++ b/katze/katze-http-auth.c @@ -18,10 +18,13 @@ #include #include #include +#include struct _KatzeHttpAuth { GObject parent_instance; + gchar* filename; + GHashTable* logins; }; struct _KatzeHttpAuthClass @@ -29,6 +32,20 @@ struct _KatzeHttpAuthClass GObjectClass parent_class; }; +typedef struct +{ + KatzeHttpAuth* http_auth; + SoupAuth* auth; + gchar* username; + gchar* password; +} KatzeHttpAuthSave; + +typedef struct +{ + gchar* username; + gchar* password; +} KatzeHttpAuthLogin; + static void katze_http_auth_session_feature_iface_init (SoupSessionFeatureInterface *iface, gpointer data); @@ -37,28 +54,87 @@ G_DEFINE_TYPE_WITH_CODE (KatzeHttpAuth, katze_http_auth, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, katze_http_auth_session_feature_iface_init)); -#ifdef HAVE_LIBSOUP_2_27_91 +enum +{ + PROP_0, + + PROP_FILENAME +}; + static void -authentication_message_got_headers_cb (SoupMessage* msg, - SoupAuth* auth) +katze_http_auth_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec); + +static void +katze_http_auth_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec); + +static void +katze_http_auth_finalize (GObject* object); + +static gchar* +katze_http_auth_soup_auth_get_hash (SoupAuth* auth) +{ + return g_strdup_printf ("%s:%s:%s", + soup_auth_get_host (auth), + soup_auth_get_scheme_name (auth), + soup_auth_get_realm (auth)); +} + +static void +authentication_message_got_headers_cb (SoupMessage* msg, + KatzeHttpAuthSave* save) { /* Anything but 401 and 5xx means the password was accepted */ if (msg->status_code != 401 && msg->status_code < 500) { - gchar* username = g_object_get_data (G_OBJECT (msg), "username"); - gchar* password = g_object_get_data (G_OBJECT (msg), "password"); - soup_auth_save_password (auth, username, password); + gchar* opaque_info; + FILE* file; + + opaque_info = katze_http_auth_soup_auth_get_hash (save->auth); + + if (!g_hash_table_lookup (save->http_auth->logins, opaque_info)) + { + KatzeHttpAuthLogin* login; + login = g_new (KatzeHttpAuthLogin, 1); + login->username = save->username; + login->password = save->password; + g_hash_table_insert (save->http_auth->logins, opaque_info, login); + + if ((file = g_fopen (save->http_auth->filename, "a"))) + { + fprintf (file, "%s\t%s\t%s\n", opaque_info, + login->username, login->password); + fclose (file); + } + } + else + { + /* FIXME g_free (save->username); + g_free (save->password); */ + } } + else + { + /* FIXME g_free (save->username); + g_free (save->password); */ + } + + /* FIXME g_object_unref (save->auth); */ + /* FIXME g_free (save); */ + g_signal_handlers_disconnect_by_func (msg, + authentication_message_got_headers_cb, save); } -#endif static void -authentication_dialog_response_cb (GtkWidget* dialog, - gint response, - SoupAuth* auth) +authentication_dialog_response_cb (GtkWidget* dialog, + gint response, + KatzeHttpAuthSave* save) { - GtkWidget* username; - GtkWidget* password; SoupSession* session; SoupMessage* msg; @@ -66,36 +142,42 @@ authentication_dialog_response_cb (GtkWidget* dialog, if (response == GTK_RESPONSE_OK) { + GtkEntry* username = g_object_get_data (G_OBJECT (dialog), "username"); + GtkEntry* password = g_object_get_data (G_OBJECT (dialog), "password"); - username = g_object_get_data (G_OBJECT (dialog), "username"); - password = g_object_get_data (G_OBJECT (dialog), "password"); + soup_auth_authenticate (save->auth, + gtk_entry_get_text (username), gtk_entry_get_text (password)); - soup_auth_authenticate (auth, - gtk_entry_get_text (GTK_ENTRY (username)), - gtk_entry_get_text (GTK_ENTRY (password))); - #ifdef HAVE_LIBSOUP_2_27_91 - g_object_set_data_full (G_OBJECT (msg), "username", - g_strdup (gtk_entry_get_text (GTK_ENTRY (username))), g_free); - g_object_set_data_full (G_OBJECT (msg), "password", - g_strdup (gtk_entry_get_text (GTK_ENTRY (password))), g_free); - g_signal_connect (msg, "got-headers", - G_CALLBACK (authentication_message_got_headers_cb), auth); - #endif + if (save->http_auth->filename) + { + save->username = g_strdup (gtk_entry_get_text (username)); + save->password = g_strdup (gtk_entry_get_text (password)); + g_signal_connect (msg, "got-headers", + G_CALLBACK (authentication_message_got_headers_cb), save); + } + else + { + g_object_unref (save->auth); + g_free (save); + } } session = g_object_get_data (G_OBJECT (dialog), "session"); - gtk_widget_destroy (dialog); if (g_object_get_data (G_OBJECT (msg), "paused")) soup_session_unpause_message (session, msg); - g_object_unref (auth); + gtk_widget_destroy (dialog); + g_object_unref (msg); } static void -katze_http_auth_session_authenticate_cb (SoupSession* session, - SoupMessage* msg, - SoupAuth* auth, - gboolean retrying) +katze_http_auth_session_authenticate_cb (SoupSession* session, + SoupMessage* msg, + SoupAuth* auth, + gboolean retrying, + KatzeHttpAuth* http_auth) { + gchar* opaque_info; + KatzeHttpAuthLogin* login; GtkWidget* dialog; GtkSizeGroup* sizegroup; GtkWidget* hbox; @@ -103,24 +185,26 @@ katze_http_auth_session_authenticate_cb (SoupSession* session, GtkWidget* label; GtkWidget* align; GtkWidget* entry; - #ifdef HAVE_LIBSOUP_2_27_91 - GSList* users; - #endif + KatzeHttpAuthSave* save; /* We want to ask for authentication exactly once, so we enforce this with a tag. There might be a better way. */ if (!retrying && g_object_get_data (G_OBJECT (msg), "katze-session-tag")) return; - if (soup_message_is_keepalive (msg)) + if (1) { /* We use another tag to indicate whether a message is paused. There doesn't seem to be API in libSoup to find that out. */ - soup_session_pause_message (session, msg); + soup_session_pause_message (session, g_object_ref (msg)); g_object_set_data (G_OBJECT (msg), "paused", (void*)1); } g_object_set_data (G_OBJECT (msg), "katze-session-tag", (void*)1); + opaque_info = katze_http_auth_soup_auth_get_hash (auth); + login = g_hash_table_lookup (http_auth->logins, opaque_info); + g_free (opaque_info); + dialog = gtk_dialog_new_with_buttons (_("Authentication Required"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, @@ -157,11 +241,8 @@ katze_http_auth_session_authenticate_cb (SoupSession* session, gtk_size_group_add_widget (sizegroup, align); gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0); entry = gtk_entry_new (); - #ifdef HAVE_LIBSOUP_2_27_91 - users = soup_auth_get_saved_users (auth); - if (users) - gtk_entry_set_text (GTK_ENTRY (entry), users->data); - #endif + if (login) + gtk_entry_set_text (GTK_ENTRY (entry), login->username); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); g_object_set_data (G_OBJECT (dialog), "username", entry); @@ -173,14 +254,8 @@ katze_http_auth_session_authenticate_cb (SoupSession* session, gtk_size_group_add_widget (sizegroup, align); gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0); entry = gtk_entry_new_with_max_length (32); - #ifdef HAVE_LIBSOUP_2_27_91 - if (users) - { - gtk_entry_set_text (GTK_ENTRY (entry), - soup_auth_get_saved_password (auth, users->data)); - g_slist_free (users); - } - #endif + if (login) + gtk_entry_set_text (GTK_ENTRY (entry), login->password); gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); @@ -191,15 +266,19 @@ katze_http_auth_session_authenticate_cb (SoupSession* session, g_object_set_data (G_OBJECT (dialog), "session", session); g_object_set_data (G_OBJECT (dialog), "msg", msg); + + save = g_new (KatzeHttpAuthSave, 1); + save->http_auth = http_auth; + save->auth = g_object_ref (auth); g_signal_connect (dialog, "response", - G_CALLBACK (authentication_dialog_response_cb), g_object_ref (auth)); + G_CALLBACK (authentication_dialog_response_cb), save); gtk_widget_show (dialog); } static void -katze_http_auth_session_request_queued_cb (SoupSession* session, - SoupMessage* msg, - gpointer data) +katze_http_auth_session_request_queued_cb (SoupSession* session, + SoupMessage* msg, + KatzeHttpAuth* http_auth) { /* WebKit has its own authentication dialog in recent versions. We want only one, and we choose our own to have localization. */ @@ -208,7 +287,7 @@ katze_http_auth_session_request_queued_cb (SoupSession* session, soup_session_remove_feature_by_type (session, type); g_signal_connect (session, "authenticate", - G_CALLBACK (katze_http_auth_session_authenticate_cb), NULL); + G_CALLBACK (katze_http_auth_session_authenticate_cb), http_auth); g_signal_handlers_disconnect_by_func (session, katze_http_auth_session_request_queued_cb, NULL); } @@ -218,7 +297,7 @@ katze_http_auth_attach (SoupSessionFeature* feature, SoupSession* session) { g_signal_connect (session, "request-queued", - G_CALLBACK (katze_http_auth_session_request_queued_cb), NULL); + G_CALLBACK (katze_http_auth_session_request_queued_cb), feature); } static void @@ -242,11 +321,139 @@ katze_http_auth_session_feature_iface_init (SoupSessionFeatureInterface *iface, static void katze_http_auth_class_init (KatzeHttpAuthClass* class) { - /* Nothing to do. */ + GObjectClass* gobject_class; + GParamFlags flags; + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = katze_http_auth_finalize; + gobject_class->set_property = katze_http_auth_set_property; + gobject_class->get_property = katze_http_auth_get_property; + + flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT; + + /** + * KatzeHttpAuth:filename: + * + * An absolute path and name of a file for storing logins. + * + * Since: 0.1.10 + */ + g_object_class_install_property (gobject_class, + PROP_FILENAME, + g_param_spec_string ( + "filename", + "Filename", + "An absolute path and name of a file for storing logins", + NULL, + flags)); +} + +static void +katze_http_auth_login_free (KatzeHttpAuthLogin* login) +{ + g_free (login->username); + g_free (login->password); + g_free (login); +} + +static void +katze_http_auth_set_filename (KatzeHttpAuth* http_auth, + const gchar* filename) +{ + FILE* file; + + katze_assign (http_auth->filename, g_strdup (filename)); + + g_hash_table_remove_all (http_auth->logins); + + if ((file = g_fopen (filename, "r"))) + { + gchar line[255]; + guint number = 0; + + while (fgets (line, 255, file)) + { + gchar** parts = g_strsplit (line, "\t", 3); + if (parts && parts[0] && parts[1] && parts[2]) + { + KatzeHttpAuthLogin* login; + gint length; + + login = g_new (KatzeHttpAuthLogin, 1); + login->username = parts[1]; + length = strlen (parts[2]); + if (parts[2][length - 1] == '\n') + length--; + login->password = g_strndup (parts[2], length); + g_hash_table_insert (http_auth->logins, parts[0], login); + g_free (parts); + } + else + { + g_strfreev (parts); + g_warning ("Error in line %d in HTTP Auth file", number); + } + number++; + } + fclose (file); + } } static void katze_http_auth_init (KatzeHttpAuth* http_auth) { - /* Nothing to do. */ + http_auth->filename = NULL; + + http_auth->logins = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)g_free, (GDestroyNotify)katze_http_auth_login_free); +} + +static void +katze_http_auth_finalize (GObject* object) +{ + KatzeHttpAuth* http_auth = KATZE_HTTP_AUTH (object); + + g_free (http_auth->filename); + + g_hash_table_unref (http_auth->logins); + + G_OBJECT_CLASS (katze_http_auth_parent_class)->finalize (object); +} + +static void +katze_http_auth_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) +{ + KatzeHttpAuth* http_auth = KATZE_HTTP_AUTH (object); + + switch (prop_id) + { + case PROP_FILENAME: + katze_http_auth_set_filename (http_auth, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +katze_http_auth_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) +{ + KatzeHttpAuth* http_auth = KATZE_HTTP_AUTH (object); + + switch (prop_id) + { + case PROP_FILENAME: + g_value_set_string (value, http_auth->filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } diff --git a/midori/main.c b/midori/main.c index c5a5457f..d445ae13 100644 --- a/midori/main.c +++ b/midori/main.c @@ -1110,21 +1110,9 @@ midori_soup_session_prepare (SoupSession* session, SoupCookieJar* cookie_jar, MidoriWebSettings* settings) { - GModule* module; SoupSessionFeature* feature; gchar* config_file; - if (g_module_supported ()) - if ((module = g_module_open ("libsoup-gnome-2.4.so", G_MODULE_BIND_LOCAL))) - { - #ifdef HAVE_LIBSOUP_2_27_91 - GType (*get_type_function) (void); - if (g_module_symbol (module, "soup_password_manager_gnome_get_type", - (void*) &get_type_function)) - soup_session_add_feature_by_type (session, get_type_function ()); - #endif - } - soup_session_settings_notify_http_proxy_cb (settings, NULL, session); g_signal_connect (settings, "notify::http-proxy", G_CALLBACK (soup_session_settings_notify_http_proxy_cb), session); @@ -1137,7 +1125,11 @@ midori_soup_session_prepare (SoupSession* session, G_CALLBACK (soup_session_settings_notify_ident_string_cb), session); #endif - soup_session_add_feature_by_type (session, KATZE_TYPE_HTTP_AUTH); + config_file = build_config_filename ("logins"); + feature = g_object_new (KATZE_TYPE_HTTP_AUTH, "filename", config_file, NULL); + g_free (config_file); + soup_session_add_feature (session, feature); + g_object_unref (feature); midori_soup_session_debug (session); feature = g_object_new (KATZE_TYPE_HTTP_COOKIES, NULL); @@ -1145,7 +1137,7 @@ midori_soup_session_prepare (SoupSession* session, g_object_set_data_full (G_OBJECT (feature), "filename", config_file, (GDestroyNotify)g_free); soup_session_add_feature (session, SOUP_SESSION_FEATURE (cookie_jar)); - soup_session_add_feature (session, feature); + g_object_unref (feature); } static void