Get rid of getopt_long and improve --help output.
[pinentry.git] / qt4 / main.cpp
1 /*
2    main.cpp - A (not yet) secure Qt 4 dialog for PIN entry.
3
4    Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB)
5    Copyright (C) 2003 g10 Code GmbH
6    Copyright 2007 Ingo Klöcker
7
8    Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>.
9    Modified by Marcus Brinkmann <marcus@g10code.de>.
10    Modified by Marc Mutz <marc@kdab.com>
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of the
15    License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful, but
18    WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "pinentryconfirm.h"
32 #include "pinentrydialog.h"
33 #include "pinentry.h"
34
35 #include <qapplication.h>
36 #include <QIcon>
37 #include <QString>
38 #include <qwidget.h>
39 #include <qmessagebox.h>
40 #include <QPushButton>
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <errno.h>
45
46 #include <memory>
47 #include <stdexcept>
48
49 #ifdef FALLBACK_CURSES
50 #include <pinentry-curses.h>
51 #endif
52
53 static QString escape_accel( const QString & s ) {
54
55   QString result;
56   result.reserve( s.size() );
57
58   bool afterUnderscore = false;
59
60   for ( unsigned int i = 0, end = s.size() ; i != end ; ++i ) {
61     const QChar ch = s[i];
62     if ( ch == QLatin1Char( '_' ) )
63       {
64         if ( afterUnderscore ) // escaped _
65           {
66             result += QLatin1Char( '_' );
67             afterUnderscore = false;
68           }
69         else // accel
70           {
71             afterUnderscore = true;
72           }
73       }
74     else
75       {
76         if ( afterUnderscore || // accel
77              ch == QLatin1Char( '&' ) ) // escape & from being interpreted by Qt
78           result += QLatin1Char( '&' );
79         result += ch;
80         afterUnderscore = false;
81       }
82   }
83
84   if ( afterUnderscore )
85     // trailing single underscore: shouldn't happen, but deal with it robustly:
86     result += QLatin1Char( '_' );
87
88   return result;
89 }
90
91 /* Hack for creating a QWidget with a "foreign" window ID */
92 class ForeignWidget : public QWidget
93 {
94 public:
95   explicit ForeignWidget( WId wid ) : QWidget( 0 )
96   {
97     QWidget::destroy();
98     create( wid, false, false );
99   }
100
101   ~ForeignWidget()
102   {
103     destroy( false, false );
104   }
105 };
106
107 namespace {
108     class InvalidUtf8 : public std::invalid_argument {
109     public:
110         InvalidUtf8() : std::invalid_argument( "invalid utf8" ) {}
111         ~InvalidUtf8() throw() {}
112     };
113 }
114
115 static const bool GPG_AGENT_IS_PORTED_TO_ONLY_SEND_UTF8 = false;
116
117 static QString from_utf8( const char * s ) {
118     const QString result = QString::fromUtf8( s );
119     if ( result.contains( QChar::ReplacementCharacter ) )
120       {
121         if ( GPG_AGENT_IS_PORTED_TO_ONLY_SEND_UTF8 )
122             throw InvalidUtf8();
123         else
124             return QString::fromLocal8Bit( s );
125       }
126
127     return result;
128 }
129
130 static int
131 qt_cmd_handler (pinentry_t pe)
132 {
133   QWidget *parent = 0;
134
135   /* FIXME: Add parent window ID to pinentry and GTK.  */
136   if (pe->parent_wid)
137     parent = new ForeignWidget ((WId) pe->parent_wid);
138
139   int want_pass = !!pe->pin;
140
141   const QString ok =
142       pe->ok             ? escape_accel( from_utf8( pe->ok ) ) :
143       pe->default_ok     ? escape_accel( from_utf8( pe->default_ok ) ) :
144       /* else */           QLatin1String( "&OK" ) ;
145   const QString cancel =
146       pe->cancel         ? escape_accel( from_utf8( pe->cancel ) ) :
147       pe->default_cancel ? escape_accel( from_utf8( pe->default_cancel ) ) :
148       /* else */           QLatin1String( "&Cancel" ) ;
149   const QString title =
150       pe->title ? from_utf8( pe->title ) :
151       /* else */  QLatin1String( "pinentry-qt4" ) ;
152
153
154   if (want_pass)
155     {
156       PinEntryDialog pinentry (parent, 0, pe->timeout, true, !!pe->quality_bar);
157
158       pinentry.setPinentryInfo (pe);
159       pinentry.setPrompt (escape_accel (from_utf8 (pe->prompt)) );
160       pinentry.setDescription (from_utf8 (pe->description));
161       if ( pe->title )
162           pinentry.setWindowTitle( from_utf8( pe->title ) );
163
164       /* If we reuse the same dialog window.  */
165       pinentry.setPin (secqstring());
166
167       pinentry.setOkText (ok);
168       pinentry.setCancelText (cancel);
169       if (pe->error)
170         pinentry.setError (from_utf8 (pe->error));
171       if (pe->quality_bar)
172         pinentry.setQualityBar (from_utf8 (pe->quality_bar));
173       if (pe->quality_bar_tt)
174         pinentry.setQualityBarTT (from_utf8 (pe->quality_bar_tt));
175
176       bool ret = pinentry.exec ();
177       if (!ret)
178         return -1;
179
180       const secstring pinUtf8 = toUtf8( pinentry.pin() );
181       const char *pin = pinUtf8.data();
182
183       int len = strlen (pin);
184       if (len >= 0)
185         {
186           pinentry_setbufferlen (pe, len + 1);
187           if (pe->pin)
188             {
189               strcpy (pe->pin, pin);
190               return len;
191             }
192         }
193       return -1;
194     }
195   else
196     {
197       const QString desc  = pe->description ? from_utf8 ( pe->description ) : QString();
198       const QString notok = pe->notok       ? escape_accel (from_utf8 ( pe->notok )) : QString();
199
200       const QMessageBox::StandardButtons buttons =
201           pe->one_button ? QMessageBox::Ok :
202           pe->notok      ? QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel :
203           /* else */       QMessageBox::Ok|QMessageBox::Cancel ;
204
205       PinentryConfirm box( QMessageBox::Information, pe->timeout, title, desc, buttons, parent );
206
207       const struct {
208           QMessageBox::StandardButton button;
209           QString label;
210       } buttonLabels[] = {
211           { QMessageBox::Ok,     ok     },
212           { QMessageBox::Yes,    ok     },
213           { QMessageBox::No,     notok  },
214           { QMessageBox::Cancel, cancel },
215       };
216
217       for ( size_t i = 0 ; i < sizeof buttonLabels / sizeof *buttonLabels ; ++i )
218         if ( (buttons & buttonLabels[i].button) && !buttonLabels[i].label.isEmpty() ) {
219             box.button( buttonLabels[i].button )->setText( buttonLabels[i].label );
220 #ifndef QT_NO_ACCESSIBILITY
221             box.button( buttonLabels[i].button )->setAccessibleDescription ( buttonLabels[i].label );
222 #endif
223         }
224
225       box.setIconPixmap( icon() );
226
227       if ( !pe->one_button )
228         box.setDefaultButton( QMessageBox::Cancel );
229
230       box.show();
231       raiseWindow( &box );
232
233       const int rc = box.exec();
234
235       if ( rc == QMessageBox::Cancel )
236         pe->canceled = true;
237
238       return rc == QMessageBox::Ok || rc == QMessageBox::Yes ;
239
240     }
241 }
242
243 static int
244 qt_cmd_handler_ex (pinentry_t pe)
245 {
246   try {
247     return qt_cmd_handler (pe);
248   } catch ( const InvalidUtf8 & ) {
249     pe->locale_err = true;
250     return pe->pin ? -1 : false ;
251   } catch ( ... ) {
252     pe->canceled = true;
253     return pe->pin ? -1 : false ;
254   }
255 }
256
257 pinentry_cmd_handler_t pinentry_cmd_handler = qt_cmd_handler_ex;
258
259 int
260 main (int argc, char *argv[])
261 {
262   pinentry_init ("pinentry-qt4");
263
264   std::auto_ptr<QApplication> app;
265
266 #ifdef FALLBACK_CURSES
267   if (!pinentry_have_display (argc, argv))
268     pinentry_cmd_handler = curses_cmd_handler;
269   else
270 #endif
271     {
272       /* Qt does only understand -display but not --display; thus we
273          are fixing that here.  The code is pretty simply and may get
274          confused if an argument is called "--display". */
275       char **new_argv, *p;
276       size_t n;
277       int i, done;
278
279       for (n=0,i=0; i < argc; i++)
280         n += strlen (argv[i])+1;
281       n++;
282       new_argv = (char**)calloc (argc+1, sizeof *new_argv);
283       if (new_argv)
284         *new_argv = (char*)malloc (n);
285       if (!new_argv || !*new_argv)
286         {
287           fprintf (stderr, "pinentry-qt4: can't fixup argument list: %s\n",
288                    strerror (errno));
289           exit (EXIT_FAILURE);
290
291         }
292       for (done=0,p=*new_argv,i=0; i < argc; i++)
293         if (!done && !strcmp (argv[i], "--display"))
294           {
295             new_argv[i] = strcpy (p, argv[i]+1);
296             p += strlen (argv[i]+1) + 1;
297             done = 1;
298           }
299         else
300           {
301             new_argv[i] = strcpy (p, argv[i]);
302             p += strlen (argv[i]) + 1;
303           }
304
305       /* We use a modal dialog window, so we don't need the application
306          window anymore.  */
307       i = argc;
308       app.reset (new QApplication (i, new_argv));
309       const QIcon icon( QLatin1String( ":/document-encrypt.png" ) );
310       app->setWindowIcon( icon );
311     }
312
313
314   pinentry_parse_opts (argc, argv);
315
316   return pinentry_loop () ? EXIT_FAILURE : EXIT_SUCCESS ;
317 }