Add a -password switch to :prompt to allow for more secure password entering

Fixes #660
This commit is contained in:
Maxime Coste 2016-04-19 09:51:09 +01:00
parent 2435949fae
commit 199ccb4021
6 changed files with 29 additions and 18 deletions

View File

@ -1391,7 +1391,8 @@ Some helper commands can be used to define composite commands:
* `prompt <prompt> <register> <command>`: Prompt the user for a string, when * `prompt <prompt> <register> <command>`: Prompt the user for a string, when
the user validates, store the result in given <register> and run <commmand>. the user validates, store the result in given <register> and run <commmand>.
the -init <str> switch allows setting initial content. the -init <str> switch allows setting initial content and -password allow
not to show the entered text (and clears the register after command execution).
* `onkey <register> <command>`: Wait for next key from user, writes it into given * `onkey <register> <command>`: Wait for next key from user, writes it into given
<register> and execute commands. <register> and execute commands.
* `menu <label1> <commands1> <label2> <commands2>...`: display a menu using * `menu <label1> <commands1> <label2> <commands2>...`: display a menu using

View File

@ -137,7 +137,8 @@ commands:
*prompt* <prompt> <register> <command>:: *prompt* <prompt> <register> <command>::
prompt the user for a string, when the user validates, store the prompt the user for a string, when the user validates, store the
result in given *register* and run *commmand*. the *-init <str>* result in given *register* and run *commmand*. the *-init <str>*
switch allows setting initial content switch allows setting initial content, the *-password* switch hides
the entered text and clears the register after command execution.
*onkey* <register> <command>:: *onkey* <register> <command>::
wait for next key from user, writes it into given <register> and wait for next key from user, writes it into given <register> and

View File

@ -1524,6 +1524,7 @@ const CommandDesc prompt_cmd = {
"stores it in <register> and then executes <command>", "stores it in <register> and then executes <command>",
ParameterDesc{ ParameterDesc{
{ { "init", { true, "set initial prompt content" } }, { { "init", { true, "set initial prompt content" } },
{ "password", { false, "Do not display entered text and clear reg after command" } },
{ "file-completion", { false, "use file completion for prompt" } }, { "file-completion", { false, "use file completion for prompt" } },
{ "client-completion", { false, "use client completion for prompt" } }, { "client-completion", { false, "use client completion for prompt" } },
{ "buffer-completion", { false, "use buffer completion for prompt" } }, { "buffer-completion", { false, "use buffer completion for prompt" } },
@ -1568,9 +1569,11 @@ const CommandDesc prompt_cmd = {
context, flags, prefix, cursor_pos); context, flags, prefix, cursor_pos);
}; };
const bool password = (bool)parser.get_switch("password");
context.input_handler().prompt( context.input_handler().prompt(
parser[0], initstr.str(), get_face("Prompt"), std::move(completer), parser[0], initstr.str(), get_face("Prompt"),
password, std::move(completer),
[=](StringView str, PromptEvent event, Context& context) [=](StringView str, PromptEvent event, Context& context)
{ {
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
@ -1580,6 +1583,9 @@ const CommandDesc prompt_cmd = {
ScopedSetBool disable_history{context.history_disabled()}; ScopedSetBool disable_history{context.history_disabled()};
CommandManager::instance().execute(command, context, shell_context); CommandManager::instance().execute(command, context, shell_context);
if (password)
RegisterManager::instance()[reg] = ConstArrayView<String>("");
}); });
} }
}; };

View File

@ -647,10 +647,10 @@ class Prompt : public InputMode
{ {
public: public:
Prompt(InputHandler& input_handler, StringView prompt, Prompt(InputHandler& input_handler, StringView prompt,
String initstr, Face face, Completer completer, String initstr, Face face, bool password,
PromptCallback callback) Completer completer, PromptCallback callback)
: InputMode(input_handler), m_prompt(prompt.str()), m_prompt_face(face), : InputMode(input_handler), m_prompt(prompt.str()), m_prompt_face(face),
m_completer(completer), m_callback(callback), m_password(password), m_completer(completer), m_callback(callback),
m_autoshowcompl{context().options()["autoshowcompl"].get<bool>()} m_autoshowcompl{context().options()["autoshowcompl"].get<bool>()}
{ {
m_history_it = ms_history[m_prompt].end(); m_history_it = ms_history[m_prompt].end();
@ -875,7 +875,9 @@ private:
return; return;
auto width = context().client().dimensions().column - m_prompt.char_length(); auto width = context().client().dimensions().column - m_prompt.char_length();
auto display_line = m_line_editor.build_display_line(width); DisplayLine display_line;
if (not m_password)
display_line = m_line_editor.build_display_line(width);
display_line.insert(display_line.begin(), { m_prompt, m_prompt_face }); display_line.insert(display_line.begin(), { m_prompt, m_prompt_face });
context().print_status(display_line); context().print_status(display_line);
} }
@ -892,6 +894,7 @@ private:
String m_prefix; String m_prefix;
LineEditor m_line_editor; LineEditor m_line_editor;
bool m_autoshowcompl; bool m_autoshowcompl;
bool m_password;
using History = Vector<String, MemoryDomain::History>; using History = Vector<String, MemoryDomain::History>;
static UnorderedMap<String, History, MemoryDomain::History> ms_history; static UnorderedMap<String, History, MemoryDomain::History> ms_history;
@ -1314,11 +1317,11 @@ void InputHandler::repeat_last_insert()
} }
void InputHandler::prompt(StringView prompt, String initstr, void InputHandler::prompt(StringView prompt, String initstr,
Face prompt_face, Completer completer, Face prompt_face, bool password,
PromptCallback callback) Completer completer, PromptCallback callback)
{ {
push_mode(new InputModes::Prompt(*this, prompt, initstr, prompt_face, push_mode(new InputModes::Prompt(*this, prompt, initstr, prompt_face,
completer, callback)); password, completer, callback));
} }
void InputHandler::set_prompt_face(Face prompt_face) void InputHandler::set_prompt_face(Face prompt_face)

