1 /*
2 Copyright (C) 2011-2014 Yubico AB.  All rights reserved.
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7 
8    1. Redistributions of source code must retain the above copyright
9       notice, this list of conditions and the following disclaimer.
10 
11    2. Redistributions in binary form must reproduce the above
12       copyright notice, this list of conditions and the following
13       disclaimer in the documentation and/or other materials provided
14       with the distribution.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "toolpage.h"
30 #include "yubikeyfinder.h"
31 #include "yubikeywriter.h"
32 #include "yubikeyutil.h"
33 #include "ui_toolpage.h"
34 #include "ui/helpbox.h"
35 #include "ui/confirmbox.h"
36 #include "mainwindow.h"
37 #include "otppage.h"
38 #include "chalresppage.h"
39 #include "oathpage.h"
40 #include "staticpage.h"
41 
42 #include <QFileDialog>
43 #include <QDebug>
44 #include <QSettings>
45 #include <QSignalMapper>
46 #include <QClipboard>
47 #include <QApplication>
48 
49 #include <ykpers.h>
50 #include <ykdef.h>
51 
52 #include "common.h"
53 
54 #define IMPORT_FILENAME_DEF "import.ycfg"
55 
56 QString ToolPage::m_filename = defaultImportFilename();
57 
ToolPage(QWidget * parent)58 ToolPage::ToolPage(QWidget *parent) :
59         QStackedWidget(parent),
60         ui(new Ui::ToolPage)
61 {
62     ui->setupUi(this);
63 
64     //Connect pages
65     connectPages();
66 
67     //Connect help buttons
68     connectHelpButtons();
69 
70     //Connect other signals and slots
71     connect(ui->converterResetBtn, SIGNAL(clicked()),
72             this, SLOT(resetConverterPage()));
73     connect(ui->chalRespResetBtn, SIGNAL(clicked()),
74             this, SLOT(resetChalRespPage()));
75     connect(ui->chalRespPerformBtn, SIGNAL(clicked()),
76             this, SLOT(performChallengeResponse()));
77     connect(ui->chalRespChallenge, SIGNAL(editingFinished()),
78             this, SLOT(on_chalRespChallenge_editingFinished()));
79     connect(ui->ndefResetBtn, SIGNAL(clicked()),
80             this, SLOT(resetNdefPage()));
81     connect(ui->ndefProgramBtn, SIGNAL(clicked()),
82             this, SLOT(programNdef()));
83 
84     connect(YubiKeyFinder::getInstance(), SIGNAL(keyFound(bool, bool*, int)),
85             this, SLOT(keyFound(bool, bool*)));
86 
87     ui->zapAccCodeEdit->setEnabled(false);
88     ui->ndefAccCodeEdit->setEnabled(false);
89 
90     ui->importBox->setVisible(false);
91 
92     loadSettings();
93 }
94 
~ToolPage()95 ToolPage::~ToolPage() {
96     delete ui;
97 }
98 
99 /*
100  Common
101 */
102 
connectPages()103 void ToolPage::connectPages() {
104     //Map the values of the navigation buttons with the indexes of
105     //the stacked widget
106 
107     //Create a QMapper
108     QSignalMapper *mapper = new QSignalMapper(this);
109 
110     //Connect the clicked signal with the QSignalMapper
111     connect(ui->converterBtn, SIGNAL(clicked()), mapper, SLOT(map()));
112     connect(ui->converterBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
113 
114     connect(ui->chalRespBtn, SIGNAL(clicked()), mapper, SLOT(map()));
115     connect(ui->chalRespBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
116 
117     connect(ui->ndefBtn, SIGNAL(clicked()), mapper, SLOT(map()));
118     connect(ui->ndefBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
119 
120     connect(ui->zapBtn, SIGNAL(clicked()), mapper, SLOT(map()));
121     connect(ui->zapBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
122 
123     connect(ui->importBtn, SIGNAL(clicked()), mapper, SLOT(map()));
124     connect(ui->importBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
125 
126     //Set a value for each button
127     mapper->setMapping(ui->converterBtn, Page_Converter);
128     mapper->setMapping(ui->converterBackBtn, Page_Base);
129 
130     mapper->setMapping(ui->chalRespBtn, Page_ChalResp);
131     mapper->setMapping(ui->chalRespBackBtn, Page_Base);
132 
133     mapper->setMapping(ui->ndefBtn, Page_Ndef);
134     mapper->setMapping(ui->ndefBackBtn, Page_Base);
135 
136     mapper->setMapping(ui->zapBtn, Page_Zap);
137     mapper->setMapping(ui->zapBackBtn, Page_Base);
138 
139     mapper->setMapping(ui->importBtn, Page_Import);
140     mapper->setMapping(ui->importBackBtn, Page_Base);
141 
142     //Connect the mapper to the widget
143     //The mapper will set a value to each button and
144     //set that value to the widget
145     //connect(pageMapper, SIGNAL(mapped(int)), this, SLOT(setCurrentIndex(int)));
146     connect(mapper, SIGNAL(mapped(int)), this, SLOT(setCurrentPage(int)));
147 
148     //Set the current page
149     m_currentPage = 0;
150     setCurrentIndex(Page_Base);
151 }
152 
loadSettings()153 void ToolPage::loadSettings() {
154     QSettings settings;
155     ui->importBox->setVisible(settings.value(SG_EXPORT_PREFERENCE).toBool());
156 }
157 
setCurrentPage(int pageIndex)158 void ToolPage::setCurrentPage(int pageIndex) {
159     //Page changed...
160 
161     m_currentPage = pageIndex;
162 
163     switch(pageIndex){
164     case Page_Converter:
165         resetConverterPage();
166         break;
167     }
168 
169     setCurrentIndex(pageIndex);
170 }
171 
connectHelpButtons()172 void ToolPage::connectHelpButtons() {
173 }
174 
resetChalRespPage()175 void ToolPage::resetChalRespPage() {
176     ui->chalRespChallenge->clear();
177     ui->chalRespResponse->clear();
178 }
179 
on_chalRespChallenge_editingFinished()180 void ToolPage::on_chalRespChallenge_editingFinished() {
181     QString challenge = ui->chalRespChallenge->text().trimmed();
182     ui->chalRespChallenge->setText(challenge);
183 }
184 
performChallengeResponse()185 void ToolPage::performChallengeResponse() {
186     QString challenge = ui->chalRespChallenge->text();
187     QString response = "";
188     bool hmac;
189     int slot;
190     if(ui->chalRespHmacRadio->isChecked()) {
191         hmac = true;
192     } else if(ui->chalRespYubicoRadio->isChecked()) {
193         hmac = false;
194     } else {
195       emit showStatusMessage(tr(ERR_CHAL_TYPE_NOT_SELECTED), 1);
196       return;
197     }
198     if(ui->chalRespSlot1Radio->isChecked()) {
199         slot = 1;
200     } else if(ui->chalRespSlot2Radio->isChecked()) {
201         slot = 2;
202     } else {
203       emit showStatusMessage(tr(ERR_CONF_SLOT_NOT_SELECTED), 1);
204       return;
205     }
206     YubiKeyWriter::getInstance()->doChallengeResponse(challenge, response, slot, hmac);
207     qDebug() << "response was: " << response;
208     ui->chalRespResponse->setText(response);
209 }
210 /*
211  Quick Page handling
212 */
resetConverterPage()213 void ToolPage::resetConverterPage() {
214     convert(0, "");
215     ui->converterHexTxt->setCursorPosition(0);
216     ui->converterHexTxt->setFocus();
217 }
218 
convert(int updatedIndex,QString txt)219 void ToolPage::convert(int updatedIndex, QString txt) {
220     unsigned char buf[32];
221     memset(buf, 0, sizeof(buf));
222     size_t bufLen;
223 
224     switch(updatedIndex) {
225     case 0: //Hex
226         YubiKeyUtil::qstrHexDecode(buf, &bufLen, txt);
227         break;
228 
229     case 1: //Modhex
230         YubiKeyUtil::qstrModhexDecode(buf, &bufLen, txt);
231         break;
232 
233     case 2: //Decimal
234         QString tmp = QString::number(txt.toULongLong(), 16);
235         size_t len = tmp.length();
236         if(len % 2 != 0) {
237             len++;
238         }
239         YubiKeyUtil::qstrClean(&tmp, (size_t)len, true);
240         YubiKeyUtil::qstrHexDecode(buf, &bufLen, tmp);
241         break;
242     }
243 
244     QString hex = YubiKeyUtil::qstrHexEncode(buf, bufLen);
245     QString modhex = YubiKeyUtil::qstrModhexEncode(buf, bufLen);
246     bool ok = false;
247     qulonglong dec = hex.toULongLong(&ok, 16);
248 
249     int hexLen = hex.length();
250     int modhexLen = modhex.length();
251 
252     ui->converterHexTxt->setText(hex);
253     ui->converterModhexTxt->setText(modhex);
254     ui->converterDecTxt->setText(QString::number(dec));
255 
256     ui->converterHexCopyBtn->setEnabled(hexLen > 0);
257     ui->converterModhexCopyBtn->setEnabled(modhexLen > 0);
258     ui->converterDecCopyBtn->setEnabled(
259             ui->converterDecTxt->text().length() > 0);
260 
261     ui->converterHexLenLbl->setText(tr("(%1 chars)").arg(hexLen));
262     ui->converterModhexLenLbl->setText(tr("(%1 chars)").arg(modhexLen));
263 
264     if(hexLen != 0 && !ok) {
265         ui->converterDecErrLbl->setText(tr(TOVERFLOW));
266     } else {
267         ui->converterDecErrLbl->setText(tr(""));
268     }
269 }
270 
on_converterHexTxt_editingFinished()271 void ToolPage::on_converterHexTxt_editingFinished() {
272     QString txt = ui->converterHexTxt->text();
273     YubiKeyUtil::qstrClean(&txt, 0, true);
274 
275     size_t len = txt.length();
276     if(len > 0) {
277         if(len % 2 != 0) {
278             len++;
279         }
280         YubiKeyUtil::qstrClean(&txt, (size_t)len, true);
281         convert(0, txt);
282     }
283     ui->converterHexTxt->setCursorPosition(len + len/2);
284 }
285 
on_converterModhexTxt_editingFinished()286 void ToolPage::on_converterModhexTxt_editingFinished() {
287     QString txt = ui->converterModhexTxt->text();
288     YubiKeyUtil::qstrModhexClean(&txt, 0, true);
289 
290     size_t len = txt.length();
291     if(len > 0) {
292         if(len % 2 != 0) {
293             len++;
294         }
295         YubiKeyUtil::qstrModhexClean(&txt, (size_t)len, true);
296         convert(1, txt);
297     }
298     ui->converterModhexTxt->setCursorPosition(len + len/2);
299 }
300 
on_converterDecTxt_editingFinished()301 void ToolPage::on_converterDecTxt_editingFinished() {
302     QString txt = ui->converterDecTxt->text();
303     bool ok = false;
304     qulonglong dec = txt.toULongLong(&ok);
305     if(ok) {
306         if(dec > 0) {
307             size_t len = txt.length();
308             YubiKeyUtil::qstrClean(&txt, (size_t)len, true);
309             convert(2, txt);
310             ui->converterDecTxt->setCursorPosition(len);
311         }
312     } else {
313         ui->converterDecErrLbl->setText(tr(TOVERFLOW));
314     }
315 
316 }
317 
copyToClipboard(const QString & str)318 void ToolPage::copyToClipboard(const QString &str) {
319     QClipboard *clipboard = QApplication::clipboard();
320     clipboard->setText(str);
321 
322     showStatusMessage(tr(VALUE_COPIED), 0);
323 }
324 
on_converterHexCopyBtn_clicked()325 void ToolPage::on_converterHexCopyBtn_clicked() {
326     QString txt = ui->converterHexTxt->text();
327     YubiKeyUtil::qstrClean(&txt, 0, true);
328 
329     copyToClipboard(txt);
330 }
331 
on_converterModhexCopyBtn_clicked()332 void ToolPage::on_converterModhexCopyBtn_clicked() {
333     QString txt = ui->converterModhexTxt->text();
334     YubiKeyUtil::qstrModhexClean(&txt, 0, true);
335 
336     copyToClipboard(txt);
337 }
338 
on_converterDecCopyBtn_clicked()339 void ToolPage::on_converterDecCopyBtn_clicked() {
340     copyToClipboard(ui->converterDecTxt->text());
341 }
342 
resetNdefPage()343 void ToolPage::resetNdefPage() {
344     ui->ndefEdit->setText("https://my.yubico.com/neo/");
345     ui->ndefTextLangEdit->setText("en-US");
346     ui->ndefUriRadio->setChecked(true);
347     ui->ndefAccCodeCheckbox->setChecked(false);
348     ui->ndefSlot1Radio->setChecked(false);
349     ui->ndefSlot2Radio->setChecked(false);
350 }
351 
programNdef()352 void ToolPage::programNdef() {
353     QSettings settings;
354     YubiKeyWriter *writer = YubiKeyWriter::getInstance();
355     bool uri = true;
356     QString language;
357     QString payload;
358     int slot;
359     if(ui->ndefTextRadio->isChecked()) {
360         uri = false;
361         language = ui->ndefTextLangEdit->text().trimmed();
362         if(language.isEmpty()) {
363             return;
364         }
365     }
366     payload = ui->ndefEdit->text().trimmed();
367     if(payload.isEmpty()) {
368         return;
369     }
370 
371     if(uri && !settings.value(SG_NDEF_WITHOUT_HTTP).toBool()) {
372         if(!payload.startsWith("http")) {
373             ConfirmBox confirm(this);
374             confirm.setConfirmIndex(ConfirmBox::Confirm_NdefWithoutHttp);
375             int ret = confirm.exec();
376             if(ret != 1) // 1 is yes
377                 return;
378         }
379     }
380 
381     if(ui->ndefSlot1Radio->isChecked()) {
382         slot = 1;
383     } else if(ui->ndefSlot2Radio->isChecked()) {
384         slot = 2;
385     } else {
386         emit showStatusMessage(tr(ERR_CONF_SLOT_NOT_SELECTED), 1);
387         return;
388     }
389 
390     connect(writer, SIGNAL(configWritten(bool, const QString &)),
391             this, SLOT(ndefWritten(bool, const QString &)));
392     writer->writeNdef(uri, language, payload, ui->ndefAccCodeEdit->text().remove(" "), slot);
393 }
394 
ndefWritten(bool written,const QString & msg)395 void ToolPage::ndefWritten(bool written, __attribute__((unused)) const QString &msg) {
396     disconnect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
397             this, SLOT(ndefWritten(bool, const QString &)));
398     if(written) {
399         showStatusMessage(tr("NDEF successfully written"));
400     }
401 }
402 
on_ndefTextRadio_toggled(bool checked)403 void ToolPage::on_ndefTextRadio_toggled(bool checked) {
404     if(checked) {
405         ui->ndefTextLangEdit->setEnabled(true);
406         ui->ndefEdit->setText("");
407     } else {
408         ui->ndefTextLangEdit->setText("en-US");
409         ui->ndefTextLangEdit->setEnabled(false);
410         ui->ndefEdit->setText("https://my.yubico.com/neo/");
411     }
412 }
413 
on_ndefAccCodeCheckbox_toggled(bool checked)414 void ToolPage::on_ndefAccCodeCheckbox_toggled(bool checked) {
415     ui->ndefAccCodeEdit->setText("00 00 00 00 00 00");
416     ui->ndefAccCodeEdit->setEnabled(checked);
417     ui->ndefUseSerial->setChecked(false);
418     ui->ndefUseSerial->setEnabled(checked);
419 }
420 
on_ndefUseSerial_toggled(bool checked)421 void ToolPage::on_ndefUseSerial_toggled(bool checked) {
422     if(checked) {
423         setSerial(ui->ndefAccCodeEdit);
424     }
425 }
426 
on_zapPerformBtn_clicked()427 void ToolPage::on_zapPerformBtn_clicked() {
428     int slot;
429     if(ui->zapSlot1Radio->isChecked()) {
430         slot = 1;
431     } else if(ui->zapSlot2Radio->isChecked()) {
432         slot = 2;
433     } else {
434       emit showStatusMessage(tr(ERR_CONF_SLOT_NOT_SELECTED), 1);
435       return;
436     }
437 
438     YubiKeyWriter *writer = YubiKeyWriter::getInstance();
439     connect(writer, SIGNAL(configWritten(bool, const QString &)),
440             this, SLOT(zapDone(bool, const QString &)));
441     writer->deleteConfig(slot, ui->zapAccCodeEdit->text().remove(" "));
442 }
443 
zapDone(bool written,const QString & msg)444 void ToolPage::zapDone(bool written, __attribute__((unused)) const QString &msg) {
445     disconnect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
446             this, SLOT(zapDone(bool, const QString &)));
447     if(written) {
448         showStatusMessage(tr("Configuration successfully deleted."));
449     }
450 }
451 
on_zapAccCodeCheckbox_toggled(bool checked)452 void ToolPage::on_zapAccCodeCheckbox_toggled(bool checked) {
453     ui->zapAccCodeEdit->setText("00 00 00 00 00 00");
454     ui->zapAccCodeEdit->setEnabled(checked);
455     ui->zapUseSerial->setChecked(false);
456     ui->zapUseSerial->setEnabled(checked);
457 }
458 
on_zapUseSerial_toggled(bool checked)459 void ToolPage::on_zapUseSerial_toggled(bool checked) {
460     if(checked) {
461         setSerial(ui->zapAccCodeEdit);
462     }
463 }
464 
on_importPerformBtn_clicked()465 void ToolPage::on_importPerformBtn_clicked() {
466     QString filename = QFileDialog::getOpenFileName(this, tr("Open file for import"), m_filename, tr("Yubico cfg format (*.ycfg);;All Files (*.*)"));
467     if(filename.isEmpty()) {
468         return;
469     }
470     QFile file(filename);
471 
472     if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
473         showStatusMessage(tr("Failed to open selected file."), 1);
474     }
475 
476     char data[1024];
477     int len = file.read(data, 1024);
478     if(!len) {
479         showStatusMessage(tr("Failed to read from selected file."), 1);
480     }
481     file.close();
482 
483     YKP_CONFIG *cfg = ykp_alloc();
484     YK_STATUS *ykds = YubiKeyFinder::getInstance()->status();
485     ykp_configure_version(cfg, ykds);
486     int ret = ykp_import_config(cfg, data, len, YKP_FORMAT_YCFG);
487     if(ret) {
488         QSettings settings;
489         m_filename = filename;
490         settings.setValue(SG_IMPORT_FILENAME, filename);
491 
492         MainWindow::Page page = MainWindow::Page_Otp;
493         int tab = OtpPage::Page_Advanced;
494         if(ykp_get_tktflag_OATH_HOTP(cfg)) {
495             if(ykp_get_cfgflag_CHAL_HMAC(cfg)) {
496                 qDebug() << "importing mode chal-resp hmac";
497                 page = MainWindow::Page_ChalResp;
498                 tab = ChalRespPage::Page_Advanced;
499                 settings.setValue(SG_REQUIRE_INPUT, ykp_get_cfgflag_CHAL_BTN_TRIG(cfg));
500                 settings.setValue(SG_HMAC_LT64, ykp_get_cfgflag_HMAC_LT64(cfg));
501             } else if(ykp_get_cfgflag_CHAL_YUBICO(cfg)) {
502                 qDebug() << "importing mode chal-resp yubico";
503                 page = MainWindow::Page_ChalResp;
504                 tab = ChalRespPage::Page_Quick;
505                 settings.setValue(SG_REQUIRE_INPUT, ykp_get_cfgflag_CHAL_BTN_TRIG(cfg));
506             } else {
507                 qDebug() << "importing mode oath hotp";
508                 page = MainWindow::Page_Oath;
509                 tab = OathPage::Page_Advanced;
510                 settings.setValue(SG_OATH_HOTP8, ykp_get_cfgflag_OATH_HOTP8(cfg));
511                 // XXX: handle seed and fixed_modhex
512             }
513         } else if(ykp_get_cfgflag_STATIC_TICKET(cfg)) {
514             qDebug() << "importing mode static";
515             page = MainWindow::Page_Static;
516             tab = StaticPage::Page_Advanced;
517             settings.setValue(SG_STRONG_PW1, ykp_get_cfgflag_STRONG_PW1(cfg));
518             if(ykp_get_cfgflag_STRONG_PW2(cfg)) {
519                 settings.setValue(SG_STRONG_PW2, true);
520                 settings.setValue(SG_STRONG_PW3, ykp_get_cfgflag_SEND_REF(cfg));
521             } else {
522                 settings.setValue(SG_STRONG_PW2, false);
523             }
524         } else {
525             qDebug() << "importing yubico otp";
526         }
527 
528         settings.setValue(SG_MAN_UPDATE, ykp_get_cfgflag_MAN_UPDATE(cfg));
529         settings.setValue(SG_PACING_10MS, ykp_get_cfgflag_PACING_10MS(cfg));
530         settings.setValue(SG_PACING_20MS, ykp_get_cfgflag_PACING_20MS(cfg));
531         settings.setValue(SG_APPEND_CR, ykp_get_tktflag_APPEND_CR(cfg));
532         settings.setValue(SG_APPEND_DELAY1, ykp_get_tktflag_APPEND_DELAY1(cfg));
533         settings.setValue(SG_APPEND_DELAY2, ykp_get_tktflag_APPEND_DELAY2(cfg));
534         settings.setValue(SG_APPEND_TAB1, ykp_get_tktflag_APPEND_TAB1(cfg));
535         settings.setValue(SG_APPEND_TAB2, ykp_get_tktflag_APPEND_TAB2(cfg));
536         settings.setValue(SG_TAB_FIRST, ykp_get_tktflag_TAB_FIRST(cfg));
537         settings.setValue(SG_SR_BTN_VISIBLE, ykp_get_extflag_SERIAL_BTN_VISIBLE(cfg));
538         settings.setValue(SG_SR_USB_VISIBLE, ykp_get_extflag_SERIAL_USB_VISIBLE(cfg));
539         settings.setValue(SG_SR_API_VISIBLE, ykp_get_extflag_SERIAL_API_VISIBLE(cfg));
540         settings.setValue(SG_USE_NUMERIC_KEYPAD, ykp_get_extflag_USE_NUMERIC_KEYPAD(cfg));
541         settings.setValue(SG_FAST_TRIG, ykp_get_extflag_FAST_TRIG(cfg));
542         settings.setValue(SG_ALLOW_UPDATE, ykp_get_extflag_ALLOW_UPDATE(cfg));
543         settings.setValue(SG_LED_INVERT, ykp_get_extflag_LED_INV(cfg));
544 
545         int config = ykp_config_num(cfg);
546 
547         emit switchPage(page, tab, config);
548         emit reloadSettings();
549     } else {
550         showStatusMessage(tr("Failed to parse the configuration."), 1);
551     }
552     ykp_free_config(cfg);
553 }
554 
keyFound(bool found,bool * featuresMatrix)555 void ToolPage::keyFound(bool found, bool* featuresMatrix) {
556     if(found && featuresMatrix[YubiKeyFinder::Feature_ChallengeResponse]) {
557         ui->chalRespPerformBtn->setEnabled(true);
558     } else {
559         ui->chalRespPerformBtn->setEnabled(false);
560     }
561     if(found && featuresMatrix[YubiKeyFinder::Feature_Ndef]) {
562         ui->ndefProgramBtn->setEnabled(true);
563     } else {
564         ui->ndefProgramBtn->setEnabled(false);
565     }
566     if(found && featuresMatrix[YubiKeyFinder::Feature_MultipleConfigurations]) {
567         ui->chalRespSlot2Radio->setEnabled(true);
568         ui->zapSlot2Radio->setEnabled(true);
569         ui->ndefSlot2Radio->setEnabled(true);
570     } else {
571         ui->chalRespSlot2Radio->setEnabled(false);
572         ui->zapSlot2Radio->setEnabled(false);
573         ui->ndefSlot2Radio->setEnabled(false);
574     }
575     ui->zapPerformBtn->setEnabled(found);
576     ui->importPerformBtn->setEnabled(found);
577 
578     if(found) {
579         m_serial = QString::number(YubiKeyFinder::getInstance()->serial());
580         int num = 12 - m_serial.length();
581         for(int i = 0; i < num; i++) {
582             m_serial.prepend("0");
583         }
584         if(!m_serial.isEmpty()) {
585             if(ui->ndefUseSerial->isChecked()) {
586                 setSerial(ui->ndefAccCodeEdit);
587             }
588             if(ui->zapUseSerial->isChecked()) {
589                 setSerial(ui->zapAccCodeEdit);
590             }
591         }
592     } else {
593         m_serial.clear();
594     }
595 }
596 
setImportFilename(QString filename)597 void ToolPage::setImportFilename(QString filename) {
598         m_filename = filename;
599 }
600 
defaultImportFilename()601 QString ToolPage::defaultImportFilename() {
602     return QDir::homePath() + "/" + IMPORT_FILENAME_DEF;
603 }
604 
setSerial(QLineEdit * line)605 void ToolPage::setSerial(QLineEdit *line) {
606     if(!m_serial.isEmpty()) {
607         line->setText(m_serial);
608     }
609 }
610