Save logins in a text file instead of a keyring

This means GNOME keyring isn't required for saving logins.
This commit is contained in:
Christian Dywan 2009-09-07 23:51:43 +02:00
parent 3dc3648920
commit 6ee9436db4
2 changed files with 270 additions and 71 deletions

View file

@ -18,10 +18,13 @@
#include <libsoup/soup.h> #include <libsoup/soup.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <glib/gstdio.h>
struct _KatzeHttpAuth struct _KatzeHttpAuth
{ {
GObject parent_instance; GObject parent_instance;
gchar* filename;
GHashTable* logins;
}; };
struct _KatzeHttpAuthClass struct _KatzeHttpAuthClass
@ -29,6 +32,20 @@ struct _KatzeHttpAuthClass
GObjectClass parent_class; GObjectClass parent_class;
}; };
typedef struct
{
KatzeHttpAuth* http_auth;
SoupAuth* auth;
gchar* username;
gchar* password;
} KatzeHttpAuthSave;
typedef struct
{
gchar* username;
gchar* password;
} KatzeHttpAuthLogin;
static void static void
katze_http_auth_session_feature_iface_init (SoupSessionFeatureInterface *iface, katze_http_auth_session_feature_iface_init (SoupSessionFeatureInterface *iface,
gpointer data); 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, G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
katze_http_auth_session_feature_iface_init)); katze_http_auth_session_feature_iface_init));
#ifdef HAVE_LIBSOUP_2_27_91 enum
{
PROP_0,
PROP_FILENAME
};
static void static void
authentication_message_got_headers_cb (SoupMessage* msg, katze_http_auth_set_property (GObject* object,
SoupAuth* auth) 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 */ /* Anything but 401 and 5xx means the password was accepted */
if (msg->status_code != 401 && msg->status_code < 500) if (msg->status_code != 401 && msg->status_code < 500)
{ {
gchar* username = g_object_get_data (G_OBJECT (msg), "username"); gchar* opaque_info;
gchar* password = g_object_get_data (G_OBJECT (msg), "password"); FILE* file;
soup_auth_save_password (auth, username, password);
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 static void
authentication_dialog_response_cb (GtkWidget* dialog, authentication_dialog_response_cb (GtkWidget* dialog,
gint response, gint response,
SoupAuth* auth) KatzeHttpAuthSave* save)
{ {
GtkWidget* username;
GtkWidget* password;
SoupSession* session; SoupSession* session;
SoupMessage* msg; SoupMessage* msg;
@ -66,36 +142,42 @@ authentication_dialog_response_cb (GtkWidget* dialog,
if (response == GTK_RESPONSE_OK) 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"); soup_auth_authenticate (save->auth,
password = g_object_get_data (G_OBJECT (dialog), "password"); gtk_entry_get_text (username), gtk_entry_get_text (password));
soup_auth_authenticate (auth, if (save->http_auth->filename)
gtk_entry_get_text (GTK_ENTRY (username)), {
gtk_entry_get_text (GTK_ENTRY (password))); save->username = g_strdup (gtk_entry_get_text (username));
#ifdef HAVE_LIBSOUP_2_27_91 save->password = g_strdup (gtk_entry_get_text (password));
g_object_set_data_full (G_OBJECT (msg), "username", g_signal_connect (msg, "got-headers",
g_strdup (gtk_entry_get_text (GTK_ENTRY (username))), g_free); G_CALLBACK (authentication_message_got_headers_cb), save);
g_object_set_data_full (G_OBJECT (msg), "password", }
g_strdup (gtk_entry_get_text (GTK_ENTRY (password))), g_free); else
g_signal_connect (msg, "got-headers", {
G_CALLBACK (authentication_message_got_headers_cb), auth); g_object_unref (save->auth);
#endif g_free (save);
}
} }
session = g_object_get_data (G_OBJECT (dialog), "session"); session = g_object_get_data (G_OBJECT (dialog), "session");
gtk_widget_destroy (dialog);
if (g_object_get_data (G_OBJECT (msg), "paused")) if (g_object_get_data (G_OBJECT (msg), "paused"))
soup_session_unpause_message (session, msg); soup_session_unpause_message (session, msg);
g_object_unref (auth); gtk_widget_destroy (dialog);
g_object_unref (msg);
} }
static void static void
katze_http_auth_session_authenticate_cb (SoupSession* session, katze_http_auth_session_authenticate_cb (SoupSession* session,
SoupMessage* msg, SoupMessage* msg,
SoupAuth* auth, SoupAuth* auth,
gboolean retrying) gboolean retrying,
KatzeHttpAuth* http_auth)
{ {
gchar* opaque_info;
KatzeHttpAuthLogin* login;
GtkWidget* dialog; GtkWidget* dialog;
GtkSizeGroup* sizegroup; GtkSizeGroup* sizegroup;
GtkWidget* hbox; GtkWidget* hbox;
@ -103,24 +185,26 @@ katze_http_auth_session_authenticate_cb (SoupSession* session,
GtkWidget* label; GtkWidget* label;
GtkWidget* align; GtkWidget* align;
GtkWidget* entry; GtkWidget* entry;
#ifdef HAVE_LIBSOUP_2_27_91 KatzeHttpAuthSave* save;
GSList* users;
#endif
/* We want to ask for authentication exactly once, so we /* We want to ask for authentication exactly once, so we
enforce this with a tag. There might be a better way. */ enforce this with a tag. There might be a better way. */
if (!retrying && g_object_get_data (G_OBJECT (msg), "katze-session-tag")) if (!retrying && g_object_get_data (G_OBJECT (msg), "katze-session-tag"))
return; return;
if (soup_message_is_keepalive (msg)) if (1)
{ {
/* We use another tag to indicate whether a message is paused. /* We use another tag to indicate whether a message is paused.
There doesn't seem to be API in libSoup to find that out. */ 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), "paused", (void*)1);
} }
g_object_set_data (G_OBJECT (msg), "katze-session-tag", (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"), dialog = gtk_dialog_new_with_buttons (_("Authentication Required"),
NULL, NULL,
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, 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_size_group_add_widget (sizegroup, align);
gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0);
entry = gtk_entry_new (); entry = gtk_entry_new ();
#ifdef HAVE_LIBSOUP_2_27_91 if (login)
users = soup_auth_get_saved_users (auth); gtk_entry_set_text (GTK_ENTRY (entry), login->username);
if (users)
gtk_entry_set_text (GTK_ENTRY (entry), users->data);
#endif
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
g_object_set_data (G_OBJECT (dialog), "username", entry); 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_size_group_add_widget (sizegroup, align);
gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0);
entry = gtk_entry_new_with_max_length (32); entry = gtk_entry_new_with_max_length (32);
#ifdef HAVE_LIBSOUP_2_27_91 if (login)
if (users) gtk_entry_set_text (GTK_ENTRY (entry), login->password);
{
gtk_entry_set_text (GTK_ENTRY (entry),
soup_auth_get_saved_password (auth, users->data));
g_slist_free (users);
}
#endif
gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE); gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); 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), "session", session);
g_object_set_data (G_OBJECT (dialog), "msg", msg); 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_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); gtk_widget_show (dialog);
} }
static void static void
katze_http_auth_session_request_queued_cb (SoupSession* session, katze_http_auth_session_request_queued_cb (SoupSession* session,
SoupMessage* msg, SoupMessage* msg,
gpointer data) KatzeHttpAuth* http_auth)
{ {
/* WebKit has its own authentication dialog in recent versions. /* WebKit has its own authentication dialog in recent versions.
We want only one, and we choose our own to have localization. */ 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); soup_session_remove_feature_by_type (session, type);
g_signal_connect (session, "authenticate", 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, g_signal_handlers_disconnect_by_func (session,
katze_http_auth_session_request_queued_cb, NULL); katze_http_auth_session_request_queued_cb, NULL);
} }
@ -218,7 +297,7 @@ katze_http_auth_attach (SoupSessionFeature* feature,
SoupSession* session) SoupSession* session)
{ {
g_signal_connect (session, "request-queued", 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 static void
@ -242,11 +321,139 @@ katze_http_auth_session_feature_iface_init (SoupSessionFeatureInterface *iface,
static void static void
katze_http_auth_class_init (KatzeHttpAuthClass* class) 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 static void
katze_http_auth_init (KatzeHttpAuth* http_auth) 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;
}
} }

View file

@ -1110,21 +1110,9 @@ midori_soup_session_prepare (SoupSession* session,
SoupCookieJar* cookie_jar, SoupCookieJar* cookie_jar,
MidoriWebSettings* settings) MidoriWebSettings* settings)
{ {
GModule* module;
SoupSessionFeature* feature; SoupSessionFeature* feature;
gchar* config_file; 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); soup_session_settings_notify_http_proxy_cb (settings, NULL, session);
g_signal_connect (settings, "notify::http-proxy", g_signal_connect (settings, "notify::http-proxy",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), session); 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); G_CALLBACK (soup_session_settings_notify_ident_string_cb), session);
#endif #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); midori_soup_session_debug (session);
feature = g_object_new (KATZE_TYPE_HTTP_COOKIES, NULL); 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", g_object_set_data_full (G_OBJECT (feature), "filename",
config_file, (GDestroyNotify)g_free); config_file, (GDestroyNotify)g_free);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (cookie_jar)); soup_session_add_feature (session, SOUP_SESSION_FEATURE (cookie_jar));
soup_session_add_feature (session, feature); g_object_unref (feature);
} }
static void static void