2003-12-20 Marcus Brinkmann <marcus@g10code.de> marcus-after-qt-rewrite
authorMarcus Brinkmann <mb@g10code.com>
Fri, 19 Dec 2003 23:28:11 +0000 (23:28 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Fri, 19 Dec 2003 23:28:11 +0000 (23:28 +0000)
* pinentry/pinentry.h (struct pinentry): New member PARENT_WID.
* pinentry/pinentry.c (pinentry): Add new member here.
(usage): Add --parent-wid.
(pinentry_parse_opts): Add case for "parent-wid".
(option_handler): Same here.

2003-12-19  Marcus Brinkmann  <marcus@g10code.de>

* pinentry/pinentry.c (cmd_setcancel): Use strcpy_escaped.
(cmd_setok): Likewise.
(cmd_setprompt): Likewise.
(pinentry_utf8_to_local): Don't use
nl_langinfo, but just lc_ctype directly.
* pinentry/pinentry.c (cmd_getpin): Do not convert passphrase to
UTF-8 here.
* gtk/pinentry-gtk.c (button_clicked): Convert passphrase to UTF8
here.
* pinentry/pinentry-curses.c (dialog_run): Likewise.

2003-12-14  Marcus Brinkmann  <marcus@g10code.de>

* pinentry/pinentry.c (pinentry_init): Register secmem_term as
atexit function.  Set assuan malloc hooks to secmem.
(pinentry_parse_opts): Add break statement to silence gcc warning.
* pinentry/pinentry.c (cmd_getpin): If canceled, release and clear
PINENTRY->pin nevertheless.

* acinclude.m4 (qt_incdirs): Add /usr/include/qt3.
* qt/Makefile.am (pinentry_qt_SOURCES): Remove cppmemory.h,
cppmemory.cpp, pinentrycontroller.h, pinentrycontroller.cpp.
(nodist_pinentry_qt_SOURCES): Remove pinentrycontroller.moc.cpp.
(libcurses): Move ../pinentry/libpinentry.a from here to ...
(pinentry_qt_LDADD): ... here.  Change order a bit to make it
work.
* qt/cppmemory.h, qt/cppmemory.cpp, qt/pinentrycontroller.h,
qt/pinentrycontroller.cpp: Files removed.
* qt/secqstring.h, qt/secqstring.cpp, secqlineedit.h,
secqlineedit.cpp: New files.
* qt/Makefile.am (pinentry_qt_SOURCES): Add secqstring.h,
secqstring.cpp, secqlineedit.h, and secqlineedit.cpp.
(nodist_pinentry_qt_SOURCES): Add secqlineedit.moc.cpp.
* qt/main.cpp: Do not include "memory.h" or "secmem-util.h", nor
<new> or "pinentrycontroller.h".  Include <qapplication.h>,
<qmessagebox.h>, <qwidget.h> and "secqstring.h".  Always include
<pinentry.h>.
[USE_KDE]: Remove all instances.
(curses_main): Function removed.
(my_new_handler): Likewise.
(qt_main): Likewise.
(qt_cmd_handler): New function.
(pinentry_cmd_handler): Define always (to qt_cmd_handler).
(main): Rewritten.
* qt/pinentrydialog.cpp: Do not include <qlineedit.h>, but
"secqlineedit.h".
(PinEntryDialog::PinEntryDialog): Make _edit a SecQLineEdit
object.  Connect accepted SIGNAL to accept SLOT, and rejected
SIGNAL to reject SLOT.
(PinEntryDialog::setText): Make argument SecQString rather than
QString.
(PinEntryDialog::text): Likewise for return value.
* qt/pinentrydialog.h: Declare SecQString and SecQLineEdit classes.
(class PinEntryDialog): Disable property text (for now).  Adjust
argument of setText and return value of text, as well as type of
_edit.

19 files changed:
ChangeLog
NEWS
TODO
acinclude.m4
gtk/pinentry-gtk.c
pinentry/pinentry-curses.c
pinentry/pinentry.c
pinentry/pinentry.h
qt/Makefile.am
qt/cppmemory.cpp [deleted file]
qt/main.cpp
qt/pinentrycontroller.cpp [deleted file]
qt/pinentrycontroller.h [deleted file]
qt/pinentrydialog.cpp
qt/pinentrydialog.h
qt/secqlineedit.cpp [new file with mode: 0644]
qt/secqlineedit.h [new file with mode: 0644]
qt/secqstring.cpp [new file with mode: 0644]
qt/secqstring.h [new file with mode: 0644]

index bdb5900..c379aac 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,70 @@
+2003-12-20  Marcus Brinkmann  <marcus@g10code.de>
+
+       * pinentry/pinentry.h (struct pinentry): New member PARENT_WID.
+       * pinentry/pinentry.c (pinentry): Add new member here.
+       (usage): Add --parent-wid.
+       (pinentry_parse_opts): Add case for "parent-wid".
+       (option_handler): Same here.
+
+2003-12-19  Marcus Brinkmann  <marcus@g10code.de>
+
+       * pinentry/pinentry.c (cmd_setcancel): Use strcpy_escaped.
+       (cmd_setok): Likewise.
+       (cmd_setprompt): Likewise.
+       (pinentry_utf8_to_local): Don't use
+       nl_langinfo, but just lc_ctype directly.
+       * pinentry/pinentry.c (cmd_getpin): Do not convert passphrase to
+       UTF-8 here.
+       * gtk/pinentry-gtk.c (button_clicked): Convert passphrase to UTF8
+       here.
+       * pinentry/pinentry-curses.c (dialog_run): Likewise.
+
+2003-12-14  Marcus Brinkmann  <marcus@g10code.de>
+
+       * pinentry/pinentry.c (pinentry_init): Register secmem_term as
+       atexit function.  Set assuan malloc hooks to secmem.
+       (pinentry_parse_opts): Add break statement to silence gcc warning.
+       * pinentry/pinentry.c (cmd_getpin): If canceled, release and clear
+       PINENTRY->pin nevertheless.
+
+       * acinclude.m4 (qt_incdirs): Add /usr/include/qt3.
+       * qt/Makefile.am (pinentry_qt_SOURCES): Remove cppmemory.h,
+       cppmemory.cpp, pinentrycontroller.h, pinentrycontroller.cpp.
+       (nodist_pinentry_qt_SOURCES): Remove pinentrycontroller.moc.cpp.
+       (libcurses): Move ../pinentry/libpinentry.a from here to ...
+       (pinentry_qt_LDADD): ... here.  Change order a bit to make it
+       work.
+       * qt/cppmemory.h, qt/cppmemory.cpp, qt/pinentrycontroller.h,
+       qt/pinentrycontroller.cpp: Files removed.
+       * qt/secqstring.h, qt/secqstring.cpp, secqlineedit.h,
+       secqlineedit.cpp: New files.
+       * qt/Makefile.am (pinentry_qt_SOURCES): Add secqstring.h,
+       secqstring.cpp, secqlineedit.h, and secqlineedit.cpp.
+       (nodist_pinentry_qt_SOURCES): Add secqlineedit.moc.cpp.
+       * qt/main.cpp: Do not include "memory.h" or "secmem-util.h", nor
+       <new> or "pinentrycontroller.h".  Include <qapplication.h>,
+       <qmessagebox.h>, <qwidget.h> and "secqstring.h".  Always include
+       <pinentry.h>.
+       [USE_KDE]: Remove all instances.
+       (curses_main): Function removed.
+       (my_new_handler): Likewise.
+       (qt_main): Likewise.
+       (qt_cmd_handler): New function.
+       (pinentry_cmd_handler): Define always (to qt_cmd_handler).
+       (main): Rewritten.
+       * qt/pinentrydialog.cpp: Do not include <qlineedit.h>, but
+       "secqlineedit.h".
+       (PinEntryDialog::PinEntryDialog): Make _edit a SecQLineEdit
+       object.  Connect accepted SIGNAL to accept SLOT, and rejected
+       SIGNAL to reject SLOT.
+       (PinEntryDialog::setText): Make argument SecQString rather than
+       QString.
+       (PinEntryDialog::text): Likewise for return value.
+       * qt/pinentrydialog.h: Declare SecQString and SecQLineEdit classes.
+       (class PinEntryDialog): Disable property text (for now).  Adjust
+       argument of setText and return value of text, as well as type of
+       _edit.  
+
 2003-12-09  Werner Koch  <wk@gnupg.org>
 
        * README.CVS: New.
diff --git a/NEWS b/NEWS
index dd980e1..0f125e4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,19 @@
+Noteworthy changes in version 0.7.0 (unreleased)
+------------------------------------------------
+
+ * Make UTF8 description (prompt, error message, button texts) work.
+
+ * Make sure that secmem_term is called before program termination.
+
+ * Make assuan in Gtk and Curses pinentry use secure memory for
+   storage.
+
+ * Fixed a bug that would occur if a canceled GETPIN was immediately
+   followed by a CONFIRM.
+
+ * Disable undo/redo in Qt pinentry.
+
+
 Noteworthy changes in version 0.6.8 (2003-02-07)
 ------------------------------------------------
 
diff --git a/TODO b/TODO
index 24aff8e..fe719d4 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,12 +1,15 @@
-* The DISPLAY setting should be honoured by the pinentry applications using XFree86.
-  This implies reconfiguring the underlying toolkit, is this always possible?
+* The DISPLAY setting should be honoured by the pinentry applications
+  using XFree86.  This implies reconfiguring the underlying toolkit,
+  is this always possible?
   (This is not so important, as pinentry is always restarted.)
 
 * The Qt and curses PIN entry should support enhanced mode (when it is
   implemented in gpg-agent).
 
-* The Qt PIN entry needs to use libpinentry (maybe).
+* The GTK+ PIN entry needs to convert the text of the button label
+  from UTF-8 to the local encoding.
 
-* A heartbeat status message should be send every few seconds, so that
-  the gpg-agent is better able to cope with jammed pinentries.
+* Set the max length of password globally (dynamically in protocol?).
 
+* A heartbeat status message should be sent every few seconds, so that
+  the gpg-agent is better able to cope with jammed pinentries.
index 1212a08..6ba9d05 100644 (file)
@@ -1152,7 +1152,7 @@ qt_incdirs=""
 for dir in $qt_dirs; do
    qt_incdirs="$qt_incdirs $dir/include $dir"
 done
-qt_incdirs="$QTINC $qt_incdirs /usr/local/qt/include /usr/include/qt /usr/include /usr/X11R6/include/X11/qt /usr/X11R6/include/qt /usr/X11R6/include/qt2 $x_includes"
+qt_incdirs="$QTINC $qt_incdirs /usr/local/qt/include  /usr/include /usr/X11R6/include/X11/qt /usr/X11R6/include/qt /usr/X11R6/include/qt2 /usr/include/qt3 $x_includes"
 if test ! "$ac_qt_includes" = "NO"; then
    qt_incdirs="$ac_qt_includes $qt_incdirs"
 fi
index 2cb0c97..1d63f5f 100644 (file)
@@ -131,7 +131,8 @@ button_clicked (GtkWidget *widget, gpointer data)
   if (data)
     { /* okay button or enter used inntext field */
       const char *s;
-      
+      char *s_utf8;
+
       if (pinentry->enhanced)
         {
           printf("Options: %s\nTimeout: %d\n\n",
@@ -144,10 +145,15 @@ button_clicked (GtkWidget *widget, gpointer data)
       s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY(entry));
       if (!s)
         s = "";
-      passphrase_ok = 1;
-      pinentry_setbufferlen (pinentry, strlen(s)+1);
-      if (pinentry->pin)
-        strcpy (pinentry->pin, s);
+      s_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1);
+      if (s_utf8)
+       {
+         passphrase_ok = 1;
+         pinentry_setbufferlen (pinentry, strlen (s_utf8) + 1);
+         if (pinentry->pin)
+           strcpy (pinentry->pin, s_utf8);
+         secmem_free (s_utf8);
+       }
     }
   gtk_main_quit ();
 }
index 2123df2..0a75f07 100644 (file)
@@ -34,6 +34,8 @@
 #include <string.h>
 #include <errno.h>
 
+#include <memory.h>
+
 #include "pinentry.h"
 
 #define STRING_OK "<OK>"
@@ -543,6 +545,7 @@ dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type)
   FILE *ttyfo = NULL;
   SCREEN *screen = 0;
   int done = 0;
+  char *pin_utf8;
 
   /* Open the desired terminal if necessary.  */
   if (tty_name)
@@ -688,6 +691,16 @@ dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type)
   /* XXX Factor out into dialog_release or something.  */
   free (diag.ok);
   free (diag.cancel);
+
+  pin_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1);
+  if (pin_utf8)
+    {
+      pinentry_setbufferlen (pinentry, strlen (pin_utf8) + 1);
+      if (pinentry->pin)
+       strcpy (pinentry->pin, pin_utf8);
+      secmem_free (pin_utf8);
+    }
+
   return diag.pin ? (done < 0 ? -1 : diag.pin_len) : (done < 0 ? 0 : 1);
 }
 
index 4c39981..ebeddbf 100644 (file)
@@ -55,6 +55,7 @@ struct pinentry pinentry =
     0,         /* Debug mode.  */
     0,         /* Enhanced mode.  */
     1,         /* Global grab.  */
+    0,         /* Parent Window ID.  */
     0          /* Result.  */
   };
 
@@ -62,8 +63,6 @@ struct pinentry pinentry =
 char *
 pinentry_utf8_to_local (char *lc_ctype, char *text)
 {
-  char *old_ctype;
-  char *target_encoding;
   iconv_t cd;
   char *input = text;
   size_t input_len = strlen (text) + 1;
@@ -77,21 +76,13 @@ pinentry_utf8_to_local (char *lc_ctype, char *text)
   if (!lc_ctype)
     return strdup (text);
 
-  old_ctype = strdup (setlocale (LC_CTYPE, NULL));
-  if (!old_ctype)
-    return NULL;
-  setlocale (LC_CTYPE, lc_ctype);
-  target_encoding = nl_langinfo (CODESET);
-  setlocale (LC_CTYPE, old_ctype);
-  free (old_ctype);
-
   /* This is overkill, but simplifies the iconv invocation greatly.  */
   output_len = input_len * MB_LEN_MAX;
   output_buf = output = malloc (output_len);
   if (!output)
     return NULL;
 
-  cd = iconv_open (target_encoding, "UTF-8");
+  cd = iconv_open (lc_ctype, "UTF-8");
   if (cd == (iconv_t) -1)
     {
       free (output);
@@ -204,11 +195,17 @@ pinentry_init (void)
   secmem_init (1);
   secmem_set_flags (SECMEM_WARN);
   drop_privs ();
+
+  if (atexit (secmem_term))
+    /* FIXME: Could not register at-exit function, bail out.  */
+    ;
+
+  assuan_set_malloc_hooks (secmem_malloc, secmem_realloc, secmem_free);
 }
 
 /* Simple test to check whether DISPLAY is set or the option --display
    was given.  Used to decide whether the GUI or curses should be
-   initialized. */
+   initialized.  */
 int
 pinentry_have_display (int argc, char **argv)
 {
@@ -236,6 +233,7 @@ Ask securely for a secret and print it to stdout.\n\
       --lc-messages     Set the tty LC_MESSAGES value\n\
   -e, --enhanced        Ask for timeout and insurance, too\n\
   -g, --no-global-grab  Grab keyboard only while window is focused\n\
+      --parent-wid     Parent window ID (for positioning)\n\
   -d, --debug           Turn on debugging output\n\
       --help            Display this help and exit\n\
       --version         Output version information and exit\n", "?");
@@ -260,6 +258,7 @@ pinentry_parse_opts (int argc, char *argv[])
      { "lc-messages", required_argument, 0, 'M' },
      { "enhanced", no_argument, &pinentry.enhanced, 1 },
      { "no-global-grab", no_argument, &pinentry.grab, 0 },
+     { "parent-wid", required_argument, 0, 'W' },
      { "help", no_argument, &opt_help, 1 },
      { "version", no_argument, &opt_version, 1 },
      { NULL, 0, NULL, 0 }};
@@ -316,8 +315,13 @@ pinentry_parse_opts (int argc, char *argv[])
              exit (EXIT_FAILURE);
            }
          break;
+       case 'W':
+         pinentry.parent_wid = atoi (optarg);
+         /* FIXME: Add some error handling.  Use strtol.  */
+         break;
         default:
           /* XXX Should never happen.  */
+         break;
         }
     }
   if (opt_version) 
@@ -386,6 +390,11 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
       if (!pinentry.lc_messages)
        return ASSUAN_Out_Of_Core;
     }
+  else if (!strcmp (key, "parent-wid"))
+    {
+      pinentry.parent_wid = atoi (value);
+      /* FIXME: Use strtol and add some error handling.  */
+    }
   else
     return ASSUAN_Invalid_Option;
   return 0;
@@ -438,7 +447,7 @@ cmd_setprompt (ASSUAN_CONTEXT ctx, char *line)
   if (!newp)
     return ASSUAN_Out_Of_Core;
 
-  strcpy (newp, line);
+  strcpy_escaped (newp, line);
   if (pinentry.prompt)
     free (pinentry.prompt);
   pinentry.prompt = newp;
@@ -472,7 +481,7 @@ cmd_setok (ASSUAN_CONTEXT ctx, char *line)
   if (!newo)
     return ASSUAN_Out_Of_Core;
 
-  strcpy (newo, line);
+  strcpy_escaped (newo, line);
   if (pinentry.ok)
     free (pinentry.ok);
   pinentry.ok = newo;
@@ -489,7 +498,7 @@ cmd_setcancel (ASSUAN_CONTEXT ctx, char *line)
   if (!newc)
     return ASSUAN_Out_Of_Core;
 
-  strcpy (newc, line);
+  strcpy_escaped (newc, line);
   if (pinentry.cancel)
     free (pinentry.cancel);
   pinentry.cancel = newc;
@@ -522,30 +531,28 @@ cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
     pinentry.prompt = NULL;
 
   if (result < 0)
-    return ASSUAN_Canceled;
-
-  if (result && pinentry.pin)
     {
-      char *p;
+      if (pinentry.pin)
+       {
+         secmem_free (pinentry.pin);
+         pinentry.pin = NULL;
+       }
+      return ASSUAN_Canceled;
+    }
 
-      p = pinentry_local_to_utf8 (pinentry.lc_ctype, pinentry.pin, 1);
-      if (p)
-        {
-          result = assuan_send_data (ctx, p, strlen (p));
-          secmem_free (p);
-        }
-      else /* Most likely we can't convert between the character sets. */
-        result = ASSUAN_Invalid_Data;
+  if (result)
+    {
+      result = assuan_send_data (ctx, pinentry.pin, result);
+      if (!result)
+       result = assuan_send_data (ctx, NULL, 0);
     }
