From 199ccb4021b9f740d9959e532e515775a00c90e9 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 19 Apr 2016 09:51:09 +0100 Subject: [PATCH] Add a -password switch to :prompt to allow for more secure password entering Fixes #660 --- README.asciidoc | 3 ++- doc/manpages/commands.asciidoc | 3 ++- src/commands.cc | 8 +++++++- src/input_handler.cc | 17 ++++++++++------- src/input_handler.hh | 4 ++-- src/normal.cc | 12 ++++++------ 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index de5ec664..42b0ac7e 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -1391,7 +1391,8 @@ Some helper commands can be used to define composite commands: * `prompt `: Prompt the user for a string, when the user validates, store the result in given and run . - the -init switch allows setting initial content. + the -init switch allows setting initial content and -password allow + not to show the entered text (and clears the register after command execution). * `onkey `: Wait for next key from user, writes it into given and execute commands. * `menu ...`: display a menu using diff --git a/doc/manpages/commands.asciidoc b/doc/manpages/commands.asciidoc index c6bb4f55..870d6027 100644 --- a/doc/manpages/commands.asciidoc +++ b/doc/manpages/commands.asciidoc @@ -137,7 +137,8 @@ commands: *prompt* :: prompt the user for a string, when the user validates, store the result in given *register* and run *commmand*. the *-init * - 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* :: wait for next key from user, writes it into given and diff --git a/src/commands.cc b/src/commands.cc index 8733bdd8..3ddfcc3a 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -1524,6 +1524,7 @@ const CommandDesc prompt_cmd = { "stores it in and then executes ", ParameterDesc{ { { "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" } }, { "client-completion", { false, "use client completion for prompt" } }, { "buffer-completion", { false, "use buffer completion for prompt" } }, @@ -1568,9 +1569,11 @@ const CommandDesc prompt_cmd = { context, flags, prefix, cursor_pos); }; + const bool password = (bool)parser.get_switch("password"); 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) { if (event != PromptEvent::Validate) @@ -1580,6 +1583,9 @@ const CommandDesc prompt_cmd = { ScopedSetBool disable_history{context.history_disabled()}; CommandManager::instance().execute(command, context, shell_context); + + if (password) + RegisterManager::instance()[reg] = ConstArrayView(""); }); } }; diff --git a/src/input_handler.cc b/src/input_handler.cc index 0edc9a1a..61da7b41 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -647,10 +647,10 @@ class Prompt : public InputMode { public: Prompt(InputHandler& input_handler, StringView prompt, - String initstr, Face face, Completer completer, - PromptCallback callback) + String initstr, Face face, bool password, + Completer completer, PromptCallback callback) : 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()} { m_history_it = ms_history[m_prompt].end(); @@ -875,7 +875,9 @@ private: return; 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 }); context().print_status(display_line); } @@ -892,6 +894,7 @@ private: String m_prefix; LineEditor m_line_editor; bool m_autoshowcompl; + bool m_password; using History = Vector; static UnorderedMap ms_history; @@ -1314,11 +1317,11 @@ void InputHandler::repeat_last_insert() } void InputHandler::prompt(StringView prompt, String initstr, - Face prompt_face, Completer completer, - PromptCallback callback) + Face prompt_face, bool password, + Completer completer, PromptCallback callback) { push_mode(new InputModes::Prompt(*this, prompt, initstr, prompt_face, - completer, callback)); + password, completer, callback)); } void InputHandler::set_prompt_face(Face prompt_face) diff --git a/src/input_handler.hh b/src/input_handler.hh index 77153c2d..35603053 100644 --- a/src/input_handler.hh +++ b/src/input_handler.hh @@ -53,8 +53,8 @@ public: // returns to normal mode after validation if callback does // not change the mode itself void prompt(StringView prompt, String initstr, - Face prompt_face, Completer completer, - PromptCallback callback); + Face prompt_face, bool password, + Completer completer, PromptCallback callback); void set_prompt_face(Face prompt_face); // enter menu mode, callback is called on each selection change, diff --git a/src/normal.cc b/src/normal.cc index 6f461c75..89ac2a9c 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -336,7 +336,7 @@ void command(Context& context, NormalParams params) return; context.input_handler().prompt( - ":", "", get_face("Prompt"), + ":", "", get_face("Prompt"), false, [](const Context& context, CompletionFlags flags, StringView cmd_line, ByteCount pos) { return CommandManager::instance().complete(context, flags, cmd_line, pos); @@ -373,7 +373,7 @@ template void pipe(Context& context, NormalParams) { 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) { if (event != PromptEvent::Validate) @@ -427,7 +427,7 @@ template void insert_output(Context& context, NormalParams) { 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) { 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{}; 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 { try { @@ -815,7 +815,7 @@ void keep(Context& context, NormalParams) void keep_pipe(Context& context, NormalParams) { 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) { if (event != PromptEvent::Validate) return; @@ -957,7 +957,7 @@ void select_object(Context& context, NormalParams params) AutoInfo::Command, context); 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) { if (event != PromptEvent::Change) hide_auto_info_ifn(context, info);