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(); | ||||
| char* buffer = new char[total_size]; | ||||
| DECRYPT: | ||||
| File->read(buffer,total_size); | ||||
| 
 | ||||
| if(total_size < DB_HEADER_SIZE){ | ||||
| error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)"); | ||||
| return false; } | ||||
| 	error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)"); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| memcpyFromLEnd32(&Signature1,buffer); | ||||
| memcpyFromLEnd32(&Signature2,buffer+4); | ||||
|  | @ -453,20 +453,27 @@ memcpy(TransfRandomSeed,buffer+88,32); | |||
| memcpyFromLEnd32(&KeyTransfRounds,buffer+120); | ||||
| 
 | ||||
| if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){ | ||||
| error=tr("Wrong Signature"); | ||||
| return false;} | ||||
| 	error=tr("Wrong Signature"); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){ | ||||
| 	error=tr("Unsupported File Version."); | ||||
| 	return false;} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| if(Flags & PWM_FLAG_RIJNDAEL) Algorithm = Rijndael_Cipher; | ||||
| else if(Flags & PWM_FLAG_TWOFISH) Algorithm = Twofish_Cipher; | ||||
| 		else{error=tr("Unknown Encryption Algorithm."); | ||||
| 			 return false;} | ||||
| if (Flags & PWM_FLAG_RIJNDAEL) | ||||
| 	Algorithm = Rijndael_Cipher; | ||||
| else if (Flags & PWM_FLAG_TWOFISH) | ||||
| 	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]; | ||||
| 
 | ||||
| SHA256 sha; | ||||
|  | @ -482,12 +489,16 @@ if(Algorithm == Rijndael_Cipher){ | |||
| } | ||||
| else if(Algorithm == Twofish_Cipher){ | ||||
| 	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, | ||||
| 	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); | ||||
| 
 | ||||
| if(memcmp(ContentsHash, FinalKey, 32) != 0){ | ||||
|  | @ -518,65 +529,73 @@ RootGroup.Title="$ROOT$"; | |||
| RootGroup.Parent=NULL; | ||||
| RootGroup.Handle=NULL; | ||||
| 
 | ||||
| 	for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) | ||||
| 	{ | ||||
| for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) | ||||
| { | ||||
| 	pField = buffer+pos; | ||||
| 
 | ||||
| 	memcpyFromLEnd16(&FieldType, pField); | ||||
| 	pField += 2; pos += 2; | ||||
| 		if(pos >= total_size) { | ||||
| 	if (pos >= total_size){ | ||||
| 		error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); | ||||
| 		 return false; } | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpyFromLEnd32(&FieldSize, pField); | ||||
| 	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]"); | ||||
| 		return false;} | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)pField); | ||||
| 		if((FieldType == 0xFFFF) && (bRet == true)){ | ||||
| 	if ((FieldType == 0xFFFF) && (bRet == true)){ | ||||
| 		Groups << group; | ||||
| 			CurGroup++;} // Now and ONLY now the counter gets increased
 | ||||
| 
 | ||||
| 		CurGroup++; // Now and ONLY now the counter gets increased
 | ||||
| 	} | ||||
| 	pField += FieldSize; | ||||
| 	pos += FieldSize; | ||||
| 		if(pos >= total_size) { | ||||
| 	if (pos >= total_size){ | ||||
| 		error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); | ||||
| 		 return false;} | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| StdEntry entry; | ||||
| 
 | ||||
| 	for(unsigned long CurEntry = 0; CurEntry < NumEntries;) | ||||
| 	{ | ||||
| for (unsigned long CurEntry = 0; CurEntry < NumEntries;) | ||||
| { | ||||
| 	pField = buffer+pos; | ||||
| 
 | ||||
| 	memcpyFromLEnd16(&FieldType, pField); | ||||
| 	pField += 2; pos += 2; | ||||
| 	if(pos >= total_size){ | ||||
| 		error=tr("Unexpected error: Offset is out of range.").append(" [E1]"); | ||||
| 		 return false;} | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpyFromLEnd32(&FieldSize, pField); | ||||
| 	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]"); | ||||
| 		 return false; } | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField); | ||||
| 
 | ||||
| 	if((FieldType == 0xFFFF) && (bRet == true)){ | ||||
| 		Entries << entry; | ||||
| 			if(!entry.GroupId)qDebug("NULL: %i, '%s'",(int)CurEntry,(char*)entry.Title.toUtf8().data()); | ||||
| 			CurEntry++;} | ||||
| 		if(!entry.GroupId) | ||||
| 			qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data()); | ||||
| 		CurEntry++; | ||||
| 	} | ||||
| 
 | ||||
