pwmd patch

Ben Kibbey <[email protected]>
Newsgroups gmane.comp.web.links
Message-ID <[email protected]>
Here's a patch to use pwmd for form history. pwmd is a daemon that
listens on a local socket and waits for commands from clients to
manipulate data. The data is an encrypted XML file. The password to
open/save the file is gotten with gpg-agent. Read
src/formhist/README.pwmd for more info.

-- 
Benjamin J. Kibbey [email protected]/jabber/freenode
3019 F5FC AA33 5BC7 BE9F 09D2 393E DBD2 40D5 FA7E

_______________________________________________
elinks-dev mailing list
[email protected]
http://linuxfromscratch.org/mailman/listinfo/elinks-dev
elinks-git-18771fd+libpwmd-2.0.x-3.diff (text/x-diff, 29.4 KB)
commit df755b42e5d58413a43c8243e68fbdc9b6842824
Author: Ben Kibbey <[email protected]>
Date:   Sun Jan 28 08:28:14 2007 -0500

    Only #include what is needed in pwmd.[ch].
    
    elinks-git-18771fd+libpwmd-2.0.x-3.diff

commit 131a990d372ddf25a4a6103440cd5a6c05eab390
Author: Ben Kibbey <[email protected]>
Date:   Sun Jan 28 08:16:30 2007 -0500

    Include pwmd.h where needed to silence prototype warnings.
    
    A couple more variable renamings to avoid confusion with libpwmd
    names.
    
    Cleaner 'struct string' usage.
    
    Fixed a couple memory leaks from pwmd_command().

commit 74369b109e3ab3257e45a19012bd88002a0ce37f
Author: Ben Kibbey <[email protected]>
Date:   Sun Jan 28 05:13:05 2007 -0500

    Don't abort configure if libpwmd is not found or if formhist was
    disabled. Also use pkg-config to configure libpwmd.

commit 3589f40931ce0954894adf298708e8952b00795a
Author: Ben Kibbey <[email protected]>
Date:   Sat Jan 27 20:54:06 2007 -0500

    Use N_() to set internationalization strings.

commit 04601083f58d4b5abfe423406e0e6ddcf01fa534
Author: Ben Kibbey <[email protected]>
Date:   Sat Jan 27 20:41:24 2007 -0500

    Don't do save_pwmd_formhist() in "anonymous" mode.

commit daaa0835464cb41cc444c9e26de1099e0fcf1823
Author: Ben Kibbey <[email protected]>
Date:   Sat Jan 27 20:32:40 2007 -0500

    Renamed some functions to be less confusing with libpwmd function
    names.

commit 29a4b2606577f61fb4c3e8be061c2b586c6d5040
Author: Ben Kibbey <[email protected]>
Date:   Sat Jan 27 20:23:00 2007 -0500

    Better configuration option description strings.
diff --git a/Makefile.config.in b/Makefile.config.in
index 98afd5b..d7ff8ec 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -132,6 +132,7 @@ CONFIG_INTERLINK = @CONFIG_INTERLINK@
 CONFIG_IPV6 = @CONFIG_IPV6@
 CONFIG_JW = @CONFIG_JW@
 CONFIG_LEDS = @CONFIG_LEDS@
+CONFIG_PWMD = @CONFIG_PWMD@
 CONFIG_LZMA = @CONFIG_LZMA@
 CONFIG_MAILCAP = @CONFIG_MAILCAP@
 CONFIG_MANUAL = @CONFIG_MANUAL@
diff --git a/configure.in b/configure.in
index 4ee940a..920f858 100644
--- a/configure.in
+++ b/configure.in
@@ -483,6 +483,7 @@ EL_ARG_ENABLE(CONFIG_LZMA, lzma, [lzma],
 	      [  --enable-lzma           enable lzma encoding support])
 
 dnl ===================================================================
+
 dnl Check for GSSAPI, optional even if installed.
 dnl ===================================================================
 
