Master key transformations (rounds) are now computed in two threads (from KeePass 1.11)
Added a button to measure the number of rounds that can be calculated in 1 second Don't try to open a URL when it's empty Improved code formatting git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@194 b624d157-de02-0410-bad0-e51aec6abb33
This commit is contained in:
parent
b0289a8b3c
commit
96ac6e31f1
|
@ -433,12 +433,12 @@ if(!File->open(QIODevice::ReadWrite)){
|
||||||
}
|
}
|
||||||
total_size=File->size();
|
total_size=File->size();
|
||||||
char* buffer = new char[total_size];
|
char* buffer = new char[total_size];
|
||||||
DECRYPT:
|
|
||||||
File->read(buffer,total_size);
|
File->read(buffer,total_size);
|
||||||
|
|
||||||
if(total_size < DB_HEADER_SIZE){
|
if(total_size < DB_HEADER_SIZE){
|
||||||
error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)");
|
error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)");
|
||||||
return false; }
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
memcpyFromLEnd32(&Signature1,buffer);
|
memcpyFromLEnd32(&Signature1,buffer);
|
||||||
memcpyFromLEnd32(&Signature2,buffer+4);
|
memcpyFromLEnd32(&Signature2,buffer+4);
|
||||||
|
@ -454,19 +454,26 @@ memcpyFromLEnd32(&KeyTransfRounds,buffer+120);
|
||||||
|
|
||||||
if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){
|
if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){
|
||||||
error=tr("Wrong Signature");
|
error=tr("Wrong Signature");
|
||||||
return false;}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){
|
if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){
|
||||||
error=tr("Unsupported File Version.");
|
error=tr("Unsupported File Version.");
|
||||||
return false;}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(Flags & PWM_FLAG_RIJNDAEL) Algorithm = Rijndael_Cipher;
|
if (Flags & PWM_FLAG_RIJNDAEL)
|
||||||
else if(Flags & PWM_FLAG_TWOFISH) Algorithm = Twofish_Cipher;
|
Algorithm = Rijndael_Cipher;
|
||||||
else{error=tr("Unknown Encryption Algorithm.");
|
else if (Flags & PWM_FLAG_TWOFISH)
|
||||||
return false;}
|
Algorithm = Twofish_Cipher;
|
||||||
|
else{
|
||||||
|
error=tr("Unknown Encryption Algorithm.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(!transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds))return false;
|
KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
|
||||||
|
|
||||||
quint8 FinalKey[32];
|
quint8 FinalKey[32];
|
||||||
|
|
||||||
SHA256 sha;
|
SHA256 sha;
|
||||||
|
@ -482,12 +489,16 @@ if(Algorithm == Rijndael_Cipher){
|
||||||
}
|
}
|
||||||
else if(Algorithm == Twofish_Cipher){
|
else if(Algorithm == Twofish_Cipher){
|
||||||
CTwofish twofish;
|
CTwofish twofish;
|
||||||
if(twofish.init(FinalKey, 32, EncryptionIV) != true){return false;}
|
if (twofish.init(FinalKey, 32, EncryptionIV) != true)
|
||||||
|
return false;
|
||||||
crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE,
|
crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE,
|
||||||
total_size - DB_HEADER_SIZE, (quint8 *)buffer + DB_HEADER_SIZE);
|
total_size - DB_HEADER_SIZE, (quint8 *)buffer + DB_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((crypto_size > 2147483446) || (!crypto_size && NumGroups)){error=tr("Decryption failed.\nThe key is wrong or the file is damaged."); return false;}
|
if ((crypto_size > 2147483446) || (!crypto_size && NumGroups)){
|
||||||
|
error=tr("Decryption failed.\nThe key is wrong or the file is damaged.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size);
|
SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size);
|
||||||
|
|
||||||
if(memcmp(ContentsHash, FinalKey, 32) != 0){
|
if(memcmp(ContentsHash, FinalKey, 32) != 0){
|
||||||
|
@ -526,24 +537,27 @@ RootGroup.Handle=NULL;
|
||||||
pField += 2; pos += 2;
|
pField += 2; pos += 2;
|
||||||
if (pos >= total_size){
|
if (pos >= total_size){
|
||||||
error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
|
error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
|
||||||
return false; }
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
memcpyFromLEnd32(&FieldSize, pField);
|
memcpyFromLEnd32(&FieldSize, pField);
|
||||||
pField += 4; pos += 4;
|
pField += 4; pos += 4;
|
||||||
if (pos >= (total_size + FieldSize)){
|
if (pos >= (total_size + FieldSize)){
|
||||||
error=tr("Unexpected error: Offset is out of range.").append(" [G2]");
|
error=tr("Unexpected error: Offset is out of range.").append(" [G2]");
|
||||||
return false;}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)pField);
|
bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)pField);
|
||||||
if ((FieldType == 0xFFFF) && (bRet == true)){
|
if ((FieldType == 0xFFFF) && (bRet == true)){
|
||||||
Groups << group;
|
Groups << group;
|
||||||
CurGroup++;} // Now and ONLY now the counter gets increased
|
CurGroup++; // Now and ONLY now the counter gets increased
|
||||||
|
}
|
||||||
pField += FieldSize;
|
pField += FieldSize;
|
||||||
pos += FieldSize;
|
pos += FieldSize;
|
||||||
if (pos >= total_size){
|
if (pos >= total_size){
|
||||||
error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
|
error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
|
||||||
return false;}
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StdEntry entry;
|
StdEntry entry;
|
||||||
|
@ -556,26 +570,31 @@ StdEntry entry;
|
||||||
pField += 2; pos += 2;
|
pField += 2; pos += 2;
|
||||||
if(pos >= total_size){
|
if(pos >= total_size){
|
||||||
error=tr("Unexpected error: Offset is out of range.").append(" [E1]");
|
error=tr("Unexpected error: Offset is out of range.").append(" [E1]");
|
||||||
return false;}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
memcpyFromLEnd32(&FieldSize, pField);
|
memcpyFromLEnd32(&FieldSize, pField);
|
||||||
pField += 4; pos += 4;
|
pField += 4; pos += 4;
|
||||||
if (pos >= (total_size + FieldSize)){
|
if (pos >= (total_size + FieldSize)){
|
||||||
error=tr("Unexpected error: Offset is out of range.").append(" [E2]");
|
error=tr("Unexpected error: Offset is out of range.").append(" [E2]");
|
||||||
return false; }
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField);
|
bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField);
|
||||||
|
|
||||||
if((FieldType == 0xFFFF) && (bRet == true)){
|
if((FieldType == 0xFFFF) && (bRet == true)){
|
||||||
Entries << entry;
|
Entries << entry;
|
||||||
if(!entry.GroupId)qDebug("NULL: %i, '%s'",(int)CurEntry,(char*)entry.Title.toUtf8().data());
|
if(!entry.GroupId)
|
||||||
CurEntry++;}
|
qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data());
|
||||||
|
CurEntry++;
|
||||||
|
}
|
||||||
|
|
||||||
pField += FieldSize;
|
pField += FieldSize;
|
||||||
pos += FieldSize;
|
pos += FieldSize;
|
||||||
if (pos >= total_size){
|
if (pos >= total_size){
|
||||||
error=tr("Unexpected error: Offset is out of range.").append(" [E3]");
|
error=tr("Unexpected error: Offset is out of range.").append(" [E3]");
|
||||||
return false; }
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!createGroupTree(Levels)){
|
if(!createGroupTree(Levels)){
|
||||||
|
@ -591,8 +610,8 @@ for(int i=0;i<Entries.size();i++){
|
||||||
if(!parseMetaStream(Entries[i]))
|
if(!parseMetaStream(Entries[i]))
|
||||||
UnknownMetaStreams << Entries[i];
|
UnknownMetaStreams << Entries[i];
|
||||||
Entries.removeAt(i);
|
Entries.removeAt(i);
|
||||||
i--;}
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int* EntryIndices=new int[Groups.size()];
|
int* EntryIndices=new int[Groups.size()];
|
||||||
|
@ -636,19 +655,6 @@ pBytes[4] = (quint8)((((quint32)d.time().minute() & 0x00000003) << 6) | ((quint3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Kdb3Database::transformKey(quint8* src,quint8* dst,quint8* KeySeed,int rounds){
|
|
||||||
quint8 tmp[32];
|
|
||||||
AESencrypt aes;
|
|
||||||
aes.key256(KeySeed);
|
|
||||||
memcpy(tmp,src,32);
|
|
||||||
for(int i=0;i<rounds;i++){
|
|
||||||
aes.ecb_encrypt(tmp,tmp,32);
|
|
||||||
}
|
|
||||||
SHA256::hashBuffer(tmp,dst,32);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Kdb3Database::numGroups(){
|
int Kdb3Database::numGroups(){
|
||||||
return Groups.size();
|
return Groups.size();
|
||||||
}
|
}
|
||||||
|
@ -657,8 +663,6 @@ int Kdb3Database::numEntries(){
|
||||||
return Entries.size();
|
return Entries.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Kdb3Database::deleteGroup(StdGroup* group){
|
void Kdb3Database::deleteGroup(StdGroup* group){
|
||||||
|
|
||||||
while(group->Childs.size())
|
while(group->Childs.size())
|
||||||
|
@ -1097,7 +1101,6 @@ else
|
||||||
}
|
}
|
||||||
|
|
||||||
void memcpyToLEnd32(char* dst,const quint32* src){
|
void memcpyToLEnd32(char* dst,const quint32* src){
|
||||||
|
|
||||||
if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
|
if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
|
||||||
memcpy(dst+0,((char*)src)+3,1);
|
memcpy(dst+0,((char*)src)+3,1);
|
||||||
memcpy(dst+1,((char*)src)+2,1);
|
memcpy(dst+1,((char*)src)+2,1);
|
||||||
|
@ -1109,7 +1112,6 @@ else
|
||||||
}
|
}
|
||||||
|
|
||||||
void memcpyToLEnd16(char* dst,const quint16* src){
|
void memcpyToLEnd16(char* dst,const quint16* src){
|
||||||
|
|
||||||
if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
|
if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
|
||||||
memcpy(dst+0,((char*)src)+1,1);
|
memcpy(dst+0,((char*)src)+1,1);
|
||||||
memcpy(dst+1,((char*)src)+0,1);
|
memcpy(dst+1,((char*)src)+0,1);
|
||||||
|
@ -1231,7 +1233,7 @@ bool Kdb3Database::save(){
|
||||||
memcpy(buffer+56,ContentsHash,32);
|
memcpy(buffer+56,ContentsHash,32);
|
||||||
memcpy(buffer+88,TransfRandomSeed,32);
|
memcpy(buffer+88,TransfRandomSeed,32);
|
||||||
memcpyToLEnd32(buffer+120,&KeyTransfRounds);
|
memcpyToLEnd32(buffer+120,&KeyTransfRounds);
|
||||||
transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
|
KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
|
||||||
quint8 FinalKey[32];
|
quint8 FinalKey[32];
|
||||||
|
|
||||||
SHA256 sha;
|
SHA256 sha;
|
||||||
|
@ -1719,3 +1721,74 @@ QList<IEntryHandle*> Kdb3Database::trashEntries(){
|
||||||
handles << &TrashHandles[i];
|
handles << &TrashHandles[i];
|
||||||
return handles;
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void KeyTransform::transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds){
|
||||||
|
KeyTransform* ktLeft = new KeyTransform(&src[0], &dst[0], KeySeed, rounds);
|
||||||
|
KeyTransform* ktRight = new KeyTransform(&src[16], &dst[16], KeySeed, rounds);
|
||||||
|
ktLeft->start();
|
||||||
|
ktRight->start();
|
||||||
|
while (ktLeft->isRunning() || ktRight->isRunning()){
|
||||||
|
QThread::msleep(100);
|
||||||
|
}
|
||||||
|
SHA256::hashBuffer(dst,dst,32);
|
||||||
|
delete ktLeft;
|
||||||
|
delete ktRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyTransform::KeyTransform(quint8* pSrc, quint8* pDst, quint8* pKeySeed, int pRounds){
|
||||||
|
src = pSrc;
|
||||||
|
dst = pDst;
|
||||||
|
KeySeed = pKeySeed;
|
||||||
|
rounds = pRounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyTransform::run(){
|
||||||
|
AESencrypt aes;
|
||||||
|
aes.key256(KeySeed);
|
||||||
|
memcpy(dst,src,16);
|
||||||
|
for (int i=0; i<rounds; i++){
|
||||||
|
aes.ecb_encrypt(dst,dst,16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int KeyTransformBenchmark::benchmark(int pMSecs){
|
||||||
|
KeyTransformBenchmark* ktbLeft = new KeyTransformBenchmark(pMSecs);
|
||||||
|
KeyTransformBenchmark* ktbRight = new KeyTransformBenchmark(pMSecs);
|
||||||
|
ktbLeft->start();
|
||||||
|
ktbRight->start();
|
||||||
|
while (ktbLeft->isRunning() || ktbRight->isRunning()){
|
||||||
|
QThread::msleep(100);
|
||||||
|
}
|
||||||
|
int num = ktbLeft->rounds + ktbRight->rounds;
|
||||||
|
delete ktbLeft;
|
||||||
|
delete ktbRight;
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyTransformBenchmark::KeyTransformBenchmark(int pMSecs){
|
||||||
|
msecs = pMSecs;
|
||||||
|
rounds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyTransformBenchmark::run(){
|
||||||
|
quint8 KeySeed[32];
|
||||||
|
memset(KeySeed, 0x4B, 32);
|
||||||
|
quint8 dst[16];
|
||||||
|
memset(dst, 0x7E, 16);
|
||||||
|
|
||||||
|
QTime t;
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
AESencrypt aes;
|
||||||
|
aes.key256(KeySeed);
|
||||||
|
|
||||||
|
do {
|
||||||
|
for (int i=0; i<64; i++){
|
||||||
|
aes.ecb_encrypt(dst,dst,16);
|
||||||
|
}
|
||||||
|
rounds += 64;
|
||||||
|
} while (t.elapsed() < msecs);
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#ifndef _STD_DATABASE_H_
|
#ifndef _STD_DATABASE_H_
|
||||||
#define _STD_DATABASE_H_
|
#define _STD_DATABASE_H_
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#define DB_HEADER_SIZE 124
|
#define DB_HEADER_SIZE 124
|
||||||
#define PWM_DBSIG_1 0x9AA2D903
|
#define PWM_DBSIG_1 0x9AA2D903
|
||||||
#define PWM_DBSIG_2 0xB54BFB65
|
#define PWM_DBSIG_2 0xB54BFB65
|
||||||
|
@ -210,7 +212,6 @@ private:
|
||||||
bool readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint32 FieldSize, quint8 *pData);
|
bool readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint32 FieldSize, quint8 *pData);
|
||||||
bool createGroupTree(QList<quint32>& Levels);
|
bool createGroupTree(QList<quint32>& Levels);
|
||||||
void createHandles();
|
void createHandles();
|
||||||
bool transformKey(quint8* src,quint8* dst,quint8* KeySeed,int rounds);
|
|
||||||
void invalidateHandle(StdEntry* entry);
|
void invalidateHandle(StdEntry* entry);
|
||||||
bool convHexToBinaryKey(char* HexKey, char* dst);
|
bool convHexToBinaryKey(char* HexKey, char* dst);
|
||||||
quint32 getNewGroupId();
|
quint32 getNewGroupId();
|
||||||
|
@ -252,4 +253,36 @@ private:
|
||||||
quint8 MasterKey[32];
|
quint8 MasterKey[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class KeyTransform : public QThread{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds);
|
||||||
|
|
||||||
|
private:
|
||||||
|
KeyTransform(quint8* pSrc, quint8* pDst, quint8* pKeySeed, int pRounds);
|
||||||
|
quint8* src;
|
||||||
|
quint8* dst;
|
||||||
|
quint8* KeySeed;
|
||||||
|
int rounds;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run();
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyTransformBenchmark : public QThread{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static int benchmark(int pMSecs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
KeyTransformBenchmark(int pMSecs);
|
||||||
|
int msecs;
|
||||||
|
int rounds;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run();
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include "DatabaseSettingsDlg.h"
|
#include "DatabaseSettingsDlg.h"
|
||||||
|
#include "Kdb3Database.h"
|
||||||
|
|
||||||
|
|
||||||
CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db, bool modal, Qt::WFlags fl)
|
CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db, bool modal, Qt::WFlags fl)
|
||||||
|
@ -35,8 +36,10 @@ CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db, bool modal, Qt::W
|
||||||
ComboAlgo->insertItem(1,tr("Twofish: 256 Bit"));
|
ComboAlgo->insertItem(1,tr("Twofish: 256 Bit"));
|
||||||
ComboAlgo->setCurrentIndex(database->cryptAlgorithm()); //Achtung: AlgoID muss gleich dem ComboBox Index sein!
|
ComboAlgo->setCurrentIndex(database->cryptAlgorithm()); //Achtung: AlgoID muss gleich dem ComboBox Index sein!
|
||||||
EditRounds->setText(QString::number( database->keyTransfRounds() ));
|
EditRounds->setText(QString::number( database->keyTransfRounds() ));
|
||||||
|
ButtonBench->setIcon(getIcon("alarmclock"));
|
||||||
connect( ButtonBox, SIGNAL( accepted() ), this, SLOT( OnOK() ) );
|
connect( ButtonBox, SIGNAL( accepted() ), this, SLOT( OnOK() ) );
|
||||||
connect( ButtonBox, SIGNAL( rejected() ), this, SLOT( OnCancel() ) );
|
connect( ButtonBox, SIGNAL( rejected() ), this, SLOT( OnCancel() ) );
|
||||||
|
connect( ButtonBench, SIGNAL( clicked() ), this, SLOT( OnBenchmark() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
CDbSettingsDlg::~CDbSettingsDlg()
|
CDbSettingsDlg::~CDbSettingsDlg()
|
||||||
|
@ -76,3 +79,7 @@ void CDbSettingsDlg::OnOK()
|
||||||
database->setCryptAlgorithm((CryptAlgorithm)ComboAlgo->currentIndex());
|
database->setCryptAlgorithm((CryptAlgorithm)ComboAlgo->currentIndex());
|
||||||
done(1);
|
done(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDbSettingsDlg::OnBenchmark(){
|
||||||
|
EditRounds->setText(QString::number( KeyTransformBenchmark::benchmark(1000) ));
|
||||||
|
}
|
||||||
|
|
|
@ -32,8 +32,9 @@ class CDbSettingsDlg : public QDialog, private Ui_DatabaseSettingsDlg
|
||||||
virtual void paintEvent(QPaintEvent *);
|
virtual void paintEvent(QPaintEvent *);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
virtual void OnCancel();
|
void OnCancel();
|
||||||
virtual void OnOK();
|
void OnOK();
|
||||||
|
void OnBenchmark();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IKdbSettings* database;
|
IKdbSettings* database;
|
||||||
|
|
|
@ -68,8 +68,28 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1" >
|
<item row="1" column="1" >
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
<widget class="QLineEdit" name="EditRounds" />
|
<widget class="QLineEdit" name="EditRounds" />
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="ButtonBench" >
|
||||||
|
<property name="sizePolicy" >
|
||||||
|
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>Calculate rounds for a 1-second delay on this computer</string>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -82,6 +82,9 @@ void openBrowser(IEntryHandle* entry){
|
||||||
}
|
}
|
||||||
|
|
||||||
void openBrowser(const QString& UrlString){
|
void openBrowser(const QString& UrlString){
|
||||||
|
if (UrlString.trimmed().isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
if (UrlString.startsWith("cmd://") && UrlString.length()>6){
|
if (UrlString.startsWith("cmd://") && UrlString.length()>6){
|
||||||
QProcess::startDetached(UrlString.right(UrlString.length()-6));
|
QProcess::startDetached(UrlString.right(UrlString.length()-6));
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue