#include "pwdin.h" #include #include #include // Input modes // Hidden: The char set as mask will be printed on input. // Revealed: The char itself will be printed on input. // None: Set implicitly when mask = 0. Nothing will be printed. #define PWDIN_INM_HIDDEN 0 #define PWDIN_INM_REVEALED 1 #define PWDIN_INM_NONE 3 // ASCII codes of interesting keys. #define PWDIN_KEY_BACK 10 #define PWDIN_KEY_TAB 9 #define PWDIN_KEY_DEL 127 #define PWDIN_KEY_ESC 27 void pwdin_setup_term(struct termios* _newt, struct termios* _oldt) { tcgetattr(STDIN_FILENO, _oldt); *_newt = *_oldt; _newt->c_lflag &= ~(ECHO); // Disable printing of characters _newt->c_lflag &= ~(ICANON); // Make typed chars available imidiatly and don't wait for tcsetattr(STDIN_FILENO, TCSANOW, _newt); } void pwdin_teardown_term(struct termios* _oldt) { tcsetattr(STDIN_FILENO, TCSANOW, _oldt); } void pwdin_print_prompt(const char* prompt) { printf("%s", prompt); } // Prints a notice to the user. // A notice is a feedback to the terminal once the user input a character. // The notice can be in form of a hidden character (mask), the character itself (typed) // or nothing (mode == 3). // // @param in Character input by user. // @param mask Mask printed instead of actual character (e.g. "*") // @param mode Current input mode. void pwdin_print_notice(char in, char mask, int mode) { switch (mode) { case PWDIN_INM_HIDDEN: putc(mask, stdout); break; case PWDIN_INM_REVEALED: putc(in, stdout); } } void pwdin_reveal_input(int default_mode, char* _buffer, size_t len) { if (default_mode == PWDIN_INM_HIDDEN && len > 0) { printf("\e[%luD", len); fflush(stdout); } write(STDOUT_FILENO, _buffer, len); } void pwdin_hide_input(int default_mode, char mask, size_t len) { if (len > 0) { printf("\e[%luD", len); // Move cursor to the beginning of the input. fflush(stdout); } if (default_mode == PWDIN_INM_NONE) { printf("\e[0K"); // Clear line to end. } else { for (; len > 0; len--) putc(mask, stdout); } } // Looks out for the mode changing character (reveal_key). If found it will toggle the current input // mode between reveal and defalt (hidden/none). It will also cause the already buffered input to be // printed / hidden. // // @param current_input_mode Id of the current mode (hidden/revealed/none). // @param default_mode Default hidden mode (hidden/none). // @param in Last typed character. Used to determine wether mode change was triggered. // @param reveal_key ASCII of key which triggers reveal toggle // @param mask Character printed in hidden mode. // @param _buffer Required of rendering new output upon toggle. // @param input_length Size of the written part of the buffer. // // @returns int New mode. int pwdin_check_special_keys(int* current_input_mode, int default_mode, char in, char reveal_key, char mask, char* _buffer, size_t* input_length) { #ifdef DEBUG printf("(%lu)", *input_length); #endif /* ifdef DEBUG */ switch (in) { case PWDIN_KEY_DEL: if (*input_length == 0) { return 1; } *input_length = (*input_length) - 1; write(STDOUT_FILENO, "\b \b", 3); return 1; break; case PWDIN_KEY_ESC: // Ignore the following special character sequence. (void)getc(stdin); (void)getc(stdin); return 1; } if (in != reveal_key) { // Return 1 when encountering a control symbol. return (in < 32 || in > 126); } else if (*current_input_mode == PWDIN_INM_REVEALED) { pwdin_hide_input(default_mode, mask, *input_length); *current_input_mode = default_mode; return 1; } else { pwdin_reveal_input(default_mode, _buffer, *input_length); *current_input_mode = PWDIN_INM_REVEALED; return 1; } } size_t pwdin_await_input(char mask, char reveal_key, char* _buffer, size_t _buffer_size) { // Keeps track of the amount of characters that were put in by the user. size_t len = 0; // Current input mode (hidden/revealed/none). Default is hidden. If mask == 0 // then NONE is by default. int default_mode = (mask == 0) ? PWDIN_INM_NONE : PWDIN_INM_HIDDEN; int input_mode = default_mode; // Latest character typed. char c = 0; for (; c != 10;) { c = getc(stdin); // Print notice only if no mode change occured. if (!pwdin_check_special_keys(&input_mode, default_mode, c, reveal_key, mask, _buffer, &len) && len < _buffer_size) { pwdin_print_notice(c, mask, input_mode); _buffer[len] = c; len++; } #ifdef DEBUG printf("(%d)", c); #endif /* ifdef DEBUG */ } putc(10, stdout); // Enter new line after password end. _buffer[len] = '\0'; return len; } size_t pwdin_getpass(const char* prompt, char mask, char reveal_key, int write_zero, char* _buffer, size_t _buffer_size) { struct termios oldt, newt; pwdin_setup_term(&newt, &oldt); pwdin_print_prompt(prompt); size_t len = pwdin_await_input(mask, reveal_key, _buffer, _buffer_size); pwdin_teardown_term(&oldt); if (write_zero) { for (size_t k = len + 1; k < _buffer_size; k++) { _buffer[k] = '\0'; } } return len; }