]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
Begin adding TLS support to bat.
[bacula/bacula] / bacula / src / qt-console / console / console.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Version $Id$
30  *
31  *  Console Class
32  *
33  *   Kern Sibbald, January MMVII
34  *
35  */ 
36
37 #include <QAbstractEventDispatcher>
38 #include "bat.h"
39 #include "console.h"
40 #include "restore.h"
41 #include "select.h"
42 #include "run/run.h"
43
44 static int tls_pem_callback(char *buf, int size, const void *userdata);
45
46
47 Console::Console(QStackedWidget *parent)
48 {
49    QFont font;
50    m_parent = parent;
51    m_closeable = false;
52    m_console = this;
53    (void)parent;
54
55    setupUi(this);
56    m_sock = NULL;
57    m_at_prompt = false;
58    m_at_main_prompt = false;
59    m_textEdit = textEdit;   /* our console screen */
60    m_cursor = new QTextCursor(m_textEdit->document());
61    mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
62
63    /* Check for messages every 5 seconds */
64    m_timer = new QTimer(this);
65    QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
66    m_timer->start(5000);
67 }
68
69 Console::~Console()
70 {
71 }
72
73 void Console::poll_messages()
74 {
75    m_messages_pending = true;
76    if (m_at_main_prompt) {
77       write(".messages");
78       displayToPrompt();
79    }
80 }
81
82 /* Terminate any open socket */
83 void Console::terminate()
84 {
85    if (m_sock) {
86       m_sock->close();
87       m_sock = NULL;
88    }
89    m_timer->stop();
90 }
91
92 /*
93  * Connect to Director. If there are more than one, put up
94  * a modal dialog so that the user chooses one.
95  */
96 void Console::connect()
97 {
98    JCR jcr;
99    utime_t heart_beat;
100
101    m_textEdit = textEdit;   /* our console screen */
102
103    if (!m_dir) {          
104       mainWin->set_status("No Director found.");
105       return;
106    }
107    if (m_sock) {
108       mainWin->set_status("Already connected.");
109       return;
110    }
111
112    memset(&jcr, 0, sizeof(jcr));
113
114    mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
115    display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
116
117    /* Give GUI a chance */
118    app->processEvents();
119    
120    LockRes();
121    /* If cons==NULL, default console will be used */
122    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
123    UnlockRes();
124
125    char buf[1024];
126    /* Initialize Console TLS context */
127    if (cons && (cons->tls_enable || cons->tls_require)) {
128       /* Generate passphrase prompt */
129       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
130
131       /* Initialize TLS context:
132        * Args: CA certfile, CA certdir, Certfile, Keyfile,
133        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
134        */
135       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
136          cons->tls_ca_certdir, cons->tls_certfile,
137          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
138
139       if (!cons->tls_ctx) {
140          display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
141             m_dir->hdr.name);
142          return;
143       }
144    }
145
146    /* Initialize Director TLS context */
147    if (m_dir->tls_enable || m_dir->tls_require) {
148       /* Generate passphrase prompt */
149       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", 
150                 m_dir->hdr.name);
151
152       /* Initialize TLS context:
153        * Args: CA certfile, CA certdir, Certfile, Keyfile,
154        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
155       m_dir->tls_ctx = new_tls_context(m_dir->tls_ca_certfile,
156                           m_dir->tls_ca_certdir, m_dir->tls_certfile,
157                           m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
158
159       if (!m_dir->tls_ctx) {
160          display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
161             m_dir->hdr.name);
162          mainWin->set_status("Connection failed");
163          return;
164       }
165    }
166
167    if (m_dir->heartbeat_interval) {
168       heart_beat = m_dir->heartbeat_interval;
169    } else if (cons) {
170       heart_beat = cons->heartbeat_interval;
171    } else {
172       heart_beat = 0;
173    }        
174
175    m_sock = bnet_connect(NULL, 5, 15, heart_beat,
176                           _("Director daemon"), m_dir->address,
177                           NULL, m_dir->DIRport, 0);
178    if (m_sock == NULL) {
179       mainWin->set_status("Connection failed");
180       return;
181    } else {
182       /* Update page selector to green to indicate that Console is connected */
183       mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
184       QBrush greenBrush(Qt::green);
185       QTreeWidgetItem *item = mainWin->getFromHash(this);
186       item->setForeground(0, greenBrush);
187    }
188
189    jcr.dir_bsock = m_sock;
190
191    if (!authenticate_director(&jcr, m_dir, cons)) {
192       display_text(m_sock->msg);
193       return;
194    }
195
196    /* Give GUI a chance */
197    app->processEvents();
198
199    mainWin->set_status(_("Initializing ..."));
200
201    /* Set up input notifier */
202    m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
203    QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
204
205    write(".api 1");
206    displayToPrompt();
207
208    beginNewCommand();
209    dir_cmd(".jobs", job_list);
210    dir_cmd(".clients", client_list);
211    dir_cmd(".filesets", fileset_list);  
212    dir_cmd(".msgs", messages_list);
213    dir_cmd(".pools", pool_list);
214    dir_cmd(".storage", storage_list);
215    dir_cmd(".types", type_list);
216    dir_cmd(".levels", level_list);
217
218    mainWin->set_status(_("Connected"));
219    return;
220 }
221
222 bool Console::dir_cmd(QString &cmd, QStringList &results)
223 {
224    return dir_cmd(cmd.toUtf8().data(), results);
225 }
226
227 /*
228  * Send a command to the Director, and return the
229  *  results in a QStringList.  
230  */
231 bool Console::dir_cmd(const char *cmd, QStringList &results)
232 {
233    int stat;
234
235    notify(false);
236    write(cmd);
237    while ((stat = read()) > 0) {
238       if (mainWin->m_displayAll) display_text(msg());
239       strip_trailing_junk(msg());
240       results << msg();
241    }
242    notify(true);
243    discardToPrompt();
244    return true;              /* ***FIXME*** return any command error */
245 }
246
247 bool Console::sql_cmd(QString &query, QStringList &results)
248 {
249    return sql_cmd(query.toUtf8().data(), results);
250 }
251
252 /*
253  * Send an sql query to the Director, and return the
254  *  results in a QStringList.  
255  */
256 bool Console::sql_cmd(const char *query, QStringList &results)
257 {
258    if (!is_connectedGui())
259       return false;
260    int stat;
261    POOL_MEM cmd(PM_MESSAGE);
262
263    notify(false);
264    
265    pm_strcpy(cmd, ".sql query=\"");
266    pm_strcat(cmd, query);
267    pm_strcat(cmd, "\"");
268    write(cmd.c_str());
269    while ((stat = read()) > 0) {
270       if (mainWin->m_displayAll) display_text(msg());
271       strip_trailing_junk(msg());
272       results << msg();
273    }
274    notify(true);
275    discardToPrompt();
276    return true;              /* ***FIXME*** return any command error */
277 }
278
279
280 /*  
281  * Send a job name to the director, and read all the resulting
282  *  defaults. 
283  */
284 bool Console::get_job_defaults(struct job_defaults &job_defs)
285 {
286    QString scmd;
287    int stat;
288    char *def;
289
290    notify(false);
291    beginNewCommand();
292    scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
293    write(scmd);
294    while ((stat = read()) > 0) {
295       if (mainWin->m_displayAll) display_text(msg());
296       def = strchr(msg(), '=');
297       if (!def) {
298          continue;
299       }
300       /* Pointer to default value */
301       *def++ = 0;
302       strip_trailing_junk(def);
303
304       if (strcmp(msg(), "job") == 0) {
305          if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
306             goto bail_out;
307          }
308          continue;
309       }
310       if (strcmp(msg(), "pool") == 0) {
311          job_defs.pool_name = def;
312          continue;
313       }
314       if (strcmp(msg(), "messages") == 0) {
315          job_defs.messages_name = def;
316          continue;
317       }
318       if (strcmp(msg(), "client") == 0) {
319          job_defs.client_name = def;
320          continue;
321       }
322       if (strcmp(msg(), "storage") == 0) {
323          job_defs.store_name = def;
324          continue;
325       }
326       if (strcmp(msg(), "where") == 0) {
327          job_defs.where = def;
328          continue;
329       }
330       if (strcmp(msg(), "level") == 0) {
331          job_defs.level = def;
332          continue;
333       }
334       if (strcmp(msg(), "type") == 0) {
335          job_defs.type = def;
336          continue;
337       }
338       if (strcmp(msg(), "fileset") == 0) {
339          job_defs.fileset_name = def;
340          continue;
341       }
342       if (strcmp(msg(), "catalog") == 0) {
343          job_defs.catalog_name = def;
344          continue;
345       }
346       if (strcmp(msg(), "enabled") == 0) {
347          job_defs.enabled = *def == '1' ? true : false;
348          continue;
349       }
350    }
351
352 #ifdef xxx
353    bsnprintf(cmd, sizeof(cmd), "job=%s pool=%s client=%s storage=%s where=%s\n"
354       "level=%s type=%s fileset=%s catalog=%s enabled=%d\n",
355       job_defs.job_name.toUtf8().data(), job_defs.pool_name.toUtf8().data(), 
356       job_defs.client_name.toUtf8().data(), 
357       job_defs.pool_name.toUtf8().data(), job_defs.messages_name.toUtf8().data(), 
358       job_defs.store_name.toUtf8().data(),
359       job_defs.where.toUtf8().data(), job_defs.level.toUtf8().data(), 
360       job_defs.type.toUtf8().data(), job_defs.fileset_name.toUtf8().data(),
361       job_defs.catalog_name.toUtf8().data(), job_defs.enabled);
362 #endif
363
364    notify(true);
365    return true;
366
367 bail_out:
368    notify(true);
369    return false;
370 }
371
372
373 /*
374  * Save user settings associated with this console
375  */
376 void Console::writeSettings()
377 {
378    QFont font = get_font();
379
380    QSettings settings(m_dir->name(), "bat");
381    settings.beginGroup("Console");
382    settings.setValue("consoleFont", font.family());
383    settings.setValue("consolePointSize", font.pointSize());
384    settings.setValue("consoleFixedPitch", font.fixedPitch());
385    settings.endGroup();
386 }
387
388 /*
389  * Read and restore user settings associated with this console
390  */
391 void Console::readSettings()
392
393    QFont font = get_font();
394
395    QSettings settings(m_dir->name(), "bat");
396    settings.beginGroup("Console");
397    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
398    font.setPointSize(settings.value("consolePointSize", 10).toInt());
399    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
400    settings.endGroup();
401    m_textEdit->setFont(font);
402 }
403
404 /*
405  * Set the console textEdit font
406  */
407 void Console::set_font()
408 {
409    bool ok;
410    QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
411    if (ok) {
412       m_textEdit->setFont(font);
413    }
414 }
415
416 /*
417  * Get the console text edit font
418  */
419 const QFont Console::get_font()
420 {
421    return m_textEdit->font();
422 }
423
424 /*
425  * Slot for responding to status dir button on button bar
426  */
427 void Console::status_dir()
428 {
429    QString cmd("status dir");
430    consoleCommand(cmd);
431 }
432
433 /*
434  * Slot for responding to messages button on button bar
435  */
436 void Console::messages()
437 {
438    QString cmd(".messages");
439    consoleCommand(cmd);
440 }
441
442 /*
443  * Put text into the console window
444  */
445 void Console::display_textf(const char *fmt, ...)
446 {
447    va_list arg_ptr;
448    char buf[1000];
449    int len;
450    va_start(arg_ptr, fmt);
451    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
452    va_end(arg_ptr);
453    display_text(buf);
454 }
455
456 void Console::display_text(const QString buf)
457 {
458    m_cursor->insertText(buf);
459    update_cursor();
460 }
461
462
463 void Console::display_text(const char *buf)
464 {
465    m_cursor->insertText(buf);
466    update_cursor();
467 }
468
469 void Console::display_html(const QString buf)
470 {
471    m_cursor->insertHtml(buf);
472    update_cursor();
473 }
474
475 /* Position cursor to end of screen */
476 void Console::update_cursor()
477 {
478    QApplication::restoreOverrideCursor();
479    m_textEdit->moveCursor(QTextCursor::End);
480    m_textEdit->ensureCursorVisible();
481 }
482
483 /* 
484  * This should be moved into a bSocket class 
485  */
486 char *Console::msg()
487 {
488    if (m_sock) {
489       return m_sock->msg;
490    }
491    return NULL;
492 }
493
494 /* Send a command to the Director */
495 void Console::write_dir(const char *msg)
496 {
497    if (m_sock) {
498       mainWin->set_status(_("Processing command ..."));
499       QApplication::setOverrideCursor(Qt::WaitCursor);
500       write(msg);
501    } else {
502       mainWin->set_status(" Director not connected. Click on connect button.");
503       mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
504       QBrush redBrush(Qt::red);
505       QTreeWidgetItem *item = mainWin->getFromHash(this);
506       item->setForeground(0, redBrush);
507       m_at_prompt = false;
508       m_at_main_prompt = false;
509    }
510 }
511
512 int Console::write(const QString msg)
513 {
514    return write(msg.toUtf8().data());
515 }
516
517 int Console::write(const char *msg)
518 {
519    m_sock->msglen = pm_strcpy(m_sock->msg, msg);
520    m_at_prompt = false;
521    m_at_main_prompt = false;
522    if (mainWin->m_commDebug) Pmsg1(000, "send: %s\n", msg);
523    return m_sock->send();
524 }
525
526 /*
527  * Get to main command prompt -- i.e. abort any subcommand
528  */
529 void Console::beginNewCommand()
530 {
531    for (int i=0; i < 3; i++) {
532       write(".\n");
533       while (read() > 0) {
534          if (mainWin->m_displayAll) display_text(msg());
535       }
536       if (m_at_main_prompt) {
537          break;
538       }
539    }
540    display_text("\n");
541 }
542
543 void Console::displayToPrompt()
544
545    int stat = 0;
546    if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
547    while (!m_at_prompt) {
548       if ((stat=read()) > 0) {
549          display_text(msg());
550       }
551    }
552    if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
553 }
554
555 void Console::discardToPrompt()
556
557    int stat = 0;
558    if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
559    while (!m_at_prompt) {
560       if ((stat=read()) > 0) {
561          if (mainWin->m_displayAll) display_text(msg());
562       }
563    }
564    if (mainWin->m_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
565 }
566
567
568 /* 
569  * Blocking read from director
570  */
571 int Console::read()
572 {
573    int stat = 0;
574    while (m_sock) {
575       for (;;) {
576          stat = bnet_wait_data_intr(m_sock, 1);
577          if (stat > 0) {
578             break;
579          } 
580          app->processEvents();
581          if (m_api_set && m_messages_pending) {
582             write_dir(".messages");
583             m_messages_pending = false;
584          }
585       }
586       stat = m_sock->recv();
587       if (stat >= 0) {
588          if (m_at_prompt) {
589             display_text("\n");
590             m_at_prompt = false;
591             m_at_main_prompt = false;
592          }
593          if (mainWin->m_commDebug) Pmsg1(000, "got: %s", m_sock->msg);
594       }
595       switch (m_sock->msglen) {
596       case BNET_MSGS_PENDING:
597          if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
598          write_dir(".messages");
599          displayToPrompt();
600          m_messages_pending = false;
601          continue;
602       case BNET_CMD_OK:
603          if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
604          m_at_prompt = false;
605          m_at_main_prompt = false;
606          continue;
607       case BNET_CMD_BEGIN:
608          if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
609          m_at_prompt = false;
610          m_at_main_prompt = false;
611          continue;
612       case BNET_MAIN_PROMPT:
613          if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
614          m_at_prompt = true;
615          m_at_main_prompt = true;
616          mainWin->set_status(_("At prompt waiting for input ..."));
617          QApplication::restoreOverrideCursor();
618          break;
619       case BNET_PROMPT:
620          if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
621          m_at_prompt = true;
622          m_at_main_prompt = false;
623          mainWin->set_status(_("At prompt waiting for input ..."));
624          QApplication::restoreOverrideCursor();
625          break;
626       case BNET_CMD_FAILED:
627          if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
628          mainWin->set_status(_("Command failed. At prompt waiting for input ..."));
629          QApplication::restoreOverrideCursor();
630          break;
631       /* We should not get this one */
632       case BNET_EOD:
633          if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
634          mainWin->set_status_ready();
635          QApplication::restoreOverrideCursor();
636          if (!m_api_set) {
637             break;
638          }
639          continue;
640       case BNET_START_SELECT:
641          if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
642          new selectDialog(this);    
643          break;
644       case BNET_RUN_CMD:
645          if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
646          new runCmdPage();
647          break;
648       case BNET_ERROR_MSG:
649          if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
650          m_sock->recv();              /* get the message */
651          display_text(msg());
652          QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
653          break;
654       case BNET_WARNING_MSG:
655          if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
656          m_sock->recv();              /* get the message */
657          display_text(msg());
658          QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
659          break;
660       case BNET_INFO_MSG:
661          if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
662          m_sock->recv();              /* get the message */
663          display_text(msg());
664          mainWin->set_status(msg());
665          break;
666       }
667       if (is_bnet_stop(m_sock)) {         /* error or term request */
668          if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
669          m_sock->close();
670          m_sock = NULL;
671          mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
672          QBrush redBrush(Qt::red);
673          QTreeWidgetItem *item = mainWin->getFromHash(this);
674          item->setForeground(0, redBrush);
675          m_notifier->setEnabled(false);
676          delete m_notifier;
677          m_notifier = NULL;
678          mainWin->set_status(_("Director disconnected."));
679          QApplication::restoreOverrideCursor();
680          stat = BNET_HARDEOF;
681       }
682       break;
683    } 
684    return stat;
685 }
686
687 /* Called by signal when the Director has output for us */
688 void Console::read_dir(int /* fd */)
689 {
690    if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
691    while (read() >= 0) {
692       display_text(msg());
693    }
694 }
695
696 /*
697  * When the notifier is enabled, read_dir() will automatically be
698  * called by the Qt event loop when ever there is any output 
699  * from the Directory, and read_dir() will then display it on
700  * the console.
701  *
702  * When we are in a bat dialog, we want to control *all* output
703  * from the Directory, so we set notify to off.
704  *    m_console->notifiy(false);
705  */
706 void Console::notify(bool enable) 
707
708    m_notifier->setEnabled(enable);   
709 }
710
711 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
712 {
713    m_directorTreeItem = item;
714 }
715
716 void Console::setDirRes(DIRRES *dir) 
717
718    m_dir = dir;
719 }
720
721 /*
722  * To have the ability to get the name of the director resource.
723  */
724 void Console::getDirResName(QString &name_returned)
725 {
726    name_returned = m_dir->name();
727 }
728
729 bool Console::is_connectedGui()
730 {
731    if (is_connected()) {
732       return true;
733    } else {
734       QString message("Director ");
735       message += " is curerntly disconnected\n  Please reconnect!!";
736       QMessageBox::warning(this, tr("Bat"),
737          tr(message.toUtf8().data()), QMessageBox::Ok );
738       return false;
739    }
740 }
741
742 /*
743  * A temporary function to prevent connecting to the director if the director
744  * is busy with a restore.
745  */
746 bool Console::preventInUseConnect()
747 {
748    if (!is_connected()) {
749       QString message("Director ");
750       message += m_dir->name();
751       message += " is curerntly disconnected\n  Please reconnect!!";
752       QMessageBox::warning(this, tr("Bat"),
753          tr(message.toUtf8().data()), QMessageBox::Ok );
754       return false;
755    } else if (!m_at_main_prompt){
756       QString message("Director ");
757       message += m_dir->name();
758       message += " is curerntly busy\n  Please complete restore or other "
759 " operation !!  This is a limitation that will be resolved before a beta"
760 " release.  This is currently an alpa release.";
761       QMessageBox::warning(this, tr("Bat"),
762          tr(message.toUtf8().data()), QMessageBox::Ok );
763       return false;
764    } else if (!m_at_prompt){
765       QString message("Director ");
766       message += m_dir->name();
767       message += " is curerntly not at a prompt\n  Please try again!!";
768       QMessageBox::warning(this, tr("Bat"),
769          tr(message.toUtf8().data()), QMessageBox::Ok );
770       return false;
771    } else {
772       return true;
773    }
774 }
775
776 /*
777  * Call-back for reading a passphrase for an encrypted PEM file
778  * This function uses getpass(), 
779  *  which uses a static buffer and is NOT thread-safe.
780  */
781 static int tls_pem_callback(char *buf, int size, const void *userdata)
782 {
783 #ifdef HAVE_TLS
784    const char *prompt = (const char *)userdata;
785 # if defined(HAVE_WIN32)
786    sendit(prompt);
787    if (win32_cgets(buf, size) == NULL) {
788       buf[0] = 0;
789       return 0;
790    } else {
791       return strlen(buf);
792    }
793 # else
794    char *passwd;
795
796    passwd = getpass(prompt);
797    bstrncpy(buf, passwd, size);
798    return strlen(buf);
799 # endif
800 #else
801    buf[0] = 0;
802    return 0;
803 #endif
804 }