@@ -1192,6 +1193,37 @@ EL_ARG_ENABLE(CONFIG_COOKIES, cookies, [Cookies],
 EL_ARG_ENABLE(CONFIG_FORMHIST, formhist, [Form history],
 	      [  --disable-formhist      disable form history support])
 
+
+CONFIG_PWMD=no
+AC_ARG_ENABLE(pwmd,
+	      [  --enable-pwmd           enable Password Manager Daemon support],
+	      [if test "$enableval" != no; then enableval="yes"; fi
+	       CONFIG_PWMD="$enableval";])
+
+if test "x$CONFIG_PWMD" = xyes; then
+	if test "x$CONFIG_FORMHIST" = xyes; then
+		PKG_CHECK_EXISTS([libpwmd], have_libpwmd=1,
+				 AC_MSG_WARN([Could not find libpwmd pkg-config module. Disabling pwmd support.]))
+
+		if test "$have_libpwmd" = 1; then
+			PKG_CHECK_MODULES([libpwmd], [libpwmd >= 2.0.0], have_libpwmd=1)
+
+			if test "$have_libpwmd" = 1; then
+				EL_CONFIG(CONFIG_PWMD, [pwmd])
+				LIBS="$LIBS $libpwmd_LIBS"
+				CFLAGS="$CFLAGS $libpwmd_CFLAGS"
+			else
+				AC_MSG_WARN($libpwmd_PKG_ERRORS)
+			fi
+		fi
+	else
+		AC_MSG_WARN([--enable-pwmd requires --enable-formhist. Disabling pwmd support.])
+		CONFIG_PWMD=no
+	fi
+fi
+
+EL_LOG_CONFIG(CONFIG_PWMD, [Password Manager Daemon], $CONFIG_PWMD)
+
 EL_ARG_ENABLE(CONFIG_GLOBHIST, globhist, [Global history],
 	      [  --disable-globhist      disable global history support])
 
diff --git a/features.conf b/features.conf
index 0a756f7..ab9fe20 100644
--- a/features.conf
+++ b/features.conf
@@ -115,6 +115,15 @@ CONFIG_COOKIES=yes
 CONFIG_FORMHIST=yes
 
 
+### libPWMD Support
+#
+# Storage and retrieval of form data from the Password Manager Daemon.
+#
+# Default: disabled
+
+CONFIG_PWMD=no
+
+
 ### Global History
 #
 # This device records each and every page you visit (to a configurable limit).
diff --git a/src/config/actions-main.inc b/src/config/actions-main.inc
index 9626c9d..cd9a55a 100644
--- a/src/config/actions-main.inc
+++ b/src/config/actions-main.inc
@@ -24,6 +24,7 @@ ACTION_(MAIN, "file-menu", FILE_MENU, N__("Open the File menu"), 0),
 ACTION_(MAIN, "find-next", FIND_NEXT, N__("Find the next occurrence of the current search text"), ACTION_REQUIRE_VIEW_STATE | ACTION_REQUIRE_LOCATION),
 ACTION_(MAIN, "find-next-back", FIND_NEXT_BACK, N__("Find the previous occurrence of the current search text"), ACTION_REQUIRE_VIEW_STATE | ACTION_REQUIRE_LOCATION),
 ACTION_(MAIN, "forget-credentials", FORGET_CREDENTIALS, N__("Forget authentication credentials"), 0),
+ACTION_(MAIN, "clear-pwmd-cache", CLEAR_PWMD_CACHE, N__("Clear PWMD file cache"), 0),
 ACTION_(MAIN, "formhist-manager", FORMHIST_MANAGER, N__("Open form history manager"), 0),
 ACTION_(MAIN, "frame-external-command", FRAME_EXTERNAL_COMMAND, N__("Pass URI of current frame to external command"), ACTION_RESTRICT_ANONYMOUS | ACTION_REQUIRE_VIEW_STATE | ACTION_REQUIRE_LOCATION),
 ACTION_(MAIN, "frame-maximize", FRAME_MAXIMIZE, N__("Maximize the current frame"), ACTION_REQUIRE_VIEW_STATE | ACTION_REQUIRE_LOCATION),
diff --git a/src/config/options.inc b/src/config/options.inc
index fe01526..a0d47f1 100644
--- a/src/config/options.inc
+++ b/src/config/options.inc
@@ -169,6 +169,24 @@ static struct option_info config_options_info[] = {
 		"forms", 0,
 		N_("Options for handling of the forms interaction.")),
 
+#ifdef CONFIG_PWMD
+	INIT_OPT_TREE("document.browse.forms", N_("Password Manager Daemon"),
+		"pwmd", 0,
+		N_("Socket and filename settings.")),
+	INIT_OPT_BOOL("document.browse.forms.pwmd", N_("Enable"),
+		"enable", 0, 1,
+		N_("To enable or disable use of the server. If disabled, the\n"
+		    "original file base storage will be used.\n")),
+	INIT_OPT_STRING("document.browse.forms.pwmd", N_("Socket location"),
+		"socket", 0, "",
+		N_("To specify the socket to connect to. Defaults to the libpwmd\n"
+		    "default of ~/.pwmd/socket if unset.\n")),
+	INIT_OPT_STRING("document.browse.forms.pwmd", N_("Data file"),
+		"filename", 0, "",
+		N_("The filename to open and save to. This option is required to\n"
+		    "use the pwmd server.\n")),
+#endif
+
 	INIT_OPT_BOOL("document.browse.forms", N_("Submit form automatically"),
 		"auto_submit", 0, 1,
 		N_("Automagically submit a form when enter is pressed with a text\n"
diff --git a/src/dialogs/menu.c b/src/dialogs/menu.c
index 101d97e..87105e7 100644
--- a/src/dialogs/menu.c
+++ b/src/dialogs/menu.c
@@ -496,6 +496,9 @@ static struct menu_item tools_menu[] = {
 #endif
 #ifdef CONFIG_FORMHIST
 	INIT_MENU_ACTION(N_("~Form history"), ACT_MAIN_FORMHIST_MANAGER),
+#ifdef CONFIG_PWMD
+	INIT_MENU_ACTION(N_("C~lear PWMD cache"), ACT_MAIN_CLEAR_PWMD_CACHE),
+#endif
 #endif
 	INIT_MENU_ACTION(N_("~Authentication"), ACT_MAIN_AUTH_MANAGER),
 	NULL_MENU_ITEM
diff --git a/src/formhist/Makefile b/src/formhist/Makefile
index 9aa0211..dbb6cfd 100644
--- a/src/formhist/Makefile
+++ b/src/formhist/Makefile
@@ -2,5 +2,6 @@ top_builddir=../..
 include $(top_builddir)/Makefile.config
 
 OBJS = formhist.o dialogs.o
+OBJS-$(CONFIG_PWMD) += pwmd.o
 
 include $(top_srcdir)/Makefile.lib
diff --git a/src/formhist/README.pwmd b/src/formhist/README.pwmd
new file mode 100644
index 0000000..c715225
--- /dev/null
+++ b/src/formhist/README.pwmd
@@ -0,0 +1,38 @@
+PWMD support enables three new configuration options which can be altered in
+the options GUI or by hand of course:
+
+    document.browse.form.pwmd.enable
+	To enable or disable use of the server. If disabled, the original file
+	based storage will be used.
+
+    document.browse.form.pwmd.socket
+	To specify the socket to connect to. Defaults to the library default
+	of ~/.pwmd/socket if unset.
+
+    document.browse.form.pwmd.filename
+	The filename to open and save to. This is required. You'll get an
+	error if it's unset.
+
+Also added is a Tool menu item to clear the daemons file cache entry. Only the
+file used by Elinks (above) will be cleared. The next open or save to this
+file will require the password.
+
+Make sure gpg-agent is running. If it isn't and the file isn't cached on the
+server, you'll get an error. If gpg-agent is running and you still get an
+error, make sure GPG_AGENT_INFO is set correctly. Read gpg-agent(1) for how to
+set this.
+
+The Form History Manager behavior changes a little when pwmd is enabled. After
+pushing the 'Delete' or 'Clear' buttons, pwmd will remove the URL's from the
+server after confirmation. When pwmd is not enabled, you have to push 'Save'
+to remove an entry from the file like before.
+
+
+TODO:
+Add support for the Authentication Manager.
+
+
+The latest version of the library, daemon/server and application patches can
+be found at http://bjk.sourceforge.net/pwmd/.
+
+Ben Kibbey <[email protected]>
diff --git a/src/formhist/dialogs.c b/src/formhist/dialogs.c
index 9eb5fec..bed5142 100644
--- a/src/formhist/dialogs.c
+++ b/src/formhist/dialogs.c
@@ -23,6 +23,10 @@
 #include "util/string.h"
 #include "viewer/text/form.h"
 
+#ifdef CONFIG_PWMD
+#include "formhist/pwmd.h"
+#endif
+
 
 static void
 lock_formhist_data(struct listbox_item *item)
@@ -112,6 +116,11 @@ delete_formhist_data(struct listbox_item *item, int last)
 
 	assert(!is_object_used(formhist_data));
 
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable"))
+	    delete_pwmd_item(formhist_data);
+#endif
+
 	delete_formhist_item(formhist_data);
 }
 
@@ -200,8 +209,18 @@ push_toggle_dontsave_button(struct dialog_data *dlg_data,
 static widget_handler_status_T
 push_save_button(struct dialog_data *dlg_data, struct widget_data *button)
 {
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable")) {
+		save_pwmd_formhist();
+		return EVENT_PROCESSED;
+	}
+
 	save_formhist_to_file();
 	return EVENT_PROCESSED;
+#else
+	save_formhist_to_file();
+	return EVENT_PROCESSED;
+#endif
 }
 
 static struct hierbox_browser_button formhist_buttons[] = {
@@ -224,6 +243,14 @@ struct_hierbox_browser(
 void
 formhist_manager(struct session *ses)
 {
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable")) {
+		load_pwmd_formhist(1);
+		hierbox_browser(&formhist_browser, ses);
+		return;
+	}
+#endif
+
 	load_formhist_from_file();
 	hierbox_browser(&formhist_browser, ses);
 }
diff --git a/src/formhist/formhist.c b/src/formhist/formhist.c
index c1227f0..7e565ee 100644
--- a/src/formhist/formhist.c
+++ b/src/formhist/formhist.c
@@ -25,6 +25,10 @@
 #include "util/string.h"
 #include "viewer/text/form.h"
 
+#ifdef CONFIG_PWMD
+#include "formhist/pwmd.h"
+#endif
+
 #define FORMS_HISTORY_FILENAME		"formhist"
 
 
@@ -44,7 +48,7 @@ static struct option_info forms_history_options[] = {
 
 INIT_LIST_HEAD(saved_forms);
 
-static struct formhist_data *
+struct formhist_data *
 new_formhist_item(unsigned char *url)
 {
 	struct formhist_data *form;
@@ -69,7 +73,7 @@ new_formhist_item(unsigned char *url)
 	return form;
 }
 
-static void
+void
 done_formhist_item(struct formhist_data *form)
 {
 	done_listbox_item(&formhist_browser, form->box_item);
@@ -219,6 +223,12 @@ save_formhist_to_file(void)
 	struct formhist_data *form;
 	int r;
 
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable") &&
+		!get_cmd_opt_bool("anonymous"))
+		return save_pwmd_formhist();
+#endif
+
 	if (!elinks_home || get_cmd_opt_bool("anonymous"))
 		return 0;
 
@@ -278,7 +288,16 @@ form_exists(struct formhist_data *form1)
 {
 	struct formhist_data *form;
 
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable")) {
+		if (!load_pwmd_formhist(0))
+			return 0;
+	}
+	else
+		if (!load_formhist_from_file()) return 0;
+#else
 	if (!load_formhist_from_file()) return 0;
+#endif
 
 	foreach (form, saved_forms) {
 		int count = 0;
@@ -314,7 +333,7 @@ form_exists(struct formhist_data *form1)
 	return 0;
 }
 
-static int
+int
 forget_forms_with_url(unsigned char *url)
 {
 	struct formhist_data *form, *next;
@@ -355,7 +374,21 @@ get_form_history_value(unsigned char *url, unsigned char *name)
 
 	if (!url || !*url || !name || !*name) return NULL;
 
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable")) {
+		/*
+		 * FIXME If theres a pwmd error a message box pops up.
+		 * src/viewer/text/form.c seems to call this repeatedly until
+		 * it's satisfied with a value.
+		 */
+		if (!load_pwmd_formhist(0))
+		    return NULL;
+	}
+	else 
+	    if (!load_formhist_from_file()) return NULL;
+#else
 	if (!load_formhist_from_file()) return NULL;
+#endif
 
 	foreach (form, saved_forms) {
 		if (form->dontsave) continue;
@@ -409,6 +442,27 @@ memorize_form(struct session *ses, struct list_head *submit,
 
 	if (form_exists(form)) goto fail;
 
+#ifdef CONFIG_PWMD
+	if (get_opt_bool("document.browse.forms.pwmd.enable"))
+		msg_box(ses->tab->term, NULL, 0,
+				N_("Form history"), ALIGN_CENTER,
+				N_("Should this login be remembered?\n\n"),
+				form, 3,
+				N_("~Yes"), remember_form, B_ENTER,
+				N_("~No"), done_formhist_item, B_ESC,
+				N_("Ne~ver for this site"), never_for_this_site, NULL);
+	else
+		msg_box(ses->tab->term, NULL, 0,
+				N_("Form history"), ALIGN_CENTER,
+				N_("Should this login be remembered?\n\n"
+					"Please note that the password will be stored "
+					"obscured (but unencrypted) in a file on your disk.\n\n"
+					"If you are using a valuable password, answer NO."),
+				form, 3,
+				N_("~Yes"), remember_form, B_ENTER,
+				N_("~No"), done_formhist_item, B_ESC,
+				N_("Ne~ver for this site"), never_for_this_site, NULL);
+#else
 	msg_box(ses->tab->term, NULL, 0,
 		N_("Form history"), ALIGN_CENTER,
 		N_("Should this login be remembered?\n\n"
@@ -420,6 +474,7 @@ memorize_form(struct session *ses, struct list_head *submit,
 		N_("~No"), done_formhist_item, B_ESC,
 		N_("Ne~ver for this site"), never_for_this_site, NULL);
 
+#endif
 	return;
 
 fail:
@@ -437,7 +492,11 @@ done_form_history(struct module *module)
 }
 
 struct module forms_history_module = struct_module(
+#ifdef CONFIG_PWMD
+	/* name: */		N_("Form History (PWMD support)"),
+#else
 	/* name: */		N_("Form History"),
+#endif
 	/* options: */		forms_history_options,
 	/* events: */		NULL,
 	/* submodules: */	NULL,
diff --git a/src/formhist/formhist.h b/src/formhist/formhist.h
index 3be6622..0f7b29a 100644
--- a/src/formhist/formhist.h
+++ b/src/formhist/formhist.h
@@ -28,9 +28,12 @@ unsigned char *get_form_history_value(unsigned char *url, unsigned char *name);
 
 void memorize_form(struct session *ses, struct list_head *submit, struct form *forminfo);
 
+extern struct list_head saved_forms;
 int save_formhist_to_file(void);
-void delete_formhist_item(struct formhist_data *form);
 int load_formhist_from_file(void);
+int forget_forms_with_url(unsigned char *url);
+struct formhist_data * new_formhist_item(unsigned char *url);
+void done_formhist_item(struct formhist_data *form);
 
 extern struct module forms_history_module;
 
diff --git a/src/formhist/pwmd.c b/src/formhist/pwmd.c
new file mode 100644
index 0000000..08395dd
--- /dev/null
+++ b/src/formhist/pwmd.c
@@ -0,0 +1,582 @@
+/*
+ * When --enable-pwmd is passed to configure and pwmd is enabled in the Elinks
+ * configuration, Elinks will store and retrieve form data with pwmd (Password
+ * Manager Daemon). The daemon listens on a local socket and the client must
+ * provide a password to open the file that contains the wanted data. libpwmd
+ * makes it easy to communicate with the daemon and uses gpg-agent for
+ * password retrieval (pinentry).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libpwmd.h>
+
+#include "elinks.h"
+#include "formhist/pwmd.h"
+#include "intl/gettext/libintl.h"
+#include "util/string.h"
+#include "viewer/text/form.h"
+
+static int did_error;
+static int last_errno;
+
+static void
+show_error(int ret, int error)
+{
+    unsigned char *buf;
+    struct terminal *term = terminals.prev;
+
+    if (ret == PWMD_PERROR && error == EPWMD_EMPTY_ELEMENT)
+	    return;
+
+    if (ret == PWMD_PERROR)
+	    buf = msg_text(term, N_("Error %03i: %s\n"), error, pwmd_strerror(error));
+    else if (error == -1)
+	    buf = msg_text(term, N_("No data filename has been specified in Document->Forms->PWMD."));
+    else if (ret == PWMD_ERROR)
+	    buf = msg_text(term, N_("%s\n"), strerror(error));
+    else if (ret == PWMD_AGENT_ERROR)
+	    buf = msg_text(term, N_("%s"), "gpg-agent failed. Is gpg-agent running? Is "
+			    "GPG_AGENT_INFO set?");
+    else
+	    return;
+
+    info_box(term, MSGBOX_FREE_TEXT, "Password Manager Daemon", ALIGN_LEFT, buf);
+}
+
+static int
+set_agent_strings(pwm_t *pwm, const char *filename, int *ret, int *error, 
+		int save)
+{
+	char *result;
+	struct string str;
+
+	if (init_string(&str) == NULL)
+		goto fail;
+
+	if ((*ret = pwmd_command(pwm, &result, error, PWMD_SETOPT, 
+					PWMD_OPTION_USEAGENT, 1)) != PWMD_OK)
+		goto fail;
+
+	add_format_to_string(&str, "Password Manager Daemon: Elinks");
+
+	if ((*ret = pwmd_command(pwm, &result, error, PWMD_SETOPT, 
+					PWMD_OPTION_TITLE, str.source)) != PWMD_OK)
+		goto fail;
+
+	str.length = 0;
+	add_format_to_string(&str, 
+			"A password is required to %s the file '%s'. Please \n"
+			"enter the password below.", (save) ? "save to" : "open",
+			filename);
+
+	if ((*ret = pwmd_command(pwm, &result, error, PWMD_SETOPT, 
+					PWMD_OPTION_DESC, str.source))
+			!= PWMD_OK)
+		goto fail;
+
+	done_string(&str);
+	return 1;
+
+fail:
+	done_string(&str);
+	return 0;
+}
+
+static pwm_t *
+do_connect(char **file, int *ret, int *error)
+{
+	char *filename = get_opt_str("document.browse.forms.pwmd.filename");
+	char *socket;
+	pwm_t *pwm;
+
+	*ret = PWMD_ERROR;
+
+	if (!filename || !*filename) {
+		*error = -1;
+		return NULL;
+	}
+
+	*file = filename;
+	socket = get_opt_str("document.browse.forms.pwmd.socket");
+
+	if (!socket || !*socket)
+		socket = NULL;
+
+	if ((pwm = pwmd_connect(socket, error)) == NULL)
+		return NULL;
+
+	if (!set_agent_strings(pwm, filename, ret, error, 0)) {
+		pwmd_close(pwm);
+		return NULL;
+	}
+
+	return pwm;
+}
+
+void
+clear_pwmd_cache()
+{
+    struct string str;
+    pwm_t *pwm;
+    int ret, error;
+    char *filename;
+    char *result;
+
+    if ((pwm = do_connect(&filename, &ret, &error)) == NULL) {
+	    show_error(ret, error);
+	    return;
+    }
+
+    if (!init_string(&str)) {
+	pwmd_close(pwm);
+	return;
+    }
+
+    add_format_to_string(&str, "CACHE CLEAR %s\n", filename);
+
+    if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND, str.source))
+		    != PWMD_OK) {
+	    if (ret != PWMD_PERROR || (error != EPWMD_CACHE_NOT_FOUND &&
+				    error != EPWMD_FILE_NOT_FOUND))
+		    show_error(ret, error);
+    }
+
+    pwmd_close(pwm);
+    done_string(&str);
+}
+
+/*
+ * This function is only called from dialog.c. The way the regular formhist
+ * manager does it is that the form isn't removed from the file until the
+ * 'Save' button is pushed. With pwmd, the 'Delete' (or 'Clear') button will
+ * remove the URL from the server after confirmation without pushing 'Save'.
+ */
+void
+delete_pwmd_item(struct formhist_data *form)
+{
+    pwm_t *pwm;
+    int ret, error;
+    char *filename;
+    struct string str;
+    char *result;
+
+    if ((pwm = do_connect(&filename, &ret, &error)) == NULL) {
+	    show_error(ret, error);
+	    return;
+    }
+
+    if (init_string(&str) == NULL)
+	goto done;
+
+    if ((ret = pwmd_command(pwm, &result, &error, PWMD_OPEN, filename)) 
+	    != PWMD_OK) {
+	    show_error(ret, error);
+	    goto done;
+    }
+
+    add_format_to_string(&str, "DELETE %s\n", form->url);
+
+    if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND, str.source)) 
+	    != PWMD_OK) {
+	    show_error(ret, error);
+	    goto done;
+    }
+
+    if (!set_agent_strings(pwm, filename, &ret, &error, 1)) {
+	    show_error(ret, error);
+	    goto done;
+    }
+
+    if ((ret = pwmd_command(pwm, &result, &error, PWMD_SAVE)) != PWMD_OK) {
+	    show_error(ret, error);
+	    goto done;
+    }
+
+done:
+    pwmd_close(pwm);
+    done_string(&str);
+}
+
+/*
+ * FIXME
+ * Same problem with gpg-agent and invalid passwords as pwmd_load_formhist().
+ */
+/*
+ * Stores the form data on the server. The "account" name is form->url with an
+ * attribute created_by="elinks". Each submitted value (sv->name) is stored as
+ * child of form->url with an attribute of "type". The sv->value is the
+ * element (sv->name) value/PCDATA.
+ */
+int
+save_pwmd_formhist(void)
+{
+	struct formhist_data *form;
+	pwm_t *pwm;
+	int error;
+	int ret = PWMD_ERROR;
+	char *result;
+	char *filename = NULL;
+	int cached = 0;
+	struct string str;
+	struct terminal *term = terminals.prev;
+
+	if ((pwm = do_connect(&filename, &ret, &error)) == NULL) {
+		show_error(ret, error);
+		return 0;
+	}
+
+	if (init_string(&str) == NULL) {
+		pwmd_close(pwm);
+		return 0;
+	}
+
+	/*
+	 * Avoid a redraw if the file is cached (gpg-agent).
+	 */
+	add_format_to_string(&str, "CACHE ISCACHED %s\n", filename);
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+					str.source)) == PWMD_OK)
+		cached = 1;
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_OPEN, filename)) 
+			!= PWMD_OK)
+		goto fail;
+
+	if (!cached) {
+		if (term->redrawing == TREDRAW_READY)
+			redraw_terminal_cls(term);
+	}
+
+	foreach (form, saved_forms) {
+		struct submitted_value *sv;
+
+		str.length = 0;
+		add_format_to_string(&str, "ATTR SET dontsave %s %i\n",
+				form->url, form->dontsave);
+		ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND, str.source);
+
+		if (form->dontsave)
+			continue;
+
+		str.length = 0;
+		add_format_to_string(&str, "STORE %s\n", form->url);
+
+		if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+						str.source)) != PWMD_OK)
+			continue;
+
+		str.length = 0;
+		add_format_to_string(&str,
+				"ATTR SET created_by %s elinks\n", form->url);
+
+		if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+						str.source)) != PWMD_OK)
+			continue;
+
+		foreach (sv, *form->submit) {
+			str.length = 0;
+			add_format_to_string(&str,
+					"STORE %s\t%s\t%s\n", form->url,
+					sv->name, sv->value);
+
+			if ((ret = pwmd_command(pwm, &result, &error, 
+							PWMD_COMMAND,
+							str.source)) != PWMD_OK)
+				continue;
+
+			str.length = 0;
+			add_format_to_string(&str, 
+					"ATTR SET type %s\t%s %s\n", form->url,
+					sv->name, form_type2str(sv->type));
+
+			if ((ret = pwmd_command(pwm, &result, &error, 
+							PWMD_COMMAND,
+							str.source)) != PWMD_OK)
+				continue;
+		}
+	}
+
+	if (!set_agent_strings(pwm, filename, &ret, &error, 1))
+		goto fail;
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_SAVE)) != PWMD_OK)
+		goto fail;
+
+	done_string(&str);
+	pwmd_close(pwm);
+	redraw_terminal_cls(term);
+	return 1;
+
+fail:
+	done_string(&str);
+	pwmd_close(pwm);
+	show_error(ret, error);
+	redraw_terminal_cls(term);
+	return 0;
+}
+
+static int
+elinks_account(pwm_t *pwm, const char *account)
+{
+	char *result;
+	int error;
+	int ret;
+	struct string str;
+
+	if (init_string(&str) == NULL)
+		return 0;
+
+	str.length = 0;
+	add_format_to_string(&str, "ATTR GET created_by %s\n",
+			account);
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+					str.source)) != PWMD_OK) {
+		if (ret == PWMD_ERROR ||
+				(ret == PWMD_PERROR && error != EPWMD_ATTR_NOT_FOUND))
+			show_error(ret, error);
+
+		done_string(&str);
+		return 0;
+	}
+
+	done_string(&str);
+
+	if (strcmp(result, "elinks") == 0) {
+		free(result);
+		return 1;
+	}
+
+	free(result);
+	return 0;
+}
+
+static int 
+get_form_values(pwm_t *pwm, char *account)
+{
+	char *result;
+	int error;
+	char *l, *p;
+	struct formhist_data *form;
+	int ret;
+	int dontsave = 0;
+	struct string str;
+	int match = 0;
+
+	if (init_string(&str) == NULL)
+		return 0;
+
+	str.length = 0;
+	add_format_to_string(&str, "ATTR GET dontsave %s\n", account);
+	ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND, str.source);
+
+	if (ret == PWMD_OK) {
+		dontsave = atoi((char *)result);
+		free(result);
+	}
+
+	str.length = 0;
+	add_format_to_string(&str, "LIST %s\n", account);
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+					str.source)) != PWMD_OK) {
+		show_error(ret, error);
+		done_string(&str);
+		return 0;
+	}
+
+	if ((form = new_formhist_item(account)) == NULL)  {
+		free(result);
+		goto fail;
+	}
+
+	form->dontsave = dontsave;
+	l = result;
+
+	while ((p = strsep(&l, "\n")) != NULL) {
+		char *t;
+		struct submitted_value *sv;
+		enum form_type ftype;
+		unsigned char *name = NULL, *value = NULL;
+
+		/*
+		 * Look for an attribute "type" and see if it's a recognized
+		 * type.
+		 */
+		str.length = 0;
+		add_format_to_string(&str, "ATTR GET type %s\n", p);
+
+		if (pwmd_command(pwm, &t, &error, PWMD_COMMAND, str.source) != PWMD_OK)
+			continue;
+
+		if ((ftype = str2form_type(t)) == -1) {
+			free(t);
+			continue;
+		}
+
+		/*
+		 * Get the value for the current element path.
+		 */
+		str.length = 0;
+		add_format_to_string(&str, "GET %s\n", p);
+
+		if (pwmd_command(pwm, &t, &error, PWMD_COMMAND, str.source) != PWMD_OK)
+			continue;
+
+		value = t;
+
+		/*
+		 * The element/form "name" is the last element in the element
+		 * path.
+		 */
+		p = strrchr(p, '\t');
+		p++;
+		name = strdup((char *)p);
+
+		/*
+		 * Append to the form.
+		 */
+		if ((sv = init_submitted_value(name, value, ftype, NULL, 0)) == NULL) {
+			free(name);
+			free(value);
+			free(result);
+			goto fail;
+		}
+
+		add_to_list(*form->submit, sv);
+		free(name);
+		free(value);
+		match = 1;
+	}
+
+	free(result);
+
+	if (!match)
+	    goto fail;
+
+	done_string(&str);
+	forget_forms_with_url(form->url);
+	add_to_list(saved_forms, form);
+	return 1;
+
+fail:
+	done_string(&str);
+	done_formhist_item(form);
+	return 0;
+}
+
+/*
+ * FIXME
+ * If a bad password is specified then elinks keeps calling this function and
+ * gpg-agent. It should give up after a count.
+ */
+int
+load_pwmd_formhist(int reload_form)
+{
+	pwm_t *pwm = NULL;
+	int error = -1;
+	char *result;
+	char *l, *p;
+	int ret = PWMD_ERROR;
+	char *filename = NULL;
+	int cached = 0;
+	struct string str;
+	struct terminal *term = terminals.prev;
+	static int loaded;
+
+	if (loaded && !reload_form) return 1;
+
+	loaded = 0;
+
+	if (init_string(&str) == NULL)
+		goto fail;
+
+	if ((pwm = do_connect(&filename, &ret, &error)) == NULL)
+		goto fail;
+
+	/*
+	 * Avoid a redraw if the file is cached (gpg-agent).
+	 */
+	str.length = 0;
+	add_format_to_string(&str, "CACHE ISCACHED %s\n", filename);
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+					str.source)) == PWMD_OK)
+		cached = 1;
+	else {
+		if (ret == PWMD_PERROR && error == EPWMD_FILE_NOT_FOUND) {
+			done_string(&str);
+			pwmd_close(pwm);
+			return 1;
+		}
+	}
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_OPEN, filename)) 
+			!= PWMD_OK)
+		goto fail;
+
+	if (!cached && !loaded) {
+		if (term->redrawing == TREDRAW_READY)
+			redraw_terminal_cls(term);
+	}
+
+	/*
+	 * Get a list of "account"s or root elements.
+	 */
+	str.length = 0;
+	add_format_to_string(&str, "LIST\n");
+
+	if ((ret = pwmd_command(pwm, &result, &error, PWMD_COMMAND,
+					str.source)) != PWMD_OK)
+		goto fail;
+
+	l = result;
+
+	while ((p = strsep(&l, "\n")) != NULL) {
+		/*
+		 * Find a "created_by" attribute with an "elinks" value.
+		 */
+		if (!elinks_account(pwm, p))
+			continue;
+
+		/*
+		 * For every element in the current tree append to the form.
+		 */
+		if (get_form_values(pwm, p))
+			continue;
+	}
+
+	free(result);
+	done_string(&str);
+	did_error = 0;
+	pwmd_close(pwm);
+	loaded = 1;
+	return 1;
+
+fail:
+	done_string(&str);
+
+	if (pwm)
+		pwmd_close(pwm);
+
+	if (!did_error) {
+		show_error(ret, error);
+		did_error = 1;
+		last_errno = error;
+		pwm = NULL;
+	}
+
+	/*
+	 * Error "stacking".
+	 */
+	if (pwm) {
+		if (last_errno != error)
+			show_error(ret, error);
+	}
+
+	return 0;
+}
diff --git a/src/formhist/pwmd.h b/src/formhist/pwmd.h
new file mode 100644
index 0000000..8a8e469
--- /dev/null
+++ b/src/formhist/pwmd.h
@@ -0,0 +1,11 @@
+#ifndef EL__FORMHIST_PWMD_H
+#define EL__FORMHIST_PWMD_H
+
+#include "formhist/formhist.h"
+
+int load_pwmd_formhist(int reload_form);
+int save_pwmd_formhist(void);
+void delete_pwmd_item(struct formhist_data *form);
+void clear_pwmd_cache(void);
+
+#endif /* EL__FORMHIST_PWMD_H */
diff --git a/src/viewer/action.c b/src/viewer/action.c
index 3089a19..6ca092b 100644
--- a/src/viewer/action.c
+++ b/src/viewer/action.c
@@ -44,6 +44,10 @@
 #include "viewer/text/search.h"
 #include "viewer/text/view.h"
 
