fltk: Add a FLTK-based pinentry.
authorAnatoly madRat L. Berenblit <madrat-@users.noreply.github.com>
Tue, 7 Feb 2017 16:18:41 +0000 (17:18 +0100)
committerJustus Winter <justus@g10code.com>
Wed, 8 Mar 2017 16:11:44 +0000 (17:11 +0100)
* NEWS: Update.
* Makefile.am: Add new subdirectory.
* configure.ac: Add configuration for FLTK.
* fltk/Makefile.am: New file.
* fltk/encrypt.xpm: Likewise.
* fltk/icon.xpm: Likewise.
* fltk/main.cxx: Likewise.
* fltk/passwindow.cxx: Likewise.
* fltk/passwindow.h: Likewise.
* fltk/pinwindow.cxx: Likewise.
* fltk/pinwindow.h: Likewise.
* fltk/qualitypasswindow.cxx: Likewise.
* fltk/qualitypasswindow.h: Likewise.

Signed-off-by: Justus Winter <justus@gnupg.org>
13 files changed:
Makefile.am
NEWS
configure.ac
fltk/Makefile.am [new file with mode: 0644]
fltk/encrypt.xpm [new file with mode: 0644]
fltk/icon.xpm [new file with mode: 0644]
fltk/main.cxx [new file with mode: 0644]
fltk/passwindow.cxx [new file with mode: 0644]
fltk/passwindow.h [new file with mode: 0644]
fltk/pinwindow.cxx [new file with mode: 0644]
fltk/pinwindow.h [new file with mode: 0644]
fltk/qualitypasswindow.cxx [new file with mode: 0644]
fltk/qualitypasswindow.h [new file with mode: 0644]

index ef80f6c..1f62939 100644 (file)
@@ -70,9 +70,15 @@ else
 pinentry_w32 =
 endif
 
+if BUILD_PINENTRY_FLTK
+pinentry_fltk = fltk
+else
+pinentry_fltk =
+endif
+
 SUBDIRS = m4 secmem pinentry ${pinentry_curses} ${pinentry_tty} \
        ${pinentry_emacs} ${pinentry_gtk_2} ${pinentry_gnome_3} \
-       ${pinentry_qt} ${pinentry_w32} doc
+       ${pinentry_qt} ${pinentry_w32} ${pinentry_fltk} doc
 
 
 install-exec-local:
diff --git a/NEWS b/NEWS
index 16988fa..107a65d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
 Noteworthy changes in version 1.0.1 (unreleased)
 ------------------------------------------------
 
+ * A FLTK1.3-based pinentry has been contributed.
 
 Noteworthy changes in version 1.0.0 (2016-11-22)
 ------------------------------------------------
index 5119e4a..8b38149 100644 (file)
@@ -588,6 +588,42 @@ pinentry_w32=no
 test $have_w32_system = yes && pinentry_w32=yes
 AM_CONDITIONAL(BUILD_PINENTRY_W32, test "$pinentry_w32" = "yes")
 
+dnl
+dnl Check for FLTK pinentry program.
+dnl
+AC_ARG_ENABLE(pinentry-fltk,
+    AC_HELP_STRING([--enable-pinentry-fltk], [build FLTK 1.3 pinentry]),
+           pinentry_fltk=$enableval, pinentry_fltk=maybe)
+
+dnl check for fltk-config
+if test "$pinentry_fltk" != "no"; then
+   AC_PATH_PROG(FLTK_CONFIG, fltk-config, no)
+   if test x"${FLTK_CONFIG}" = xno ; then
+       AC_MSG_WARN([fltk-config is not found])
+       pinentry_fltk=no
+   fi
+fi
+
+dnl check for FLTK libraries and set flags
+if test "$pinentry_fltk" != "no"; then
+   AC_MSG_CHECKING([for FLTK 1.3])
+   FLTK_VERSION=`${FLTK_CONFIG} --api-version`
+   if test ${FLTK_VERSION} != "1.3" ; then
+       AC_MSG_RESULT([no])
+       AC_MSG_WARN([FLTK 1.3 not found (available $FLTK_VERSION)])
+       pinentry_fltk=no
+   else
+       AC_MSG_RESULT([yes])
+       FLTKCFLAGS=`${FLTK_CONFIG} --cflags`
+       FLTKCXXFLAGS=`${FLTK_CONFIG} --cxxflags`
+       FLTKLIBS=`${FLTK_CONFIG} --ldflags`
+       AC_SUBST(FLTKCFLAGS)
+       AC_SUBST(FLTKCXXFLAGS)
+       AC_SUBST(FLTKLIBS)
+       pinentry_fltk=yes
+   fi
+fi
+AM_CONDITIONAL(BUILD_PINENTRY_FLTK, test "$pinentry_fltk" = "yes")
 
 # Figure out the default pinentry.  We are very conservative here.
 # Please change the order only after verifying that the preferred
@@ -611,7 +647,11 @@ else
           if test "$pinentry_w32" = "yes"; then
             PINENTRY_DEFAULT=pinentry-w32
           else