-  else
-    result = assuan_send_data (ctx, pinentry.pin, result);
+
   if (pinentry.pin)
     {
       secmem_free (pinentry.pin);
       pinentry.pin = NULL;
     }
-  if (!result)
-    result = assuan_send_data (ctx, NULL, 0);
+
   return result;
 }
 
index 982c386..9494a6a 100644 (file)
@@ -59,6 +59,9 @@ struct pinentry
   int enhanced;
   /* True if caller should grab the keyboard.  */
   int grab;
+  /* The window ID of the parent window over which the pinentry window
+     should be displayed.  */
+  int parent_wid;
 
   /* The user should set this to -1 if the user canceled the request,
      and to the length of the PIN stored in pin otherwise.  */
index d2bd747..9511a7e 100644 (file)
@@ -23,7 +23,7 @@ bin_PROGRAMS = pinentry-qt
 
 if FALLBACK_CURSES
 ncurses_include = -I$(top_srcdir)/pinentry $(NCURSES_INCLUDE)
-libcurses = ../pinentry/libpinentry-curses.a ../pinentry/libpinentry.a $(LIBCURSES) $(LIBICONV)
+libcurses = ../pinentry/libpinentry-curses.a $(LIBCURSES) $(LIBICONV)
 else
 ncurses_include =
 libcurses =
@@ -32,15 +32,17 @@ endif
 
 AM_CPPFLAGS = -I$(top_srcdir)/assuan -I$(top_srcdir)/secmem $(QT_INCLUDES) $(ncurses_include)
 AM_CXXFLAGS = $(QT_CXXFLAGS)
-pinentry_qt_LDADD = $(top_builddir)/assuan/libassuan.a \
-       $(top_builddir)/secmem/libsecmem.a $(LIBCAP) \
-       $(QT_LIBS) $(libcurses)
+pinentry_qt_LDADD = $(QT_LIBS) $(libcurses) ../pinentry/libpinentry.a \
+       $(top_builddir)/assuan/libassuan.a \
+       $(top_builddir)/secmem/libsecmem.a $(LIBCAP)
 pinentry_qt_LDFLAGS = $(QT_LDFLAGS)
 
-pinentry_qt_SOURCES = pinentrydialog.h pinentrydialog.cpp \
-       pinentrycontroller.h pinentrycontroller.cpp \
-       cppmemory.cpp main.cpp 
-nodist_pinentry_qt_SOURCES = pinentrydialog.moc.cpp pinentrycontroller.moc.cpp
+pinentry_qt_SOURCES = secqstring.h secqstring.cpp \
+       secqlineedit.h secqlineedit.cpp \
+       pinentrydialog.h pinentrydialog.cpp \
+       main.cpp 
+nodist_pinentry_qt_SOURCES = secqlineedit.moc.cpp \
+       pinentrydialog.moc.cpp
 
 DISTCLEANFILES = $(nodist_pinentry_qt_SOURCES)
 
diff --git a/qt/cppmemory.cpp b/qt/cppmemory.cpp
deleted file mode 100644 (file)
index f431dc3..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* cppmemory.cpp - Memory management for a secure KDE dialog for PIN entry.
-   Copyright (C) 2002 Klarälvdalens Datakonsult AB
-   Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
-   
-   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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA  */
-
-#include <new>
-#include <stdlib.h>
-extern "C"
-{
-#include "memory.h"
-}
-
-bool is_secure = false;
-
-void *operator new[] (size_t s) throw (std::bad_alloc)
-{
-  if( s == 0 ) s = 1; // never allocate 0 bytes!
-
-  while( true ) { // the standard requires us to keep trying
-    void* p;
-    if( is_secure ) p = ::secmem_malloc( s );
-    else p = ::malloc( s );
-    if(p) return p; // succes!
-    
-    std::new_handler h = std::set_new_handler(0);
-    std::set_new_handler(h);
-    if( h ) (*h)();
-    else throw std::bad_alloc();
-  }
-}
-
-/* We dont need to override non-array new/delete
- * because Qt always uses new[] for allocating
- * string data
- */
-#if 0
-void *operator new (size_t s) throw (std::bad_alloc)
-{
-  return operator new[]( s );
-}
-#endif
-
-void operator delete[] (void* p) throw() 
-{  
-  if( !p ) return;
-  if( ::m_is_secure(p) ) ::secmem_free( p );
-  else ::free( p );
-}
-
-#if 0
-void operator delete (void* p) throw()
-{
-  operator delete[]( p );
-}
-#endif
index 243e1b7..ec8fed7 100644 (file)
@@ -1,6 +1,8 @@
 /* main.cpp - Secure KDE dialog for PIN entry.
    Copyright (C) 2002 Klarälvdalens Datakonsult AB
+   Copyright (C) 2003 g10 Code GmbH
    Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
+   Modified by Marcus Brinkmann <marcus@g10code.de>.
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
 #include "config.h"
 #endif
 
-
-extern "C"
-{
-#include "memory.h"
-#include "secmem-util.h"
-}
-
-#include <new>
-
 #include <stdlib.h>
 
-#ifdef USE_KDE
-# include <kapp.h>
-# include <kcmdlineargs.h>
-# include <kaboutdata.h>
-# include <klocale.h>
-#else
-# include <qapplication.h>
-#endif // USE_KDE
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qmessagebox.h>
+#include "secqstring.h"
 
 #include "pinentrydialog.h"
-#include "pinentrycontroller.h"
-
 
+#include <pinentry.h>
 
 #ifdef FALLBACK_CURSES
-#include <pinentry.h>
 #include <pinentry-curses.h>
+#endif
 
-pinentry_cmd_handler_t pinentry_cmd_handler = curses_cmd_handler;
-
-int curses_main (int argc, char *argv[])
+/* Hack for creating a QWidget with a "foreign" window ID */
+class ForeignWidget : public QWidget
 {
-  pinentry_init ();
+public:
+  ForeignWidget( WId wid ) : QWidget( 0 )
+  {
+    QWidget::destroy();
+    create( wid, false, false );
+  }
+  ~ForeignWidget()
+  {
+    destroy( false, false );
+  }
+};
 
-  /* Consumes all arguments.  */
-  if (pinentry_parse_opts (argc, argv))
-    {
-      printf ("pinentry-curses " VERSION "\n");
-      exit (EXIT_SUCCESS);
-    }
+static int
+qt_cmd_handler (pinentry_t pe)
+{
+  QWidget *parent = 0;
 
-  if (pinentry_loop ())
-    return 1;
+  int want_pass = !!pe->pin;
 
-  return 0;
-}
-#endif
+  if (want_pass)
+    {
+      /* FIXME: Add parent window ID to pinentry and GTK.  */
+      if (pe->parent_wid)
+       parent = new ForeignWidget (pe->parent_wid);
 
-extern "C++" {
-  extern bool is_secure;
-};
+      PinEntryDialog pinentry (parent, 0, true);
 
-#ifndef VERSION
-#define VERSION "0.1"
+      pinentry.setPrompt (QString::fromUtf8 (pe->prompt));
+      pinentry.setDescription (QString::fromUtf8 (pe->description));
+      /* If we reuse the same dialog window.  */
+#if 0
+      pinentry.setText (SecQString::null);
 #endif
 
-#ifdef USE_KDE
-static const char *description =
-        I18N_NOOP("Pinentry");
-// INSERT A DESCRIPTION FOR YOUR APPLICATION HERE
-static KCmdLineOptions options[] =
-{
-  { 0, 0, 0 }
-  // INSERT YOUR COMMANDLINE OPTIONS HERE
-};
-#else
-static void 
-usage( const char* appname )
-{
-  fprintf (stderr, "Usage: %s [OPTION]...\n\
-Ask securely for a secret and print it to stdout.\n\
-\n\
-      --display DISPLAY Set the X display\n\
-      --parent-wid      Set the window id the dialogs should appear over\n\
-      --help, -h        Display this help and exit\n", appname);
-
-}
-#endif // USE_KDE
-
-void my_new_handler()
-{
-  secmem_term();
-  qFatal("Out of memory!");
-}
-
-int qt_main( int argc, char *argv[] )
-{
-       secmem_init( 16384*4 ); /* this should be enough, if not, increase it! */
-    secmem_set_flags(SECMEM_WARN);
-    drop_privs();
-    std::set_new_handler(my_new_handler);
-    try {
-#ifdef USE_KDE
-      KAboutData aboutData( "pinentry", I18N_NOOP("Pinentry"),
-                           VERSION, description, KAboutData::License_GPL,
-                           "(c) 2001, Steffen Hansen, Klarälvdalens Datakonsult AB", 0, 0, "klaralvdalens-datakonsult.se");
-      aboutData.addAuthor("Steffen Hansen, Klarälvdalens Datakonsult AB",0, "steffen@klaralvdalens-datakonsult.se");
-      KCmdLineArgs::init( argc, argv, &aboutData );
-      KCmdLineArgs::addCmdLineOptions( options ); // TODO(steffen): Add KDE option handling
-      KApplication app;
-#else
-      QApplication app( argc, argv );
-      WId parentwid = 0;
-      for( int i = 0; i < argc; ++i ) {
-       if( !strncmp( argv[i], "--parent-wid", 12 ) ) {
-         int len =  strlen( argv[i] );
-         if( len > 12 && argv[i][12] == '=' ) {
-           parentwid = strtol( argv[i]+13, 0, 0 );
-         } else if( len == 12 && i+1 < argc ) {
-           parentwid = strtol( argv[i+1], 0, 0 );
-         }
-       } else if( !strcmp( argv[i], "--help" ) || !strcmp( argv[i], "-h" ) ) { 
-         usage( argv[0] );
-         exit(0);
+      if (pe->ok)
+       pinentry.setOkText (QString::fromUtf8 (pe->ok));
+      if (pe->cancel)
+       pinentry.setCancelText (QString::fromUtf8 (pe->cancel));
+      if (pe->error)
+       pinentry.setError (QString::fromUtf8 (pe->error));
+
+      bool ret = pinentry.exec ();
+      if (!ret)
+       return -1;
+
+      char *pin = (char *) pinentry.text().utf8();
+      if (!pin)
+       return -1;
+
+      int len = strlen (pin);
+      if (len >= 0)
+       {
+         pinentry_setbufferlen (pe, len + 1);
+         if (pe->pin)
+           {
+             strcpy (pe->pin, pin);
+             ::secmem_free (pin);
+             return len;
+           }
        }
-      }
-#endif // USE_KDE
-      is_secure = true;
-
-      PinEntryController ctrl( parentwid );
-      ctrl.exec();
-      return 0;
-    } catch( std::bad_alloc& ex ) {
-      qDebug("Out of memory, got a %s", ex.what());
+      ::secmem_free (pin);
       return -1;
     }
+  else
+    {
+      bool ret = QMessageBox::information (parent, "", pe->description,
+                                          pe->ok ? pe->ok : "OK",
+                                          pe->cancel ? pe->cancel : "Cancel");
+      return !ret;
+    }
 }
 
-int main( int argc, char* argv[] ) 
+pinentry_cmd_handler_t pinentry_cmd_handler = qt_cmd_handler;
+
+int 
+main (int argc, char *argv[])
 {
+  pinentry_init ();
+
 #ifdef FALLBACK_CURSES
-  if( pinentry_have_display (argc, argv) ) {
-#endif
-       // Work around non-standard handling of DISPLAY
-       for( int i = 1; i < argc; ++i ) {
-       if( !strcmp( "--display", argv[i] ) ) {
-               argv[i] = "-display";
-       }
-       }
-    return qt_main( argc, argv );
-#ifdef FALLBACK_CURSES
-  } else {
-    return curses_main( argc, argv );
-  }
+  if (!pinentry_have_display (argc, argv))
+    pinentry_cmd_handler = curses_cmd_handler;
+  else
 #endif
+    /* We use a modal dialog window, so we don't need the application
+       window anymore.  */
+    new QApplication (argc, argv);
+
+
+  /* Consumes all arguments.  */
+  if (pinentry_parse_opts (argc, argv))
+    {
+      printf ("pinentry-gtk " VERSION "\n");
+      exit (EXIT_SUCCESS);
+    }
+
+  if (pinentry_loop ())
+    return 1;
+
+  return 0;
 }
diff --git a/qt/pinentrycontroller.cpp b/qt/pinentrycontroller.cpp
deleted file mode 100644 (file)
index 451e41c..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-/* pinentrycontroller.cpp - A secure KDE dialog for PIN entry.
-   Copyright (C) 2002 Klarälvdalens Datakonsult AB
-   Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
-   
-   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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA  */
-
-#include "pinentrycontroller.h"
-#include "pinentrydialog.h"
-extern "C"
-{
-#include "memory.h"
-}
-#ifdef USE_KDE
-# include <kmessagebox.h>
-#else
-# include <qmessagebox.h>
-#endif
-
-#include <stdlib.h>
-#include <util.h>
-
-static void strcpy_escaped (char *d, const char *s)
-{
-  while (*s)
-    {
-      if (*s == '%' && s[1] && s[2])
-        {
-          s++;
-          *d++ = xtoi_2 ( s);
-          s += 2;
-        }
-      else
-        *d++ = *s++;
-    }
-  *d = 0;
-}
-
-PinEntryController::PinEntryController( WId parentwid ) : _pinentry( 0 ), _parent(0)
-{
-  int fds[2];
-  fds[0] = 0;
-  fds[1] = 1;
-  
-  assuan_set_malloc_hooks( secmem_malloc, secmem_realloc, secmem_free );
-  int rc = assuan_init_pipe_server( &_ctx, fds );
-#if 0
-  assuan_set_log_stream (_ctx, stderr);
-#endif
-
-  if( rc ) {
-    qDebug(assuan_strerror( static_cast<AssuanError>(rc) ));
-    exit(-1);
-  }
-  rc = registerCommands();
-
-  if( parentwid ) createParentWidget( parentwid );
-
-  assuan_set_pointer( _ctx, this );
-}
-
-PinEntryController::~PinEntryController()
-{
-  assuan_deinit_server( _ctx );
-  delete _parent;
-}
-
-void PinEntryController::exec()
-{
-  while( true ) {
-    int rc = assuan_accept( _ctx );
-    if( rc == -1 ) {
-      qDebug("Assuan terminated");
-      break;
-    } else if( rc ) {
-      qDebug("Assuan accept problem: %s", assuan_strerror( static_cast<AssuanError>(rc) ) );
-      break;
-    }
-    rc = assuan_process( _ctx );
-    if( rc ) {
-      qDebug("Assuan processing failed: %s", assuan_strerror( static_cast<AssuanError>(rc) ) );
-      continue;
-    }
-  }
-}
-
-int PinEntryController::registerCommands()
-{
-  static struct {
-    const char *name;
-    int cmd_id;
-    int (*handler)(ASSUAN_CONTEXT, char *line);
-  } table[] = {
-    { "SETDESC",      0,  PinEntryController::assuanDesc },
-    { "SETPROMPT",    0,  PinEntryController::assuanPrompt },
-    { "SETERROR",     0,  PinEntryController::assuanError },
-    { "SETOK",        0,  PinEntryController::assuanOk },
-    { "SETCANCEL",    0,  PinEntryController::assuanCancel },
-    { "GETPIN",       0,  PinEntryController::assuanGetpin },
-    { "CONFIRM",      0,  PinEntryController::assuanConfirm },
-    { 0,0,0 }
-  };
-  int i, j, rc;
-  
-  for (i=j=0; table[i].name; i++) {
-    rc = assuan_register_command (_ctx,
-                                 table[i].cmd_id? table[i].cmd_id
-                                 : (ASSUAN_CMD_USER + j++),
-                                 table[i].name, table[i].handler);
-    if (rc) return rc;
-  }
-  assuan_register_option_handler(_ctx, PinEntryController::optionHandler);
-
-  return 0;
-}
-
-/* Hack for creating a QWidget with a "foreign" window ID */
-class ForeignWidget : public QWidget
-{
-public:
-  ForeignWidget( WId wid ) : QWidget( 0 ) 
-  {
-    QWidget::destroy();
-    create( wid, false, false );
-  }
-
-  ~ForeignWidget()
-  {
-    destroy( false, false );
-  }
-};
-
-void
-PinEntryController::createParentWidget( WId parentwid )
-{
-  if( _parent ) delete _parent;
-  _parent = new ForeignWidget( parentwid );  
-}
-
-int
-PinEntryController::optionHandler( ASSUAN_CONTEXT ctx, const char* key, const char* value )
-{
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-  if( !strcmp( key, "parent-wid" ) ) {
-    WId id = strtol( value, 0, 0 );
-    that->createParentWidget( id );
-    return 0;
-  }
-
-  /* FIXME: For now we simply ignore all options.  This module should
-     be converted to make use of the pinentry framework. */
-  return 0;
-}
-
-int PinEntryController::assuanDesc( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanDesc( %s )", line );
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-
-  char* newl = new char[strlen (line) + 1];
-  strcpy_escaped( newl, line );
-
-  that->_desc = QString::fromUtf8(newl);
-  that->_error = QString::null;
-
-  delete[] newl;
-  return 0;
-}
-
-int PinEntryController::assuanPrompt( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanPrompt( %s )", line );
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-
-  char* newl = new char[strlen (line) + 1];
-  strcpy_escaped( newl, line );
-
-  that->_prompt = QString::fromUtf8(newl);
-  that->_error = QString::null;
-
-  delete[] newl;
-  return 0;
-}
-
-int PinEntryController::assuanError( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanError( %s )", line );
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-
-  char* newl = new char[strlen (line) + 1];
-  strcpy_escaped( newl, line );
-
-  that->_error = QString::fromUtf8(newl);
-
-  delete[] newl;
-  return 0;
-}
-
-int PinEntryController::assuanOk ( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanOk( %s )", line );
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-
-  char* newl = new char[strlen (line) + 1];
-  strcpy_escaped( newl, line );
-
-  that->_ok = QString::fromUtf8(line);
-
-  delete[] newl;
-  return 0;
-}
-
-int PinEntryController::assuanCancel( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanCancel( %s )", line );
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-
-  char* newl = new char[strlen (line) + 1];
-  strcpy_escaped( newl, line );
-
-  that->_cancel = QString::fromUtf8(line);
-
-  delete[] newl;
-  return 0;
-}
-
-int PinEntryController::assuanGetpin( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanGetpin( %s )", line );  
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-  return that->getPin( line );
-}
-
-int PinEntryController::getPin( char* line ) {
-  if( _pinentry == 0 ) {
-    _pinentry = new PinEntryDialog(_parent,0,true);
-  }
-  _pinentry->setPrompt( _prompt );
-  _pinentry->setDescription( _desc );
-  _pinentry->setText(QString::null);
-  if( !_ok.isNull() ) _pinentry->setOkText( _ok );
-  if( !_cancel.isNull() ) _pinentry->setCancelText( _cancel );
-  if( !_error.isNull() ) _pinentry->setError( _error );
-  connect( _pinentry, SIGNAL( accepted() ),
-          this, SLOT( slotAccepted() ) );
-  connect( _pinentry, SIGNAL( rejected() ),
-          this, SLOT( slotRejected() ) );
-  bool ret = _pinentry->exec();  
-  FILE* fp = assuan_get_data_fp( _ctx );
-  if( ret ) {
-    fputs( static_cast<const char*>(_pinentry->text().utf8()), fp );
-    return 0;
-  } else {
-    assuan_set_error( _ctx, ASSUAN_Canceled, "Dialog cancelled by user" );
-    return ASSUAN_Canceled;
-  }
-}
-
-int PinEntryController::assuanConfirm( ASSUAN_CONTEXT ctx, char* line )
-{
-  //qDebug("PinEntryController::assuanConfirm( %s )", line );  
-  PinEntryController* that =   static_cast<PinEntryController*>(assuan_get_pointer(ctx));
-  return that->confirm( line );  
-}
-
-int PinEntryController::confirm( char* line )
-{
-  int ret;
-#ifdef USE_KDE
-  if( !_error.isNull() ) {
-    ret = KMessageBox::questionYesNo( _parent, _error );
-  } else {
-    ret = KMessageBox::questionYesNo( _parent, _desc );    
-  }
-  FILE* fp = assuan_get_data_fp( _ctx );
-  if( ret == KMessageBox::Yes ) {
-#else
-  if( !_error.isNull() ) {
-    ret = QMessageBox::critical( _parent, "", _error, QMessageBox::Yes, QMessageBox::No );
-  } else {
-    ret = QMessageBox::information( _parent, "", _desc, QMessageBox::Yes, QMessageBox::No );
-  }    
-  FILE* fp = assuan_get_data_fp( _ctx );
-  if( ret == QMessageBox::Yes ) {
-#endif // USE_KDE
-    //fputs( "YES", fp );    
-    return ASSUAN_No_Error;
-  } else {
-    //fputs( "NO", fp );
-    return ASSUAN_Not_Confirmed;
-  }
-}
-
-void PinEntryController::slotAccepted()
-{
-  //qDebug("PinEntryController::slotAccepted() NYI");
-  _pinentry->accept();
-}
-
-void PinEntryController::slotRejected()
-{
-  //qDebug("PinEntryController::slotRejected() NYI");
-  _pinentry->reject();
-}
diff --git a/qt/pinentrycontroller.h b/qt/pinentrycontroller.h
deleted file mode 100644 (file)
index 0799995..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/* pinentrycontroller.h - A secure KDE dialog for PIN entry.
-   Copyright (C) 2002 Klarälvdalens Datakonsult AB
-   Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
-   
-   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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA  */
-
-#ifndef __PINENTRYCONTROLLER_H__
-#define __PINENTRYCONTROLLER_H__
-
-#include <cstdio>
-#include <assuan.h>
-
-#include <qobject.h>
-
-class PinEntryDialog;
-
-class PinEntryController : public QObject {
-  Q_OBJECT
-public:
-  PinEntryController( WId parent );
-  virtual ~PinEntryController();
-
-  static int optionHandler( ASSUAN_CONTEXT, const char* key, const char* value );
-
-  static int assuanDesc( ASSUAN_CONTEXT, char* );
-  static int assuanPrompt( ASSUAN_CONTEXT, char* );
-  static int assuanError( ASSUAN_CONTEXT, char* );
-  static int assuanGetpin( ASSUAN_CONTEXT, char* );
-  static int assuanConfirm( ASSUAN_CONTEXT, char* );
-  static int assuanOk( ASSUAN_CONTEXT, char* );
-  static int assuanCancel( ASSUAN_CONTEXT, char* );
-
-public slots:
-  void slotAccepted();
-  void slotRejected();
-
-  virtual void exec();
-private:
-  int getPin( char* );
-  int confirm( char* );
-
-  int registerCommands();
-
-  void createParentWidget( WId parentwid );
-
-  ASSUAN_CONTEXT _ctx;
-
-  PinEntryDialog* _pinentry;
-  QWidget* _parent;
-  QString _desc;
-  QString _prompt;
-  QString _error;
-  QString _ok;
-  QString _cancel;
-};
-
-#endif // __PINENTRYCONTROLLER_H__
index a1532b6..ecd05b5 100644 (file)
    02111-1307, USA  */
 
 #include <qlayout.h>
-#include <qlineedit.h>
 #include <qpushbutton.h>
 #include <qlabel.h>
 #include <qmessagebox.h>
 
+#include "secqlineedit.h"
+
 #include "pinentrydialog.h"
 
 PinEntryDialog::PinEntryDialog( QWidget* parent, const char* name, bool modal )
@@ -46,9 +47,9 @@ PinEntryDialog::PinEntryDialog( QWidget* parent, const char* name, bool modal )
   QBoxLayout* l = new QHBoxLayout( top );
   _prompt = new QLabel( this );
   l->addWidget( _prompt );
-  _edit  = new QLineEdit( this );
+  _edit = new SecQLineEdit( this );
   _edit->setMaxLength( 256 );
-  _edit->setEchoMode( QLineEdit::Password );
+  _edit->setEchoMode( SecQLineEdit::Password );
   l->addWidget( _edit );
 
   l = new QHBoxLayout( top );
@@ -66,6 +67,12 @@ PinEntryDialog::PinEntryDialog( QWidget* parent, const char* name, bool modal )
           this, SIGNAL( accepted() ) );
   connect( _cancel, SIGNAL( clicked() ),
           this, SIGNAL( rejected() ) );
+
+  connect (this, SIGNAL (accepted ()),
+          this, SLOT (accept ()));
+  connect (this, SIGNAL (rejected ()),
+          this, SLOT (reject ()));
+
   _edit->setFocus();
 }
 
@@ -119,12 +126,12 @@ QString PinEntryDialog::error() const
   return _error->text();
 }
 