+#ifdef CONFIG_PWMD
+#include "formhist/pwmd.h"
+#endif
+
 
 static void
 goto_url_action(struct session *ses,
@@ -205,6 +209,11 @@ do_action(struct session *ses, enum main_action action_id, int verbose)
 			status = find_next(ses, doc_view, -1);
 			break;
 
+#ifdef CONFIG_PWMD
+		case ACT_MAIN_CLEAR_PWMD_CACHE:
+			clear_pwmd_cache();
+			break;
+#endif
 		case ACT_MAIN_FORGET_CREDENTIALS:
 			free_auth();
 			shrink_memory(1); /* flush caches */
@@ -215,7 +224,6 @@ do_action(struct session *ses, enum main_action action_id, int verbose)
 			formhist_manager(ses);
 #endif
 			break;
-
 		case ACT_MAIN_FRAME_EXTERNAL_COMMAND:
 			status = pass_uri_to_command(ses, doc_view,
 			                             PASS_URI_FRAME);
diff --git a/src/viewer/text/form.c b/src/viewer/text/form.c
index f37673c..306c4e1 100644
--- a/src/viewer/text/form.c
+++ b/src/viewer/text/form.c
@@ -1172,8 +1172,8 @@ get_form_uri(struct session *ses, struct document_view *doc_view,
 	 * a file that is to be uploaded. TODO: Distinguish between
 	 * these two classes of errors (is it worth it?). -- Miciah */
 	if (data.source
-	    && get_opt_bool("document.browse.forms.show_formhist"))
-		memorize_form(ses, &submit, form);
+		&& get_opt_bool("document.browse.forms.show_formhist"))
+	    memorize_form(ses, &submit, form);
 #endif
 
 	done_submitted_value_list(&submit);
signature.asc (application/pgp-signature, 189 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFFvOagaQ6p7xBEKVIRAl0JAKCIUfmQU8X5G6jPSoHrxLamgX6EFQCgqV0g
fREDBFyQvn4lFpW2m0xGCdI=
=RIUa
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.