| 	pField += FieldSize; | ||||
| 	pos += FieldSize; | ||||
| 		if(pos >= total_size) { | ||||
| 	if (pos >= total_size){ | ||||
| 		error=tr("Unexpected error: Offset is out of range.").append(" [E3]"); | ||||
| 		 return false; } | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| if(!createGroupTree(Levels)){ | ||||
| 	error=tr("Invalid group tree."); | ||||
|  | @ -591,8 +610,8 @@ for(int i=0;i<Entries.size();i++){ | |||
| 		if(!parseMetaStream(Entries[i])) | ||||
| 			UnknownMetaStreams << Entries[i]; | ||||
| 		Entries.removeAt(i); | ||||
| 		i--;} | ||||
| 
 | ||||
| 		i--; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int* EntryIndices=new int[Groups.size()]; | ||||
|  | @ -614,38 +633,25 @@ return true; | |||
| } | ||||
| 
 | ||||
| QDateTime Kdb3Database::dateFromPackedStruct5(const unsigned char* pBytes){ | ||||
| quint32 dw1, dw2, dw3, dw4, dw5; | ||||
| dw1 = (quint32)pBytes[0]; dw2 = (quint32)pBytes[1]; dw3 = (quint32)pBytes[2]; | ||||
| dw4 = (quint32)pBytes[3]; dw5 = (quint32)pBytes[4]; | ||||
| int y = (dw1 << 6) | (dw2 >> 2); | ||||
| int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); | ||||
| int d = (dw3 >> 1) & 0x0000001F; | ||||
| int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); | ||||
| int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); | ||||
| int s = dw5 & 0x0000003F; | ||||
| return QDateTime(QDate(y,mon,d),QTime(h,min,s)); | ||||
| 	quint32 dw1, dw2, dw3, dw4, dw5; | ||||
| 	dw1 = (quint32)pBytes[0]; dw2 = (quint32)pBytes[1]; dw3 = (quint32)pBytes[2]; | ||||
| 	dw4 = (quint32)pBytes[3]; dw5 = (quint32)pBytes[4]; | ||||
| 	int y = (dw1 << 6) | (dw2 >> 2); | ||||
| 	int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); | ||||
| 	int d = (dw3 >> 1) & 0x0000001F; | ||||
| 	int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); | ||||
| 	int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); | ||||
| 	int s = dw5 & 0x0000003F; | ||||
| 	return QDateTime(QDate(y,mon,d),QTime(h,min,s)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Kdb3Database::dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){ | ||||
| pBytes[0] = (quint8)(((quint32)d.date().year() >> 6) & 0x0000003F); | ||||
| pBytes[1] = (quint8)((((quint32)d.date().year() & 0x0000003F) << 2) | (((quint32)d.date().month() >> 2) & 0x00000003)); | ||||
| pBytes[2] = (quint8)((((quint32)d.date().month() & 0x00000003) << 6) | (((quint32)d.date().day() & 0x0000001F) << 1) | (((quint32)d.time().hour() >> 4) & 0x00000001)); | ||||
| pBytes[3] = (quint8)((((quint32)d.time().hour() & 0x0000000F) << 4) | (((quint32)d.time().minute() >> 2) & 0x0000000F)); | ||||
| pBytes[4] = (quint8)((((quint32)d.time().minute() & 0x00000003) << 6) | ((quint32)d.time().second() & 0x0000003F)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 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; | ||||
| 	pBytes[0] = (quint8)(((quint32)d.date().year() >> 6) & 0x0000003F); | ||||
| 	pBytes[1] = (quint8)((((quint32)d.date().year() & 0x0000003F) << 2) | (((quint32)d.date().month() >> 2) & 0x00000003)); | ||||
| 	pBytes[2] = (quint8)((((quint32)d.date().month() & 0x00000003) << 6) | (((quint32)d.date().day() & 0x0000001F) << 1) | (((quint32)d.time().hour() >> 4) & 0x00000001)); | ||||
| 	pBytes[3] = (quint8)((((quint32)d.time().hour() & 0x0000000F) << 4) | (((quint32)d.time().minute() >> 2) & 0x0000000F)); | ||||
| 	pBytes[4] = (quint8)((((quint32)d.time().minute() & 0x00000003) << 6) | ((quint32)d.time().second() & 0x0000003F)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -657,8 +663,6 @@ int Kdb3Database::numEntries(){ | |||
| 	return Entries.size(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void Kdb3Database::deleteGroup(StdGroup* group){ | ||||
| 
 | ||||
| 	while(group->Childs.size()) | ||||
|  | @ -1097,24 +1101,22 @@ else | |||
| } | ||||
| 
 | ||||
| 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+1,((char*)src)+2,1); | ||||
| 		memcpy(dst+2,((char*)src)+1,1); | ||||
| 		memcpy(dst+3,((char*)src)+0,1); | ||||
| } | ||||
| else | ||||
| 	} | ||||
| 	else | ||||
| 		memcpy(dst,src,4); | ||||
| } | ||||
| 
 | ||||
| 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+1,((char*)src)+0,1); | ||||
| } | ||||
| else | ||||
| 	} | ||||
| 	else | ||||
| 		memcpy(dst,src,2); | ||||
| } | ||||
| 
 | ||||
