1 /* 2 main.cpp - A Fltk based dialog for PIN entry. 3 4 Copyright (C) 2016 Anatoly madRat L. Berenblit 5 6 Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>. 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of the 11 License, or (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 SPDX-License-Identifier: GPL-2.0+ 22 */ 23 24 #ifdef HAVE_CONFIG_H 25 #include "config.h" 26 #endif 27 28 #define PGMNAME (PACKAGE_NAME"-fltk") 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <errno.h> 34 #include <getopt.h> 35 #include <assert.h> 36 37 #include "memory.h" 38 #include <memory> 39 40 #include <pinentry.h> 41 #ifdef FALLBACK_CURSES 42 #include <pinentry-curses.h> 43 #endif 44 45 #include <string> 46 #include <string.h> 47 #include <stdexcept> 48 49 50 #include <FL/Fl.H> 51 #include <FL/Fl_Window.H> 52 #include <FL/fl_ask.H> 53 54 #include "pinwindow.h" 55 #include "passwindow.h" 56 #include "qualitypasswindow.h" 57 58 #define CONFIRM_STRING "Confirm" 59 #define REPEAT_ERROR_STRING "Texts do not match" 60 #define OK_STRING "OK" 61 #define CANCEL_STRING "Cancel" 62 63 char *application = NULL; 64 65 static std::string escape_accel_utf8(const char *s) 66 { 67 std::string result; 68 if (NULL != s) 69 { 70 result.reserve(strlen(s)); 71 for (const char *p = s; *p; ++p) 72 { 73 if ('&' == *p) 74 result.push_back(*p); 75 result.push_back(*p); 76 } 77 } 78 return result; 79 } 80 81 // For button labels 82 // Accelerator '_' (used e.g. by GPG2) is converted to '&' (for FLTK) 83 // '&' is escaped as in escape_accel_utf8() 84 static std::string convert_accel_utf8(const char *s) 85 { 86 static bool last_was_underscore = false; 87 std::string result; 88 if (NULL != s) 89 { 90 result.reserve(strlen(s)); 91 for (const char *p = s; *p; ++p) 92 { 93 // & => && 94 if ('&' == *p) 95 result.push_back(*p); 96 // _ => & (handle '__' as escaped underscore) 97 if ('_' == *p) 98 { 99 if (last_was_underscore) 100 { 101 result.push_back(*p); 102 last_was_underscore = false; 103 } 104 else 105 last_was_underscore = true; 106 } 107 else 108 { 109 if (last_was_underscore) 110 result.push_back('&'); 111 result.push_back(*p); 112 last_was_underscore = false; 113 } 114 } 115 } 116 return result; 117 } 118 119 class cancel_exception 120 { 121 122 }; 123 124 static int get_quality(const char *passwd, void *ptr) 125 { 126 if (NULL == passwd || 0 == *passwd) 127 return 0; 128 129 pinentry_t* pe = reinterpret_cast<pinentry_t*>(ptr); 130 return pinentry_inq_quality(*pe, passwd, strlen(passwd)); 131 } 132 133 bool is_short(const char *str) 134 { 135 return fl_utf_nb_char(reinterpret_cast<const unsigned char*>(str), strlen(str)) < 16; 136 } 137 138 bool is_empty(const char *str) 139 { 140 return (NULL == str) || (0 == *str); 141 } 142 143 static int fltk_cmd_handler(pinentry_t pe) 144 { 145 int ret = -1; 146 147 try 148 { 149 // TODO: Add parent window to pinentry-fltk window 150 //if (pe->parent_wid){} 151 std::string title = !is_empty(pe->title)?pe->title:PGMNAME; 152 std::string ok = convert_accel_utf8(pe->ok?pe->ok:(pe->default_ok?pe->default_ok:OK_STRING)); 153 std::string cancel = convert_accel_utf8(pe->cancel?pe->cancel:(pe->default_cancel?pe->default_cancel:CANCEL_STRING)); 154 155 if (!!pe->pin) // password (or confirmation) 156 { 157 std::unique_ptr<PinWindow> window; 158 159 bool isSimple = (NULL == pe->quality_bar) && // pinenty.h: If this is not NULL ... 160 is_empty(pe->error) && is_empty(pe->description) && 161 is_short(pe->prompt); 162 if (isSimple) 163 { 164 assert(NULL == pe->description); 165 window.reset(PinWindow::create()); 166 window->prompt(pe->prompt); 167 } 168 else 169 { 170 PassWindow *pass = NULL; 171 172 if (pe->quality_bar) // pinenty.h: If this is not NULL ... 173 { 174 QualityPassWindow *p = QualityPassWindow::create(get_quality, &pe); 175 window.reset(p); 176 pass = p; 177 p->quality(pe->quality_bar); 178 } 179 else 180 { 181 pass = PassWindow::create(); 182 window.reset(pass); 183 } 184 185 if (NULL == pe->description) 186 { 187 pass->description(pe->prompt); 188 pass->prompt(" "); 189 } 190 else 191 { 192 pass->description(pe->description); 193 pass->prompt(escape_accel_utf8(pe->prompt).c_str()); 194 } 195 pass->description(pe->description); 196 pass->prompt(escape_accel_utf8(pe->prompt).c_str()); 197 198 199 if (NULL != pe->error) 200 pass->error(pe->error); 201 } 202 203 window->ok(ok.c_str()); 204 window->cancel(cancel.c_str()); 205 window->title(title.c_str()); 206 window->showModal((NULL != application)?1:0, &application); 207 208 if (NULL == window->passwd()) 209 throw cancel_exception(); 210 211 const std::string password = window->passwd(); 212 window.reset(); 213 214 if (pe->repeat_passphrase) 215 { 216 const char *dont_match = NULL; 217 do 218 { 219 if (NULL == dont_match && is_short(pe->repeat_passphrase)) 220 { 221 window.reset(PinWindow::create()); 222 window->prompt(escape_accel_utf8(pe->repeat_passphrase).c_str()); 223 } 224 else 225 { 226 PassWindow *pass = PassWindow::create(); 227 window.reset(pass); 228 pass->description(pe->repeat_passphrase); 229 pass->prompt(" "); 230 pass->error(dont_match); 231 } 232 233 window->ok(ok.c_str()); 234 window->cancel(cancel.c_str()); 235 window->title(title.c_str()); 236 window->showModal(); 237 238 if (NULL == window->passwd()) 239 throw cancel_exception(); 240 241 if (password == window->passwd()) 242 { 243 pe->repeat_okay = 1; 244 ret = 1; 245 break; 246 } 247 else 248 { 249 dont_match = (NULL!=pe->repeat_error_string)? pe->repeat_error_string:REPEAT_ERROR_STRING; 250 } 251 } while (true); 252 } 253 else 254 ret = 1; 255 256 pinentry_setbufferlen(pe, password.size()+1); 257 if (pe->pin) 258 { 259 memcpy(pe->pin, password.c_str(), password.size()+1); 260 pe->result = password.size(); 261 ret = password.size(); 262 } 263 } 264 else 265 { 266 // Confirmation or Message Dialog title, desc 267 Fl_Window dummy(0,0, 1,1); 268 269 dummy.border(0); 270 dummy.show((NULL != application)?1:0, &application); 271 dummy.hide(); 272 273 fl_message_title(title.c_str()); 274 275 int result = -1; 276 277 const char *message = (NULL != pe->description)?pe->description:CONFIRM_STRING; 278 279 if (pe->one_button) 280 { 281 fl_ok = ok.c_str(); 282 fl_message("%s", message); 283 result = 1; // OK 284 } 285 else if (pe->notok) 286 { 287 switch (fl_choice("%s", ok.c_str(), cancel.c_str(), pe->notok, message)) 288 { 289 case 0: result = 1; break; 290 case 2: result = 0; break; 291 default: 292 case 1: result = -1;break; 293 } 294 } 295 else 296 { 297 switch (fl_choice("%s", ok.c_str(), cancel.c_str(), NULL, message)) 298 { 299 case 0: result = 1; break; 300 default: 301 case 1: result = -1;break; 302 } 303 } 304 305 // cancel -> pe->canceled = true, 0 306 // ok/y -> 1 307 // no -> 0 308 if (-1 == result) 309 pe->canceled = true; 310 ret = (1 == result); 311 } 312 Fl::check(); 313 } 314 catch (const cancel_exception&) 315 { 316 ret = -1; 317 } 318 catch (...) 319 { 320 ret = -1; 321 } 322 // do_touch_file(pe); only for NCURSES? 323 return ret; 324 } 325 326 pinentry_cmd_handler_t pinentry_cmd_handler = fltk_cmd_handler; 327 328 int main(int argc, char *argv[]) 329 { 330 application = *argv; 331 pinentry_init(PGMNAME); 332 333 #ifdef FALLBACK_CURSES 334 if (!pinentry_have_display(argc, argv)) 335 pinentry_cmd_handler = curses_cmd_handler; 336 else 337 #endif 338 { 339 //FLTK understood only -D (--display) 340 // and should be converted into -di[splay] 341 const static struct option long_options[] = 342 { 343 {"display", required_argument, 0, 'D' }, 344 {NULL, no_argument, 0, 0 } 345 }; 346 347 for (int i = 0; i < argc-1; ++i) 348 { 349 switch (getopt_long(argc-i, argv+i, "D:", long_options, NULL)) 350 { 351 case 'D': 352 { 353 char* emul[] = {application, (char*)"-display", optarg}; 354 Fl::args(3, emul); 355 i = argc; 356 break; 357 } 358 default: 359 break; 360 } 361 } 362 } 363 364 pinentry_parse_opts(argc, argv); 365 return pinentry_loop() ?EXIT_FAILURE:EXIT_SUCCESS; 366 } 367