This adds the --use-askpass option which is disabled by default.

--use-askpass will request the username and password for a given URL by
executing the external program pointed to by the environment variable
WGET_ASKPASS.  If the environment variable is not set, SSH_ASKPASS is
used.  If neither environment variable are set, an error is returned.
If an error occurs requesting the username or password, wget will exit.

Changes from V1:
 - Removed debug strace command.
 - Removed fork in favour of posix_spawnp.
 - Changed option from --ssh-askpass to --use-askpass.
 - Check WGET_ASKPASS then fall back to SSH_ASKPASS.
 - Updated documentation

Liam R. Howlett (1):
  wget: Add --use-askpass support

 ChangeLog     |  15 ++++++++
 doc/wget.texi |   5 +++
 src/init.c    |   5 +++
 src/main.c    | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/options.h |   2 ++
 src/url.c     |   6 ++++
 src/url.h     |   1 +
 7 files changed, 145 insertions(+)

-- 
1.9.1
From 28afb072b162a675a6b7f52eea51b369b7b6f902 Mon Sep 17 00:00:00 2001
From: "Liam R. Howlett" <[email protected]>
Date: Fri, 22 Jul 2016 14:10:41 -0400
Subject: [PATCH v2] wget: Add --use-askpass support
To: [email protected]
Cc: [email protected]

This adds the --use-askpass option which is disabled by default.

--use-askpass will request the username and password for a given URL by
executing the external program pointed to by the environment variable
WGET_ASKPASS.  If the environment variable is not set, SSH_ASKPASS is
used.  If neither environment variable are set, an error is returned.
If an error occurs requesting the username or password, wget will exit.

Signed-off-by: Liam R. Howlett <[email protected]>
---
 ChangeLog     |  15 ++++++++
 doc/wget.texi |   5 +++
 src/init.c    |   5 +++
 src/main.c    | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/options.h |   2 ++
 src/url.c     |   6 ++++
 src/url.h     |   1 +
 7 files changed, 145 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index dca7846..368d7c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2016-07-25  Liam R. Howlett  <[email protected]>
+
+	Add --use-askpass support
+	* doc/wget.texi: Add --use-askpass to documentation.
+	* src/init.c: initialize opt.use_use_askpass and opt.use_askpass.
+	* src/main.c: Update options & add spawn process of either
+	WGET_ANSPASS or SSH_ASKPASS.
+	* src/options.h: Addition of use_use_askpass boolean and use_askpass
+	string.
+	* src/url.c: Function scheme_leading_string to access the leading
+	string of a parsed url.
+	* src/url.h: Prototype for scheme_leading_string for returning the
+	leading string.
+
+
 2016-06-09  Giuseppe Scrivano  <[email protected]>
 
 	NEWS: update
diff --git a/doc/wget.texi b/doc/wget.texi
index 1e55e63..0ce9ecf 100644
--- a/doc/wget.texi
+++ b/doc/wget.texi
@@ -1154,6 +1154,11 @@ options for @sc{http} connections.
 Prompt for a password for each connection established. Cannot be specified
 when @samp{--password} is being used, because they are mutually exclusive.
 
+@item --use-askpass
+Prompt for a user and password using the program specified in the environment
+variable WGET_ASKPASS.  If WGET_ASKPASS is not set, then the program specified
+in the environment SSH_ASKPASS is used.
+
 @cindex iri support
 @cindex idn support
 @item --no-iri
