1 /* pinentry-efl.c 2 Copyright (C) 2017 Obsidian-Studios, Inc. 3 Author William L. Thomson Jr. <wlt@o-sinc.com> 4 5 Based on pinentry-gtk2.c 6 Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at> 7 Copyright (C) 2001, 2002, 2007, 2015 g10 Code GmbH 8 Copyright (C) 2004 by Albrecht Dreß <albrecht.dress@arcor.de> 9 10 pinentry-efl is a pinentry application for the EFL widget set. 11 It tries to follow the Gnome Human Interface Guide as close as 12 possible. 13 14 This program is free software; you can redistribute it and/or modify 15 it under the terms of the GNU General Public License as published by 16 the Free Software Foundation; either version 2 of the License, or 17 (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 GNU General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 27 */ 28 29 #ifdef HAVE_CONFIG_H 30 #include "config.h" 31 #endif 32 #include <Elementary.h> 33 #include <Ecore_X.h> 34 #include <gpg-error.h> 35 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) 36 #pragma GCC diagnostic push 37 #pragma GCC diagnostic ignored "-Wstrict-prototypes" 38 #endif 39 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) 40 #pragma GCC diagnostic pop 41 #endif 42 43 #ifdef HAVE_GETOPT_H 44 #include <getopt.h> 45 #else 46 #include "getopt.h" 47 #endif /* HAVE_GETOPT_H */ 48 49 #include "pinentry.h" 50 51 #ifdef FALLBACK_CURSES 52 #include "pinentry-curses.h" 53 #endif 54 55 #define PGMNAME "pinentry-efl" 56 57 #ifndef VERSION 58 #define VERSION 59 #endif 60 61 #define ENTRY_HIDE "Hide entry" 62 #define ENTRY_SHOW "Show entry" 63 64 typedef enum { CONFIRM_CANCEL, CONFIRM_OK, CONFIRM_NOTOK } confirm_value_t; 65 66 static const int WIDTH = 480; 67 static const int BUTTON_HEIGHT = 27; 68 static const int BUTTON_WIDTH = 70; 69 static const int BUTTON_ICON_SIZE = 13; 70 static const int PADDING = 5; 71 72 static Eina_Bool got_input; 73 static Ecore_Timer *timer; 74 static Evas_Object *check_label; 75 static Evas_Object *error_label; 76 static Evas_Object *entry; 77 static Evas_Object *repeat_entry; 78 static Evas_Object *qualitybar; 79 static Evas_Object *win; 80 static char **pargv; 81 static int grab_failed; 82 static int passphrase_ok; 83 static int confirm_mode; 84 static int pargc; 85 static confirm_value_t confirm_value; 86 static pinentry_t pinentry; 87 88 pinentry_cmd_handler_t pinentry_cmd_handler; 89 90 static void 91 quit (void) 92 { 93 evas_object_del(win); 94 elm_exit(); 95 ecore_main_loop_quit (); 96 } 97 98 static void 99 delete_event (void *data EINA_UNUSED, 100 Evas_Object *obj EINA_UNUSED, 101 void *event EINA_UNUSED) 102 { 103 pinentry->close_button = 1; 104 quit (); 105 } 106 107 static void 108 changed_text_handler (void *data EINA_UNUSED, 109 Evas_Object *obj, 110 void *event EINA_UNUSED) 111 { 112 const char *s; 113 int length; 114 int percent; 115 116 got_input = EINA_TRUE; 117 118 if (pinentry->repeat_passphrase && repeat_entry) 119 { 120 elm_object_text_set (repeat_entry, ""); 121 elm_object_text_set (error_label, ""); 122 } 123 124 if (!qualitybar || !pinentry->quality_bar) 125 return; 126 127 s = elm_object_text_get (obj); 128 if (!s) 129 s = ""; 130 length = strlen (s); 131 percent = length? pinentry_inq_quality (pinentry, s, length) : 0; 132 evas_object_color_set(qualitybar, 133 255 - ( 2.55 * percent ), 134 2.55 * percent, 0, 255); 135 elm_progressbar_value_set (qualitybar, (double) percent / 100.0); 136 } 137 138 static void 139 on_check (void *data EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED) 140 { 141 if(elm_check_state_get(obj)) 142 { 143 elm_entry_password_set(entry, EINA_FALSE); 144 elm_object_text_set(check_label,ENTRY_HIDE); 145 } 146 else 147 { 148 elm_entry_password_set(entry, EINA_TRUE); 149 elm_object_text_set(check_label,ENTRY_SHOW); 150 } 151 evas_object_size_hint_min_set(check_label, 152 ELM_SCALE_SIZE(BUTTON_WIDTH), 153 ELM_SCALE_SIZE(BUTTON_HEIGHT)); 154 evas_object_size_hint_align_set(check_label, 0, 1); 155 } 156 157 static void 158 on_click (void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) 159 { 160 if (confirm_mode) 161 { 162 confirm_value = (confirm_value_t) data; 163 quit (); 164 return; 165 } 166 167 if (data) 168 { 169 const char *s; 170 const char *s2; 171 172 s = elm_entry_entry_get (entry); 173 if (!s) 174 s = ""; 175 176 if (pinentry->repeat_passphrase && repeat_entry) 177 { 178 s2 = elm_entry_entry_get (repeat_entry); 179 if (!s2) 180 s2 = ""; 181 if (strcmp (s, s2)) 182 { 183 elm_object_text_set(error_label, 184 pinentry->repeat_error_string? 185 pinentry->repeat_error_string: 186 "not correctly repeated"); 187 elm_object_focus_set(entry,EINA_TRUE); 188 return; 189 } 190 pinentry->repeat_okay = 1; 191 } 192 193 passphrase_ok = 1; 194 pinentry_setbufferlen (pinentry, strlen (s) + 1); 195 if (pinentry->pin) 196 strncpy (pinentry->pin, s, strlen(s) + 1); 197 } 198 quit (); 199 } 200 201 static void 202 enter_callback (void *data, Evas_Object * obj, void *event_info EINA_UNUSED) 203 { 204 if (data) 205 elm_object_focus_set (data, 1); 206 else 207 on_click ((void *) CONFIRM_OK, obj, NULL); 208 } 209 210 static Eina_Bool 211 timeout_cb (const void * data) 212 { 213 pinentry_t pe = (pinentry_t)data; 214 if (!got_input) 215 { 216 ecore_main_loop_quit(); 217 if (pe) 218 pe->specific_err = gpg_error (GPG_ERR_TIMEOUT); 219 } 220 221 timer = NULL; 222 return ECORE_CALLBACK_DONE; 223 } 224 225 static void 226 create_window (void) 227 { 228 char *txt; 229 Evas_Object *icon; 230 Evas_Object *obj; 231 Evas_Object *table; 232 int btn_txt_len = 0; 233 int row = 0; 234 int ok_len = 0; 235 236 win = elm_win_util_dialog_add(NULL,"pinentry","enter pin"); 237 elm_win_autodel_set(win, EINA_TRUE); 238 elm_win_center(win,EINA_TRUE,EINA_TRUE); 239 evas_object_smart_callback_add(win, "delete,request", delete_event, NULL); 240 241 table = elm_table_add(win); 242 elm_table_padding_set(table,ELM_SCALE_SIZE(PADDING),0); 243 evas_object_size_hint_padding_set (table, 244 ELM_SCALE_SIZE(PADDING), 245 ELM_SCALE_SIZE(PADDING), 246 ELM_SCALE_SIZE(PADDING), 247 ELM_SCALE_SIZE(PADDING)); 248 evas_object_show(table); 249 250 if (pinentry->title) 251 { 252 txt = pinentry_utf8_to_local (pinentry->lc_ctype, 253 pinentry->title); 254 elm_win_title_set ( win, txt ); 255 free (txt); 256 } 257 258 /* Description Label */ 259 if (pinentry->description) 260 { 261 char* aligned; 262 int len; 263 264 obj = elm_label_add(table); 265 elm_label_line_wrap_set (obj, ELM_WRAP_WORD); 266 txt = pinentry_utf8_to_local (pinentry->lc_ctype, pinentry->description); 267 len = strlen(txt)+20; // 20 chars for align tag 268 aligned = calloc(len+1,sizeof(char)); 269 if(aligned) 270 { 271 snprintf(aligned,len, "<align=left>%s</align>",txt); 272 elm_object_text_set(obj,aligned); 273 free (aligned); 274 } else 275 elm_object_text_set(obj,txt); 276 free (txt); 277 evas_object_size_hint_weight_set(obj, EVAS_HINT_EXPAND, 0); 278 evas_object_size_hint_align_set(obj, EVAS_HINT_FILL, 0); 279 elm_table_pack(table, obj, 1, row, 5, 1); 280 evas_object_show(obj); 281 row++; 282 } 283 if (!confirm_mode && (pinentry->error || pinentry->repeat_passphrase)) 284 { 285 /* Error Label */ 286 if (pinentry->error) 287 txt = pinentry_utf8_to_local (pinentry->lc_ctype, pinentry->error); 288 else 289 txt = ""; 290 obj = elm_label_add(table); 291 evas_object_color_set(obj, 255, 0, 0, 255); 292 elm_object_text_set(obj,txt); 293 elm_object_style_set(obj,"slide_bounce"); 294 elm_label_slide_duration_set(obj, 10); 295 elm_label_slide_mode_set(obj, ELM_LABEL_SLIDE_MODE_ALWAYS); 296 elm_label_slide_go(obj); 297 evas_object_size_hint_weight_set(obj, EVAS_HINT_EXPAND, 0); 298 evas_object_size_hint_align_set(obj, EVAS_HINT_FILL, 0); 299 elm_table_pack(table, obj, 1, row, 5, 1); 300 evas_object_show(obj); 301 if (pinentry->error) 302 free (txt); 303 row++; 304 } 305 306 qualitybar = NULL; 307 308 if (!confirm_mode) 309 { 310 311 if (pinentry->prompt) 312 { 313 /* Entry/Prompt Label */ 314 obj = elm_label_add(table); 315 txt = pinentry_utf8_to_local (pinentry->lc_ctype, pinentry->prompt); 316 elm_object_text_set(obj,txt); 317 free (txt); 318 evas_object_size_hint_weight_set(obj, 0, EVAS_HINT_EXPAND); 319 evas_object_size_hint_align_set(obj, 1, EVAS_HINT_FILL); 320 elm_table_pack(table, obj, 1, row, 1, 1); 321 evas_object_show(obj); 322 } 323 324 entry = elm_entry_add(table); 325 elm_entry_scrollable_set(entry, EINA_TRUE); 326 elm_scroller_policy_set(entry, 327 ELM_SCROLLER_POLICY_OFF, 328 ELM_SCROLLER_POLICY_OFF); 329 elm_entry_password_set(entry, EINA_TRUE); 330 elm_entry_single_line_set(entry, EINA_TRUE); 331 evas_object_size_hint_weight_set(entry, 0, 0); 332 evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, 0); 333 elm_table_pack(table, entry, 2, row, 4, 1); 334 evas_object_smart_callback_add(entry, 335 "changed", 336 changed_text_handler, 337 NULL); 338 evas_object_show(entry); 339 row++; 340 341 /* Check box */ 342 obj = elm_check_add(table); 343 evas_object_size_hint_align_set(obj, 1, EVAS_HINT_FILL); 344 elm_table_pack(table, obj, 1, row, 1, 1); 345 evas_object_smart_callback_add(obj, "changed", on_check, NULL); 346 evas_object_show(obj); 347 348 /* Check Label */ 349 check_label = elm_label_add(table); 350 on_check((void *)NULL, obj, (void *)NULL); 351 elm_table_pack(table, check_label, 2, row, 4, 1); 352 evas_object_show(check_label); 353 row++; 354 355 if (pinentry->quality_bar) 356 { 357 /* Quality Bar Label */ 358 obj = elm_label_add(table); 359 txt = pinentry_utf8_to_local (pinentry->lc_ctype, 360 pinentry->quality_bar); 361 elm_object_text_set(obj,txt); 362 free (txt); 363 evas_object_size_hint_weight_set(obj, 0, EVAS_HINT_EXPAND); 364 evas_object_size_hint_align_set(obj, 1, EVAS_HINT_FILL); 365 elm_table_pack(table, obj, 1, row, 1, 1); 366 evas_object_show(obj); 367 368 qualitybar = elm_progressbar_add(table); 369 evas_object_color_set(qualitybar, 255, 0, 0, 255); 370 evas_object_show(qualitybar); 371 if (pinentry->quality_bar_tt) 372 elm_object_tooltip_text_set (qualitybar, 373 pinentry->quality_bar_tt); 374 evas_object_size_hint_weight_set(qualitybar, EVAS_HINT_EXPAND, 0); 375 evas_object_size_hint_align_set(qualitybar, EVAS_HINT_FILL, 0); 376 elm_table_pack(table, qualitybar, 2, row, 4, 1); 377 row++; 378 } 379 380 if (pinentry->repeat_passphrase) 381 { 382 /* Repeat Label */ 383 obj = elm_label_add(table); 384 txt = pinentry_utf8_to_local (pinentry->lc_ctype, 385 pinentry->repeat_passphrase); 386 elm_object_text_set(obj,txt); 387 free (txt); 388 evas_object_size_hint_weight_set(obj, 0, EVAS_HINT_EXPAND); 389 evas_object_size_hint_align_set(obj, 1, EVAS_HINT_FILL); 390 elm_table_pack(table, obj, 1, row, 1, 1); 391 evas_object_show(obj); 392 393 repeat_entry = elm_entry_add(table); 394 elm_entry_scrollable_set(repeat_entry, EINA_TRUE); 395 elm_scroller_policy_set(repeat_entry, 396 ELM_SCROLLER_POLICY_OFF, 397 ELM_SCROLLER_POLICY_OFF); 398 elm_entry_password_set(repeat_entry, EINA_TRUE); 399 elm_entry_single_line_set(repeat_entry, EINA_TRUE); 400 evas_object_size_hint_weight_set(repeat_entry, 0, 0); 401 evas_object_size_hint_align_set(repeat_entry, EVAS_HINT_FILL, 0); 402 elm_table_pack(table, repeat_entry, 2, row, 4, 1); 403 evas_object_smart_callback_add (repeat_entry, "activated", 404 enter_callback, NULL); 405 evas_object_show(repeat_entry); 406 evas_object_smart_callback_add (entry, 407 "activated", 408 enter_callback, 409 repeat_entry); 410 evas_object_smart_callback_add(repeat_entry, 411 "activated", 412 on_click, 413 (void *) CONFIRM_OK); 414 row++; 415 } 416 else 417 evas_object_smart_callback_add(entry, 418 "activated", 419 on_click, 420 (void *) CONFIRM_OK); 421 } 422 423 /* Cancel Button */ 424 if (!pinentry->one_button) 425 { 426 obj = elm_button_add(table); 427 icon = elm_icon_add (table); 428 evas_object_size_hint_aspect_set (icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1); 429 if (elm_icon_standard_set (icon, "dialog-cancel") || 430 elm_icon_standard_set (icon, "window-close")) 431 { 432 evas_object_size_hint_min_set(icon, 433 ELM_SCALE_SIZE(BUTTON_ICON_SIZE), 434 ELM_SCALE_SIZE(BUTTON_ICON_SIZE)); 435 elm_object_part_content_set(obj, "icon", icon); 436 evas_object_show (icon); 437 } 438 else 439 evas_object_del(icon); 440 if (pinentry->cancel || pinentry->default_cancel) 441 { 442 if(pinentry->cancel) 443 txt = pinentry_utf8_to_local (pinentry->lc_ctype, pinentry->cancel); 444 else 445 txt = pinentry_utf8_to_local (pinentry->lc_ctype, 446 pinentry->default_cancel); 447 if(txt[0]=='_') 448 elm_object_text_set(obj,txt+1); 449 else 450 elm_object_text_set(obj,txt); 451 btn_txt_len = ELM_SCALE_SIZE(strlen(txt) * (PADDING * 1.5)); 452 free (txt); 453 } 454 else 455 elm_object_text_set(obj, "Cancel"); //STOCK_CANCEL 456 evas_object_size_hint_align_set(obj, 0, 0); 457 if(btn_txt_len>ELM_SCALE_SIZE(BUTTON_WIDTH)) 458 evas_object_size_hint_min_set(obj, 459 btn_txt_len, 460 ELM_SCALE_SIZE(BUTTON_HEIGHT)); 461 else 462 evas_object_size_hint_min_set(obj, 463 ELM_SCALE_SIZE(BUTTON_WIDTH), 464 ELM_SCALE_SIZE(BUTTON_HEIGHT)); 465 elm_table_pack(table, obj, 4, row, 1, 1); 466 evas_object_smart_callback_add(obj, 467 "clicked", 468 on_click, 469 (void *) CONFIRM_CANCEL); 470 evas_object_show(obj); 471 } 472 473 /* OK Button */ 474 obj = elm_button_add(table); 475 icon = elm_icon_add (table); 476 evas_object_size_hint_aspect_set (icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1); 477 if (elm_icon_standard_set (icon, "dialog-ok") || 478 elm_icon_standard_set (icon, "list-add")) 479 { 480 evas_object_size_hint_min_set(icon, 481 ELM_SCALE_SIZE(BUTTON_ICON_SIZE), 482 ELM_SCALE_SIZE(BUTTON_ICON_SIZE)); 483 elm_object_part_content_set(obj, "icon", icon); 484 evas_object_show (icon); 485 } 486 else 487 evas_object_del(icon); 488 if (pinentry->ok || pinentry->default_ok) 489 { 490 if(pinentry->ok) 491 txt = pinentry_utf8_to_local (pinentry->lc_ctype, pinentry->ok); 492 else 493 txt = pinentry_utf8_to_local (pinentry->lc_ctype, pinentry->default_ok); 494 if(txt[0]=='_') 495 elm_object_text_set(obj,txt+1); 496 else 497 elm_object_text_set(obj,txt); 498 ok_len = ELM_SCALE_SIZE(strlen(txt) * (PADDING * 1.5)); 499 if(ok_len>btn_txt_len) 500 btn_txt_len = ok_len; 501 free (txt); 502 } 503 else 504 elm_object_text_set(obj,"OK"); //STOCK_OK 505 evas_object_size_hint_align_set(obj, 0, 0); 506 if(btn_txt_len>ELM_SCALE_SIZE(BUTTON_WIDTH)) 507 evas_object_size_hint_min_set(obj, 508 btn_txt_len, 509 ELM_SCALE_SIZE(BUTTON_HEIGHT)); 510 else 511 evas_object_size_hint_min_set(obj, 512 ELM_SCALE_SIZE(BUTTON_WIDTH), 513 ELM_SCALE_SIZE(BUTTON_HEIGHT)); 514 elm_table_pack(table, obj, 5, row, 1, 1); 515 evas_object_smart_callback_add(obj, "clicked", on_click, (void *) CONFIRM_OK); 516 evas_object_show(obj); 517 518 /* Key/Lock Icon */ 519 obj = elm_icon_add (win); 520 evas_object_size_hint_aspect_set (obj, EVAS_ASPECT_CONTROL_BOTH, 1, 1); 521 if (elm_icon_standard_set (obj, "dialog-password")) 522 { 523 double ic_size = WIDTH/5; 524 if(row==0) 525 ic_size = ic_size/3.5; 526 else if(row<4) 527 ic_size = ic_size - ic_size/row; 528 evas_object_size_hint_min_set(obj, 529 ELM_SCALE_SIZE(ic_size), 530 ELM_SCALE_SIZE(ic_size)); 531 evas_object_size_hint_weight_set(obj, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); 532 evas_object_size_hint_align_set(obj, EVAS_HINT_FILL, 0.5); 533 elm_table_pack(table, obj, 0, 0, 1, row? row:1); 534 evas_object_show (obj); 535 } 536 else 537 evas_object_del(obj); 538 539 /* Box for padding */ 540 obj = elm_box_add (win); 541 elm_box_pack_end (obj, table); 542 evas_object_show (obj); 543 544 elm_win_resize_object_add(win,obj); 545 evas_object_show(win); 546 547 if(entry) 548 elm_object_focus_set (entry, EINA_TRUE); 549 550 if (pinentry->timeout > 0) 551 timer = ecore_timer_add (pinentry->timeout, 552 (Ecore_Task_Cb)timeout_cb, 553 pinentry); 554 } 555 556 static int 557 efl_cmd_handler (pinentry_t pe) 558 { 559 int want_pass = !!pe->pin; 560 561 got_input = EINA_FALSE; 562 pinentry = pe; 563 confirm_value = CONFIRM_CANCEL; 564 passphrase_ok = 0; 565 confirm_mode = want_pass ? 0 : 1; 566 /* init ecore-x explicitly using DISPLAY since this can launch 567 * from console 568 */ 569 if (pe->display) 570 ecore_x_init (pe->display); 571 elm_init (pargc, pargv); 572 create_window (); 573 ecore_main_loop_begin (); 574 575 if (timer) 576 { 577 ecore_timer_del (timer); 578 timer = NULL; 579 } 580 581 if (confirm_value == CONFIRM_CANCEL || grab_failed) 582 pe->canceled = 1; 583 584 pinentry = NULL; 585 if (want_pass) 586 { 587 if (passphrase_ok && pe->pin) 588 return strlen (pe->pin); 589 else 590 return -1; 591 } 592 else 593 return (confirm_value == CONFIRM_OK) ? 1 : 0; 594 } 595 596 int 597 main (int argc, char *argv[]) 598 { 599 pinentry_init (PGMNAME); 600 601 #ifdef FALLBACK_CURSES 602 if (pinentry_have_display (argc, argv)) 603 { 604 #endif 605 606 pinentry_cmd_handler = efl_cmd_handler; 607 pargc = argc; 608 pargv = argv; 609 610 #ifdef FALLBACK_CURSES 611 } 612 else 613 { 614 pinentry_cmd_handler = curses_cmd_handler; 615 } 616 #endif 617 618 pinentry_parse_opts (argc, argv); 619 if (pinentry_loop ()) 620 return 1; 621 622 return 0; 623 } 624