-            AC_MSG_ERROR([[No pinentry enabled.]])
+            if test "$pinentry_fltk" = "yes"; then
+              PINENTRY_DEFAULT=pinentry-fltk
+            else
+              AC_MSG_ERROR([[No pinentry enabled.]])
+            fi
           fi
         fi
       fi
@@ -690,6 +730,7 @@ gtk+-2/Makefile
 gnome3/Makefile
 qt/Makefile
 w32/Makefile
+fltk/Makefile
 doc/Makefile
 Makefile
 ])
@@ -710,6 +751,7 @@ AC_MSG_NOTICE([
        GNOME 3 Pinentry .: $pinentry_gnome_3
        Qt Pinentry ......: $pinentry_qt $pinentry_qt_lib_version
        W32 Pinentry .....: $pinentry_w32
+       FLTK Pinentry ....: $pinentry_fltk
 
        Fallback to Curses: $fallback_curses
        Emacs integration : $inside_emacs
diff --git a/fltk/Makefile.am b/fltk/Makefile.am
new file mode 100644 (file)
index 0000000..2c9338f
--- /dev/null
@@ -0,0 +1,16 @@
+# Makefile.am - PIN entry FLTK frontend.
+
+bin_PROGRAMS = pinentry-fltk
+
+if FALLBACK_CURSES
+ncurses_include = $(NCURSES_INCLUDE)
+libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV)
+else
+ncurses_include =
+libcurses =
+endif
+
+AM_CPPFLAGS = $(COMMON_CFLAGS) $(FLTKCXXFLAGS) $(ncurses_include) -I$(top_srcdir)/secmem -I$(top_srcdir)/pinentry
+LDADD = ../pinentry/libpinentry.a ../secmem/libsecmem.a $(COMMON_LIBS) $(LIBCAP) $(FLTKLIBS) $(libcurses)
+
+pinentry_fltk_SOURCES = main.cxx pinwindow.cxx passwindow.cxx qualitypasswindow.cxx
diff --git a/fltk/encrypt.xpm b/fltk/encrypt.xpm
new file mode 100644 (file)
index 0000000..80402e3
--- /dev/null
@@ -0,0 +1,83 @@
+/* XPM */
+static const char * const encrypt_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 45 32 1 ",
+"  c #9B7738",
+". c #B08830",
+"X c #CA9703",
+"o c #C99607",
+"O c #CD9A02",
+"+ c #D19D03",
+"@ c #D4A002",
+"# c #D7A305",
+"$ c #D8A501",
+"% c #DFAB01",
+"& c #DCA803",
+"* c #E3B103",
+"= c #E7B702",
+"- c #C39A31",
+"; c #D4AC2F",
+": c #E6BE26",
+"> c #EFC002",
+", c #F5CD06",
+"< c #FEE21D",
+"1 c #FCE01F",
+"2 c #F6D424",
+"3 c #B49A55",
+"4 c #CAAC5F",
+"5 c #FCE854",
+"6 c #888887",
+"7 c #9A9A9A",
+"8 c #A7A7A7",
+"9 c #B8B8B7",
+"0 c #F3F08F",
+"q c #C3C3C3",
+"w c gray85",
+"e c None",
+/* pixels */
+"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+"eeeeeeeeeee889qqqq987eeeeeeeeeee",
+"eeeeeeeee89qwwwqqwwww88eeeeeeeee",
+"eeeeeeee99qq877777669wq8eeeeeeee",
+"eeeeeee99q888888888876ww9eeeeeee",
+"eeeeee99q888888887778869q9eeeeee",
+"eeeee89q8888eeeeeeee7786wq8eeeee",
+"eeeee999888eeeeeeeeee7777wqeeeee",
+"eeee899778eeeeeeeeeeee776wq8eeee",
+"eeee8q977eeeeeeeeeeeeee779q9eeee",
+"eeee8q977eeeeeeeeeeeeee777w9eeee",
+"eeee8q867eeeeeeeeeeeeee776wqeeee",
+"eeee8q768eeeeeeeeeeeeee766wqeeee",
+"eeee8q768eeeeeeeeeeeeee766wqeeee",
+"eeee8q778eeeeeeeeeeeeee776wqeeee",
+"eeee8q778eeeeeeeeeeeeee776wqeeee",
+"eeee8q888eeeeeeeeeeeeee786wqeeee",
+"eeee8q888eeeeeeeeeeeeee786wqeeee",
+"eeee8q898eeeeeeeeeeeeee796wqeeee",
+"eeee8q898eeeeeeeeeeeeee896wqeeee",
+"e45444443333         3333444003e",
+"e4552::;;--...    ...--;;:25003e",
+"e4552,>>=&@OXXooXO+@$&*=>,15003e",
+"e4552,>>*&#+ooooo++##&*=>,15003e",
+"e4552,>>*&#+XXXXXO+@#&*=>,<5003e",
+"e4552,>>=%$+OXXOO++##&*=>,15003e",
+"e4552,>>=%$OooXXO+@@#&%=,,<5003e",
+"e4552,>>=%$@OXXO+@@#&&*=>,15003e",
+"e4552,>>*&$+XXXXOO++@#%=>,<5003e",
+"e4552,>>=&$+OXXO++@@$&*=,,<5003e",
+"e4552,>>=%$+XXXXOO+@#&*=>,<5003e",
+"e4552,>>*&#+ooXO++@##&*>,,<5003e",
+"e4552,>>=%$@OXXO++@$$%*>,,<5003e",
+"e4552,>>=%$+OXXO++@#$&*=,,<5003e",
+"e4552,>>=%$@OOOO++@$&%*=>,<5003e",
+"e4552,>>=%$+OXXOO+@#&%=>,,<5003e",
+"e4552,>>=%$+OXXOO+@##%*=>,<5003e",
+"e4552,>>*&#+oooO++@#&&*>,,<5003e",
+"e4552,>>=%$@OOOO+@$&%%*>,,<5003e",
+"e4552,>>=%$@OOXO+@$&%%*>,,<5003e",
+"e4552,>>=%$+OXXO+@$&&%*>,,<5003e",
+"e4552,>>=%$@OOO+@@$&%%=>,,<5003e",
+"e4552,>>=%$@OOO+@@$&%%=>,,<5003e",
+"e4552,>>=%$@OOO+@@$&%%=>,,<5003e",
+"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
+};
diff --git a/fltk/icon.xpm b/fltk/icon.xpm
new file mode 100644 (file)
index 0000000..eed3cd6
--- /dev/null
@@ -0,0 +1,37 @@
+/* XPM */
+static const char *const icon_xpm[] = {
+"31 29 3 1 ",
+"  c black",
+". c yellow",
+"X c None",
+
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"             XXXXXXXXXXXXXXXXXX",
+" ........... XXXXXXXXXXXXXXXXXX",
+" ........... XXXXXXXXXXXXXXXXXX",
+" ...     ... XXXXXXXXXXXXXXXXXX",
+" ... XXX ...                   ",
+" ... XXX ... ................. ",
+" ... XXX ... ................. ",
+" ... XXX ...         ...   ... ",
+" ...     ... XXXXXXX ... X ... ",
+" ........... XXXXXXX ... X ... ",
+" ........... XXXXXXX     X ... ",
+"             XXXXXXXXXXXXX     ",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+};
diff --git a/fltk/main.cxx b/fltk/main.cxx
new file mode 100644 (file)
index 0000000..7526656
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+   main.cpp - A Fltk based dialog for PIN entry.
+
+   Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+   Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define PGMNAME (PACKAGE_NAME"-fltk")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <assert.h>
+
+#include "memory.h"
+#include <memory>
+
+#include <pinentry.h>
+#ifdef FALLBACK_CURSES
+#include <pinentry-curses.h>
+#endif
+
+#include <string>
+#include <string.h>
+#include <stdexcept>
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/fl_ask.H>
+
+#include "pinwindow.h"
+#include "passwindow.h"
+#include "qualitypasswindow.h"
+
+#define CONFIRM_STRING "Confirm"
+#define REPEAT_ERROR_STRING "Texts do not match"
+#define OK_STRING "OK"
+#define CANCEL_STRING "Cancel"
+
+char *application = NULL;
+
+static std::string escape_accel_utf8(const char *s)
+{
+       std::string result;
+       if (NULL != s)
+       {
+               result.reserve(strlen(s));
+               for (const char *p = s; *p; ++p)
+               {
+                       if ('&' == *p)
+                               result.push_back(*p);
+                       result.push_back(*p);
+               }
+       }
+       return result;
+}
+
+class cancel_exception
+{
+
+};
+
+static int get_quality(const char *passwd, void *ptr)
+{
+       if (NULL == passwd || 0 == *passwd)
+               return 0;
+
+       pinentry_t* pe = reinterpret_cast<pinentry_t*>(ptr);
+       return pinentry_inq_quality(*pe, passwd, strlen(passwd));
+}
+
+bool is_short(const char *str)
+{
+       return fl_utf_nb_char(reinterpret_cast<const unsigned char*>(str), strlen(str)) < 16;
+}
+
+bool is_empty(const char *str)
+{
+       return (NULL == str) || (0 == *str);
+}
+
+static int fltk_cmd_handler(pinentry_t pe)
+{
+       int ret = -1;
+
+       try
+       {
+               // TODO: Add parent window to pinentry-fltk window
+               //if (pe->parent_wid){}
+               std::string title  = !is_empty(pe->title)?pe->title:PGMNAME;
+               std::string ok     = escape_accel_utf8(pe->ok?pe->ok:(pe->default_ok?pe->default_ok:OK_STRING));
+               std::string cancel = escape_accel_utf8(pe->cancel?pe->cancel:(pe->default_cancel?pe->default_cancel:CANCEL_STRING));
+
+               if (!!pe->pin) // password (or confirmation)
+               {
+                       std::auto_ptr<PinWindow> window;
+
+                       bool isSimple = (NULL == pe->quality_bar) &&    // pinenty.h: If this is not NULL ...
+                                                       is_empty(pe->error) && is_empty(pe->description) &&
+                                                       is_short(pe->prompt);
+                       if (isSimple)
+                       {
+                               assert(NULL == pe->description);
+                               window.reset(PinWindow::create());
+                               window->prompt(pe->prompt);
+                       }
+                       else
+                       {
+                               PassWindow *pass = NULL;
+
+                               if (pe->quality_bar) // pinenty.h: If this is not NULL ...
+                               {
+                                       QualityPassWindow *p = QualityPassWindow::create(get_quality, &pe);
+                                       window.reset(p);
+                                       pass = p;
+                                       p->quality(pe->quality_bar);
+                               }
+                               else
+                               {
+                                       pass = PassWindow::create();
+                                       window.reset(pass);
+                               }
+
+                               if (NULL == pe->description)
+                               {
+                                       pass->description(pe->prompt);
+                                       pass->prompt(" ");
+                               }
+                               else
+                               {
+                                       pass->description(pe->description);
+                                       pass->prompt(escape_accel_utf8(pe->prompt).c_str());
+                               }
+                               pass->description(pe->description);
+                               pass->prompt(escape_accel_utf8(pe->prompt).c_str());
+
+
+                               if (NULL != pe->error)
+                                       pass->error(pe->error);
+                       }
+
+                       window->ok(ok.c_str());
+                       window->cancel(cancel.c_str());
+                       window->title(title.c_str());
+                       window->showModal((NULL != application)?1:0, &application);
+
+                       if (NULL == window->passwd())
+                               throw cancel_exception();
+
+                       const std::string password = window->passwd();
+                       window.reset();
+
+                       if (pe->repeat_passphrase)
+                       {
+                               const char *dont_match = NULL;
+                               do
+                               {
+                                       if (NULL == dont_match && is_short(pe->repeat_passphrase))
+                                       {
+                                               window.reset(PinWindow::create());
+                                               window->prompt(escape_accel_utf8(pe->repeat_passphrase).c_str());
+                                       }
+                                       else
+                                       {
+                                               PassWindow *pass = PassWindow::create();
+                                               window.reset(pass);
+                                               pass->description(pe->repeat_passphrase);
+                                               pass->prompt(" ");
+                                               pass->error(dont_match);
+                                       }
+
+                                       window->ok(ok.c_str());
+                                       window->cancel(cancel.c_str());
+                                       window->title(title.c_str());
+                                       window->showModal();
+
+                                       if (NULL == window->passwd())
+                                               throw cancel_exception();
+
+                                       if (password == window->passwd())
+                                       {
+                                               pe->repeat_okay = 1;
+                                               ret = 1;
+                                               break;
+                                       }
+                                       else
+                                       {
+                                                       dont_match = (NULL!=pe->repeat_error_string)? pe->repeat_error_string:REPEAT_ERROR_STRING;
+                                               }
+                                       } while (true);
+                               }
+                               else
+                                       ret = 1;
+
+                               pinentry_setbufferlen(pe, password.size()+1);
+                               if (pe->pin)
+                               {
+                                       memcpy(pe->pin, password.c_str(), password.size()+1);
+                                       pe->result = password.size();
+                                       ret = password.size();
+                               }
+                       }
+                       else
+                       {
+                               // Confirmation or Message Dialog title, desc
+                               Fl_Window dummy(0,0, 1,1);
+
+                               dummy.border(0);
+                               dummy.show((NULL != application)?1:0, &application);
+                               dummy.hide();
+
+                               fl_message_title(title.c_str());
+
+                               int result = -1;
+
+                               const char *message = (NULL != pe->description)?pe->description:CONFIRM_STRING;
+
+                               if (pe->one_button)
+                               {
+                                       fl_ok = ok.c_str();
+                                       fl_message(message);
+                                       result = 1; // OK
+                               }
+                               else if (pe->notok)
+                               {
+                                       switch (fl_choice(message, ok.c_str(), cancel.c_str(), pe->notok))
+                                       {
+                                       case 0: result = 1; break;
+                                       case 2: result = 0; break;
+                                       default:
+                                       case 1: result = -1;break;
+                                       }
+                               }
+                               else
+                               {
+                                       switch (fl_choice(message, ok.c_str(), cancel.c_str(), NULL))
+                                       {
+                                       case 0: result = 1; break;
+                                       default:
+                                       case 1: result = -1;break;
+                                       }
+                               }
+
+                               // cancel ->         pe->canceled = true, 0
+                               // ok/y -> 1
+                               // no -> 0
+                               if (-1 == result)
+                                       pe->canceled = true;
+                               ret = (1 == result);
+                       }
+                       Fl::check();
+               }
+               catch (const cancel_exception&)
+               {
+                       ret = -1;
+               }
+               catch (...)
+               {
+                       ret = -1;
+               }
+                       // do_touch_file(pe); only for NCURSES?
+               return ret;
+       }
+
+pinentry_cmd_handler_t pinentry_cmd_handler = fltk_cmd_handler;
+
+int main(int argc, char *argv[])
+{
+       application = *argv;
+       pinentry_init(PGMNAME);
+
+#ifdef FALLBACK_CURSES
+       if (!pinentry_have_display(argc, argv))
+               pinentry_cmd_handler = curses_cmd_handler;
+       else
+#endif
+       {
+               //FLTK understood only -D (--display)
+               // and should be converted into -di[splay]
+               const static struct option long_options[] =
+               {
+                       {"display", required_argument,  0, 'D' },
+                       {NULL,          no_argument,            0, 0   }
+               };
+
+               for (int i = 0; i < argc-1; ++i)
+               {
+                       switch (getopt_long(argc-i, argv+i, "D:", long_options, NULL))
+                       {
+                               case 'D':
+                                       {
+                                               char* emul[] = {application, (char*)"-display", optarg};
+                                               Fl::args(3, emul);
+                                               i = argc;
+                                               break;
+                                       }
+                               default:
+                                       break;
+                       }
+               }
+       }
+
+       pinentry_parse_opts(argc, argv);
+       return pinentry_loop() ?EXIT_FAILURE:EXIT_SUCCESS;
+}
+
+/*
+int get_quality(const char *pass)
+{
+       size_t len = strlen(pass);
+       return len>4?(80+len):-len*10;
+}
+
+int main(int argc, char *argv[])
+{
+       std::auto_ptr<PassWindow> window;
+       window.reset(QualityPassWindow::create(get_quality));
+
+//     window->message("Lorem ipsum dolor sit amet");
+//     window->message("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus porttitor nisi a fringilla porttitor. Phasellus tempor orci vel metus eleifend ultrices. Curabitur tempor euismod lorem");
+       window->prompt("Lorem ipsum:");
+       window->ok("YES!");
+       window->cancel("OTMEHA");
+//     window->error("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus porttitor nisi a fringilla");
+//     window->error("Some error  ellus adipiscing elit portt text dolor sit amet, consectetur adipiscing elit. Phasellus porttitor a porttitor!");
+       window->timeout(100);
+       window->title(PGMNAME);
+       window->showModal(argc, argv);
+       return 0;
+}
+// */
+
+/*
+int main(int argc, char *argv[])
+{
+       Fl::args(argc, argv);
+       std::auto_ptr<PinWindow> window;
+       window.reset(PinWindow::create());
+//     window->message("PIN:");
+//     window->message("Phasellus adipiscing elit porttitor nisi a fringilla porttitor:");
+       window->ok("YES!");
+       window->cancel("OTMEHA");
+       window->timeout(100);
+       window->title(PGMNAME);
+       window->showModal(argc, argv);
+       return 0;
+}
+// */
+
+/*
+int main(int argc, char *argv[])
+{
+       Fl::args(argc, argv);
+       std::auto_ptr<PassWindow> window;
+       window.reset(PassWindow::create());
+//     window->message("Descr&iption");
+//     window->message("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus porttitor nisi a fringilla porttitor:");
+//     window->prompt("Prompt sit amet:");
+       window->ok("YES!");
+       window->cancel("OTMEHA");
+//     window->error("Password is empty.");
+//     window->error("Some error text dolor sit amet, consectetur adipiscing elit. Phasellus porttitor nisi a fringilla porttitor!");
+//     window->error("Some error  ellus adipiscing elit portt text dolor sit amet, consectetur adipiscing elit. Phasellus porttitor nisi a fringilla porttitor!");
+       window->timeout(100);
+       window->title(PGMNAME);
+       window->showModal(argc, argv);
+       return 0;
+}
+// */
diff --git a/fltk/passwindow.cxx b/fltk/passwindow.cxx
new file mode 100644 (file)
index 0000000..78b3b2c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    passwindow.cxx - PassWindow is a more complex fltk dialog with more longer
+    desc field and possibility to show some error text.
+    if needed qualitybar - should be used QualityPassWindow.
+
+    Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+    Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "passwindow.h"
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Input.H>
+
+const char *PassWindow::DESCRIPTION  = "Please enter the passphrase:";
+
+PassWindow::PassWindow() : error_(NULL)
+{
+}
+
+void PassWindow::prompt(const char *name)
+{
+       set_label(input_, name, PROMPT);
+}
+
+void PassWindow::description(const char *name)
+{
+       set_label(message_, name, DESCRIPTION);
+}
+
+void PassWindow::error(const char *name)
+{
+       set_label(error_, name, "");
+}
+
+int PassWindow::init(const int cx, const int cy)
+{
+       int y = PinWindow::init(cx, cy);
+
+       assert(window_ == Fl_Group::current()); // make_window should all add current
+
+       y = icon_->y(); // move back to icon's
+
+       const int mx = icon_->x()+icon_->w();
+       message_->resize(mx, icon_->y(), cx-mx-10, icon_->h());
+       message_->align(Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP | FL_ALIGN_WRAP | FL_ALIGN_INSIDE));
+       description(NULL);
+       y += icon_->h();
+
+       input_->resize(130, y+5, cx-150, 25);
+       input_->labeltype(FL_NORMAL_LABEL);
+       prompt(NULL);
+       y = input_->y()+input_->h();
+
+       error_ = new Fl_Box(20, y+5, cx-30, 30);
+       error_->labelcolor(FL_RED);
+       error_->align(Fl_Align(FL_ALIGN_CENTER | FL_ALIGN_WRAP | FL_ALIGN_INSIDE)); // if not fit - user can read
+       y = error_->y()+error_->h();
+       return y;
+}
+
+PassWindow* PassWindow::create()
+{
+       PassWindow* p = new PassWindow;
+        p->init(460, 185);
+        p->window_->end();
+        p->input_->take_focus();
+        return p;
+}
diff --git a/fltk/passwindow.h b/fltk/passwindow.h
new file mode 100644 (file)
index 0000000..43813cd
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    passwindow.h - PassWindow is a more complex fltk dialog with more longer
+    desc field and possibility to show some error text.
+    if needed qualitybar - should be used QualityPassWindow.
+
+    Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+    Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __PASSWINDOW_H__
+#define __PASSWINDOW_H__
+
+#include "pinwindow.h"
+
+class PassWindow : public PinWindow
+{
+protected:
+       static const char *DESCRIPTION;
+
+protected:
+       Fl_Box      *error_;
+       PassWindow();
+
+public:
+       virtual void prompt(const char *message);
+       virtual void description(const char *desc);
+       virtual void error(const char *err);
+
+       static PassWindow* create();
+
+protected:
+       virtual int init(const int cx, const int cy);
+};
+
+#endif //#ifndef __PASSWINDOW_H__
diff --git a/fltk/pinwindow.cxx b/fltk/pinwindow.cxx
new file mode 100644 (file)
index 0000000..ad11e91
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+    pinwindow.cxx - PinWindow is a simple fltk dialog for entring password
+    with timeout. if needed description (long text), error message, qualitybar
+    and etc should used PassWindow.
+
+    Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+    Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Secret_Input.H>
+#include <FL/Fl_Return_Button.H>
+#include <FL/Fl_Pixmap.H>
+
+#include "memory.h"
+
+#include "encrypt.xpm"
+#include "icon.xpm"
+
+#include "pinwindow.h"
+
+const char *PinWindow::TITLE           = "Password";
+const char *PinWindow::BUTTON_OK       = "OK";
+const char *PinWindow::BUTTON_CANCEL = "Cancel";
+const char *PinWindow::PROMPT          = "Passphrase:";
+
+static const char *timeout_format = "%s(%d)";
+
+static Fl_Pixmap encrypt(encrypt_xpm);
+static Fl_Pixmap icon(icon_xpm);
+
+PinWindow::PinWindow() : window_(NULL)
+                               ,message_(NULL) ,input_(NULL) ,ok_(NULL) ,cancel_(NULL)
+                               ,cancel_name_(BUTTON_CANCEL)
+                               ,passwd_(NULL) ,timeout_(0)
+{
+}
+
+PinWindow::~PinWindow()
+{
+       wipe();
+       release();
+       delete window_;
+}
+
+void PinWindow::release()
+{
+       if (NULL != passwd_)
+       {
+               memset(passwd_, 0, strlen(passwd_));
+               secmem_free(passwd_);
+       }
+       passwd_ = NULL;
+}
+
+void PinWindow::title(const char *name)
+{
+       set_label(window_, name, TITLE);
+}
+
+void PinWindow::ok(const char* name)
+{
+       set_label(ok_, name, BUTTON_OK);
+}
+
+void PinWindow::cancel(const char* label)
+{
+       if (NULL != label && 0 != *label)
+               cancel_name_ = label;
+       else
+               cancel_name_ = BUTTON_CANCEL;
+
+       update_cancel_label();
+}
+
+void PinWindow::prompt(const char *name)
+{
+       set_label(message_, name, PROMPT);
+}
+
+void PinWindow::timeout(unsigned int time)
+{
+       if (timeout_ == time)
+               return;
+
+       // A xor B ~ A != B
+       if ( (time>0) != (timeout_>0))
+       {
+               //enable or disable
+               if (time>0)
+                       Fl::add_timeout(1.0, timeout_cb, this);
+               else
+                       Fl::remove_timeout(timeout_cb, this);
+       }
+
+       timeout_=time;
+       update_cancel_label();
+       --timeout_;
+}
+
+void PinWindow::showModal()
+{
+       if (NULL != window_)
+       {
+               window_->show();
+               Fl::run();
+       }
+       Fl::check();
+}
+
+void PinWindow::showModal(const int argc, char* argv[])
+{
+       if (NULL != window_)
+       {
+               window_->show(argc, argv);
+               Fl::run();
+       }
+       Fl::check();
+}
+
+int PinWindow::init(const int cx, const int cy)
+{
+       assert(NULL == window_);
+       window_ = new Fl_Window(cx, cy, TITLE);
+
+       Fl_RGB_Image app(&icon);
+       window_->icon(&app);
+
+       icon_ = new Fl_Box(10, 10, 64, 64);
+       icon_->image(encrypt);
+
+    message_ = new Fl_Box(79, 5, cx-99, 44, PROMPT);
+       message_->align(Fl_Align(FL_ALIGN_LEFT_TOP | FL_ALIGN_WRAP | FL_ALIGN_INSIDE)); // left
+
+       input_ = new Fl_Secret_Input(79, 59, cx-99, 25);
+       input_->labeltype(FL_NO_LABEL);
+
+
+       const int button_y = cy-40;
+    ok_ = new Fl_Return_Button(cx-300, button_y, 120, 25, BUTTON_OK);
+       ok_->callback(ok_cb, this);
+
+       cancel_ = new Fl_Button(cx-160, button_y, 120, 25);
+       update_cancel_label();
+       cancel_->callback(cancel_cb, this);
+
+       window_->hotspot(input_);
+       window_->set_modal();
+
+       return 84;
+};
+
+void PinWindow::update_cancel_label()
+{
+       if (timeout_ == 0)
+       {
+               cancel_->label(cancel_name_.c_str());
+       }
+       else
+       {
+               const size_t len = cancel_name_.size()+strlen(timeout_format)+10+1;
+               char *buf = new char[len];
+               snprintf(buf, len, timeout_format, cancel_name_.c_str(), timeout_);
+               cancel_->copy_label(buf);
+               delete[] buf; // no way to attach label
+       }
+}
+
+void PinWindow::timeout_cb(void* val)
+{
+       PinWindow *self = reinterpret_cast<PinWindow*>(val);
+       if (self->timeout_ == 0)
+       {
+               cancel_cb(self->cancel_, self);
+       }
+       else
+       {
+               self->update_cancel_label();
+               --self->timeout_;
+               Fl::repeat_timeout(1.0, timeout_cb, val);
+       }
+}
+
+void PinWindow::cancel_cb(Fl_Widget *button, void *val)
+{
+       PinWindow *self = reinterpret_cast<PinWindow*>(val);
+
+       self->wipe();
+       self->release();
+       self->window_->hide();
+}
+
+void PinWindow::ok_cb(Fl_Widget *button, void *val)
+{
+       PinWindow *self = reinterpret_cast<PinWindow*>(val);
+
+       self->release();
+
+       const char *passwd = self->input_->value();
+       size_t len = strlen(passwd)+1;
+       self->passwd_ = reinterpret_cast<char*>(secmem_malloc(len));
+       if (NULL != self->passwd_)
+               memcpy(self->passwd_, passwd, len);
+
+       self->wipe();
+       self->window_->hide();
+}
+
+void PinWindow::wipe()
+{
+       int len = input_->size();
+       char* emul = new char[len+1];
+       for (int i=0; i<len; ++i)
+       {
+               emul[i] = 0x20 + rand()%(128-20); // [20..127]
+       }
+       emul[len] = 0;
+       input_->replace(0, len, emul, len);
+       delete[] emul;
+
+       input_->value(TITLE); // hide size too
+}
+
+PinWindow*  PinWindow::create()
+{
+       PinWindow* p = new  PinWindow;
+        p->init(410, 140);
+        p->window_->end();
+        p->input_->take_focus();
+        return p;
+}
diff --git a/fltk/pinwindow.h b/fltk/pinwindow.h
new file mode 100644 (file)
index 0000000..e1d009e
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+    pinwindow.h - PinWindow is a simple fltk dialog for entring password
+    with timeout. if needed description (long text), error message, qualitybar
+    and etc should used PassWindow.
+
+    Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+    Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __PINWINDOW_H__
+#define __PINWINDOW_H__
+
+#include "config.h"
+
+class Fl_Window;
+class Fl_Box;
+class Fl_Input;
+class Fl_Button;
+class Fl_Widget;
+
+#include <assert.h>
+#include <string>
+
+class PinWindow
+{
+protected:
+       static const char *TITLE;
+       static const char *BUTTON_OK;
+       static const char *BUTTON_CANCEL;
+       static const char *PROMPT;
+
+protected:
+       PinWindow(const PinWindow&);
+       PinWindow& operator=(const PinWindow&);
+
+       Fl_Window       *window_;
+       Fl_Box          *icon_;
+
+       Fl_Box          *message_;
+       Fl_Input        *input_;
+
+       Fl_Button       *ok_, *cancel_;
+
+       std::string cancel_name_;
+       char *passwd_;                  // SECURE_MEMORY
+       unsigned int timeout_;  // click cancel if timeout
+
+public:
+       virtual ~PinWindow();
+
+       static PinWindow* create();
+
+       inline const char*   passwd() const { return passwd_; }
+
+       virtual void timeout(unsigned int time);                                                // 0 - infinity, seconds
+       virtual void title(const char *title);
+       virtual void ok(const char* ok);
+       virtual void cancel(const char* cancel);
+       virtual void prompt(const char *message);
+
+       virtual void showModal();
+       virtual void showModal(const int argc, char* argv[]);
+
+protected:
+       PinWindow();
+
+       void wipe();            // clear UI memory
+       void release();         // clear secure memory
+       void update_cancel_label();
+
+       virtual int init(const int cx, const int cy);
+
+       //callbacks
+       static void cancel_cb(Fl_Widget *button, void *val);
+       static void ok_cb(Fl_Widget *button, void *val);
+       static void timeout_cb(void*);
+
+       // ISSUE: Fl_Window component in tinycore works only as Fl_Window::label(...); not Fl_Widget
+       template <class TWidget> void set_label(TWidget* widget, const char *label, const char *def)
+       {
+               assert(NULL != widget); // widget must be created
+
+               if (NULL != widget)
+               {
+                       if (NULL != label && 0 != *label)
+                               widget->copy_label(label);
+                       else
+                               widget->label(def);
+               }
+       };
+};
+
+#endif //#ifndef __PINWINDOW_H__
diff --git a/fltk/qualitypasswindow.cxx b/fltk/qualitypasswindow.cxx
new file mode 100644 (file)
index 0000000..6d7f7cc
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+    qualitypasswindow.cxx - QualityPassWindow pin entry
+    with Password QualityBar and etc
+
+    Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+    Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Progress.H>
+
+#include "qualitypasswindow.h"
+
+const char *QualityPassWindow::QUALITY = "Quality";
+
+QualityPassWindow::QualityPassWindow(QualityPassWindow::GetQualityFn qualify, void* ptr)
+                                               : get_quality_(qualify)
+                                               ,get_quality_user_(ptr)
+                                               ,quality_(NULL)
+{
+       assert(NULL != qualify);
+}
+
+void QualityPassWindow::input_changed(Fl_Widget *input, void *val)
+{
+    QualityPassWindow  *self = reinterpret_cast<QualityPassWindow*>(val);
+
+    assert(NULL != self->get_quality_);   // function should be assigned in ctor
+    assert(NULL != self->quality_);       // quality progress bar must be created in init
+
+       if (NULL != self->quality_ && NULL != self->get_quality_)
+       {
+               int result = self->get_quality_(self->input_->value(), self->get_quality_user_);
+               bool isErr = (result <= 0);
+               if (isErr)
+                       result = -result;
+               self->quality_->selection_color(isErr?FL_RED:FL_GREEN);
+               self->quality_->value(std::min(result, 100));
+       }
+}
+
+QualityPassWindow* QualityPassWindow::create(QualityPassWindow::GetQualityFn qualify, void *user)
+{
+       QualityPassWindow *p = new QualityPassWindow(qualify, user);
+       p->init(460, 215);
+       p->window_->end();
+       p->input_->take_focus();
+       return p;
+}
+
+void QualityPassWindow::quality(const char *name)
+{
+       set_label(quality_, name, QUALITY);
+}
+
+int QualityPassWindow::init(const int cx, const int cy)
+{
+       int y = PassWindow::init(cx, cy);
+       assert(window_ == Fl_Group::current()); // make_window should all add current
+
+       input_->when(FL_WHEN_CHANGED);
+       input_->callback(input_changed, this);
+
+       y = input_->y() + input_->h();
+
+       quality_ = new Fl_Progress(input_->x(), y+5, input_->w(), 25, QUALITY);
+       quality_->align(Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP | FL_ALIGN_WRAP));
+       quality_->maximum(100.1);
+       quality_->minimum(0.0);
+       y = quality_->y() + quality_->h();
+
+       error_->position(error_->x(), y+5);
+
+       return error_->y() + error_->h();
+}
diff --git a/fltk/qualitypasswindow.h b/fltk/qualitypasswindow.h
new file mode 100644 (file)
index 0000000..164c10d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    qualitypasswindow.h - QualityPassWindow pin entry with Password QualityBar
+    and etc
+
+    Copyright (C) 2016 Anatoly madRat L. Berenblit
+
+    Written by Anatoly madRat L. Berenblit <madrat-@users.noreply.github.com>.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __QUALITYPASSWINDOW_H__
+#define __QUALITYPASSWINDOW_H__
+
+#include "passwindow.h"
+class Fl_Progress;
+
+class QualityPassWindow : public PassWindow
+{
+protected:
+       static const char *QUALITY;
+
+public:
+       typedef int (*GetQualityFn)(const char *passwd, void *ptr);
+
+       static QualityPassWindow* create(GetQualityFn qualify, void* user);
+
+       void quality(const char *name);
+
+protected:
+       QualityPassWindow(GetQualityFn qualify, void*);
+
+       const GetQualityFn  get_quality_;
+       void* const         get_quality_user_;
+
+       Fl_Progress *quality_;
+       virtual int init(const int cx, const int cy);
+
+       static void input_changed(Fl_Widget *input, void *val);
+};
+
+#endif //#ifndef __QUALITYPASSWINDOW_H__