diff --git a/src/init.c b/src/init.c
index d043d83..3d68d8e 100644
--- a/src/init.c
+++ b/src/init.c
@@ -322,6 +322,7 @@ static const struct {
   { "user",             &opt.user,              cmd_string },
   { "useragent",        NULL,                   cmd_spec_useragent },
   { "useservertimestamps", &opt.useservertimestamps, cmd_boolean },
+  { "useuseaskpass",    &opt.use_use_askpass,   cmd_boolean},
   { "verbose",          NULL,                   cmd_spec_verbose },
   { "wait",             &opt.wait,              cmd_time },
   { "waitretry",        &opt.waitretry,         cmd_time },
@@ -392,6 +393,10 @@ defaults (void)
   tmp = getenv ("no_proxy");
   if (tmp)
     opt.no_proxy = sepstring (tmp);
+  opt.use_use_askpass = false;
+  opt.use_askpass = getenv ("WGET_ASKPASS");
+  if (!opt.use_askpass)
+    opt.use_askpass = getenv("SSH_ASKPASS");
   opt.prefer_family = prefer_none;
   opt.allow_cache = true;
   opt.if_modified_since = true;
diff --git a/src/main.c b/src/main.c
index e7d5c66..27f5d5c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -36,6 +36,7 @@ as that of the covered work.  */
 #include <unistd.h>
 #include <string.h>
 #include <signal.h>
+#include <spawn.h>
 #ifdef ENABLE_NLS
 # include <locale.h>
 #endif
@@ -415,6 +416,7 @@ static struct cmdline_option option_data[] =
     { "unlink", 0, OPT_BOOLEAN, "unlink", -1 },
     { "trust-server-names", 0, OPT_BOOLEAN, "trustservernames", -1 },
     { "use-server-timestamps", 0, OPT_BOOLEAN, "useservertimestamps", -1 },
+    { "use-askpass", 0, OPT_BOOLEAN, "useuseaskpass", -1 },
     { "user", 0, OPT_VALUE, "user", -1 },
     { "user-agent", 'U', OPT_VALUE, "useragent", -1 },
     { "verbose", 'v', OPT_BOOLEAN, "verbose", -1 },
@@ -691,6 +693,8 @@ Download:\n"),
     N_("\
        --ask-password              prompt for passwords\n"),
     N_("\
+       --use-askpass               Use WGET_ASKPASS for credential requests.  Use SSH_ASKPASS if WGET_ASKPASS is not set.\n"),
+    N_("\
        --no-iri                    turn off IRI support\n"),
     N_("\
        --local-encoding=ENC        use ENC as the local encoding for IRIs\n"),
@@ -1019,6 +1023,99 @@ prompt_for_password (void)
   return getpass("");
 }
 
+
+/* Execute external application WGET_ASKPASS or SSH_ASKPASS which is stored in opt.use_askpass
+ */
+void
+run_use_askpass(char *question, char **answer)
+{
+  char tmp[1024];
+  pid_t pid;
+  int status;
+  int com[2];
+  char * const argv[] = {question, NULL};
+  posix_spawn_file_actions_t fa;
+
+  if (pipe(com) == -1)
+  {
+    fprintf(stderr, _("Cannot create pipe"));
+    exit (WGET_EXIT_GENERIC_ERROR);
+  }
+
+  status = posix_spawn_file_actions_init(&fa);
+  if (status)
+  {
+    fprintf(stderr, "Error initializing spawn file actions for --use-askpass: %d",
+		    status);
+    exit (WGET_EXIT_GENERIC_ERROR);
+  }
+
+  status = posix_spawn_file_actions_adddup2(&fa, com[1], STDOUT_FILENO);
+  if (status)
+  {
+    fprintf(stderr, "Error setting spawn file actions for --use-askpass: %d", status);
+    exit (WGET_EXIT_GENERIC_ERROR);
+  }
+
+  status = posix_spawnp(&pid, opt.use_askpass, &fa, NULL, argv, environ);
+  if (status)
+  {
+    fprintf(stderr, "Error spawning %s: %d", opt.use_askpass, status);
+    exit (WGET_EXIT_GENERIC_ERROR);
+  }
+
+  /* Success */
+  if (pid == 0)
+  {
+    /* Child */
+    close(com[0]);
+    close(com[1]);
+    execlp(opt.use_askpass, question, (char*)NULL);
+    assert("Execlp failed!");
+    }
+  else
+  {
+    close(com[1]);
+    unsigned int bytes = read(com[0], tmp, sizeof(tmp));
+    if (!bytes)
+    {
+      fprintf(stderr,
+        _("Error reading response from --use-askpass %s %s\n"),
+        opt.use_askpass, question);
+      exit (WGET_EXIT_GENERIC_ERROR);
+    }
+    else if (bytes > 1)
+      *answer = strndup(tmp, bytes-1);
+  }
+}
+
+/* set the user name and password*/
+void
+use_askpass (struct url *u)
+{
+  static char question[1024];
+
+  if (u->user == NULL || u->user[0] == '\0')
+  {
+    sprintf(question, "Username for '%s%s': ",
+        scheme_leading_string(u->scheme), u->host);
+    /* Prompt for username */
+    run_use_askpass(question, &u->user);
+    if (opt.recursive)
+      opt.user = strdup(u->user);
+  }
+
+  if (u->passwd == NULL || u->passwd[0] == '\0')
+  {
+    sprintf(question, "Password for '%s%s@%s': ",
+        scheme_leading_string(u->scheme), u->user,
+        u->host);
+    /* Prompt for password */
+    run_use_askpass(question, &u->passwd);
+    if (opt.recursive)
+      opt.passwd = strdup(u->passwd);
+  }
+}
 /* Function that prints the line argument while limiting it
    to at most line_length. prefix is printed on the first line
    and an appropriate number of spaces are added on subsequent
@@ -1702,6 +1799,16 @@ for details.\n\n"));
         exit (WGET_EXIT_GENERIC_ERROR);
     }
 
+  if (opt.use_use_askpass)
+  {
+    /* can't request credentials until the URL is known. */
+    if (opt.use_askpass == NULL || opt.use_askpass[0] == '\0')
+    {
+    fprintf(stderr, _("--use-askpass requires environment variable WGET_ASKPASS or SSH_ASKPASS to be set.\n"));
+      exit(WGET_EXIT_GENERIC_ERROR);
+    }
+  }
+
 #ifdef USE_WATT32
   if (opt.wdebug)
      dbug_init();
@@ -1920,6 +2027,10 @@ only if outputting to a regular file.\n"));
         }
       else
         {
+          if (opt.use_use_askpass)
+          {
+            use_askpass(url_parsed);
+          }
           if ((opt.recursive || opt.page_requisites)
               && ((url_scheme (*t) != SCHEME_FTP
 #ifdef HAVE_SSL
diff --git a/src/options.h b/src/options.h
index a8c494b..0bad59b 100644
--- a/src/options.h
+++ b/src/options.h
@@ -130,6 +130,8 @@ struct options
   char *user;                   /* Generic username */
   char *passwd;                 /* Generic password */
   bool ask_passwd;              /* Ask for password? */
+  bool use_use_askpass;         /* Use [WGET|SSH]_ASKPASS infrastructure */
+  char *use_askpass;            /* value of [WGET|SSH]_ASKPASS */
 
   bool always_rest;             /* Always use REST. */
   wgint start_pos;              /* Start position of a download. */
diff --git a/src/url.c b/src/url.c
index ec38d6f..c133d91 100644
--- a/src/url.c
+++ b/src/url.c
@@ -512,6 +512,12 @@ scheme_disable (enum url_scheme scheme)
   supported_schemes[scheme].flags |= scm_disabled;
 }
 
+const char *
+scheme_leading_string (enum url_scheme scheme)
+{
+  return supported_schemes[scheme].leading_string;
+}
+
 /* Skip the username and password, if present in the URL.  The
    function should *not* be called with the complete URL, but with the
    portion after the scheme.
diff --git a/src/url.h b/src/url.h
index 7c77737..bf2e3f7 100644
--- a/src/url.h
+++ b/src/url.h
@@ -124,6 +124,7 @@ bool url_has_scheme (const char *);
 bool url_valid_scheme (const char *);
 int scheme_default_port (enum url_scheme);
 void scheme_disable (enum url_scheme);
+const char *scheme_leading_string(enum url_scheme);
 
 char *url_string (const struct url *, enum url_auth_mode);
 char *url_file_name (const struct url *, char *);
-- 
1.9.1

Reply via email to