- improved authentication dialog
authorJan Kupec <jkupec@suse.cz>
Sun, 28 Sep 2008 10:44:51 +0000 (10:44 +0000)
committerJan Kupec <jkupec@suse.cz>
Sun, 28 Sep 2008 10:44:51 +0000 (10:44 +0000)
- fixed showing password as typed (bnc #400104)

src/callbacks/media.h
src/output/OutNormal.cc
src/utils/prompt.cc
src/utils/prompt.h

index 6ae3aee..9a463d0 100644 (file)
@@ -217,20 +217,36 @@ namespace ZmartRecipients
         dynamic_cast<zypp::media::CurlAuthData*> (&auth_data);
       if (auth_data_ptr)
       {
-//! \todo move this to prompt help once it's done
-//        cout_vv << "available auth types: "
-//          << auth_data_ptr->authTypeAsString() << std::endl;
-
-        Zypper::instance()->out().prompt(
-            PROMPT_AUTH_USERNAME, description, PromptOptions(_("User Name"), 0));
+        
+        // user name
+        
         std::string username;
-        std::cin >> username;
-        auth_data_ptr->setUserName(username);
+        // expect the input from machine on stdin
+        if (Zypper::instance()->globalOpts().machine_readable)
+        {
+          Zypper::instance()->out().prompt(
+              PROMPT_AUTH_USERNAME, _("User Name"), PromptOptions(), description);
+          std::cin >> username;
+        }
+        // input from human using readline
+        else
+        {
+          std::cout << description << std::endl;
+          username = get_text(_("User Name") + std::string(": "), auth_data.username());
+        }
+        auth_data_ptr->setUsername(username);
 
+        // password
+        
         Zypper::instance()->out().prompt(
-            PROMPT_AUTH_PASSWORD, description, PromptOptions(_("Password"), 0));
+            PROMPT_AUTH_PASSWORD, _("Password"), PromptOptions());
+
         std::string password;
-        std::cin >> password;
+        // expect the input from machine on stdin
+        if (Zypper::instance()->globalOpts().machine_readable)
+          std::cin >> password;
+        else
+          password = get_password();
         if (password.empty()) return false;
         auth_data_ptr->setPassword(password);
 
index 40199d6..32900d7 100644 (file)
@@ -237,7 +237,10 @@ void OutNormal::prompt(PromptId id,
     cout << CLEARLN;
   else
     cout << startdesc << endl;
-  cout << prompt << " [" << poptions.optionString() << "]: " << std::flush;
+  cout << prompt;
+  if (!poptions.empty())
+    cout << " [" << poptions.optionString() << "]";
+  cout << ": " << std::flush;
 }
 
 void OutNormal::promptHelp(const PromptOptions & poptions)
index b2e6ed9..48f74ef 100644 (file)
@@ -2,6 +2,10 @@
 #include <iostream>
 #include <sstream>
 #include <poll.h>
+#include <readline/readline.h>
+//#include <unistd.h>
+#include <termios.h>
+
 
 #include <boost/format.hpp>
 
@@ -265,3 +269,91 @@ unsigned int get_prompt_reply(Zypper & zypper,
 
   return reply_int;
 }
+
+// ---------------------------------------------------------------------------
+
+static const char * prefill = NULL;
+
+static int init_line(void)
+{
+  if (prefill)
+  {
+    rl_replace_line(prefill, 1);
+    rl_end_of_line(1,0);
+    rl_redisplay();
+  }
+  return 1;
+}
+
+string get_text(const string & prompt, const string & prefilled)
+{
+  // A static variable for holding the line.
+  static char * line_read = NULL;
+  
+  prefill = prefilled.c_str();
+
+  /* If the buffer has already been allocated,
+     return the memory to the free pool. */
+  if (line_read)
+  {
+    free (line_read);
+    line_read = NULL;
+  }
+
+  rl_pre_input_hook = init_line;
+
+  /* Get a line from the user. */
+  line_read = ::readline (prompt.c_str());
+
+  if (line_read)
+    return line_read;
+  return string();
+}
+
+static int silent_getch ( void ) 
+{
+  int ch;
+  struct termios oldt, newt;
+
+  tcgetattr ( STDIN_FILENO, &oldt );
+  newt = oldt;
+  newt.c_lflag &= ~( ICANON | ECHO );
+  tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
+  ch = getchar();
+  tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
+
+  return ch;
+}
+
+/** FIXME is there really not some nice standard library call for this???
+ * is it possible to get this from readline? */
+/** \todo restore old terminal settings on interrupt */
+string get_password()
+{
+  int ch;
+  char pw[20];
+  unsigned i = 0;
+
+  while ((ch = silent_getch()) != EOF 
+          && ch != '\n'
+          && ch != '\r'
+          && i < sizeof(pw) - 1)
+  {
+    if (i && (ch == '\b'  || ch == 127 /* DEL */))
+    {
+//      printf("\b \b"); // does not work for me :O(
+//      fflush(stdout);
+      pw[--i] = '\0';
+    }
+    else if (isalnum(ch))
+    {
+//      putchar('*');
+      pw[i++] = (char)ch;
+    }
+  }
+
+  pw[i] = '\0';
+  cout << endl;
+
+  return pw;
+}
index 5d4d00e..f4657f8 100644 (file)
@@ -31,6 +31,7 @@ public:
   void setOptions(const std::string & option_str, unsigned int default_opt);
   unsigned int defaultOpt() const { return _default; }
   const std::string optionString() const;
+  bool empty() const { return _options.empty(); }
 
   const std::string optionHelp(unsigned int opt) const
   { static std::string empty; return opt < _opt_help.size() ? _opt_help[opt] : empty; } 
@@ -77,6 +78,19 @@ unsigned int get_prompt_reply(Zypper & zypper,
                               PromptId pid,
                               const PromptOptions & poptions);
 
+/**
+ * Get text from user using readline without history.
+ * \param prompt prompt text or empty string
+ * \param prefilled prefilled text
+ */
+std::string get_text(const std::string & prompt, const std::string & prefilled = "");
+
+/**
+ * Get text from user without showing the typed text on the terminal.
+ * Uses getchar(), not suitable for machines.
+ */
+std::string get_password();
+
 /*
 enum Error {
     NO_ERROR,