View File

@ -53,8 +53,8 @@ public:
// returns to normal mode after validation if callback does // returns to normal mode after validation if callback does
// not change the mode itself // not change the mode itself
void prompt(StringView prompt, String initstr, void prompt(StringView prompt, String initstr,
Face prompt_face, Completer completer, Face prompt_face, bool password,
PromptCallback callback); Completer completer, PromptCallback callback);
void set_prompt_face(Face prompt_face); void set_prompt_face(Face prompt_face);
// enter menu mode, callback is called on each selection change, // enter menu mode, callback is called on each selection change,

View File

@ -336,7 +336,7 @@ void command(Context& context, NormalParams params)
return; return;
context.input_handler().prompt( context.input_handler().prompt(
":", "", get_face("Prompt"), ":", "", get_face("Prompt"), false,
[](const Context& context, CompletionFlags flags, [](const Context& context, CompletionFlags flags,
StringView cmd_line, ByteCount pos) { StringView cmd_line, ByteCount pos) {
return CommandManager::instance().complete(context, flags, cmd_line, pos); return CommandManager::instance().complete(context, flags, cmd_line, pos);
@ -373,7 +373,7 @@ template<bool replace>
void pipe(Context& context, NormalParams) void pipe(Context& context, NormalParams)
{ {
const char* prompt = replace ? "pipe:" : "pipe-to:"; const char* prompt = replace ? "pipe:" : "pipe-to:";
context.input_handler().prompt(prompt, "", get_face("Prompt"), shell_complete, context.input_handler().prompt(prompt, "", get_face("Prompt"), false, shell_complete,
[](StringView cmdline, PromptEvent event, Context& context) [](StringView cmdline, PromptEvent event, Context& context)
{ {
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
@ -427,7 +427,7 @@ template<InsertMode mode>
void insert_output(Context& context, NormalParams) void insert_output(Context& context, NormalParams)
{ {
const char* prompt = mode == InsertMode::Insert ? "insert-output:" : "append-output:"; const char* prompt = mode == InsertMode::Insert ? "insert-output:" : "append-output:";
context.input_handler().prompt(prompt, "", get_face("Prompt"), shell_complete, context.input_handler().prompt(prompt, "", get_face("Prompt"), false, shell_complete,
[](StringView cmdline, PromptEvent event, Context& context) [](StringView cmdline, PromptEvent event, Context& context)
{ {
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
@ -588,7 +588,7 @@ void regex_prompt(Context& context, const String prompt, T func)
{ {
CharCoord position = context.has_window() ? context.window().position() : CharCoord{}; CharCoord position = context.has_window() ? context.window().position() : CharCoord{};
SelectionList selections = context.selections(); SelectionList selections = context.selections();
context.input_handler().prompt(prompt, "", get_face("Prompt"), complete_nothing, context.input_handler().prompt(prompt, "", get_face("Prompt"), false, complete_nothing,
[=](StringView str, PromptEvent event, Context& context) mutable { [=](StringView str, PromptEvent event, Context& context) mutable {
try try
{ {
@ -815,7 +815,7 @@ void keep(Context& context, NormalParams)
void keep_pipe(Context& context, NormalParams) void keep_pipe(Context& context, NormalParams)
{ {
context.input_handler().prompt( context.input_handler().prompt(
"keep pipe:", "", get_face("Prompt"), shell_complete, "keep pipe:", "", get_face("Prompt"), false, shell_complete,
[](StringView cmdline, PromptEvent event, Context& context) { [](StringView cmdline, PromptEvent event, Context& context) {
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
return; return;
@ -957,7 +957,7 @@ void select_object(Context& context, NormalParams params)
AutoInfo::Command, context); AutoInfo::Command, context);
context.input_handler().prompt( context.input_handler().prompt(
"object desc:", "", get_face("Prompt"), complete_nothing, "object desc:", "", get_face("Prompt"), false, complete_nothing,
[level,info](StringView cmdline, PromptEvent event, Context& context) { [level,info](StringView cmdline, PromptEvent event, Context& context) {
if (event != PromptEvent::Change) if (event != PromptEvent::Change)
hide_auto_info_ifn(context, info); hide_auto_info_ifn(context, info);