|  | @ -1231,7 +1233,7 @@ bool Kdb3Database::save(){ | |||
| 	memcpy(buffer+56,ContentsHash,32); | ||||
| 	memcpy(buffer+88,TransfRandomSeed,32); | ||||
| 	memcpyToLEnd32(buffer+120,&KeyTransfRounds); | ||||
| 	transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds); | ||||
| 	KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds); | ||||
| 	quint8 FinalKey[32]; | ||||
| 
 | ||||
| 	SHA256 sha; | ||||
|  | @ -1719,3 +1721,74 @@ QList<IEntryHandle*> Kdb3Database::trashEntries(){ | |||
| 			handles << &TrashHandles[i]; | ||||
| 	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_ | ||||
| #define _STD_DATABASE_H_ | ||||
| 
 | ||||
| #include <QThread> | ||||
| 
 | ||||
| #define DB_HEADER_SIZE	124 | ||||
| #define PWM_DBSIG_1		0x9AA2D903 | ||||
| #define PWM_DBSIG_2 	0xB54BFB65 | ||||
|  | @ -210,7 +212,6 @@ private: | |||
| 	bool readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint32 FieldSize, quint8 *pData); | ||||
| 	bool createGroupTree(QList<quint32>& Levels); | ||||
| 	void createHandles(); | ||||
| 	bool transformKey(quint8* src,quint8* dst,quint8* KeySeed,int rounds); | ||||
| 	void invalidateHandle(StdEntry* entry); | ||||
| 	bool convHexToBinaryKey(char* HexKey, char* dst); | ||||
| 	quint32 getNewGroupId(); | ||||
|  | @ -252,4 +253,36 @@ private: | |||
| 	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 | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
|   | ||||
| 
 | ||||
| #include "DatabaseSettingsDlg.h" | ||||
| #include "Kdb3Database.h" | ||||
| 
 | ||||
| 
 | ||||
| CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db,  bool modal, Qt::WFlags fl) | ||||
|  | @ -34,9 +35,11 @@ CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db,  bool modal, Qt::W | |||
| 	ComboAlgo->insertItem(0,tr("AES(Rijndael):  256 Bit   (default)")); | ||||
| 	ComboAlgo->insertItem(1,tr("Twofish:  256 Bit")); | ||||
| 	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( rejected() ), this, SLOT( OnCancel() ) ); | ||||
| 	connect( ButtonBench, SIGNAL( clicked() ), this, SLOT( OnBenchmark() ) ); | ||||
| } | ||||
| 
 | ||||
| CDbSettingsDlg::~CDbSettingsDlg() | ||||
|  | @ -76,3 +79,7 @@ void CDbSettingsDlg::OnOK() | |||
| 	database->setCryptAlgorithm((CryptAlgorithm)ComboAlgo->currentIndex()); | ||||
| 	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 *); | ||||
| 
 | ||||
| 	private slots: | ||||
|     	virtual void OnCancel(); | ||||
|     	virtual void OnOK(); | ||||
|     	void OnCancel(); | ||||
|     	void OnOK(); | ||||
| 		void OnBenchmark(); | ||||
| 
 | ||||
| 	private: | ||||
| 		IKdbSettings* database; | ||||
|  |  | |||
|  | @ -68,8 +68,28 @@ | |||
|        </widget> | ||||
|       </item> | ||||
|       <item row="1" column="1" > | ||||
|        <layout class="QHBoxLayout" > | ||||
|         <item> | ||||
|          <widget class="QLineEdit" name="EditRounds" /> | ||||
|         </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> | ||||
|     </widget> | ||||
|    </item> | ||||
|  |  | |||
|  | @ -82,6 +82,9 @@ void openBrowser(IEntryHandle* entry){ | |||
| } | ||||
| 
 | ||||
| void openBrowser(const QString& UrlString){ | ||||
| 	if (UrlString.trimmed().isEmpty()) | ||||
| 		return; | ||||
| 	 | ||||
| 	if (UrlString.startsWith("cmd://") && UrlString.length()>6){ | ||||
| 		QProcess::startDetached(UrlString.right(UrlString.length()-6)); | ||||
| 		return; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 sniperbeamer
						sniperbeamer