1 //**********************************************************************************
2 //EncryptPad Copyright 2016 Evgeny Pokhilko
3 //<http://www.evpo.net/encryptpad>
4 //
5 //This file is part of EncryptPad
6 //
7 //EncryptPad is free software: you can redistribute it and/or modify
8 //it under the terms of the GNU General Public License as published by
9 //the Free Software Foundation, either version 2 of the License, or
10 //(at your option) any later version.
11 //
12 //EncryptPad is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with EncryptPad.  If not, see <http://www.gnu.org/licenses/>.
19 //**********************************************************************************
20 #include "get_passphrase.h"
21 #include <string>
22 #include <iostream>
23 #include "assert.h"
24 #include "epad_utilities.h"
25 
26 #if defined(__MINGW32__) || defined(_MSC_VER)
27 
28 #include <windows.h>
29 
30 namespace
31 {
32     class HandleTraits
33     {
34     public:
InvalidHandle()35         static HANDLE InvalidHandle()
36         {
37             return INVALID_HANDLE_VALUE;
38         }
39 
Close(HANDLE h)40         static void Close(HANDLE h)
41         {
42             CloseHandle(h);
43         }
44     };
45 
46     typedef EncryptPad::UniqueHandler<HANDLE, HandleTraits> Hndl;
47 
WriteToConsole(const std::string & prompt)48     void WriteToConsole(const std::string &prompt)
49     {
50         using namespace EncryptPad;
51 
52         FileHndl file = fopen("CONOUT$", "wt");
53         if(!file.Valid())
54         {
55             std::cerr << "Cannot open the terminal descriptor for output" << std::endl;
56             return;
57         }
58 
59         fputs(prompt.data(), file.get());
60     }
61 }
62 
63 namespace EncryptPad
64 {
GetPassphrase(const std::string & prompt,std::string & passphrase)65     void GetPassphrase(const std::string &prompt, std::string &passphrase)
66     {
67         const char kBackSpace=8;
68         const int kReturn='\n';
69 
70         passphrase.clear();
71 
72         unsigned char ch=0;
73 
74         WriteToConsole(prompt);
75 
76         DWORD con_mode;
77         Hndl h = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
78         if(!h.Valid())
79         {
80             std::cerr << "Cannot open the terminal for input" << std::endl;
81             return;
82         }
83 
84         GetConsoleMode(h.get(), &con_mode);
85         SetConsoleMode(h.get(), con_mode & (~ENABLE_ECHO_INPUT));
86 
87         FileHndl file=fopen("CONIN$", "rt");
88         if(!file.Valid())
89         {
90             std::cerr << "Cannot open the terminal descriptor for input" << std::endl;
91             return;
92         }
93 
94         int result = -1;
95         while((result=getc(file.get()))!=EOF && result != kReturn)
96         {
97             ch = static_cast<unsigned char>(result);
98             if(ch == kBackSpace)
99             {
100                 if(passphrase.length() != 0)
101                 {
102                     passphrase.resize(passphrase.length() - 1);
103                 }
104             }
105             else
106             {
107                 passphrase+=ch;
108             }
109         }
110 
111         SetConsoleMode(h.get(), con_mode);
112         WriteToConsole("\n");
113     }
114 
115 }
116 
117 #elif defined(__APPLE__) || defined(unix) || defined(__unix__) || defined(__unix)
118 
119 #include "termios.h"
120 #include "unistd.h"
121 #include <cstdio>
122 
123 namespace
124 {
125     const char kBackspace = 127;
126     const char kReturn = 10;
127     const char kInvalid = -1;
128 
Getch(FILE * f)129     char Getch(FILE *f)
130     {
131         struct termios t_old, t_new;
132         int file_no = fileno(f);
133 
134         tcgetattr(file_no, &t_old);
135         t_new = t_old;
136         t_new.c_lflag &= ~(ICANON | ECHO);
137         tcsetattr(file_no, TCSANOW, &t_new);
138 
139         char ch;
140         if(fread(&ch, 1, 1, f) != 1)
141         {
142             ch = kInvalid;
143         }
144 
145         tcsetattr(file_no, TCSANOW, &t_old);
146         return ch;
147     }
148 }
149 
150 namespace EncryptPad
151 {
GetPassphrase(const std::string & prompt,std::string & passphrase)152     void GetPassphrase(const std::string &prompt, std::string &passphrase)
153     {
154         passphrase.clear();
155         unsigned char ch=0;
156 
157         const char *termid = ctermid(nullptr);
158         FileHndl term_file(fopen(termid, "r+"));
159         if(!term_file.Valid())
160         {
161             std::cerr << "Cannot open the terminal for output" << std::endl;
162             return;
163         }
164 
165         size_t res = fwrite(prompt.c_str(), 1, prompt.size(), term_file.get());
166         assert(res == prompt.size());
167         (void)res;
168 
169         while((ch = Getch(term_file.get())) != kReturn && ch != static_cast<unsigned char>(kInvalid))
170         {
171             if(ch == kBackspace)
172             {
173                 if(passphrase.length() != 0)
174                 {
175                     passphrase.resize(passphrase.length() - 1);
176                 }
177             }
178             else
179             {
180                 passphrase += ch;
181             }
182         }
183 
184         fputc('\n', term_file.get());
185     }
186 }
187 #endif
188