-void PinEntryDialog::setText( const QString& txt ) 
+void PinEntryDialog::setText( const SecQString& txt ) 
 {
   _edit->setText( txt );
 }
 
-QString PinEntryDialog::text() const 
+SecQString PinEntryDialog::text() const 
 {
   return _edit->text();
 }
index d94bc79..b1eed36 100644 (file)
 
 class QLabel;
 class QPushButton;
-class QLineEdit;
+class SecQLineEdit;
+class SecQString;
 
 class PinEntryDialog : public QDialog {
   Q_OBJECT
 
   Q_PROPERTY( QString description READ description WRITE setDescription )
   Q_PROPERTY( QString error READ error WRITE setError )
-  Q_PROPERTY( QString text READ text WRITE setText )
+    //  Q_PROPERTY( SecQString text READ text WRITE setText )
   Q_PROPERTY( QString prompt READ prompt WRITE setPrompt )
 public:
   friend class PinEntryController; // TODO: remove when assuan lets me use Qt eventloop.
@@ -43,8 +44,8 @@ public:
   void setError( const QString& );
   QString error() const;
 
-  void setText( const QString& );
-  QString text() const;
+  void setText( const SecQString& );
+  SecQString text() const;
 
   void setPrompt( const QString& );
   QString prompt() const;
@@ -66,7 +67,7 @@ private:
   QLabel*    _desc;
   QLabel*    _error;
   QLabel*    _prompt;
-  QLineEdit* _edit;
+  SecQLineEdit* _edit;
   QPushButton* _ok;
   QPushButton* _cancel;  
   bool       _grabbed;
diff --git a/qt/secqlineedit.cpp b/qt/secqlineedit.cpp
new file mode 100644 (file)
index 0000000..ddeba45
--- /dev/null
@@ -0,0 +1,2033 @@
+/* secqlineedit.cpp - Secure version of QLineEdit.
+   Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+   Copyright (C) 2003 g10 Code GmbH
+
+   The license of the original qlineedit.cpp file from which this file
+   is derived can be found below.  Modified by Marcus Brinkmann
+   <marcus@g10code.de>.  All modifications are licensed as follows, so
+   that the intersection of the two licenses is then the GNU General
+   Public License version 2.
+
+   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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA  */
+
+
+/* Undo/Redo is disabled, because it uses unsecure memory for the
+   history buffer.  */
+#define SECURE_NO_UNDO 1
+
+
+/**********************************************************************
+** $Id$
+**
+** Implementation of SecQLineEdit widget class
+**
+** Created : 941011
+**
+** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "secqlineedit.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qfontmetrics.h"
+#include "qpixmap.h"
+#include "qclipboard.h"
+#include "qapplication.h"
+#include "qtimer.h"
+#include "qpopupmenu.h"
+#include "qstringlist.h"
+#include "qguardedptr.h"
+#include "qstyle.h"
+#include "qwhatsthis.h"
+#include "private/qinternal_p.h"
+#include "private/qtextlayout_p.h"
+#include "qvaluevector.h"
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+#include "qaccessible.h"
+#endif
+
+#ifndef QT_NO_ACCEL
+#include "qkeysequence.h"
+#define ACCEL_KEY(k) "\t" + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k ))
+#else
+#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k)
+#endif
+
+#define innerMargin 1
+
+struct SecQLineEditPrivate : public Qt
+{
+    SecQLineEditPrivate( SecQLineEdit *q )
+       : q(q), cursor(0), cursorTimer(0), tripleClickTimer(0), frame(1),
+         cursorVisible(0), separator(0), readOnly(0), modified(0),
+         direction(QChar::DirON), alignment(0),
+         echoMode(0), textDirty(0), selDirty(0),
+         ascent(0), maxLength(32767), menuId(0),
+         hscroll(0),
+         undoState(0), selstart(0), selend(0),
+         imstart(0), imend(0), imselstart(0), imselend(0)
+       {}
+    void init( const SecQString&);
+
+    SecQLineEdit *q;
+    SecQString text;
+    int cursor;
+    int cursorTimer;
+    QPoint tripleClick;
+    int tripleClickTimer;
+    uint frame : 1;
+    uint cursorVisible : 1;
+    uint separator : 1;
+    uint readOnly : 1;
+    uint modified : 1;
+    uint direction : 5;
+    uint alignment : 3;
+    uint echoMode : 2;
+    uint textDirty : 1;
+    uint selDirty : 1;
+    int ascent;
+    int maxLength;
+    int menuId;
+    int hscroll;
+
+    void finishChange( int validateFromState = -1, bool setModified = TRUE );
+
+    void setCursorVisible( bool visible );
+
+
+    // undo/redo handling
+    enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection };
+    struct Command {
+       inline Command(){}
+       inline Command( CommandType type, int pos, QChar c )
+           :type(type),c(c),pos(pos){}
+       uint type : 4;
+       QChar c;
+       int pos;
+    };
+    int undoState;
+    QValueVector<Command> history;
+#ifndef SECURE_NO_UNDO
+    void addCommand( const Command& cmd );
+#endif /* SECURE_NO_UNDO */
+
+    void insert( const SecQString& s );
+    void del( bool wasBackspace = FALSE );
+    void remove( int pos );
+
+    inline void separate() { separator = TRUE; }
+#ifndef SECURE_NO_UNDO
+    inline void undo( int until = -1 ) {
+       if ( !isUndoAvailable() )
+           return;
+       deselect();
+       while ( undoState && undoState > until ) {
+           Command& cmd = history[--undoState];
+           switch ( cmd.type ) {
+           case Insert:
+               text.remove( cmd.pos, 1);
+               cursor = cmd.pos;
+               break;
+           case Remove:
+           case RemoveSelection:
+               text.insert( cmd.pos, cmd.c );
+               cursor = cmd.pos + 1;
+               break;
+           case Delete:
+           case DeleteSelection:
+               text.insert( cmd.pos, cmd.c );
+               cursor = cmd.pos;
+               break;
+           case Separator:
+               continue;
+           }
+           if ( until < 0 && undoState ) {
+               Command& next = history[undoState-1];
+               if ( next.type != cmd.type && next.type < RemoveSelection
+                    && !( cmd.type >= RemoveSelection && next.type != Separator ) )
+                   break;
+           }
+       }
+       modified = ( undoState != 0 );
+       textDirty = TRUE;
+    }
+    inline void redo() {
+       if ( !isRedoAvailable() )
+           return;
+       deselect();
+       while ( undoState < (int)history.size() ) {
+           Command& cmd = history[undoState++];
+           switch ( cmd.type ) {
+           case Insert:
+               text.insert( cmd.pos, cmd.c );
+               cursor = cmd.pos + 1;
+               break;
+           case Remove:
+           case Delete:
+           case RemoveSelection:
+           case DeleteSelection:
+               text.remove( cmd.pos, 1 );
+               cursor = cmd.pos;
+               break;
+           case Separator:
+               continue;
+           }
+           if ( undoState < (int)history.size() ) {
+               Command& next = history[undoState];
+               if ( next.type != cmd.type && cmd.type < RemoveSelection
+                    && !( next.type >= RemoveSelection && cmd.type != Separator ) )
+                   break;
+           }
+       }
+       textDirty = TRUE;
+    }
+#endif  /* SECURE_NO_UNDO */
+    inline bool isUndoAvailable() const { return !readOnly && undoState; }
+    inline bool isRedoAvailable() const { return !readOnly && undoState < (int)history.size(); }
+
+
+    // bidi
+    inline bool isRightToLeft() const { return direction==QChar::DirON?text.isRightToLeft():(direction==QChar::DirR); }
+
+    // selection
+    int selstart, selend;
+    inline bool allSelected() const { return !text.isEmpty() && selstart == 0 && selend == (int)text.length(); }
+    inline bool hasSelectedText() const { return !text.isEmpty() && selend > selstart; }
+    inline void deselect() { selDirty |= (selend > selstart); selstart = selend = 0; }
+    void removeSelectedText();
+#ifndef QT_NO_CLIPBOARD
+    void copy( bool clipboard = TRUE ) const;
+#endif
+    inline bool inSelection( int x ) const
+    { if ( selstart >= selend ) return FALSE;
+    int pos = xToPos( x, QTextItem::OnCharacters );  return pos >= selstart && pos < selend; }
+
+    // input methods
+    int imstart, imend, imselstart, imselend;
+
+    // complex text layout
+    QTextLayout textLayout;
+    void updateTextLayout();
+    void moveCursor( int pos, bool mark = FALSE );
+    void setText( const SecQString& txt );
+    int xToPos( int x, QTextItem::CursorPosition = QTextItem::BetweenCharacters ) const;
+    inline int visualAlignment() const { return alignment ? alignment : int( isRightToLeft() ? AlignRight : AlignLeft ); }
+    QRect cursorRect() const;
+    void updateMicroFocusHint();
+
+};
+
+
+/*!
+    \class SecQLineEdit
+    \brief The SecQLineEdit widget is a one-line text editor.
+
+    \ingroup basic
+    \mainclass
+
+    A line edit allows the user to enter and edit a single line of
+    plain text with a useful collection of editing functions,
+    including undo and redo, cut and paste,
+
+    By changing the echoMode() of a line edit, it can also be used as
+    a "write-only" field, for inputs such as passwords.
+
+    The length of the text can be constrained to maxLength().
+
+    A related class is QTextEdit which allows multi-line, rich-text
+    editing.
+
+    You can change the text with setText() or insert(). The text is
+    retrieved with text(); the displayed text (which may be different,
+    see \l{EchoMode}) is retrieved with displayText(). Text can be
+    selected with setSelection() or selectAll(), and the selection can
+    be cut(), copy()ied and paste()d. The text can be aligned with
+    setAlignment().
+
+    When the text changes the textChanged() signal is emitted; when
+    the Return or Enter key is pressed the returnPressed() signal is
+    emitted.
+
+    By default, SecQLineEdits have a frame as specified by the Windows
+    and Motif style guides; you can turn it off by calling
+    setFrame(FALSE).
+
+    The default key bindings are described below.
+    \target desc
+    \table
+    \header \i Keypress \i Action
+    \row \i Left Arrow \i Moves the cursor one character to the left.
+    \row \i Shift+Left Arrow \i Moves and selects text one character to the left.
+    \row \i Right Arrow \i Moves the cursor one character to the right.
+    \row \i Shift+Right Arrow \i Moves and selects text one character to the right.
+    \row \i Home \i Moves the cursor to the beginning of the line.
+    \row \i End \i Moves the cursor to the end of the line.
+    \row \i Backspace \i Deletes the character to the left of the cursor.
+    \row \i Ctrl+Backspace \i Deletes the word to the left of the cursor.
+    \row \i Delete \i Deletes the character to the right of the cursor.
+    \row \i Ctrl+Delete \i Deletes the word to the right of the cursor.
+    \row \i Ctrl+A \i Moves the cursor to the beginning of the line.
+    \row \i Ctrl+B \i Moves the cursor one character to the left.
+    \row \i Ctrl+C \i Copies the selected text to the clipboard.
+                     (Windows also supports Ctrl+Insert for this operation.)
+    \row \i Ctrl+D \i Deletes the character to the right of the cursor.
+    \row \i Ctrl+E \i Moves the cursor to the end of the line.
+    \row \i Ctrl+F \i Moves the cursor one character to the right.
+    \row \i Ctrl+H \i Deletes the character to the left of the cursor.
+    \row \i Ctrl+K \i Deletes to the end of the line.
+    \row \i Ctrl+V \i Pastes the clipboard text into line edit.
+                     (Windows also supports Shift+Insert for this operation.)
+    \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard.
+                     (Windows also supports Shift+Delete for this operation.)
+    \row \i Ctrl+Z \i Undoes the last operation.
+    \row \i Ctrl+Y \i Redoes the last undone operation.
+    \endtable
+
+    Any other key sequence that represents a valid character, will
+    cause the character to be inserted into the line edit.
+
+    <img src=qlined-m.png> <img src=qlined-w.png>
+
+    \sa QTextEdit QLabel QComboBox
+       \link guibooks.html#fowler GUI Design Handbook: Field, Entry\endlink
+*/
+
+
+/*!
+    \fn void SecQLineEdit::textChanged( const SecQString& )
+
+    This signal is emitted whenever the text changes. The argument is
+    the new text.
+*/
+
+/*!
+    \fn void SecQLineEdit::selectionChanged()
+
+    This signal is emitted whenever the selection changes.
+
+    \sa hasSelectedText(), selectedText()
+*/
+
+/*!
+    \fn void SecQLineEdit::lostFocus()
+
+    This signal is emitted when the line edit has lost focus.
+
+    \sa hasFocus(), QWidget::focusInEvent(), QWidget::focusOutEvent()
+*/
+
+
+
+/*!
+    Constructs a line edit with no text.
+
+    The maximum text length is set to 32767 characters.
+
+    The \a parent and \a name arguments are sent to the QWidget constructor.
+
+    \sa setText(), setMaxLength()
+*/
+
+SecQLineEdit::SecQLineEdit( QWidget* parent, const char* name )
+    : QFrame( parent, name, WNoAutoErase ), d(new SecQLineEditPrivate( this ))
+{
+    d->init( SecQString::null );
+}
+
+/*!
+    Constructs a line edit containing the text \a contents.
+
+    The cursor position is set to the end of the line and the maximum
+    text length to 32767 characters.
+
+    The \a parent and \a name arguments are sent to the QWidget
+    constructor.
+
+    \sa text(), setMaxLength()
+*/
+
+SecQLineEdit::SecQLineEdit( const SecQString& contents, QWidget* parent, const char* name )
+    : QFrame( parent, name, WNoAutoErase ), d(new SecQLineEditPrivate( this ))
+{
+    d->init( contents );
+}
+
+/*!
+    Destroys the line edit.
+*/
+
+SecQLineEdit::~SecQLineEdit()
+{
+    delete d;
+}
+
+
+/*!
+    \property SecQLineEdit::text
+    \brief the line edit's text
+
+    Setting this property clears the selection, clears the undo/redo
+    history, moves the cursor to the end of the line and resets the
+    \c modified property to FALSE.
+
+    The text is truncated to maxLength() length.
+
+    \sa insert()
+*/
+SecQString SecQLineEdit::text() const
+{
+    return ( d->text.isNull() ? SecQString ("") : d->text );
+}
+
+void SecQLineEdit::setText( const SecQString& text)
+{
+    resetInputContext();
+    d->setText( text );
+    d->modified = FALSE;
+    d->finishChange( -1, FALSE );
+}
+
+
+/*!
+    \property SecQLineEdit::displayText
+    \brief the displayed text
+
+    If \c EchoMode is \c Normal this returns the same as text(); if
+    \c EchoMode is \c Password it returns a string of asterisks
+    text().length() characters long, e.g. "******"; if \c EchoMode is
+    \c NoEcho returns an empty string, "".
+
+    \sa setEchoMode() text() EchoMode
+*/
+
+QString SecQLineEdit::displayText() const
+{
+    if ( d->echoMode == NoEcho )
+       return QString::fromLatin1("");
+
+    QChar pwd_char = QChar (style().styleHint( QStyle::SH_LineEdit_PasswordCharacter, this));
+    QString res;
+    res.fill (pwd_char, d->text.length ());
+    return res;
+}
+
+
+/*!
+    \property SecQLineEdit::maxLength
+    \brief the maximum permitted length of the text
+
+    If the text is too long, it is truncated at the limit.
+
+    If truncation occurs any selected text will be unselected, the
+    cursor position is set to 0 and the first part of the string is
+    shown.
+*/
+
+int SecQLineEdit::maxLength() const
+{
+    return d->maxLength;
+}
+
+void SecQLineEdit::setMaxLength( int maxLength )
+{
+    d->maxLength = maxLength;
+    setText( d->text );
+}
+
+
+
+/*!
+    \property SecQLineEdit::frame
+    \brief whether the line edit draws itself with a frame
+
+    If enabled (the default) the line edit draws itself inside a
+    two-pixel frame, otherwise the line edit draws itself without any
+    frame.
+*/
+bool SecQLineEdit::frame() const
+{
+    return frameShape() != NoFrame;
+}
+
+
+void SecQLineEdit::setFrame( bool enable )
+{
+    setFrameStyle( enable ? ( LineEditPanel | Sunken ) : NoFrame  );
+}
+
+
+/*!
+    \enum SecQLineEdit::EchoMode
+
+    This enum type describes how a line edit should display its
+    contents.
+
+    \value Normal   Display characters as they are entered. This is the
+                   default.
+    \value NoEcho   Do not display anything. This may be appropriate
+                   for passwords where even the length of the
+                   password should be kept secret.
+    \value Password  Display asterisks instead of the characters
+                   actually entered.
+
+    \sa setEchoMode() echoMode()
+*/
+
+
+/*!
+    \property SecQLineEdit::echoMode
+    \brief the line edit's echo mode
+
+    The initial setting is \c Normal, but SecQLineEdit also supports \c
+    NoEcho and \c Password modes.
+
+    The widget's display and the ability to copy the text is affected
+    by this setting.
+
+    \sa EchoMode displayText()
+*/
+
+SecQLineEdit::EchoMode SecQLineEdit::echoMode() const
+{
+    return (EchoMode) d->echoMode;
+}
+
+void SecQLineEdit::setEchoMode( EchoMode mode )
+{
+    d->echoMode = mode;
+    d->updateTextLayout();
+    update();
+}
+
+
+
+/*!
+    Returns a recommended size for the widget.
+
+    The width returned, in pixels, is usually enough for about 15 to
+    20 characters.
+*/
+
+QSize SecQLineEdit::sizeHint() const
+{
+    constPolish();
+    QFontMetrics fm( font() );
+    int h = QMAX(fm.lineSpacing(), 14) + 2*innerMargin;
+    int w = fm.width( 'x' ) * 17; // "some"
+    int m = frameWidth() * 2;
+    return (style().sizeFromContents(QStyle::CT_LineEdit, this,
+                                    QSize( w + m, h + m ).
+                                    expandedTo(QApplication::globalStrut())));
+}
+
+
+/*!
+    Returns a minimum size for the line edit.
+
+    The width returned is enough for at least one character.
+*/
+
+QSize SecQLineEdit::minimumSizeHint() const
+{
+    constPolish();
+    QFontMetrics fm = fontMetrics();
+    int h = fm.height() + QMAX( 2*innerMargin, fm.leading() );
+    int w = fm.maxWidth();
+    int m = frameWidth() * 2;
+    return QSize( w + m, h + m );
+}
+
+
+/*!
+    \property SecQLineEdit::cursorPosition
+    \brief the current cursor position for this line edit
+
+    Setting the cursor position causes a repaint when appropriate.
+*/
+
+int SecQLineEdit::cursorPosition() const
+{
+    return d->cursor;
+}
+
+
+void SecQLineEdit::setCursorPosition( int pos )
+{
+    if ( pos <= (int) d->text.length() )
+       d->moveCursor( pos );
+}
+
+
+/*!
+    \property SecQLineEdit::alignment
+    \brief the alignment of the line edit
+
+    Possible Values are \c Qt::AlignAuto, \c Qt::AlignLeft, \c
+    Qt::AlignRight and \c Qt::AlignHCenter.
+
+    Attempting to set the alignment to an illegal flag combination
+    does nothing.
+
+    \sa Qt::AlignmentFlags
+*/
+
+int SecQLineEdit::alignment() const
+{
+    return d->alignment;
+}
+
+void SecQLineEdit::setAlignment( int flag )
+{
+    d->alignment = flag & 0x7;
+    update();
+}
+
+
+/*!
+  \obsolete
+  \fn void SecQLineEdit::cursorRight( bool, int )
+
+  Use cursorForward() instead.
+
+  \sa cursorForward()
+*/
+
+/*!
+  \obsolete
+  \fn void SecQLineEdit::cursorLeft( bool, int )
+  For compatibilty with older applications only. Use cursorBackward()
+  instead.
+  \sa cursorBackward()
+*/
+
+/*!
+    Moves the cursor forward \a steps characters. If \a mark is TRUE
+    each character moved over is added to the selection; if \a mark is
+    FALSE the selection is cleared.
+
+    \sa cursorBackward()
+*/
+
+void SecQLineEdit::cursorForward( bool mark, int steps )
+{
+    int cursor = d->cursor;
+    if ( steps > 0 ) {
+       while( steps-- )
+           cursor = d->textLayout.nextCursorPosition( cursor );
+    } else if ( steps < 0 ) {
+       while ( steps++ )
+           cursor = d->textLayout.previousCursorPosition( cursor );
+    }
+    d->moveCursor( cursor, mark );
+}
+
+
+/*!
+    Moves the cursor back \a steps characters. If \a mark is TRUE each
+    character moved over is added to the selection; if \a mark is
+    FALSE the selection is cleared.
+
+    \sa cursorForward()
+*/
+void SecQLineEdit::cursorBackward( bool mark, int steps )
+{
+    cursorForward( mark, -steps );
+}
+
+/*!
+    Moves the cursor one word forward. If \a mark is TRUE, the word is
+    also selected.
+
+    \sa cursorWordBackward()
+*/
+void SecQLineEdit::cursorWordForward( bool mark )
+{
+    d->moveCursor( d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords), mark );
+}
+
+/*!
+    Moves the cursor one word backward. If \a mark is TRUE, the word
+    is also selected.
+
+    \sa cursorWordForward()
+*/
+
+void SecQLineEdit::cursorWordBackward( bool mark )
+{
+    d->moveCursor( d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords), mark );
+}
+
+
+/*!
+    If no text is selected, deletes the character to the left of the
+    text cursor and moves the cursor one position to the left. If any
+    text is selected, the cursor is moved to the beginning of the
+    selected text and the selected text is deleted.
+
+    \sa del()
+*/
+void SecQLineEdit::backspace()
+{
+    int priorState = d->undoState;
+    if ( d->hasSelectedText() ) {
+       d->removeSelectedText();
+    } else if ( d->cursor ) {
+           --d->cursor;
+           d->del( TRUE );
+    }
+    d->finishChange( priorState );
+}
+
+/*!
+    If no text is selected, deletes the character to the right of the
+    text cursor. If any text is selected, the cursor is moved to the
+    beginning of the selected text and the selected text is deleted.
+
+    \sa backspace()
+*/
+
+void SecQLineEdit::del()
+{
+    int priorState = d->undoState;
+    if ( d->hasSelectedText() ) {
+       d->removeSelectedText();
+    } else {
+       int n = d->textLayout.nextCursorPosition( d->cursor ) - d->cursor;
+       while ( n-- )
+           d->del();
+    }
+    d->finishChange( priorState );
+}
+
+/*!
+    Moves the text cursor to the beginning of the line unless it is
+    already there. If \a mark is TRUE, text is selected towards the
+    first position; otherwise, any selected text is unselected if the
+    cursor is moved.
+
+    \sa end()
+*/
+
+void SecQLineEdit::home( bool mark )
+{
+    d->moveCursor( 0, mark );
+}
+
+/*!
+    Moves the text cursor to the end of the line unless it is already
+    there. If \a mark is TRUE, text is selected towards the last
+    position; otherwise, any selected text is unselected if the cursor
+    is moved.
+
+    \sa home()
+*/
+
+void SecQLineEdit::end( bool mark )
+{
+    d->moveCursor( d->text.length(), mark );
+}
+
+
+/*!
+    \property SecQLineEdit::modified
+    \brief whether the line edit's contents has been modified by the user
+
+    The modified flag is never read by SecQLineEdit; it has a default value
+    of FALSE and is changed to TRUE whenever the user changes the line
+    edit's contents.
+
+    This is useful for things that need to provide a default value but
+    do not start out knowing what the default should be (perhaps it
+    depends on other fields on the form). Start the line edit without
+    the best default, and when the default is known, if modified()
+    returns FALSE (the user hasn't entered any text), insert the
+    default value.
+
+    Calling clearModified() or setText() resets the modified flag to
+    FALSE.
+*/
+
+bool SecQLineEdit::isModified() const
+{
+    return d->modified;
+}
+
+/*!
+    Resets the modified flag to FALSE.
+
+    \sa isModified()
+*/
+void SecQLineEdit::clearModified()
+{
+    d->modified = FALSE;
+    d->history.clear();
+    d->undoState = 0;
+}
+
+/*!
+  \obsolete
+  \property SecQLineEdit::edited
+  \brief whether the line edit has been edited. Use modified instead.
+*/
+bool SecQLineEdit::edited() const { return d->modified; }
+void SecQLineEdit::setEdited( bool on ) { d->modified = on; }
+
+/*!
+    \obsolete
+    \property SecQLineEdit::hasMarkedText
+    \brief whether part of the text has been selected by the user. Use hasSelectedText instead.
+*/
+
+/*!
+    \property SecQLineEdit::hasSelectedText
+    \brief whether there is any text selected
+
+    hasSelectedText() returns TRUE if some or all of the text has been
+    selected by the user; otherwise returns FALSE.
+
+    \sa selectedText()
+*/
+
+
+bool SecQLineEdit::hasSelectedText() const
+{
+    return d->hasSelectedText();
+}
+
+/*!
+  \obsolete
+  \property SecQLineEdit::markedText
+  \brief the text selected by the user. Use selectedText instead.
+*/
+
+/*!
+    \property SecQLineEdit::selectedText
+    \brief the selected text
+
+    If there is no selected text this property's value is
+    QString::null.
+
+    \sa hasSelectedText()
+*/
+
+SecQString SecQLineEdit::selectedText() const
+{
+    if ( d->hasSelectedText() )
+       return d->text.mid( d->selstart, d->selend - d->selstart );
+    return SecQString::null;
+}
+
+/*!
+    selectionStart() returns the index of the first selected character in the
+    line edit or -1 if no text is selected.
+
+    \sa selectedText()
+*/
+
+int SecQLineEdit::selectionStart() const
+{
+    return d->hasSelectedText() ? d->selstart : -1;
+}
+
+
+/*!
+    Selects text from position \a start and for \a length characters.
+
+    \sa deselect() selectAll()
+*/
+
+void SecQLineEdit::setSelection( int start, int length )
+{
+    if ( start < 0 || start > (int)d->text.length() || length < 0 ) {
+       d->selstart = d->selend = 0;
+    } else {
+       d->selstart = start;
+       d->selend = QMIN( start + length, (int)d->text.length() );
+       d->cursor = d->selend;
+    }
+    update();
+}
+
+
+/*!
+    \property SecQLineEdit::undoAvailable
+    \brief whether undo is available
+*/
+
+bool SecQLineEdit::isUndoAvailable() const
+{
+    return d->isUndoAvailable();
+}
+
+/*!
+    \property SecQLineEdit::redoAvailable
+    \brief whether redo is available
+*/
+
+bool SecQLineEdit::isRedoAvailable() const
+{
+    return d->isRedoAvailable();
+}
+
+/*!
+    Selects all the text (i.e. highlights it) and moves the cursor to
+    the end. This is useful when a default value has been inserted
+    because if the user types before clicking on the widget, the
+    selected text will be deleted.
+
+    \sa setSelection() deselect()
+*/
+
+void SecQLineEdit::selectAll()
+{
+    d->selstart = d->selend = d->cursor = 0;
+    d->moveCursor( d->text.length(), TRUE );
+}
+
+/*!
+    Deselects any selected text.
+
+    \sa setSelection() selectAll()
+*/
+
+void SecQLineEdit::deselect()
+{
+    d->deselect();
+    d->finishChange();
+}
+
+
+/*!
+    Deletes any selected text, inserts \a newText and sets it as the
+    new contents of the line edit.
+*/
+void SecQLineEdit::insert( const SecQString &newText )
+{
+//     q->resetInputContext(); //#### FIX ME IN QT
+    int priorState = d->undoState;
+    d->removeSelectedText();
+    d->insert( newText );
+    d->finishChange( priorState );
+}
+
+/*!
+    Clears the contents of the line edit.
+*/
+void SecQLineEdit::clear()
+{
+    int priorState = d->undoState;
+    resetInputContext();
+    d->selstart = 0;
+    d->selend = d->text.length();
+    d->removeSelectedText();
+    d->separate();
+    d->finishChange( priorState );
+}
+
+/*!
+    Undoes the last operation if undo is \link
+    SecQLineEdit::undoAvailable available\endlink. Deselects any current
+    selection, and updates the selection start to the current cursor
+    position.
+*/
+void SecQLineEdit::undo()
+{
+#ifndef SECURE_NO_UNDO
+    resetInputContext();
+    d->undo();
+    d->finishChange( -1, FALSE );
+#endif
+}
+
+/*!
+    Redoes the last operation if redo is \link
+    SecQLineEdit::redoAvailable available\endlink.
+*/
+void SecQLineEdit::redo()
+{
+#ifndef SECURE_NO_UNDO
+    resetInputContext();
+    d->redo();
+    d->finishChange();
+#endif
+}
+
+
+/*!
+    \property SecQLineEdit::readOnly
+    \brief whether the line edit is read only.
+
+    In read-only mode, the user can still copy the text to the
+    clipboard (if echoMode() is \c Normal), but cannot edit it.
+
+    SecQLineEdit does not show a cursor in read-only mode.
+
+    \sa setEnabled()
+*/
+
+bool SecQLineEdit::isReadOnly() const
+{
+    return d->readOnly;
+}
+
+void SecQLineEdit::setReadOnly( bool enable )
+{
+    d->readOnly = enable;
+#ifndef QT_NO_CURSOR
+    setCursor( enable ? arrowCursor : ibeamCursor );
+#endif
+    update();
+}
+
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+    Copies the selected text to the clipboard and deletes it, if there
+    is any, and if echoMode() is \c Normal.
+
+    \sa copy() paste() setValidator()
+*/
+
+void SecQLineEdit::cut()
+{
+    if ( hasSelectedText() ) {
+       copy();
+       del();
+    }
+}
+
+
+/*!
+    Copies the selected text to the clipboard, if there is any, and if
+    echoMode() is \c Normal.
+
+    \sa cut() paste()
+*/
+
+void SecQLineEdit::copy() const
+{
+    d->copy();
+}
+
+/*!
+    Inserts the clipboard's text at the cursor position, deleting any
+    selected text, providing the line edit is not \link
+    SecQLineEdit::readOnly read-only\endlink.
+
+    \sa copy() cut()
+*/
+
+void SecQLineEdit::paste()
+{
+    d->removeSelectedText();
+    insert( QApplication::clipboard()->text( QClipboard::Clipboard ) );
+}
+
+void SecQLineEditPrivate::copy( bool clipboard ) const
+{
+#ifndef SECURE
+    QString t = q->selectedText();
+    if ( !t.isEmpty() && echoMode == SecQLineEdit::Normal ) {
+       q->disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), q, 0);
+       QApplication::clipboard()->setText( t, clipboard ? QClipboard::Clipboard : QClipboard::Selection );
+       q->connect( QApplication::clipboard(), SIGNAL(selectionChanged()),
+                q, SLOT(clipboardChanged()) );
+    }
+#endif
+}
+
+#endif // !QT_NO_CLIPBOARD
+
+/*!\reimp
+*/
+
+void SecQLineEdit::resizeEvent( QResizeEvent *e )
+{
+    QFrame::resizeEvent( e );
+}
+
+/*! \reimp
+*/
+bool SecQLineEdit::event( QEvent * e )
+{
+    if ( e->type() == QEvent::AccelOverride && !d->readOnly ) {
+       QKeyEvent* ke = (QKeyEvent*) e;
+       if ( ke->state() == NoButton || ke->state() == ShiftButton
+            || ke->state() == Keypad ) {
+           if ( ke->key() < Key_Escape ) {
+               ke->accept();
+           } else if ( ke->state() == NoButton
+                       || ke->state() == ShiftButton ) {
+               switch ( ke->key() ) {
+               case Key_Delete:
+               case Key_Home:
+               case Key_End:
+               case Key_Backspace:
+               case Key_Left:
+               case Key_Right:
+                   ke->accept();
+               default:
+                   break;
+               }
+           }
+       } else if ( ke->state() & ControlButton ) {
+           switch ( ke->key() ) {
+// Those are too frequently used for application functionality
+/*         case Key_A:
+           case Key_B:
+           case Key_D:
+           case Key_E:
+           case Key_F:
+           case Key_H:
+           case Key_K:
+*/
+           case Key_C:
+           case Key_V:
+           case Key_X:
+           case Key_Y:
+           case Key_Z:
+           case Key_Left:
+           case Key_Right:
+#if defined (Q_WS_WIN)
+           case Key_Insert:
+           case Key_Delete:
+#endif
+               ke->accept();
+           default:
+               break;
+           }
+       }
+    } else if ( e->type() == QEvent::Timer ) {
+       // should be timerEvent, is here for binary compatibility
+       int timerId = ((QTimerEvent*)e)->timerId();
+       if ( timerId == d->cursorTimer ) {
+           if(!hasSelectedText() || style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ))
+               d->setCursorVisible( !d->cursorVisible );
+       } else if ( timerId == d->tripleClickTimer ) {
+           killTimer( d->tripleClickTimer );
+           d->tripleClickTimer = 0;
+       }
+    }
+    return QWidget::event( e );
+}
+
+/*! \reimp
+*/
+void SecQLineEdit::mousePressEvent( QMouseEvent* e )
+{
+    if ( e->button() == RightButton )
+       return;
+    if ( d->tripleClickTimer && ( e->pos() - d->tripleClick ).manhattanLength() <
+        QApplication::startDragDistance() ) {
+       selectAll();
+       return;
+    }
+    bool mark = e->state() & ShiftButton;
+    int cursor = d->xToPos( e->pos().x() );
+    d->moveCursor( cursor, mark );
+}
+
+/*! \reimp
+*/
+void SecQLineEdit::mouseMoveEvent( QMouseEvent * e )
+{
+
+#ifndef QT_NO_CURSOR
+    if ( ( e->state() & MouseButtonMask ) == 0 ) {
+       if ( !d->readOnly )
+         setCursor( ( d->inSelection( e->pos().x() ) ? arrowCursor : ibeamCursor ) );
+    }
+#endif
+
+    if ( e->state() & LeftButton ) {
+      d->moveCursor( d->xToPos( e->pos().x() ), TRUE );
+    }
+}
+
+/*! \reimp
+*/
+void SecQLineEdit::mouseReleaseEvent( QMouseEvent* e )
+{
+#ifndef QT_NO_CLIPBOARD
+    if (QApplication::clipboard()->supportsSelection() ) {
+       if ( e->button() == LeftButton ) {
+           d->copy( FALSE );
+       } else if ( !d->readOnly && e->button() == MidButton ) {
+           d->deselect();
+           insert( QApplication::clipboard()->text( QClipboard::Selection ) );
+       }
+    }
+#endif
+}
+
+/*! \reimp
+*/
+void SecQLineEdit::mouseDoubleClickEvent( QMouseEvent* e )
+{
+    if ( e->button() == Qt::LeftButton ) {
+       deselect();
+       d->cursor = d->xToPos( e->pos().x() );
+       d->cursor = d->textLayout.previousCursorPosition( d->cursor, QTextLayout::SkipWords );
+       // ## text layout should support end of words.
+       int end = d->textLayout.nextCursorPosition( d->cursor, QTextLayout::SkipWords );
+       while ( end > d->cursor && d->text[end-1].isSpace() )
+           --end;
+       d->moveCursor( end, TRUE );
+       d->tripleClickTimer = startTimer( QApplication::doubleClickInterval() );
+       d->tripleClick = e->pos();
+    }
+}
+
+/*!
+    \fn void  SecQLineEdit::returnPressed()
+
+    This signal is emitted when the Return or Enter key is pressed.
+*/
+
+/*!
+    Converts key press event \a e into a line edit action.
+
+    If Return or Enter is pressed the signal returnPressed() is
+    emitted.
+
+    The default key bindings are listed in the \link #desc detailed
+    description.\endlink
+*/
+
+void SecQLineEdit::keyPressEvent( QKeyEvent * e )
+{
+    d->setCursorVisible( TRUE );
+    if ( e->key() == Key_Enter || e->key() == Key_Return ) {
+       emit returnPressed();
+       e->ignore();
+       return;
+    }
+    if ( !d->readOnly ) {
+       QString t = e->text();
+       if ( !t.isEmpty() && (!e->ascii() || e->ascii()>=32) &&
+            e->key() != Key_Delete &&
+            e->key() != Key_Backspace ) {
+#ifdef Q_WS_X11
+           extern bool qt_hebrew_keyboard_hack;
+           if ( qt_hebrew_keyboard_hack ) {
+               // the X11 keyboard layout is broken and does not reverse
+               // braces correctly. This is a hack to get halfway correct
+               // behaviour
+               if ( d->isRightToLeft() ) {
+                   QChar *c = (QChar *)t.unicode();
+                   int l = t.length();
+                   while( l-- ) {
+                       if ( c->mirrored() )
+                           *c = c->mirroredChar();
+                       c++;
+                   }
+               }
+           }
+#endif
+           insert( t );
+           return;
+       }
+    }
+    bool unknown = FALSE;
+    if ( e->state() & ControlButton ) {
+       switch ( e->key() ) {
+       case Key_A:
+#if defined(Q_WS_X11)
+           home( e->state() & ShiftButton );
+#else
+           selectAll();
+#endif
+           break;
+       case Key_B:
+           cursorForward( e->state() & ShiftButton, -1 );
+           break;
+#ifndef QT_NO_CLIPBOARD
+       case Key_C:
+           copy();
+           break;
+#endif
+       case Key_D:
+           if ( !d->readOnly ) {
+               del();
+           }
+           break;
+       case Key_E:
+           end( e->state() & ShiftButton );
+           break;
+       case Key_F:
+           cursorForward( e->state() & ShiftButton, 1 );
+           break;
+       case Key_H:
+           if ( !d->readOnly ) {
+               backspace();
+           }
+           break;
+       case Key_K:
+           if ( !d->readOnly ) {
+               int priorState = d->undoState;
+               d->deselect();
+               while ( d->cursor < (int) d->text.length() )
+                   d->del();
+               d->finishChange( priorState );
+           }
+           break;
+#if defined(Q_WS_X11)
+        case Key_U:
+           if ( !d->readOnly )
+               clear();
+           break;
+#endif
+#ifndef QT_NO_CLIPBOARD
+       case Key_V:
+           if ( !d->readOnly )
+               paste();
+           break;
+       case Key_X:
+           if ( !d->readOnly && d->hasSelectedText() && echoMode() == Normal ) {
+               copy();
+               del();
+           }
+           break;
+#if defined (Q_WS_WIN)
+       case Key_Insert:
+           copy();
+           break;
+#endif
+#endif
+       case Key_Delete:
+           if ( !d->readOnly ) {
+               cursorWordForward( TRUE );
+               del();
+           }
+           break;
+       case Key_Backspace:
+           if ( !d->readOnly ) {
+               cursorWordBackward( TRUE );
+               del();
+           }
+           break;
+       case Key_Right:
+       case Key_Left:
+           if ( d->isRightToLeft() == (e->key() == Key_Right) ) {
+               if ( echoMode() == Normal )
+                   cursorWordBackward( e->state() & ShiftButton );
+               else
+                   home( e->state() & ShiftButton );
+           } else {
+               if ( echoMode() == Normal )
+                   cursorWordForward( e->state() & ShiftButton );
+               else
+                   end( e->state() & ShiftButton );
+           }
+           break;
+       case Key_Z:
+           if ( !d->readOnly ) {
+               if(e->state() & ShiftButton)
+                   redo();
+               else
+                   undo();
+           }
+           break;
+       case Key_Y:
+           if ( !d->readOnly )
+               redo();
+           break;
+       default:
+           unknown = TRUE;
+       }
+    } else { // ### check for *no* modifier
+       switch ( e->key() ) {
+       case Key_Shift:
+           // ### TODO
+           break;
+       case Key_Left:
+       case Key_Right: {
+           int step =  (d->isRightToLeft() == (e->key() == Key_Right)) ? -1 : 1;
+           cursorForward( e->state() & ShiftButton, step );
+       }
+       break;
+       case Key_Backspace:
+           if ( !d->readOnly ) {
+               backspace();
+           }
+           break;
+       case Key_Home:
+#ifdef Q_WS_MACX
+       case Key_Up:
+#endif
+           home( e->state() & ShiftButton );
+           break;
+       case Key_End:
+#ifdef Q_WS_MACX
+       case Key_Down:
+#endif
+           end( e->state() & ShiftButton );
+           break;
+       case Key_Delete:
+           if ( !d->readOnly ) {
+#if defined (Q_WS_WIN)
+               if ( e->state() & ShiftButton ) {
+                   cut();
+                   break;
+               }
+#endif
+               del();
+           }
+           break;
+#if defined (Q_WS_WIN)
+       case Key_Insert:
+           if ( !d->readOnly && e->state() & ShiftButton )
+               paste();
+           else
+               unknown = TRUE;
+           break;
+#endif
+       case Key_F14: // Undo key on Sun keyboards
+           if ( !d->readOnly )
+               undo();
+           break;
+#ifndef QT_NO_CLIPBOARD
+       case Key_F16: // Copy key on Sun keyboards
+           copy();
+           break;
+       case Key_F18: // Paste key on Sun keyboards
+           if ( !d->readOnly )
+               paste();
+           break;
+       case Key_F20: // Cut key on Sun keyboards
+           if ( !d->readOnly && hasSelectedText() && echoMode() == Normal ) {
+               copy();
+               del();
+           }
+           break;
+#endif
+       default:
+           unknown = TRUE;
+       }
+    }
+    if ( e->key() == Key_Direction_L )
+       d->direction = QChar::DirL;
+    else if ( e->key() == Key_Direction_R )
+       d->direction = QChar::DirR;
+
+    if ( unknown )
+       e->ignore();
+}
+
+/*! \reimp
+ */
+void SecQLineEdit::imStartEvent( QIMEvent *e )
+{
+    if ( d->readOnly ) {
+       e->ignore();
+       return;
+    }
+    d->removeSelectedText();
+    d->updateMicroFocusHint();
+    d->imstart = d->imend = d->imselstart = d->imselend = d->cursor;
+}
+
+/*! \reimp
+ */
+void SecQLineEdit::imComposeEvent( QIMEvent *e )
+{
+    if ( d->readOnly ) {
+       e->ignore();
+    } else {
+       d->text.replace( d->imstart, d->imend - d->imstart, e->text() );
+       d->imend = d->imstart + e->text().length();
+       d->imselstart = d->imstart + e->cursorPos();
+       d->imselend = d->imselstart + e->selectionLength();
+       d->cursor = e->selectionLength() ? d->imend : d->imselend;
+       d->updateTextLayout();
+       update();
+    }
+}
+
+/*! \reimp
+ */
+void SecQLineEdit::imEndEvent( QIMEvent *e )
+{
+    if ( d->readOnly ) {
+       e->ignore();
+    } else {
+       d->text.remove( d->imstart, d->imend - d->imstart );
+       d->cursor = d->imselstart = d->imselend = d->imend = d->imstart;
+       d->textDirty = TRUE;
+       insert( e->text() );
+    }
+}
+
+/*!\reimp
+*/
+
+void SecQLineEdit::focusInEvent( QFocusEvent* e )
+{
+    if ( e->reason() == QFocusEvent::Tab ||
+        e->reason() == QFocusEvent::Backtab  ||
+        e->reason() == QFocusEvent::Shortcut )
+       selectAll();
+    if ( !d->cursorTimer ) {
+       int cft = QApplication::cursorFlashTime();
+       d->cursorTimer = cft ? startTimer( cft/2 ) : -1;
+    }
+    d->updateMicroFocusHint();
+}
+
+/*!\reimp
+*/
+
+void SecQLineEdit::focusOutEvent( QFocusEvent* e )
+{
+    if ( e->reason() != QFocusEvent::ActiveWindow &&
+        e->reason() != QFocusEvent::Popup )
+       deselect();
+    d->setCursorVisible( FALSE );
+    if ( d->cursorTimer > 0 )
+       killTimer( d->cursorTimer );
+    d->cursorTimer = 0;
+    emit lostFocus();
+}
+
+/*!\reimp
+*/
+void SecQLineEdit::drawContents( QPainter *p )
+{
+    const QColorGroup& cg = colorGroup();
+    QRect cr = contentsRect();
+    QFontMetrics fm = fontMetrics();
+    QRect lineRect( cr.x() + innerMargin, cr.y() + (cr.height() - fm.height() + 1) / 2,
+                   cr.width() - 2*innerMargin, fm.height() );
+    QBrush bg = QBrush( paletteBackgroundColor() );
+    if ( paletteBackgroundPixmap() )
+       bg = QBrush( cg.background(), *paletteBackgroundPixmap() );
+    else if ( !isEnabled() )
+       bg = cg.brush( QColorGroup::Background );
+    p->save();
+    p->setClipRegion( QRegion(cr) - lineRect );
+    p->fillRect( cr, bg );
+    p->restore();
+    QSharedDoubleBuffer buffer( p, lineRect.x(), lineRect.y(),
+                               lineRect.width(), lineRect.height(),
+                               hasFocus() ? QSharedDoubleBuffer::Force : 0 );
+    p = buffer.painter();
+    p->fillRect( lineRect, bg );
+
+    // locate cursor position
+    int cix = 0;
+    QTextItem ci = d->textLayout.findItem( d->cursor );
+    if ( ci.isValid() ) {
+       if ( d->cursor != (int)d->text.length() && d->cursor == ci.from() + ci.length()
+            && ci.isRightToLeft() != d->isRightToLeft() )
+           ci = d->textLayout.findItem( d->cursor + 1 );
+       cix = ci.x() + ci.cursorToX( d->cursor - ci.from() );
+    }
+
+    // horizontal scrolling
+    int minLB = QMAX( 0, -fm.minLeftBearing() );
+    int minRB = QMAX( 0, -fm.minRightBearing() );
+    int widthUsed = d->textLayout.widthUsed() + 1 + minRB;
+    if ( (minLB + widthUsed) <=  lineRect.width() ) {
+       switch ( d->visualAlignment() ) {
+       case AlignRight:
+           d->hscroll = widthUsed - lineRect.width();
+           break;
+       case AlignHCenter:
+           d->hscroll = ( widthUsed - lineRect.width() ) / 2;
+           break;
+       default:
+           d->hscroll = 0;
+           break;
+       }
+       d->hscroll -= minLB;
+    } else if ( cix - d->hscroll >= lineRect.width() ) {
+       d->hscroll = cix - lineRect.width() + 1;
+    } else if ( cix - d->hscroll < 0 ) {
+       d->hscroll = cix;
+    } else if ( widthUsed - d->hscroll < lineRect.width() ) {
+       d->hscroll = widthUsed - lineRect.width() + 1;
+    }
+    // the y offset is there to keep the baseline constant in case we have script changes in the text.
+    QPoint topLeft = lineRect.topLeft() - QPoint(d->hscroll, d->ascent-fm.ascent());
+
+    // draw text, selections and cursors
+    p->setPen( cg.text() );
+    bool supressCursor = d->readOnly, hasRightToLeft = d->isRightToLeft();
+    int textflags = 0;
+    if ( font().underline() )
+       textflags |= Qt::Underline;
+    if ( font().strikeOut() )
+       textflags |= Qt::StrikeOut;
+    if ( font().overline() )
+       textflags |= Qt::Overline;
+
+    for ( int i = 0; i < d->textLayout.numItems(); i++ ) {
+       QTextItem ti = d->textLayout.itemAt( i );
+       hasRightToLeft |= ti.isRightToLeft();
+       int tix = topLeft.x() + ti.x();
+       int first = ti.from();
+       int last = ti.from() + ti.length() - 1;
+
+       // text and selection
+       if ( d->selstart < d->selend && (last >= d->selstart && first < d->selend ) ) {
+           QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->selstart - first, 0 ) ),
+                                            lineRect.top() ),
+                                    QPoint( tix + ti.cursorToX( QMIN( d->selend - first, last - first + 1 ) ) - 1,
+                                            lineRect.bottom() ) ).normalize();
+           p->save();
+           p->setClipRegion( QRegion( lineRect ) - highlight, QPainter::CoordPainter );
+           p->drawTextItem( topLeft, ti, textflags );
+           p->setClipRect( lineRect & highlight, QPainter::CoordPainter );
+           p->fillRect( highlight, cg.highlight() );
+           p->setPen( cg.highlightedText() );
+           p->drawTextItem( topLeft, ti, textflags );
+           p->restore();
+       } else {
+           p->drawTextItem( topLeft, ti, textflags );
+       }
+
+       // input method edit area
+       if ( d->imstart < d->imend && (last >= d->imstart && first < d->imend ) ) {
+           QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imstart - first, 0 ) ), lineRect.top() ),
+                             QPoint( tix + ti.cursorToX( QMIN( d->imend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize();
+           p->save();
+           p->setClipRect( lineRect & highlight, QPainter::CoordPainter );
+
+           int h1, s1, v1, h2, s2, v2;
+           cg.color( QColorGroup::Base ).hsv( &h1, &s1, &v1 );
+           cg.color( QColorGroup::Background ).hsv( &h2, &s2, &v2 );
+           QColor imCol;
+           imCol.setHsv( h1, s1, ( v1 + v2 ) / 2 );
+           p->fillRect( highlight, imCol );
+           p->drawTextItem( topLeft, ti, textflags );
+           p->restore();
+       }
+
+       // input method selection
+       if ( d->imselstart < d->imselend && (last >= d->imselstart && first < d->imselend ) ) {
+           QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imselstart - first, 0 ) ), lineRect.top() ),
+                             QPoint( tix + ti.cursorToX( QMIN( d->imselend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize();
+           p->save();
+           p->setClipRect( lineRect & highlight, QPainter::CoordPainter );
+           p->fillRect( highlight, cg.text() );
+           p->setPen( paletteBackgroundColor() );
+           p->drawTextItem( topLeft, ti, textflags );
+           p->restore();
+       }
+    }
+
+    // draw cursor
+    if ( d->cursorVisible && !supressCursor ) {
+       QPoint from( topLeft.x() + cix, lineRect.top() );
+       QPoint to = from + QPoint( 0, lineRect.height() );
+       p->drawLine( from, to );
+       if ( hasRightToLeft ) {
+           to = from + QPoint( (ci.isRightToLeft()?-2:2), 2 );
+           p->drawLine( from, to );
+           from.ry() += 4;
+           p->drawLine( from, to );
+       }
+    }
+    buffer.end();
+}
+
+
+enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
+
+
+/*!\reimp
+*/
+void SecQLineEdit::contextMenuEvent( QContextMenuEvent * e )
+{
+#ifndef QT_NO_POPUPMENU
+    d->separate();
+
+    QGuardedPtr<QPopupMenu> popup = createPopupMenu();
+    QGuardedPtr<SecQLineEdit> that = this;
+    QPoint pos = e->reason() == QContextMenuEvent::Mouse ? e->globalPos() :
+                mapToGlobal( QPoint(e->pos().x(), 0) ) + QPoint( width() / 2, height() / 2 );
+    int r = popup->exec( pos );
+    delete (QPopupMenu*)popup;
+    if ( that && d->menuId ) {
+       switch ( d->menuId - r ) {
+       case IdClear: clear(); break;
+       case IdSelectAll: selectAll(); break;
+#ifndef SECURE_NO_UNDO
+       case IdUndo: undo(); break;
+       case IdRedo: redo(); break;
+#endif
+#ifndef QT_NO_CLIPBOARD
+       case IdCut: cut(); break;
+       case IdCopy: copy(); break;
+       case IdPaste: paste(); break;
+#endif
+       default:
+           ; // nothing selected or lineedit destroyed. Be careful.
+       }
+    }
+#endif //QT_NO_POPUPMENU
+}
+
+/*!
+    This function is called to create the popup menu which is shown
+    when the user clicks on the line edit with the right mouse button.
+    If you want to create a custom popup menu, reimplement this
+    function and return the popup menu you create. The popup menu's
+    ownership is transferred to the caller.
+*/
+
+QPopupMenu *SecQLineEdit::createPopupMenu()
+{
+#ifndef QT_NO_POPUPMENU
+    QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" );
+    int id = d->menuId = popup->insertItem( QString( "&Undo" ) + ACCEL_KEY( Z ) );
+    popup->insertItem( QString ("&Redo") + ACCEL_KEY( Y ) );
+    popup->insertSeparator();
+    popup->insertItem( QString ("Cu&t") + ACCEL_KEY( X ) );
+    popup->insertItem( QString ("&Copy") + ACCEL_KEY( C ) );
+    popup->insertItem( QString ("&Paste") + ACCEL_KEY( V ) );
+    popup->insertItem( QString ("Clear") );
+    popup->insertSeparator();
+    popup->insertItem( QString ("Select All")
+#ifndef Q_WS_X11
+    + ACCEL_KEY( A )
+#endif
+       );
+#ifndef SECURE_NO_UNDO
+    popup->setItemEnabled( id - IdUndo, d->isUndoAvailable() );
+    popup->setItemEnabled( id - IdRedo, d->isRedoAvailable() );
+#else
+    popup->setItemVisible( id - IdUndo, FALSE );
+    popup->setItemVisible( id - IdRedo, FALSE );
+#endif /* SECURE_NO_UNDO */
+
+#ifndef QT_NO_CLIPBOARD
+    popup->setItemEnabled( id - IdCut, !d->readOnly && d->hasSelectedText() );
+    popup->setItemEnabled( id - IdCopy, d->hasSelectedText() );
+    popup->setItemEnabled( id - IdPaste, !d->readOnly && !QApplication::clipboard()->text().isEmpty() );
+#else
+    popup->setItemVisible( id - IdCut, FALSE );
+    popup->setItemVisible( id - IdCopy, FALSE );
+    popup->setItemVisible( id - IdPaste, FALSE );
+#endif
+    popup->setItemEnabled( id - IdClear, !d->readOnly && !d->text.isEmpty() );
+    popup->setItemEnabled( id - IdSelectAll, !d->text.isEmpty() && !d->allSelected() );
+    return popup;
+#else
+    return 0;
+#endif
+}
+
+
+/*! \reimp */
+void SecQLineEdit::windowActivationChange( bool b )
+{
+    //### remove me with WHighlightSelection attribute
+    if ( palette().active() != palette().inactive() )
+       update();
+    QWidget::windowActivationChange( b );
+}
+
+/*! \reimp */
+
+void SecQLineEdit::setPalette( const QPalette & p )
+{
+    //### remove me with WHighlightSelection attribute
+    QWidget::setPalette( p );
+    update();
+}
+
+/*!
+  \obsolete
+  \fn void SecQLineEdit::repaintArea( int from, int to )
+  Repaints all characters from \a from to \a to. If cursorPos is
+  between from and to, ensures that cursorPos is visible.
+*/
+
+/*! \reimp
+ */
+void SecQLineEdit::setFont( const QFont & f )
+{
+    QWidget::setFont( f );
+    d->updateTextLayout();
+}
+
+
+void SecQLineEdit::clipboardChanged()
+{
+}
+
+void SecQLineEditPrivate::init( const SecQString& txt )
+{
+#ifndef QT_NO_CURSOR
+    q->setCursor( readOnly ? arrowCursor : ibeamCursor );
+#endif
+    q->setFocusPolicy( QWidget::StrongFocus );
+    q->setInputMethodEnabled( TRUE );
+    //   Specifies that this widget can use more, but is able to survive on
+    //   less, horizontal space; and is fixed vertically.
+    q->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+    q->setBackgroundMode( PaletteBase );
+    q->setKeyCompression( TRUE );
+    q->setMouseTracking( TRUE );
+    q->setAcceptDrops( TRUE );
+    q->setFrame( TRUE );
+    text = txt;
+    updateTextLayout();
+    cursor = text.length();
+}
+
+void SecQLineEditPrivate::updateTextLayout()
+{
+    // replace all non-printable characters with spaces (to avoid
+    // drawing boxes when using fonts that don't have glyphs for such
+    // characters)
+    const QString &displayText = q->displayText();
+    QString str(displayText.unicode(), displayText.length());
+    QChar* uc = (QChar*)str.unicode();
+    for (int i = 0; i < (int)str.length(); ++i) {
+       if (! uc[i].isPrint())
+           uc[i] = QChar(0x0020);
+    }
+    textLayout.setText( str, q->font() );
+    // ### want to do textLayout.setRightToLeft( text.isRightToLeft() );
+    textLayout.beginLayout( QTextLayout::SingleLine );
+    textLayout.beginLine( INT_MAX );
+    while ( !textLayout.atEnd() )
+       textLayout.addCurrentItem();
+    ascent = 0;
+    textLayout.endLine(0, 0, Qt::AlignLeft, &ascent);
+}
+
+int SecQLineEditPrivate::xToPos( int x, QTextItem::CursorPosition betweenOrOn ) const
+{
+    x-= q->contentsRect().x() - hscroll + innerMargin;
+    for ( int i = 0; i < textLayout.numItems(); ++i ) {
+       QTextItem ti = textLayout.itemAt( i );
+       QRect tir = ti.rect();
+       if ( x >= tir.left() && x <= tir.right() )
+           return ti.xToCursor( x - tir.x(), betweenOrOn ) + ti.from();
+    }
+    return x < 0 ? 0 : text.length();
+}
+
+
+QRect SecQLineEditPrivate::cursorRect() const
+{
+    QRect cr = q->contentsRect();
+    int cix = cr.x() - hscroll + innerMargin;
+    QTextItem ci = textLayout.findItem( cursor );
+    if ( ci.isValid() ) {
+       if ( cursor != (int)text.length() && cursor == ci.from() + ci.length()
+            && ci.isRightToLeft() != isRightToLeft() )
+           ci = textLayout.findItem( cursor + 1 );
+       cix += ci.x() + ci.cursorToX( cursor - ci.from() );
+    }
+    int ch = q->fontMetrics().height();
+    return QRect( cix-4, cr.y() + ( cr.height() -  ch + 1) / 2, 8, ch + 1 );
+}
+
+void SecQLineEditPrivate::updateMicroFocusHint()
+{
+    if ( q->hasFocus() ) {
+       QRect r = cursorRect();
+       q->setMicroFocusHint( r.x(), r.y(), r.width(), r.height() );
+    }
+}
+
+void SecQLineEditPrivate::moveCursor( int pos, bool mark )
+{
+    if ( pos != cursor )
+       separate();
+    bool fullUpdate = mark || hasSelectedText();
+    if ( mark ) {
+       int anchor;
+       if ( selend > selstart && cursor == selstart )
+           anchor = selend;
+       else if ( selend > selstart && cursor == selend )
+           anchor = selstart;
+       else
+           anchor = cursor;
+       selstart = QMIN( anchor, pos );
+       selend = QMAX( anchor, pos );
+    } else {
+       selstart = selend = 0;
+    }
+    if ( fullUpdate ) {
+       cursor = pos;
+       q->update();
+    } else {
+       setCursorVisible( FALSE );
+       cursor = pos;
+       setCursorVisible( TRUE );
+    }
+    updateMicroFocusHint();
+    if ( mark ) {
+       if( !q->style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ))
+           setCursorVisible( FALSE );
+       emit q->selectionChanged();
+    }
+}
+
+void SecQLineEditPrivate::finishChange( int validateFromState, bool setModified )
+{
+    bool lineDirty = selDirty;
+    if ( textDirty ) {
+       if ( validateFromState >= 0 ) {
+#ifndef SECURE_NO_UNDO
+           undo( validateFromState );
+#endif /* SECURE_NO_UNDO */
+           history.resize( undoState );
+           textDirty = setModified = FALSE;
+       }
+       updateTextLayout();
+       updateMicroFocusHint();
+       lineDirty |= textDirty;
+       if ( setModified )
+           modified = TRUE;
+       if ( textDirty ) {
+           textDirty = FALSE;
+           emit q->textChanged( text );
+       }
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+       QAccessible::updateAccessibility( q, 0, QAccessible::ValueChanged );
+#endif
+    }
+    if ( selDirty ) {
+       selDirty = FALSE;
+       emit q->selectionChanged();
+    }
+    if ( lineDirty || !setModified )
+       q->update();
+}
+
+void SecQLineEditPrivate::setText( const SecQString& txt )
+{
+    deselect();
+    SecQString oldText = text;
+    text = txt.isEmpty() ? SecQString ("") : txt.left( maxLength );
+    history.clear();
+    undoState = 0;
+    cursor = text.length();
+    textDirty = 1; // Err on safe side.
+}
+
+
+void SecQLineEditPrivate::setCursorVisible( bool visible )
+{
+    if ( (bool)cursorVisible == visible )
+       return;
+    if ( cursorTimer )
+       cursorVisible = visible;
+    QRect r = cursorRect();
+    if ( !q->contentsRect().contains( r ) )
+       q->update();
+    else
+       q->update( r );
+}
+
+#ifndef SECURE_NO_UNDO
+
+void SecQLineEditPrivate::addCommand( const Command& cmd )
+{
+    if ( separator && undoState && history[undoState-1].type != Separator ) {
+       history.resize( undoState + 2 );
+       history[undoState++] = Command( Separator, 0, 0 );
+    } else {
+       history.resize( undoState + 1);
+    }
+    separator = FALSE;
+    history[ undoState++ ] = cmd;
+}
+#endif /* SECURE_NO_UNDO */
+
+void SecQLineEditPrivate::insert( const SecQString& s )
+{
+  int remaining = maxLength - text.length();
+  text.insert( cursor, s.left(remaining) );
+  for ( int i = 0; i < (int) s.left(remaining).length(); ++i )
+    {
+#ifndef SECURE_NO_UNDO
+      addCommand( Command( Insert, cursor, s.at(i) ) );
+#endif /* SECURE_NO_UNDO */
+      cursor++;
+    }
+  textDirty = TRUE;
+}
+
+void SecQLineEditPrivate::del( bool wasBackspace )
+{
+    if ( cursor < (int) text.length() ) {
+#ifndef SECURE_NO_UNDO
+       addCommand ( Command( (CommandType)(wasBackspace?Remove:Delete), cursor, text.at(cursor) ) );
+#endif /* SECURE_NO_UNDO */
+       text.remove( cursor, 1 );
+       textDirty = TRUE;
+    }
+}
+
+void SecQLineEditPrivate::removeSelectedText()
+{
+    if ( selstart < selend && selend <= (int) text.length() ) {
+       separate();
+#ifndef SECURE_NO_UNDO
+       int i ;
+       if ( selstart <= cursor && cursor < selend ) {
+           // cursor is within the selection. Split up the commands
+           // to be able to restore the correct cursor position
+           for ( i = cursor; i >= selstart; --i )
+               addCommand ( Command( DeleteSelection, i, text.at(i) ) );
+           for ( i = selend - 1; i > cursor; --i )
+               addCommand ( Command( DeleteSelection, i - cursor + selstart - 1, text.at(i) ) );
+       } else {
+           for ( i = selend-1; i >= selstart; --i )
+               addCommand ( Command( RemoveSelection, i, text.at(i) ) );
+       }
+#endif /* SECURE_NO_UNDO */
+       text.remove( selstart, selend - selstart );
+       if ( cursor > selstart )
+           cursor -= QMIN( cursor, selend ) - selstart;
+       deselect();
+       textDirty = TRUE;
+    }
+}
diff --git a/qt/secqlineedit.h b/qt/secqlineedit.h
new file mode 100644 (file)
index 0000000..defda10
--- /dev/null
@@ -0,0 +1,229 @@
+/* secqlineedit.h - Secure version of QLineEdit.
+   Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+   Copyright (C) 2003 g10 Code GmbH
+
+   The license of the original qlineedit.h file from which this file
+   is derived can be found below.  Modified by Marcus Brinkmann
+   <marcus@g10code.de>.  All modifications are licensed as follows, so
+   that the intersection of the two licenses is then the GNU General
+   Public License version 2.
+
+   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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA  */
+
+#include "secqstring.h"
+
+/* This disables some insecure code.  */
+#define SECURE 1
+
+/**********************************************************************
+** $Id$
+**
+** Definition of SecQLineEdit widget class
+**
+** Created : 941011
+**
+** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef SECQLINEEDIT_H
+#define SECQLINEEDIT_H
+
+struct SecQLineEditPrivate;
+
+class QPopupMenu;
+
+#ifndef QT_H
+#include "qframe.h"
+#include "qstring.h"
+#endif // QT_H
+
+class QTextParagraph;
+class QTextCursor;
+
+class Q_EXPORT SecQLineEdit : public QFrame
+{
+    Q_OBJECT
+    Q_ENUMS( EchoMode )
+      //    Q_PROPERTY( SecQString text READ text WRITE setText )
+    Q_PROPERTY( int maxLength READ maxLength WRITE setMaxLength )
+    Q_PROPERTY( bool frame READ frame WRITE setFrame )
+    Q_PROPERTY( EchoMode echoMode READ echoMode WRITE setEchoMode )
+    Q_PROPERTY( QString displayText READ displayText )
+    Q_PROPERTY( int cursorPosition READ cursorPosition WRITE setCursorPosition )
+    Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment )
+    Q_PROPERTY( bool edited READ edited WRITE setEdited DESIGNABLE false )
+    Q_PROPERTY( bool modified READ isModified )
+    Q_PROPERTY( bool hasMarkedText READ hasMarkedText DESIGNABLE false )
+    Q_PROPERTY( bool hasSelectedText READ hasSelectedText )
+      //    Q_PROPERTY( SecQString markedText READ markedText DESIGNABLE false )
+      //    Q_PROPERTY( SecQString selectedText READ selectedText )
+    Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+    Q_PROPERTY( bool undoAvailable READ isUndoAvailable )
+    Q_PROPERTY( bool redoAvailable READ isRedoAvailable )
+
+public:
+    SecQLineEdit( QWidget* parent, const char* name=0 );
+    SecQLineEdit( const SecQString &, QWidget* parent, const char* name=0 );
+    SecQLineEdit( const SecQString &, const QString &, QWidget* parent, const char* name=0 );
+    ~SecQLineEdit();
+
+    SecQString text() const;
+
+    QString displayText() const;
+
+    int maxLength() const;
+
+    bool frame() const;
+
+    enum EchoMode { Normal, NoEcho, Password };
+    EchoMode echoMode() const;
+
+    bool isReadOnly() const;
+
+    QSize sizeHint() const;
+    QSize minimumSizeHint() const;
+
+    int cursorPosition() const;
+    bool validateAndSet( const SecQString &, int, int, int ); // obsolete
+
+    int alignment() const;
+
+#ifndef QT_NO_COMPAT
+    void cursorLeft( bool mark, int steps = 1 ) { cursorForward( mark, -steps ); }
+    void cursorRight( bool mark, int steps = 1 ) { cursorForward( mark, steps ); }
+#endif
+    void cursorForward( bool mark, int steps = 1 );
+    void cursorBackward( bool mark, int steps = 1 );
+    void cursorWordForward( bool mark );
+    void cursorWordBackward( bool mark );
+    void backspace();
+    void del();
+    void home( bool mark );
+    void end( bool mark );
+
+    bool isModified() const;
+    void clearModified();
+
+    bool edited() const; // obsolete, use isModified()
+    void setEdited( bool ); // obsolete, use clearModified()
+
+    bool hasSelectedText() const;
+    SecQString selectedText() const;
+    int selectionStart() const;
+
+    bool isUndoAvailable() const;
+    bool isRedoAvailable() const;
+
+#ifndef QT_NO_COMPAT
+    bool hasMarkedText() const { return hasSelectedText(); }
+    SecQString markedText() const { return selectedText(); }
+#endif
+
+public slots:
+    virtual void setText( const SecQString &);
+    virtual void selectAll();
+    virtual void deselect();
+    virtual void insert( const SecQString &);
+    virtual void clear();
+    virtual void undo();
+    virtual void redo();
+    virtual void setMaxLength( int );
+    virtual void setFrame( bool );
+    virtual void setEchoMode( EchoMode );
+    virtual void setReadOnly( bool );
+    virtual void setFont( const QFont & );
+    virtual void setPalette( const QPalette & );
+    virtual void setSelection( int, int );
+    virtual void setCursorPosition( int );
+    virtual void setAlignment( int flag );
+#ifndef QT_NO_CLIPBOARD
+    virtual void cut();
+    virtual void copy() const;
+    virtual void paste();
+#endif
+
+signals:
+    void textChanged( const SecQString &);
+    void returnPressed();
+    void lostFocus();
+    void selectionChanged();
+
+protected:
+    bool event( QEvent * );
+    void mousePressEvent( QMouseEvent * );
+    void mouseMoveEvent( QMouseEvent * );
+    void mouseReleaseEvent( QMouseEvent * );
+    void mouseDoubleClickEvent( QMouseEvent * );
+    void keyPressEvent( QKeyEvent * );
+    void imStartEvent( QIMEvent * );
+    void imComposeEvent( QIMEvent * );
+    void imEndEvent( QIMEvent * );
+    void focusInEvent( QFocusEvent * );
+    void focusOutEvent( QFocusEvent * );
+    void resizeEvent( QResizeEvent * );
+    void drawContents( QPainter * );
+    void contextMenuEvent( QContextMenuEvent * );
+    virtual QPopupMenu *createPopupMenu();
+    void windowActivationChange( bool );
+#ifndef QT_NO_COMPAT
+    void repaintArea( int, int ) { update(); }
+#endif
+
+private slots:
+    void clipboardChanged();
+
+public:
+    QChar passwordChar() const; // obsolete internal
+
+private:
+    friend struct SecQLineEditPrivate;
+    SecQLineEditPrivate * d;
+
+private:       // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+    SecQLineEdit( const SecQLineEdit & );
+    SecQLineEdit &operator=( const SecQLineEdit & );
+#endif
+};
+
+#endif // SECQLINEEDIT_H
diff --git a/qt/secqstring.cpp b/qt/secqstring.cpp
new file mode 100644 (file)
index 0000000..bb5d990
--- /dev/null
@@ -0,0 +1,942 @@
+/* secqstring.cpp - Secure version of QString.
+   Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+   Copyright (C) 2003 g10 Code GmbH
+
+   The license of the original qstring.cpp file from which this file
+   is derived can be found below.  Modified by Marcus Brinkmann
+   <marcus@g10code.de>.  All modifications are licensed as follows, so
+   that the intersection of the two licenses is then the GNU General
+   Public License version 2.
+
+   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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA  */
+
+/****************************************************************************
+** $Id$
+**
+** Implementation of the SecQString class and related Unicode functions
+**
+** Created : 920722
+**
+** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the tools module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+// Don't define it while compiling this module, or USERS of Qt will
+// not be able to link.
+
+#include "secqstring.h"
+
+#include "private/qunicodetables_p.h"
+
+static uint computeNewMax( uint len )
+{
+    uint newMax = 4;
+    while ( newMax < len )
+       newMax *= 2;
+    // try to save some memory
+    if ( newMax >= 1024 * 1024 && len <= newMax - (newMax >> 2) )
+       newMax -= newMax >> 2;
+    return newMax;
+}
+
+// These macros are used for efficient allocation of QChar strings.
+// IMPORTANT! If you change these, make sure you also change the
+// "delete unicode" statement in ~SecQStringData() in SecQString.h correspondingly!
+
+#define QT_ALLOC_SECQCHAR_VEC(N) (QChar*) ::secmem_malloc (sizeof(QChar) * (N))
+#define QT_DELETE_SECQCHAR_VEC(P) ::secmem_free (P)
+
+
+/*****************************************************************************
+  SecQString member functions
+ *****************************************************************************/
+
+/*!
+    \class SecQString SecQString.h
+    \reentrant
+
+    \brief The SecQString class provides an abstraction of Unicode text
+    and the classic C '\0'-terminated char array.
+
+    \ingroup tools
+    \ingroup shared
+    \ingroup text
+    \mainclass
+
+    SecQString uses \link shclass.html implicit sharing\endlink, which
+    makes it very efficient and easy to use.
+
+    In all of the SecQString methods that take \c {const char *}
+    parameters, the \c {const char *} is interpreted as a classic
+    C-style '\0'-terminated ASCII string. It is legal for the \c
+    {const char *} parameter to be 0. If the \c {const char *} is not
+    '\0'-terminated, the results are undefined. Functions that copy
+    classic C strings into a SecQString will not copy the terminating
+    '\0' character. The QChar array of the SecQString (as returned by
+    unicode()) is generally not terminated by a '\0'. If you need to
+    pass a SecQString to a function that requires a C '\0'-terminated
+    string use latin1().
+
+    \keyword SecQString::null
+    A SecQString that has not been assigned to anything is \e null, i.e.
+    both the length and data pointer is 0. A SecQString that references
+    the empty string ("", a single '\0' char) is \e empty. Both null
+    and empty SecQStrings are legal parameters to the methods. Assigning
+    \c{(const char *) 0} to SecQString gives a null SecQString. For
+    convenience, \c SecQString::null is a null SecQString. When sorting,
+    empty strings come first, followed by non-empty strings, followed
+    by null strings. We recommend using \c{if ( !str.isNull() )} to
+    check for a non-null string rather than \c{if ( !str )}; see \l
+    operator!() for an explanation.
+
+    Note that if you find that you are mixing usage of \l QCString,
+    SecQString, and \l QByteArray, this causes lots of unnecessary
+    copying and might indicate that the true nature of the data you
+    are dealing with is uncertain. If the data is '\0'-terminated 8-bit
+    data, use \l QCString; if it is unterminated (i.e. contains '\0's)
+    8-bit data, use \l QByteArray; if it is text, use SecQString.
+
+    Lists of strings are handled by the SecQStringList class. You can
+    split a string into a list of strings using SecQStringList::split(),
+    and join a list of strings into a single string with an optional
+    separator using SecQStringList::join(). You can obtain a list of
+    strings from a string list that contain a particular substring or
+    that match a particular \link qregexp.html regex\endlink using
+    SecQStringList::grep().
+
+    <b>Note for C programmers</b>
+
+    Due to C++'s type system and the fact that SecQString is implicitly
+    shared, SecQStrings may be treated like ints or other simple base
+    types. For example:
+
+    \code
+    SecQString boolToString( bool b )
+    {
+       SecQString result;
+       if ( b )
+           result = "True";
+       else
+           result = "False";
+       return result;
+    }
+    \endcode
+
+    The variable, result, is an auto variable allocated on the stack.
+    When return is called, because we're returning by value, The copy
+    constructor is called and a copy of the string is returned. (No
+    actual copying takes place thanks to the implicit sharing, see
+    below.)
+
+    Throughout Qt's source code you will encounter SecQString usages like
+    this:
+    \code
+    SecQString func( const SecQString& input )
+    {
+       SecQString output = input;
+       // process output
+       return output;
+    }
+    \endcode
+
+    The 'copying' of input to output is almost as fast as copying a
+    pointer because behind the scenes copying is achieved by
+    incrementing a reference count. SecQString (like all Qt's implicitly
+    shared classes) operates on a copy-on-write basis, only copying if
+    an instance is actually changed.
+
+    If you wish to create a deep copy of a SecQString without losing any
+    Unicode information then you should use QDeepCopy.
+
+    \sa QChar QCString QByteArray SecQConstString
+*/
+
+Q_EXPORT SecQStringData *SecQString::shared_null = 0;
+QT_STATIC_CONST_IMPL SecQString SecQString::null;
+QT_STATIC_CONST_IMPL QChar QChar::null;
+QT_STATIC_CONST_IMPL QChar QChar::replacement((ushort)0xfffd);
+QT_STATIC_CONST_IMPL QChar QChar::byteOrderMark((ushort)0xfeff);
+QT_STATIC_CONST_IMPL QChar QChar::byteOrderSwapped((ushort)0xfffe);
+QT_STATIC_CONST_IMPL QChar QChar::nbsp((ushort)0x00a0);
+
+SecQStringData* SecQString::makeSharedNull()
+{
+    SecQString::shared_null = new SecQStringData;
+#if defined( Q_OS_MAC )
+    SecQString *that = const_cast<SecQString *>(&SecQString::null);
+    that->d = SecQString::shared_null;
+#endif
+    return SecQString::shared_null;
+}
+
+/*!
+    \fn SecQString::SecQString()
+
+    Constructs a null string, i.e. both the length and data pointer
+    are 0.
+
+    \sa isNull()
+*/
+
+/*!
+    Constructs a string of length one, containing the character \a ch.
+*/
+SecQString::SecQString( QChar ch )
+{
+    d = new SecQStringData( QT_ALLOC_SECQCHAR_VEC( 1 ), 1, 1 );
+    d->unicode[0] = ch;
+}
+
+/*!
+    Constructs an implicitly shared copy of \a s. This is very fast
+    since it only involves incrementing a reference count.
+*/
+SecQString::SecQString( const SecQString &s ) :
+    d(s.d)
+{
+    d->ref();
+}
+
+
+SecQString::SecQString( int size, bool /*dummy*/ )
+{
+    if ( size ) {
+       int l = size;
+       QChar* uc = QT_ALLOC_SECQCHAR_VEC( l );
+       d = new SecQStringData( uc, 0, l );
+    } else {
+       d = shared_null ? shared_null : (shared_null=new SecQStringData);
+       d->ref();
+    }
+}
+
+
+/* Deep copy of STR.  */
+SecQString::SecQString( const QString &str )
+{
+    const QChar *unicode = str.unicode ();
+    uint length = str.length ();
+
+    if ( !unicode && !length ) {
+       d = shared_null ? shared_null : makeSharedNull();
+       d->ref();
+    } else {
+       QChar* uc = QT_ALLOC_SECQCHAR_VEC( length );
+       if ( unicode )
+           memcpy(uc, unicode, length*sizeof(QChar));
+       d = new SecQStringData(uc,unicode ? length : 0,length);
+    }  
+}
+
+
+/*!
+    Constructs a string that is a deep copy of the first \a length
+    characters in the QChar array.
+
+    If \a unicode and \a length are 0, then a null string is created.
+
+    If only \a unicode is 0, the string is empty but has \a length
+    characters of space preallocated: SecQString expands automatically
+    anyway, but this may speed up some cases a little. We recommend
+    using the plain constructor and setLength() for this purpose since
+    it will result in more readable code.
+
+    \sa isNull() setLength()
+*/
+
+SecQString::SecQString( const QChar* unicode, uint length )
+{
+    if ( !unicode && !length ) {
+       d = shared_null ? shared_null : makeSharedNull();
+       d->ref();
+    } else {
+       QChar* uc = QT_ALLOC_SECQCHAR_VEC( length );
+       if ( unicode )
+           memcpy(uc, unicode, length*sizeof(QChar));
+       d = new SecQStringData(uc,unicode ? length : 0,length);
+    }
+}
+
+/*!
+    \fn SecQString::~SecQString()
+
+    Destroys the string and frees the string's data if this is the
+    last reference to the string.
+*/
+
+
+/*!
+    Deallocates any space reserved solely by this SecQString.
+
+    If the string does not share its data with another SecQString
+    instance, nothing happens; otherwise the function creates a new,
+    unique copy of this string. This function is called whenever the
+    string is modified.
+*/
+
+void SecQString::real_detach()
+{
+    setLength( length() );
+}
+
+void SecQString::deref()
+{
+    if ( d && d->deref() ) {
+       if ( d != shared_null )
+           delete d;
+       d = 0;
+    }
+}
+
+void SecQStringData::deleteSelf()
+{
+    delete this;
+}
+
+/*!
+    \fn SecQString& SecQString::operator=( QChar c )
+
+    Sets the string to contain just the single character \a c.
+*/
+
+
+/*!
+    \overload
+
+    Assigns a shallow copy of \a s to this string and returns a
+    reference to this string. This is very fast because the string
+    isn't actually copied.
+*/
+SecQString &SecQString::operator=( const SecQString &s )
+{
+    s.d->ref();
+    deref();
+    d = s.d;
+    return *this;
+}
+
+
+/*!
+    \fn bool SecQString::isNull() const
+
+    Returns TRUE if the string is null; otherwise returns FALSE. A
+    null string is always empty.
+
+    \code
+       SecQString a;          // a.unicode() == 0, a.length() == 0
+       a.isNull();         // TRUE, because a.unicode() == 0
+       a.isEmpty();        // TRUE, because a.length() == 0
+    \endcode
+
+    \sa isEmpty(), length()
+*/
+
+/*!
+    \fn bool SecQString::isEmpty() const
+
+    Returns TRUE if the string is empty, i.e. if length() == 0;
+    otherwise returns FALSE. Null strings are also empty.
+
+    \code
+       SecQString a( "" );
+       a.isEmpty();        // TRUE
+       a.isNull();         // FALSE
+
+       SecQString b;
+       b.isEmpty();        // TRUE
+       b.isNull();         // TRUE
+    \endcode
+
+    \sa isNull(), length()
+*/
+
+/*!
+    \fn uint SecQString::length() const
+
+    Returns the length of the string.
+
+    Null strings and empty strings have zero length.
+
+    \sa isNull(), isEmpty()
+*/
+
+/*!
+    If \a newLen is less than the length of the string, then the
+    string is truncated at position \a newLen. Otherwise nothing
+    happens.
+
+    \code
+       SecQString s = "truncate me";
+       s.truncate( 5 );            // s == "trunc"
+    \endcode
+
+    \sa setLength()
+*/
+
+void SecQString::truncate( uint newLen )
+{
+    if ( newLen < d->len )
+       setLength( newLen );
+}
+
+/*!
+    Ensures that at least \a newLen characters are allocated to the
+    string, and sets the length of the string to \a newLen. Any new
+    space allocated contains arbitrary data.
+
+    \sa reserve(), truncate()
+*/
+void SecQString::setLength( uint newLen )
+{
+    if ( d->count != 1 || newLen > d->maxl ||
+        ( newLen * 4 < d->maxl && d->maxl > 4 ) ) {
+       // detach, grow or shrink
+       uint newMax = computeNewMax( newLen );
+       QChar* nd = QT_ALLOC_SECQCHAR_VEC( newMax );
+       if ( nd ) {
+           uint len = QMIN( d->len, newLen );
+           memcpy( nd, d->unicode, sizeof(QChar) * len );
+           deref();
+           d = new SecQStringData( nd, newLen, newMax );
+       }
+    } else {
+       d->len = newLen;
+    }
+}
+
+
+/*!
+    \internal
+
+    Like setLength, but doesn't shrink the allocated memory.
+*/
+void SecQString::grow( uint newLen )
+{
+    if ( d->count != 1 || newLen > d->maxl ) {
+       setLength( newLen );
+    } else {
+       d->len = newLen;
+    }
+}
+
+
+/*!
+    Returns a substring that contains the \a len leftmost characters
+    of the string.
+
+    The whole string is returned if \a len exceeds the length of the
+    string.
+
+    \code
+       SecQString s = "Pineapple";
+       SecQString t = s.left( 4 );    // t == "Pine"
+    \endcode
+
+    \sa right(), mid(), isEmpty()
+*/
+
+SecQString SecQString::left( uint len ) const
+{
+    if ( isEmpty() ) {
+       return SecQString();
+    } else if ( len == 0 ) {                    // ## just for 1.x compat:
+       return SecQString ("");
+    } else if ( len >= length() ) {
+       return *this;
+    } else {
+       SecQString s( len, TRUE );
+       memcpy( s.d->unicode, d->unicode, len * sizeof(QChar) );
+       s.d->len = len;
+       return s;
+    }
+}
+
+/*!
+    Returns a string that contains the \a len rightmost characters of
+    the string.
+
+    If \a len is greater than the length of the string then the whole
+    string is returned.
+
+    \code
+       SecQString string( "Pineapple" );
+       SecQString t = string.right( 5 );   // t == "apple"
+    \endcode
+
+    \sa left(), mid(), isEmpty()
+*/
+
+SecQString SecQString::right( uint len ) const
+{
+    if ( isEmpty() ) {
+       return SecQString();
+    } else if ( len == 0 ) {                    // ## just for 1.x compat:
+       return SecQString ("");
+    } else {
+       uint l = length();
+       if ( len >= l )
+           return *this;
+       SecQString s( len, TRUE );
+       memcpy( s.d->unicode, d->unicode+(l-len), len*sizeof(QChar) );
+       s.d->len = len;
+       return s;
+    }
+}
+
+/*!
+    Returns a string that contains the \a len characters of this
+    string, starting at position \a index.
+
+    Returns a null string if the string is empty or \a index is out of
+    range. Returns the whole string from \a index if \a index + \a len
+    exceeds the length of the string.
+
+    \code
+       SecQString s( "Five pineapples" );
+       SecQString t = s.mid( 5, 4 );                  // t == "pine"
+    \endcode
+
+    \sa left(), right()
+*/
+
+SecQString SecQString::mid( uint index, uint len ) const
+{
+    uint slen = length();
+    if ( isEmpty() || index >= slen ) {
+       return SecQString();
+    } else if ( len == 0 ) {                    // ## just for 1.x compat:
+       return SecQString ("");
+    } else {
+       if ( len > slen-index )
+           len = slen - index;
+       if ( index == 0 && len == slen )
+           return *this;
+       register const QChar *p = unicode()+index;
+       SecQString s( len, TRUE );
+       memcpy( s.d->unicode, p, len * sizeof(QChar) );
+       s.d->len = len;
+       return s;
+    }
+}
+
+/*!
+    Inserts \a s into the string at position \a index.
+
+    If \a index is beyond the end of the string, the string is
+    extended with spaces to length \a index and \a s is then appended
+    and returns a reference to the string.
+
+    \code
+       SecQString string( "I like fish" );
+       str = string.insert( 2, "don't " );
+       // str == "I don't like fish"
+    \endcode
+
+    \sa remove(), replace()
+*/
+
+SecQString &SecQString::insert( uint index, const SecQString &s )
+{
+    // the sub function takes care of &s == this case.
+    return insert( index, s.unicode(), s.length() );
+}
+
+/*!
+    \overload
+
+    Inserts the first \a len characters in \a s into the string at
+    position \a index and returns a reference to the string.
+*/
+
+SecQString &SecQString::insert( uint index, const QChar* s, uint len )
+{
+    if ( len == 0 )
+       return *this;
+    uint olen = length();
+    int nlen = olen + len;
+
+    if ( s >= d->unicode && (uint)(s - d->unicode) < d->maxl ) {
+       // Part of me - take a copy.
+       QChar *tmp = QT_ALLOC_SECQCHAR_VEC( len );
+       memcpy(tmp,s,len*sizeof(QChar));
+       insert(index,tmp,len);
+       QT_DELETE_SECQCHAR_VEC( tmp );
+       return *this;
+    }
+
+    if ( index >= olen ) {                      // insert after end of string
+       grow( len + index );
+       int n = index - olen;
+       QChar* uc = d->unicode+olen;
+       while (n--)
+           *uc++ = ' ';
+       memcpy( d->unicode+index, s, sizeof(QChar)*len );
+    } else {                                    // normal insert
+       grow( nlen );
+       memmove( d->unicode + index + len, unicode() + index,
+                sizeof(QChar) * (olen - index) );
+       memcpy( d->unicode + index, s, sizeof(QChar) * len );
+    }
+    return *this;
+}
+
+/*!
+    Removes \a len characters from the string starting at position \a
+    index, and returns a reference to the string.
+
+    If \a index is beyond the length of the string, nothing happens.
+    If \a index is within the string, but \a index + \a len is beyond
+    the end of the string, the string is truncated at position \a
+    index.
+
+    \code
+       SecQString string( "Montreal" );
+       string.remove( 1, 4 );      // string == "Meal"
+    \endcode
+
+    \sa insert(), replace()
+*/
+
+SecQString &SecQString::remove( uint index, uint len )
+{
+    uint olen = length();
+    if ( index >= olen  ) {
+       // range problems
+    } else if ( index + len >= olen ) {  // index ok
+       setLength( index );
+    } else if ( len != 0 ) {
+       real_detach();
+       memmove( d->unicode+index, d->unicode+index+len,
+                sizeof(QChar)*(olen-index-len) );
+       setLength( olen-len );
+    }
+    return *this;
+}
+
+
+/*!
+    \overload
+
+    Replaces \a len characters with \a slen characters of QChar data
+    from \a s, starting at position \a index, and returns a reference
+    to the string.
+
+    \sa insert(), remove()
+*/
+
+SecQString &SecQString::replace( uint index, uint len, const QChar* s, uint slen )
+{
+    real_detach();
+    if ( len == slen && index + len <= length() ) {
+       // Optimized common case: replace without size change
+       memcpy( d->unicode+index, s, len * sizeof(QChar) );
+    } else if ( s >= d->unicode && (uint)(s - d->unicode) < d->maxl ) {
+       // Part of me - take a copy.
+       QChar *tmp = QT_ALLOC_SECQCHAR_VEC( slen );
+       memcpy( tmp, s, slen * sizeof(QChar) );
+       replace( index, len, tmp, slen );
+       QT_DELETE_SECQCHAR_VEC( tmp );
+    } else {
+       remove( index, len );
+       insert( index, s, slen );
+    }
+    return *this;
+}
+
+
+/*!
+    Replaces \a len characters from the string with \a s, starting at
+    position \a index, and returns a reference to the string.
+
+    If \a index is beyond the length of the string, nothing is deleted
+    and \a s is appended at the end of the string. If \a index is
+    valid, but \a index + \a len is beyond the end of the string,
+    the string is truncated at position \a index, then \a s is
+    appended at the end.
+
+    \code
+       QString string( "Say yes!" );
+       string = string.replace( 4, 3, "NO" );
+       // string == "Say NO!"
+    \endcode
+
+    \sa insert(), remove()
+*/
+
+SecQString &SecQString::replace( uint index, uint len, const SecQString &s )
+{
+    return replace( index, len, s.unicode(), s.length() );
+}
+
+
+/*!
+    Appends \a str to the string and returns a reference to the string.
+*/
+SecQString& SecQString::operator+=( const SecQString &str )
+{
+    uint len1 = length();
+    uint len2 = str.length();
+    if ( len2 ) {
+       if ( isEmpty() ) {
+           operator=( str );
+       } else {
+           grow( len1+len2 );
+           memcpy( d->unicode+len1, str.unicode(), sizeof(QChar)*len2 );
+       }
+    } else if ( isNull() && !str.isNull() ) {   // ## just for 1.x compat:
+       *this = SecQString ("");
+    }
+    return *this;
+}
+
+
+/*!
+    Returns the string encoded in UTF-8 format.
+
+    See QTextCodec for more diverse coding/decoding of Unicode strings.
+
+    \sa fromUtf8(), ascii(), latin1(), local8Bit()
+*/
+uchar *SecQString::utf8() const
+{
+    int l = length();
+    int rlen = l*3+1;
+    uchar* rstr = (uchar*) ::secmem_malloc (rlen);
+    uchar* cursor = rstr;
+    const QChar *ch = d->unicode;
+    for (int i=0; i < l; i++) {
+       uint u = ch->unicode();
+       if ( u < 0x80 ) {
+           *cursor++ = (uchar)u;
+       } else {
+           if ( u < 0x0800 ) {
+               *cursor++ = 0xc0 | ((uchar) (u >> 6));
+           } else {
+               if (u >= 0xd800 && u < 0xdc00 && i < l-1) {
+                   unsigned short low = ch[1].unicode();
+                   if (low >= 0xdc00 && low < 0xe000) {
+                       ++ch;
+                       ++i;
+                       u = (u - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+                   }
+               }
+               if (u > 0xffff) {
+                   // if people are working in utf8, but strings are encoded in eg. latin1, the resulting
+                   // name might be invalid utf8. This and the corresponding code in fromUtf8 takes care
+                   // we can handle this without loosing information. This can happen with latin filenames
+                   // and a utf8 locale under Unix.
+                   if (u > 0x10fe00 && u < 0x10ff00) {
+                       *cursor++ = (u - 0x10fe00);
+                       ++ch;
+                       continue;
+                   } else {
+                       *cursor++ = 0xf0 | ((uchar) (u >> 18));
+                       *cursor++ = 0x80 | ( ((uchar) (u >> 12)) & 0x3f);
+                   }
+               } else {
+                   *cursor++ = 0xe0 | ((uchar) (u >> 12));
+               }
+               *cursor++ = 0x80 | ( ((uchar) (u >> 6)) & 0x3f);
+           }
+           *cursor++ = 0x80 | ((uchar) (u&0x3f));
+       }
+       ++ch;
+    }
+    /* FIXME: secmem_realloc doesn't release extra memory.  */
+    *cursor = '\0';
+    return rstr;
+}
+
+
+/*!
+  \fn QChar SecQString::at( uint ) const
+
+    Returns the character at index \a i, or 0 if \a i is beyond the
+    length of the string.
+
+    \code
+       const SecQString string( "abcdefgh" );
+       QChar ch = string.at( 4 );
+       // ch == 'e'
+    \endcode
+
+    If the SecQString is not const (i.e. const SecQString) or const& (i.e.
+    const SecQString &), then the non-const overload of at() will be used
+    instead.
+*/
+
+/*!
+    \fn QChar SecQString::constref(uint i) const
+
+    Returns the QChar at index \a i by value.
+
+    Equivalent to at(\a i).
+
+    \sa ref()
+*/
+
+/*!
+    \fn QChar& SecQString::ref(uint i)
+
+    Returns the QChar at index \a i by reference, expanding the string
+    with QChar::null if necessary. The resulting reference can be
+    assigned to, or otherwise used immediately, but becomes invalid
+    once furher modifications are made to the string.
+
+    \code
+       SecQString string("ABCDEF");
+       QChar ch = string.ref( 3 );         // ch == 'D'
+    \endcode
+
+    \sa constref()
+*/
+
+/*!
+    \fn QChar SecQString::operator[]( int ) const
+
+    Returns the character at index \a i, or QChar::null if \a i is
+    beyond the length of the string.
+
+    If the SecQString is not const (i.e., const SecQString) or const\&
+    (i.e., const SecQString\&), then the non-const overload of operator[]
+    will be used instead.
+*/
+
+/*!
+    \fn QCharRef SecQString::operator[]( int )
+
+    \overload
+
+    The function returns a reference to the character at index \a i.
+    The resulting reference can then be assigned to, or used
+    immediately, but it will become invalid once further modifications
+    are made to the original string.
+
+    If \a i is beyond the length of the string then the string is
+    expanded with QChar::nulls, so that the QCharRef references a
+    valid (null) character in the string.
+
+    The QCharRef internal class can be used much like a constant
+    QChar, but if you assign to it, you change the original string
+    (which will detach itself because of SecQString's copy-on-write
+    semantics). You will get compilation errors if you try to use the
+    result as anything but a QChar.
+*/
+
+/*!
+    \fn QCharRef SecQString::at( uint i )
+
+    \overload
+
+    The function returns a reference to the character at index \a i.
+    The resulting reference can then be assigned to, or used
+    immediately, but it will become invalid once further modifications
+    are made to the original string.
+
+    If \a i is beyond the length of the string then the string is
+    expanded with QChar::null.
+*/
+
+/*
+  Internal chunk of code to handle the
+  uncommon cases of at() above.
+*/
+void SecQString::subat( uint i )
+{
+    uint olen = d->len;
+    if ( i >= olen ) {
+       setLength( i+1 );               // i is index; i+1 is needed length
+       for ( uint j=olen; j<=i; j++ )
+           d->unicode[j] = QChar::null;
+    } else {
+       // Just be sure to detach
+       real_detach();
+    }
+}
+
+
+/*! \internal
+ */
+bool SecQString::isRightToLeft() const
+{
+    int len = length();
+    QChar *p = d->unicode;
+    while ( len-- ) {
+       switch( ::direction( *p ) )
+       {
+       case QChar::DirL:
+       case QChar::DirLRO:
+       case QChar::DirLRE:
+           return FALSE;
+       case QChar::DirR:
+       case QChar::DirAL:
+       case QChar::DirRLO:
+       case QChar::DirRLE:
+           return TRUE;
+       default:
+           break;
+       }
+       ++p;
+    }
+    return FALSE;
+}
+
+
+/*!
+    \fn const SecQString operator+( const SecQString &s1, const SecQString &s2 )
+
+    \relates SecQString
+
+    Returns a string which is the result of concatenating the string
+    \a s1 and the string \a s2.
+
+    Equivalent to \a {s1}.append(\a s2).
+*/
+
+
+/*! \fn void SecQString::detach()
+  If the string does not share its data with another SecQString instance,
+  nothing happens; otherwise the function creates a new, unique copy of
+  this string. This function is called whenever the string is modified. The
+  implicit sharing mechanism is implemented this way.
+*/
+
diff --git a/qt/secqstring.h b/qt/secqstring.h
new file mode 100644 (file)
index 0000000..f604464
--- /dev/null
@@ -0,0 +1,307 @@
+/* secqstring.h - Secure version of QString.
+   Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+   Copyright (C) 2003 g10 Code GmbH
+
+   The license of the original qstring.h file from which this file is
+   derived can be found below.  Modified by Marcus Brinkmann
+   <marcus@g10code.de>.  All modifications are licensed as follows, so
+   that the intersection of the two licenses is then the GNU General
+   Public License version 2.
+
+   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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA  */
+
+/****************************************************************************
+** $Id$
+**
+** Definition of the SecQString class, and related Unicode functions.
+**
+** Created : 920609
+**
+** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the tools module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef SECQSTRING_H
+#define SECQSTRING_H
+
+extern "C"
+{
+#include "memory.h"
+}
+
+/* We need the original qchar and qstring for transparent conversion
+   from QChar to QChar and QString to SecQString (but not the other
+   way round).  */
+#include <qstring.h>
+
+#ifndef QT_H
+#include "qcstring.h"
+#endif // QT_H
+
+
+/*****************************************************************************
+  SecQString class
+ *****************************************************************************/
+
+class SecQString;
+class SecQCharRef;
+template <class T> class QDeepCopy;
+#include <stdio.h>
+// internal
+struct Q_EXPORT SecQStringData : public QShared {
+    SecQStringData() :
+        QShared(), unicode(0), len(0), maxl(0) { ref(); }
+    SecQStringData(QChar *u, uint l, uint m) :
+        QShared(), unicode(u), len(l), maxl(m) { }
+    ~SecQStringData() { if ( unicode ) ::secmem_free ((char*) unicode); }
+
+    void deleteSelf();
+    QChar *unicode;
+#ifdef Q_OS_MAC9
+    uint len;
+#else
+    uint len : 30;
+#endif
+#ifdef Q_OS_MAC9
+    uint maxl;
+#else
+    uint maxl : 30;
+#endif
+};
+
+
+class Q_EXPORT SecQString
+{
+public:
+    SecQString();                                  // make null string
+    SecQString( QChar );                           // one-char string
+    SecQString( const SecQString & );                 // impl-shared copy
+    /* We need a way to convert a QString to a SecQString ("importing"
+       it).  Having no conversion for the other way prevents
+       accidential bugs where the secure string is copied to insecure
+       memory.  */
+    SecQString( const QString & ); // deep copy
+    SecQString( const QChar* unicode, uint length ); // deep copy
+    ~SecQString();
+
+    SecQString    &operator=( const SecQString & );   // impl-shared copy
+
+    QT_STATIC_CONST SecQString null;
+
+    bool        isNull()        const;
+    bool        isEmpty()       const;
+    uint        length()        const;
+    void        truncate( uint pos );
+
+    SecQString     left( uint len )  const;
+    SecQString     right( uint len ) const;
+    SecQString     mid( uint index, uint len=0xffffffff) const;
+
+
+    SecQString    &insert( uint index, const SecQString & );
+    SecQString    &insert( uint index, const QChar*, uint len );
+    SecQString    &remove( uint index, uint len );
+    SecQString    &replace( uint index, uint len, const SecQString & );
+    SecQString    &replace( uint index, uint len, const QChar*, uint clen );
+
+    SecQString    &operator+=( const SecQString &str );
+
+    QChar at( uint i ) const
+        { return i < d->len ? d->unicode[i] : QChar::null; }
+    QChar operator[]( int i ) const { return at((uint)i); }
+    SecQCharRef at( uint i );
+    SecQCharRef operator[]( int i );
+
+    QChar constref(uint i) const
+        { return at(i); }
+    QChar& ref(uint i)
+        { // Optimized for easy-inlining by simple compilers.
+            if ( d->count != 1 || i >= d->len )
+                subat( i );
+            return d->unicode[i];
+        }
+
+    const QChar* unicode() const { return d->unicode; }
+
+    uchar* utf8() const;
+
+    void setLength( uint newLength );
+
+    bool isRightToLeft() const;
+
+
+private:
+    SecQString( int size, bool /* dummy */ );  // allocate size incl. \0
+
+    void deref();
+    void real_detach();
+    void subat( uint );
+
+    void grow( uint newLength );
+
+    SecQStringData *d;
+    static SecQStringData* shared_null;
+    static SecQStringData* makeSharedNull();
+
+    friend class SecQConstString;
+    friend class QTextStream;
+    SecQString( SecQStringData* dd, bool /* dummy */ ) : d(dd) { }
+
+    // needed for QDeepCopy
+    void detach();
+    friend class QDeepCopy<SecQString>;
+};
+
+class Q_EXPORT SecQCharRef {
+    friend class SecQString;
+    SecQString& s;
+    uint p;
+    SecQCharRef(SecQString* str, uint pos) : s(*str), p(pos) { }
+
+public:
+    // most QChar operations repeated here
+
+    // all this is not documented: We just say "like QChar" and let it be.
+#ifndef Q_QDOC
+    ushort unicode() const { return s.constref(p).unicode(); }
+
+    // An operator= for each QChar cast constructors
+    SecQCharRef operator=(char c ) { s.ref(p)=c; return *this; }
+    SecQCharRef operator=(uchar c ) { s.ref(p)=c; return *this; }
+    SecQCharRef operator=(QChar c ) { s.ref(p)=c; return *this; }
+    SecQCharRef operator=(const SecQCharRef& c ) { s.ref(p)=c.unicode(); return *this; }
+    SecQCharRef operator=(ushort rc ) { s.ref(p)=rc; return *this; }
+    SecQCharRef operator=(short rc ) { s.ref(p)=rc; return *this; }
+    SecQCharRef operator=(uint rc ) { s.ref(p)=rc; return *this; }
+    SecQCharRef operator=(int rc ) { s.ref(p)=rc; return *this; }
+
+    operator QChar () const { return s.constref(p); }
+
+    // each function...
+    bool isNull() const { return unicode()==0; }
+    bool isPrint() const { return s.constref(p).isPrint(); }
+    bool isPunct() const { return s.constref(p).isPunct(); }
+    bool isSpace() const { return s.constref(p).isSpace(); }
+    bool isMark() const { return s.constref(p).isMark(); }
+    bool isLetter() const { return s.constref(p).isLetter(); }
+    bool isNumber() const { return s.constref(p).isNumber(); }
+    bool isLetterOrNumber() { return s.constref(p).isLetterOrNumber(); }
+    bool isDigit() const { return s.constref(p).isDigit(); }
+
+    int digitValue() const { return s.constref(p).digitValue(); }
+    QChar lower() const { return s.constref(p).lower(); }
+    QChar upper() const { return s.constref(p).upper(); }
+
+    QChar::Category category() const { return s.constref(p).category(); }
+    QChar::Direction direction() const { return s.constref(p).direction(); }
+    QChar::Joining joining() const { return s.constref(p).joining(); }
+    bool mirrored() const { return s.constref(p).mirrored(); }
+    QChar mirroredChar() const { return s.constref(p).mirroredChar(); }
+    //    const SecQString &decomposition() const { return s.constref(p).decomposition(); }
+    QChar::Decomposition decompositionTag() const { return s.constref(p).decompositionTag(); }
+    unsigned char combiningClass() const { return s.constref(p).combiningClass(); }
+
+    // Not the non-const ones of these.
+    uchar cell() const { return s.constref(p).cell(); }
+    uchar row() const { return s.constref(p).row(); }
+#endif
+};
+
+inline SecQCharRef SecQString::at( uint i ) { return SecQCharRef(this,i); }
+inline SecQCharRef SecQString::operator[]( int i ) { return at((uint)i); }
+
+class Q_EXPORT SecQConstString : private SecQString {
+public:
+    SecQConstString( const QChar* unicode, uint length );
+    ~SecQConstString();
+    const SecQString& string() const { return *this; }
+};
+
+
+/*****************************************************************************
+  SecQString inline functions
+ *****************************************************************************/
+
+// These two move code into makeSharedNull() and deletesData()
+// to improve cache-coherence (and reduce code bloat), while
+// keeping the common cases fast.
+//
+// No safe way to pre-init shared_null on ALL compilers/linkers.
+inline SecQString::SecQString() :
+    d(shared_null ? shared_null : makeSharedNull())
+{
+    d->ref();
+}
+//
+inline SecQString::~SecQString()
+{
+    if ( d->deref() ) {
+        if ( d != shared_null )
+           d->deleteSelf();
+    }
+}
+
+// needed for QDeepCopy
+inline void SecQString::detach()
+{ real_detach(); }
+
+inline bool SecQString::isNull() const
+{ return unicode() == 0; }
+
+inline uint SecQString::length() const
+{ return d->len; }
+
+inline bool SecQString::isEmpty() const
+{ return length() == 0; }
+
+/*****************************************************************************
+  SecQString non-member operators
+ *****************************************************************************/
+
+Q_EXPORT inline const SecQString operator+( const SecQString &s1, const SecQString &s2 )
+{
+    SecQString tmp( s1 );
+    tmp += s2;
+    return tmp;
+}
+
+#endif // SECQSTRING_H