Release 0.4.3-3maemo with patches to disable menus/actions, add ScrollArea and fix...
[keepassx] / src / Kdb3Database.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2008 by Tarek Saidi                                *
3  *   tarek.saidi@arcor.de                                                  *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; version 2 of the License.               *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19
20 #include "Kdb3Database.h"
21
22 #include "crypto/twoclass.h"
23 #include <QBuffer>
24 #include <algorithm>
25
26 #define UNEXP_ERROR error=QString("Unexpected error in: %1, Line:%2").arg(__FILE__).arg(__LINE__);
27
28 const QDateTime Date_Never(QDate(2999,12,28),QTime(23,59,59));
29
30
31 bool Kdb3Database::EntryHandleLessThan(const IEntryHandle* This,const IEntryHandle* Other){
32         if(!This->isValid() && Other->isValid())return true;
33         if(This->isValid() && !Other->isValid())return false;
34         if(!This->isValid() && !Other->isValid())return false;
35         return This->visualIndex()<Other->visualIndex();
36 }
37
38 bool Kdb3Database::EntryHandleLessThanStd(const IEntryHandle* This,const IEntryHandle* Other){
39         int comp = This->title().compare(Other->title());
40         if (comp < 0) return true;
41         else if (comp > 0) return false;
42         
43         comp = This->username().compare(Other->username());
44         if (comp < 0) return true;
45         else if (comp > 0) return false;
46         
47         return true;
48 }
49
50 bool Kdb3Database::StdEntryLessThan(const Kdb3Database::StdEntry& This,const Kdb3Database::StdEntry& Other){
51         return This.Index<Other.Index;
52 }
53
54
55 Kdb3Database::Kdb3Database() : File(NULL), RawMasterKey(32), RawMasterKey_CP1252(32),
56         RawMasterKey_Latin1(32), RawMasterKey_UTF8(32), MasterKey(32){
57 }
58
59 QString Kdb3Database::getError(){
60         return error;
61 }
62
63 void Kdb3Database::addIcon(const QPixmap& icon){
64         CustomIcons << icon;
65         emit iconsModified();
66 }
67
68 QPixmap& Kdb3Database::icon(int i){
69         if(i>=builtinIcons()+CustomIcons.size())
70                 return EntryIcons[0];
71         if(i<builtinIcons())
72                 return EntryIcons[i];
73         return CustomIcons[i-builtinIcons()];
74 }
75
76 void Kdb3Database::removeIcon(int id){
77         id-=builtinIcons();
78         if(id < 0 ) return;
79         if(id >= CustomIcons.size()) return;
80         CustomIcons.removeAt(id); // .isNull()==true
81         for(int i=0;i<Entries.size();i++){
82                 if(Entries[i].Image == id+builtinIcons())
83                         Entries[i].Image=0;
84                 if(Entries[i].Image>id+builtinIcons())
85                         Entries[i].Image--;
86         }
87         for(int i=0;i<Groups.size();i++){
88                 if(Groups[i].Image == id+builtinIcons())
89                         Groups[i].Image=0;
90                 if(Groups[i].Image>id+builtinIcons())
91                         Groups[i].Image--;
92         }
93         emit iconsModified();
94 }
95
96 void Kdb3Database::replaceIcon(int id,const QPixmap& icon){
97         if(id<builtinIcons())return;
98                 CustomIcons[id-builtinIcons()]=icon;
99         emit iconsModified();
100 }
101
102 int Kdb3Database::numIcons(){
103         return builtinIcons()+CustomIcons.size();
104 }
105
106 bool Kdb3Database::parseMetaStream(const StdEntry& entry){
107
108         qDebug("Found Metastream: %s", CSTR(entry.Comment));
109
110         if(entry.Comment=="KPX_GROUP_TREE_STATE"){
111                 parseGroupTreeStateMetaStream(entry.Binary);
112                 return true;
113         }
114         else if(entry.Comment=="KPX_CUSTOM_ICONS_4"){
115                 parseCustomIconsMetaStream(entry.Binary);
116                 return true;
117         }
118         else if(entry.Comment=="KPX_CUSTOM_ICONS_3"){
119                 if (!hasV4IconMetaStream)
120                         parseCustomIconsMetaStreamV3(entry.Binary);
121                 return true;
122         }
123         else if(entry.Comment=="KPX_CUSTOM_ICONS_2"){
124                 qDebug("Removed old CuIcMeSt v2");
125                 return true;
126         }
127         else if(entry.Comment=="KPX_CUSTOM_ICONS"){
128                 qDebug("Removed old CuIcMeSt v1");
129                 return true;
130         }
131
132         return false; //unknown MetaStream
133 }
134
135 bool Kdb3Database::isMetaStream(StdEntry& p){
136         if(p.Binary.isNull()) return false;
137         if(p.Comment == "") return false;
138         if(p.BinaryDesc != "bin-stream") return false;
139         if(p.Title != "Meta-Info") return false;
140         if(p.Username != "SYSTEM") return false;
141         if(p.Url != "$") return false;
142         if(p.Image != 0) return false;
143         return true;
144 }
145
146 void Kdb3Database::parseCustomIconsMetaStream(const QByteArray& dta){
147         //Rev 4 (KeePassX 0.3.2)
148         quint32 NumIcons,NumEntries,NumGroups,offset;
149         memcpyFromLEnd32(&NumIcons,dta.data());
150         memcpyFromLEnd32(&NumEntries,dta.data()+4);
151         memcpyFromLEnd32(&NumGroups,dta.data()+8);
152         offset=12;
153         CustomIcons.clear();
154         for(int i=0;i<NumIcons;i++){
155                 CustomIcons << QPixmap();
156                 quint32 Size;
157                 memcpyFromLEnd32(&Size,dta.data()+offset);
158                 if(offset+Size > dta.size()){
159                         CustomIcons.clear();
160                         qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error.");
161                         return;
162                 }
163                 offset+=4;
164                 if(!CustomIcons.back().loadFromData((const unsigned char*)dta.data()+offset,Size,"PNG")){
165                         CustomIcons.clear();
166                         qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error.");
167                         return;
168                 }
169                 offset+=Size;
170                 if(offset > dta.size()){
171                         CustomIcons.clear();
172                         qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error.");
173                         return;
174                 }
175         }
176         for(int i=0;i<NumEntries;i++){
177                 quint32 Icon;
178                 KpxUuid EntryUuid;
179                 EntryUuid.fromRaw(dta.data()+offset);
180                 offset+=16;
181                 memcpyFromLEnd32(&Icon,dta.data()+offset);
182                 offset+=4;
183                 StdEntry* entry=getEntry(EntryUuid);
184                 if(entry)
185                         entry->Image=Icon+BUILTIN_ICONS;
186         }
187         for(int i=0;i<NumGroups;i++){
188                 quint32 GroupId,Icon;
189                 memcpyFromLEnd32(&GroupId,dta.data()+offset);
190                 offset+=4;
191                 memcpyFromLEnd32(&Icon,dta.data()+offset);
192                 offset+=4;
193                 StdGroup* Group=getGroup(GroupId);
194                 if(Group)
195                         Group->Image=Icon+BUILTIN_ICONS;
196         }
197         return;
198 }
199
200 void Kdb3Database::parseCustomIconsMetaStreamV3(const QByteArray& dta){
201         //Rev 3
202         quint32 NumIcons,NumEntries,NumGroups,offset;
203         memcpyFromLEnd32(&NumIcons,dta.data());
204         memcpyFromLEnd32(&NumEntries,dta.data()+4);
205         memcpyFromLEnd32(&NumGroups,dta.data()+8);
206         offset=12;
207         CustomIcons.clear();
208         for(int i=0;i<NumIcons;i++){
209                 CustomIcons << QPixmap();
210                 quint32 Size;
211                 memcpyFromLEnd32(&Size,dta.data()+offset);
212                 if(offset+Size > dta.size()){
213                         CustomIcons.clear();
214                         qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error.");
215                         return;
216                 }
217                 offset+=4;
218                 if(!CustomIcons.back().loadFromData((const unsigned char*)dta.data()+offset,Size,"PNG")){
219                         CustomIcons.clear();
220                         qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error.");
221                         return;
222                 }
223                 offset+=Size;
224                 if(offset > dta.size()){
225                         CustomIcons.clear();
226                         qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error.");
227                         return;
228                 }
229         }
230         for(int i=0;i<NumEntries;i++){
231                 quint32 Icon;
232                 KpxUuid EntryUuid;
233                 EntryUuid.fromRaw(dta.data()+offset);
234                 offset+=16;
235                 memcpyFromLEnd32(&Icon,dta.data()+offset);
236                 offset+=4;
237                 StdEntry* entry=getEntry(EntryUuid);
238                 if(entry){
239                         if (Icon>=65)
240                                 entry->Image=Icon+4; // Since v0.3.2 the BUILTIN_ICONS number has increased by 4
241                         else
242                                 entry->Image=Icon;
243                 }
244         }
245         for(int i=0;i<NumGroups;i++){
246                 quint32 GroupId,Icon;
247                 memcpyFromLEnd32(&GroupId,dta.data()+offset);
248                 offset+=4;
249                 memcpyFromLEnd32(&Icon,dta.data()+offset);
250                 offset+=4;
251                 StdGroup* Group=getGroup(GroupId);
252                 if(Group){
253                         if (Group->Image>=65)
254                                 Group->Image=Icon+4; // Since v0.3.2 the BUILTIN_ICONS number has increased by 4
255                         else
256                                 Group->Image=Icon;
257                 }
258         }
259         return;
260 }
261
262 void Kdb3Database::parseGroupTreeStateMetaStream(const QByteArray& dta){
263         if(dta.size()<4){
264                 qWarning("Discarded metastream KPX_GROUP_TREE_STATE because of a parsing error.");
265                 return;
266         }
267         quint32 Num;
268         memcpyFromLEnd32(&Num,dta.data());
269         if(Num*5!=dta.size()-4){
270                 qWarning("Discarded metastream KPX_GROUP_TREE_STATE because of a parsing error.");
271                 return;
272         }
273         TreeStateMetaStream.clear();
274         for(int i=0;i<Num;i++){
275                 quint32 GroupID;
276                 quint8 IsExpanded;
277                 memcpyFromLEnd32(&GroupID,dta.data()+4+5*i);
278                 memcpy(&IsExpanded,dta.data()+8+5*i,1);
279                 TreeStateMetaStream.insert(GroupID,(bool)IsExpanded);
280         }
281         return;
282 }
283
284 void Kdb3Database::createGroupTreeStateMetaStream(StdEntry* e){
285         e->BinaryDesc="bin-stream";
286         e->Title="Meta-Info";
287         e->Username="SYSTEM";
288         e->Comment="KPX_GROUP_TREE_STATE";
289         e->Url="$";
290         e->Image=0;
291         if(Groups.size())e->GroupId=Groups[0].Id;
292         QByteArray bin;
293         quint32 Num=Groups.size();
294         bin.resize(Num*5+4);
295         memcpyToLEnd32(bin.data(),&Num);
296         for(int i=0;i<Num;i++){
297                 memcpyToLEnd32(bin.data()+4+5*i,&Groups[i].Id);
298                 if(Groups[i].IsExpanded)
299                         bin.data()[8+5*i]=1;
300                 else
301                         bin.data()[8+5*i]=0;
302         }
303         e->Binary=bin;
304 }
305
306 Kdb3Database::StdEntry* Kdb3Database::getEntry(const KpxUuid& uuid){
307         for(int i=0; i<Entries.size();i++)
308                 if(Entries[i].Uuid==uuid)return &Entries[i];
309         return NULL;
310 }
311
312 Kdb3Database::StdGroup* Kdb3Database::getGroup(quint32 Id){
313         for(int i=0; i<Groups.size();i++)
314                 if(Groups[i].Id==Id)return &Groups[i];
315         return NULL;
316 }
317
318
319 //! Extracts one entry from raw decrypted data.
320 bool Kdb3Database::readEntryField(StdEntry* entry, quint16 FieldType, quint32 FieldSize, quint8 *pData){
321 switch(FieldType)
322         {
323         case 0x0000:
324                 // Ignore field
325                 break;
326         case 0x0001:
327                 entry->Uuid=KpxUuid(pData);
328                 break;
329         case 0x0002:
330                 memcpyFromLEnd32(&entry->GroupId, (char*)pData);
331                 break;
332         case 0x0003:
333                 memcpyFromLEnd32(&entry->Image, (char*)pData);
334                 break;
335         case 0x0004:
336                 entry->Title=QString::fromUtf8((char*)pData);
337                 break;
338         case 0x0005:
339                 entry->Url=QString::fromUtf8((char*)pData);
340                 break;
341         case 0x0006:
342                 entry->Username=QString::fromUtf8((char*)pData);
343                 break;
344         case 0x0007:{
345                 QString s=QString::fromUtf8((char*)pData);
346                 entry->Password.setString(s,true);
347                 break;}
348         case 0x0008:
349                 entry->Comment=QString::fromUtf8((char*)pData);
350                 break;
351         case 0x0009:
352                 entry->Creation=dateFromPackedStruct5(pData);
353                 break;
354         case 0x000A:
355                 entry->LastMod=dateFromPackedStruct5(pData);
356                 break;
357         case 0x000B:
358                 entry->LastAccess=dateFromPackedStruct5(pData);
359                 break;
360         case 0x000C:
361                 entry->Expire=dateFromPackedStruct5(pData);
362                 break;
363         case 0x000D:
364                 entry->BinaryDesc=QString::fromUtf8((char*)pData);
365                 break;
366         case 0x000E:
367                 if(FieldSize != 0)
368                         entry->Binary=QByteArray((char*)pData,FieldSize);
369                 else
370                         entry->Binary=QByteArray();
371                 break;
372         case 0xFFFF:
373                 break;
374         default:
375                 return false;
376         }
377         return true;
378 }
379
380 //! Extracts one group from raw decrypted data.
381 bool Kdb3Database::readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint8 *pData)
382 {
383         switch(FieldType)
384         {
385         case 0x0000:
386                 // Ignore field
387                 break;
388         case 0x0001:
389                 memcpyFromLEnd32(&group->Id, (char*)pData);
390                 break;
391         case 0x0002:
392                 group->Title=QString::fromUtf8((char*)pData);
393                 break;
394         case 0x0003: //not longer used by KeePassX but part of the KDB format
395                 break;
396         case 0x0004: //not longer used by KeePassX but part of the KDB format
397                 break;
398         case 0x0005: //not longer used by KeePassX but part of the KDB format
399                 break;
400         case 0x0006: //not longer used by KeePassX but part of the KDB format
401                 break;
402         case 0x0007:
403                 memcpyFromLEnd32(&group->Image, (char*)pData);
404                 break;
405         case 0x0008:
406                 quint16 Level;
407                 memcpyFromLEnd16(&Level, (char*)pData);
408                 Levels.append(Level);
409                 break;
410         case 0x0009:
411                  //not used by KeePassX but part of the KDB format
412                  //memcpyFromLEnd32(&Flags, (char*)pData);
413                 break;
414         case 0xFFFF:
415                 break;
416         default:
417                 return false; // Field unsupported
418         }
419
420         return true; // Field supported
421 }
422
423 bool Kdb3Database::createGroupTree(QList<quint32>& Levels){
424         if(Levels[0]!=0) return false;
425         //find the parent for every group
426         for(int i=0;i<Groups.size();i++){
427                 if(Levels[i]==0){
428                         Groups[i].Parent=&RootGroup;
429                         Groups[i].Index=RootGroup.Children.size();
430                         RootGroup.Children.append(&Groups[i]);
431                         continue;
432                 }
433                 int j;
434                 //the first item with a lower level is the parent
435                 for(j=i-1;j>=0;j--){
436                         if(Levels[j]<Levels[i]){
437                                 if(Levels[i]-Levels[j]!=1)return false;
438                                 break;
439                         }
440                         if(j==0)return false; //No parent found
441                 }
442                 Groups[i].Parent=&Groups[j];
443                 Groups[i].Index=Groups[j].Children.size();
444                 Groups[i].Parent->Children.append(&Groups[i]);
445         }
446
447         QList<int> EntryIndexCounter;
448         for(int i=0;i<Groups.size();i++)EntryIndexCounter << 0;
449
450         for(int e=0;e<Entries.size();e++){
451                 int groupIndex = -1;
452                 for(int g=0;g<Groups.size();g++){
453                         if(Entries[e].GroupId==Groups[g].Id){
454                                 groupIndex = g;
455                                 break;
456                         }
457                 }
458                 
459                 if (groupIndex == -1) {
460                         qWarning("Orphaned entry found, assigning to first group");
461                         for(int g=0;g<Groups.size();g++){
462                                 if(Groups[g].Id == RootGroup.Children[0]->Id){
463                                         groupIndex = g;
464                                         break;
465                                 }
466                         }
467                 }
468                 
469                 Groups[groupIndex].Entries.append(&Entries[e]);
470                 Entries[e].Group=&Groups[groupIndex];
471                 Entries[e].Index=EntryIndexCounter[groupIndex];
472                 EntryIndexCounter[groupIndex]++;
473         }
474
475         return true;
476 }
477
478 void Kdb3Database::createHandles(){
479         for(int i=0;i<Groups.size();i++){
480                 GroupHandles.append(GroupHandle(this));
481                 Groups[i].Handle=&GroupHandles.back();
482                 GroupHandles.back().Group=&Groups[i];
483         }
484         for(int i=0;i<Entries.size();i++){
485                 EntryHandles.append(EntryHandle(this));
486                 Entries[i].Handle=&EntryHandles.back();
487                 EntryHandles.back().Entry=&Entries[i];
488         }
489 }
490
491 void Kdb3Database::restoreGroupTreeState(){
492         switch (config->groupTreeState()){
493                 case KpxConfig::RestoreLast:
494                         for(int i=0;i<Groups.size();i++){
495                                 if(TreeStateMetaStream.contains(Groups[i].Id))
496                                         Groups[i].IsExpanded=TreeStateMetaStream.value(Groups[i].Id);
497                         }
498                         break;
499
500                 case KpxConfig::ExpandAll:
501                         for(int i=0;i<Groups.size();i++)
502                                 Groups[i].IsExpanded=true;
503                         break;
504                 
505                 case KpxConfig::DoNothing:
506                         break;
507         }
508 }
509
510 bool Kdb3Database::load(QString identifier, bool readOnly){
511         return loadReal(identifier, readOnly, false);
512 }
513
514 #define LOAD_RETURN_CLEANUP \
515         delete File; \
516         File = NULL; \
517         delete[] buffer; \
518         return false;
519
520 bool Kdb3Database::loadReal(QString filename, bool readOnly, bool differentEncoding) {
521         File = new QFile(filename);
522         if (readOnly) {
523                 if(!File->open(QIODevice::ReadOnly)){
524                         error=tr("Could not open file.");
525                         delete File;
526                         File = NULL;
527                         return false;
528                 }
529         }
530         else {
531                 if(!File->open(QIODevice::ReadWrite)){
532                         if(!File->open(QIODevice::ReadOnly)){
533                                 error=tr("Could not open file.");
534                                 delete File;
535                                 File = NULL;
536                                 return false;
537                         }
538                         else{
539                                 readOnly = true;
540                         }
541                 }
542         }
543         
544         openedReadOnly = readOnly;
545         
546         unsigned long total_size,crypto_size;
547         quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags;
548         quint8 FinalRandomSeed[16];
549         quint8 ContentsHash[32];
550         quint8 EncryptionIV[16];
551         
552         total_size=File->size();
553         char* buffer = new char[total_size];
554         File->read(buffer,total_size);
555         
556         if(total_size < DB_HEADER_SIZE){
557                 error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)");
558                 LOAD_RETURN_CLEANUP
559         }
560         
561         memcpyFromLEnd32(&Signature1,buffer);
562         memcpyFromLEnd32(&Signature2,buffer+4);
563         memcpyFromLEnd32(&Flags,buffer+8);
564         memcpyFromLEnd32(&Version,buffer+12);
565         memcpy(FinalRandomSeed,buffer+16,16);
566         memcpy(EncryptionIV,buffer+32,16);
567         memcpyFromLEnd32(&NumGroups,buffer+48);
568         memcpyFromLEnd32(&NumEntries,buffer+52);
569         memcpy(ContentsHash,buffer+56,32);
570         memcpy(TransfRandomSeed,buffer+88,32);
571         memcpyFromLEnd32(&KeyTransfRounds,buffer+120);
572         
573         if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){
574                 error=tr("Wrong Signature");
575                 LOAD_RETURN_CLEANUP
576         }
577         
578         if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){
579                 error=tr("Unsupported File Version.");
580                 LOAD_RETURN_CLEANUP
581         }
582         
583         if (Flags & PWM_FLAG_RIJNDAEL)
584                 Algorithm = Rijndael_Cipher;
585         else if (Flags & PWM_FLAG_TWOFISH)
586                 Algorithm = Twofish_Cipher;
587         else{
588                 error=tr("Unknown Encryption Algorithm.");
589                 LOAD_RETURN_CLEANUP
590         }
591         
592         RawMasterKey.unlock();
593         MasterKey.unlock();
594         KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds);
595         
596         quint8 FinalKey[32];
597         
598         SHA256 sha;
599         sha.update(FinalRandomSeed,16);
600         sha.update(*MasterKey,32);
601         sha.finish(FinalKey);
602         
603         RawMasterKey.lock();
604         MasterKey.lock();
605         
606         if(Algorithm == Rijndael_Cipher){
607                 AESdecrypt aes;
608                 aes.key256(FinalKey);
609                 aes.cbc_decrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,total_size-DB_HEADER_SIZE,(unsigned char*)EncryptionIV);
610                 crypto_size=total_size-((quint8*)buffer)[total_size-1]-DB_HEADER_SIZE;
611         }
612         else if(Algorithm == Twofish_Cipher){
613                 CTwofish twofish;
614                 if (twofish.init(FinalKey, 32, EncryptionIV) != true){
615                         error=tr("Unable to initialize the twofish algorithm.");
616                         LOAD_RETURN_CLEANUP
617                 }
618                 crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE,
619                 total_size - DB_HEADER_SIZE, (quint8 *)buffer + DB_HEADER_SIZE);
620         }
621         else{
622                 error=tr("Unknown encryption algorithm.");
623                 LOAD_RETURN_CLEANUP
624         }
625         
626         if ((crypto_size > 2147483446) || (!crypto_size && NumGroups)){
627                 error=tr("Decryption failed.\nThe key is wrong or the file is damaged.");
628                 KeyError=true;
629                 LOAD_RETURN_CLEANUP
630         }
631         SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size);
632         
633         if(memcmp(ContentsHash, FinalKey, 32) != 0){
634                 if(PotentialEncodingIssueLatin1){
635                         delete[] buffer;
636                         delete File;
637                         File = NULL;
638                         
639                         RawMasterKey.copyData(RawMasterKey_Latin1);
640                         PotentialEncodingIssueLatin1 = false;
641                         qDebug("Decryption failed. Retrying with Latin-1.");
642                         return loadReal(filename, readOnly, true); // second try
643                 }
644                 if(PotentialEncodingIssueUTF8){
645                         delete[] buffer;
646                         delete File;
647                         File = NULL;
648                         
649                         RawMasterKey.copyData(RawMasterKey_UTF8);
650                         PotentialEncodingIssueUTF8 = false;
651                         qDebug("Decryption failed. Retrying with UTF-8.");
652                         return loadReal(filename, readOnly, true); // second/third try
653                 }
654                 error=tr("Hash test failed.\nThe key is wrong or the file is damaged.");
655                 KeyError=true;
656                 LOAD_RETURN_CLEANUP
657         }
658         
659         unsigned long pos = DB_HEADER_SIZE;
660         quint16 FieldType;
661         quint32 FieldSize;
662         char* pField;
663         bool bRet;
664         StdGroup group;
665         QList<quint32> Levels;
666         RootGroup.Title="$ROOT$";
667         RootGroup.Parent=NULL;
668         RootGroup.Handle=NULL;
669         
670         for(unsigned long CurGroup = 0; CurGroup < NumGroups; )
671         {
672                 pField = buffer+pos;
673         
674                 memcpyFromLEnd16(&FieldType, pField);
675                 pField += 2; pos += 2;
676                 if (pos >= total_size){
677                         error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
678                         LOAD_RETURN_CLEANUP
679                 }
680         
681                 memcpyFromLEnd32(&FieldSize, pField);
682                 pField += 4; pos += 4;
683                 if (pos >= (total_size + FieldSize)){
684                         error=tr("Unexpected error: Offset is out of range.").append(" [G2]");
685                         LOAD_RETURN_CLEANUP
686                 }
687         
688                 bRet = readGroupField(&group,Levels, FieldType, (quint8 *)pField);
689                 if ((FieldType == 0xFFFF) && (bRet == true)){
690                         Groups << group;
691                         CurGroup++; // Now and ONLY now the counter gets increased
692                 }
693                 pField += FieldSize;
694                 pos += FieldSize;
695                 if (pos >= total_size){
696                         error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
697                         LOAD_RETURN_CLEANUP
698                 }
699         }
700         
701         StdEntry entry;
702         
703         for (unsigned long CurEntry = 0; CurEntry < NumEntries;)
704         {
705                 pField = buffer+pos;
706         
707                 memcpyFromLEnd16(&FieldType, pField);
708                 pField += 2; pos += 2;
709                 if(pos >= total_size){
710                         error=tr("Unexpected error: Offset is out of range.").append(" [E1]");
711                         LOAD_RETURN_CLEANUP
712                 }
713         
714                 memcpyFromLEnd32(&FieldSize, pField);
715                 pField += 4; pos += 4;
716                 if (pos >= (total_size + FieldSize)){
717                         error=tr("Unexpected error: Offset is out of range.").append(" [E2]");
718                         LOAD_RETURN_CLEANUP
719                 }
720         
721                 bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField);
722         
723                 if((FieldType == 0xFFFF) && (bRet == true)){
724                         Entries << entry;
725                         if(!entry.GroupId)
726                                 qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data());
727                         CurEntry++;
728                 }
729         
730                 pField += FieldSize;
731                 pos += FieldSize;
732                 if (pos >= total_size){
733                         error=tr("Unexpected error: Offset is out of range.").append(" [E3]");
734                         LOAD_RETURN_CLEANUP
735                 }
736         }
737         
738         if(!createGroupTree(Levels)){
739                 error=tr("Invalid group tree.");
740                 LOAD_RETURN_CLEANUP
741         }
742         
743         delete [] buffer;
744         
745         hasV4IconMetaStream = false;
746         for(int i=0;i<Entries.size();i++){
747                 if(isMetaStream(Entries[i]) && Entries[i].Comment=="KPX_CUSTOM_ICONS_4"){
748                         hasV4IconMetaStream = true;
749                         break;
750                 }
751         }
752         
753         //Remove the metastreams from the entry list
754         for(int i=0;i<Entries.size();i++){
755                 if(isMetaStream(Entries[i])){
756                         if(!parseMetaStream(Entries[i]))
757                                 UnknownMetaStreams << Entries[i];
758                         Entries.removeAt(i);
759                         i--;
760                 }
761         }
762         
763         int* EntryIndices=new int[Groups.size()];
764         for(int i=0;i<Groups.size();i++)EntryIndices[i]=0;
765         
766         for(int g=0;g<Groups.size();g++){
767                 for(int e=0;e<Entries.size();e++){
768                         if(Entries[e].GroupId==Groups[g].Id){
769                                 Entries[e].Index=EntryIndices[g];
770                                 EntryIndices[g]++;
771                         }
772                 }
773         }
774         delete [] EntryIndices;
775         createHandles();
776         restoreGroupTreeState();
777         
778         passwordEncodingChanged = differentEncoding;
779         if (differentEncoding) {
780                 RawMasterKey.copyData(RawMasterKey_CP1252);
781                 generateMasterKey();
782         }
783         
784         return true;
785 }
786
787 QDateTime Kdb3Database::dateFromPackedStruct5(const unsigned char* pBytes){
788         quint32 dw1, dw2, dw3, dw4, dw5;
789         dw1 = (quint32)pBytes[0]; dw2 = (quint32)pBytes[1]; dw3 = (quint32)pBytes[2];
790         dw4 = (quint32)pBytes[3]; dw5 = (quint32)pBytes[4];
791         int y = (dw1 << 6) | (dw2 >> 2);
792         int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6);
793         int d = (dw3 >> 1) & 0x0000001F;
794         int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4);
795         int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6);
796         int s = dw5 & 0x0000003F;
797         return QDateTime(QDate(y,mon,d),QTime(h,min,s));
798 }
799
800
801 void Kdb3Database::dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){
802         pBytes[0] = (quint8)(((quint32)d.date().year() >> 6) & 0x0000003F);
803         pBytes[1] = (quint8)((((quint32)d.date().year() & 0x0000003F) << 2) | (((quint32)d.date().month() >> 2) & 0x00000003));
804         pBytes[2] = (quint8)((((quint32)d.date().month() & 0x00000003) << 6) | (((quint32)d.date().day() & 0x0000001F) << 1) | (((quint32)d.time().hour() >> 4) & 0x00000001));
805         pBytes[3] = (quint8)((((quint32)d.time().hour() & 0x0000000F) << 4) | (((quint32)d.time().minute() >> 2) & 0x0000000F));
806         pBytes[4] = (quint8)((((quint32)d.time().minute() & 0x00000003) << 6) | ((quint32)d.time().second() & 0x0000003F));
807 }
808
809
810 int Kdb3Database::numGroups(){
811         return Groups.size();
812 }
813
814 int Kdb3Database::numEntries(){
815         return Entries.size();
816 }
817
818 void Kdb3Database::deleteGroup(StdGroup* group){
819
820         while(group->Children.size())
821                 deleteGroup(group->Children.front());
822
823         QList<IEntryHandle*> GroupEntries;
824         GroupEntries=entries(group->Handle);
825         deleteEntries(GroupEntries);
826
827         Q_ASSERT(group==group->Parent->Children[group->Index]);
828         group->Parent->Children.removeAt(group->Index);
829         for(int i=group->Index;i<group->Parent->Children.size();i++){
830                 group->Parent->Children[i]->Index--;
831         }
832         group->Handle->invalidate();
833
834         for(int i=0;i<Groups.size();i++){
835                 if(&Groups[i]==group){
836                         Groups.removeAt(i);
837                         break;
838                 }
839         }
840
841 }
842
843
844 void Kdb3Database::deleteGroup(IGroupHandle* group){
845         deleteGroup(((GroupHandle*)group)->Group);
846 }
847
848 /*
849 void Kdb3Database::GroupHandle::setIndex(int index){
850         quint32 ParentId=((GroupHandle*)parent())->Id;
851         int Pos=pDB->getGroupListIndex(this);
852         int NewPos=0;
853         // Move the group to the new position in the list
854         if(ParentId)
855                 NewPos=pDB->getGroupListIndex((GroupHandle*)parent());
856         if(!index){
857                 if(ParentId)
858                         pDB->Groups.move(Pos,NewPos+1);
859                 else
860                         pDB->Groups.move(Pos,NewPos);
861         }
862         else{
863                 for(NewPos;NewPos<pDB->Groups.size();NewPos++){
864                         if(pDB->Groups[NewPos].ParentId==ParentId && pDB->Groups[NewPos].Index+1==index)
865                                 break;
866                 }
867                 //skip the children of the found sibling
868                 for(NewPos;NewPos<Groups.size();NewPos++){
869                         if(Groups[NewPos]
870                         pDB->Groups.move(Pos,NewPos);
871                 }
872
873         }
874         // adjust the indices
875         int NewIndex=0;
876         for(int i=0;i<pDB->Groups.size();i++){
877                 if(pDB->Groups[i].ParentId==ParentId){
878                         pDB->Groups[i].Index=NewIndex;
879                         NewIndex++;
880                 }
881         }
882 }
883 */
884
885 bool Kdb3Database::convHexToBinaryKey(char* HexKey, char* dst){
886         QString hex=QString::fromAscii(HexKey,64);
887         for(int i=0; i<64; i+=2){
888                 bool err;
889                 quint8 bin;
890                 bin=hex.mid(i,2).toUInt(&err,16);
891                 if(!err)return false;
892                 memcpy(dst+(i/2),&bin,1);
893         }
894         return true;
895 }
896
897 bool Kdb3Database::setKey(const QString& password,const QString& keyfile){
898         if(!password.isEmpty() && !keyfile.isEmpty())
899                 return setCompositeKey(password,keyfile);
900         if(!password.isEmpty())
901                 return setPasswordKey(password);
902         if(!keyfile.isEmpty())
903                 return setFileKey(keyfile);
904         Q_ASSERT(false);
905         return false;
906 }
907
908 bool Kdb3Database::setPasswordKey(const QString& Password){
909         Q_ASSERT(Password.size());
910         QTextCodec* codec=QTextCodec::codecForName("Windows-1252");
911         QByteArray Password_CP1252 = codec->fromUnicode(Password);
912         RawMasterKey_CP1252.unlock();
913         SHA256::hashBuffer(Password_CP1252.data(),*RawMasterKey_CP1252,Password_CP1252.size());
914         RawMasterKey_CP1252.lock();
915         RawMasterKey.copyData(RawMasterKey_CP1252);
916         
917         QByteArray Password_Latin1 = Password.toLatin1();
918         QByteArray Password_UTF8 = Password.toUtf8();
919         PotentialEncodingIssueLatin1 = false;
920         PotentialEncodingIssueUTF8 = false;
921         
922         if (Password_Latin1 != Password_CP1252){
923                 // KeePassX used Latin-1 encoding for passwords until version 0.3.1
924                 // but KeePass/Win32 uses Windows Codepage 1252.
925                 // To stay compatible with databases created with KeePassX <= 0.3.1
926                 // the loading function gives both encodings a try.
927                 PotentialEncodingIssueLatin1 = true;
928                 RawMasterKey_Latin1.unlock();
929                 SHA256::hashBuffer(Password_Latin1.data(),*RawMasterKey_Latin1,Password_Latin1.size());
930                 RawMasterKey_Latin1.lock();
931         }
932         
933         if (Password_UTF8 != Password_CP1252){
934                 // KeePassX used UTF-8 encoding for passwords until version 0.2.2
935                 // but KeePass/Win32 uses Windows Codepage 1252.
936                 // To stay compatible with databases created with KeePassX <= 0.2.2
937                 // the loading function gives both encodings a try.
938                 PotentialEncodingIssueUTF8 = true;
939                 RawMasterKey_UTF8.unlock();
940                 SHA256::hashBuffer(Password_UTF8.data(),*RawMasterKey_UTF8,Password_UTF8.size());
941                 RawMasterKey_UTF8.lock();
942         }
943         
944         return true;
945 }
946
947 bool Kdb3Database::setFileKey(const QString& filename){
948         QFile file(filename);
949         if(!file.open(QIODevice::ReadOnly|QIODevice::Unbuffered)){
950                 error=decodeFileError(file.error());
951                 return false;
952         }
953         qint64 FileSize=file.size();
954         if(FileSize == 0){
955                 error=tr("Key file is empty.");
956                 return false;
957         }
958         RawMasterKey.unlock();
959         if(FileSize == 32){
960                 if(file.read((char*)(*RawMasterKey),32) != 32){
961                         error=decodeFileError(file.error());
962                         RawMasterKey.lock();
963                         return false;
964                 }
965                 RawMasterKey.lock();
966                 return true;
967         }
968         if(FileSize == 64){
969                 char hex[64];
970                 if(file.read(hex,64) != 64){
971                         error=decodeFileError(file.error());
972                         RawMasterKey.lock();
973                         return false;
974                 }
975                 if (convHexToBinaryKey(hex,(char*)(*RawMasterKey))){
976                         RawMasterKey.lock();
977                         return true;
978                 }
979         }
980         SHA256 sha;
981         unsigned char* buffer[2048];
982         unsigned long read;
983         do {
984                 read = file.read((char*)buffer,2048);
985                 if (read != 0)
986                         sha.update(buffer,read);
987         } while (read == 2048);
988         sha.finish(*RawMasterKey);
989         RawMasterKey.lock();
990         return true;
991 }
992
993 bool Kdb3Database::setCompositeKey(const QString& Password,const QString& filename){
994         SHA256 sha;
995         
996         setPasswordKey(Password);
997         RawMasterKey.unlock();
998         sha.update(*RawMasterKey,32);
999         RawMasterKey.lock();
1000         
1001         if(!setFileKey(filename))return false;
1002         RawMasterKey.unlock();
1003         sha.update(*RawMasterKey,32);
1004         sha.finish(*RawMasterKey);
1005         RawMasterKey.lock();
1006         
1007         return true;
1008 }
1009
1010 QList<IEntryHandle*> Kdb3Database::entries(){
1011         QList<IEntryHandle*> handles;
1012         for(int i=0; i<EntryHandles.size(); i++){
1013                 if(EntryHandles[i].isValid())handles.append(&EntryHandles[i]);
1014         }
1015         return handles;
1016 }
1017
1018 QList<IEntryHandle*> Kdb3Database::expiredEntries(){
1019         QList<IEntryHandle*> handles;
1020         for(int i=0; i<EntryHandles.size(); i++){
1021                 if(EntryHandles[i].isValid() &&
1022                   (EntryHandles[i].expire()<=QDateTime::currentDateTime()) &&
1023                   (EntryHandles[i].expire()!=Date_Never))
1024                         handles.append(&EntryHandles[i]);
1025         }
1026         return handles;
1027 }
1028
1029 QList<IEntryHandle*> Kdb3Database::entries(IGroupHandle* Group){
1030         QList<IEntryHandle*> handles;
1031         for(int i=0; i<EntryHandles.size(); i++){
1032                 if(EntryHandles[i].isValid() && (EntryHandles[i].group()==Group))
1033                         handles.append(&EntryHandles[i]);
1034         }
1035         qSort(handles.begin(),handles.end(),EntryHandleLessThan);
1036
1037         return handles;
1038 }
1039
1040 QList<IEntryHandle*> Kdb3Database::entriesSortedStd(IGroupHandle* Group){
1041         QList<IEntryHandle*> handles;
1042         for(int i=0; i<EntryHandles.size(); i++){
1043                 if(EntryHandles[i].isValid() && (EntryHandles[i].group()==Group))
1044                         handles.append(&EntryHandles[i]);
1045         }
1046         qSort(handles.begin(),handles.end(),EntryHandleLessThanStd);
1047
1048         return handles;
1049 }
1050
1051 void Kdb3Database::deleteEntry(IEntryHandle* entry){
1052         if(!entry)return;
1053         int j;
1054         for(j=0;j<Entries.size();j++){
1055                 if(&Entries[j]==((EntryHandle*)entry)->Entry)
1056                         break;
1057         }
1058         Entries[j].Handle->invalidate();
1059         Entries.removeAt(j);
1060 }
1061
1062 void Kdb3Database::moveEntry(IEntryHandle* entry, IGroupHandle* group){
1063         ((EntryHandle*)entry)->Entry->GroupId=((GroupHandle*)group)->Group->Id;
1064         ((EntryHandle*)entry)->Entry->Group=((GroupHandle*)group)->Group;
1065 }
1066
1067
1068 void Kdb3Database::deleteEntries(QList<IEntryHandle*> entries){
1069         if(!entries.size())return;
1070         StdGroup* Group=((EntryHandle*)entries[0])->Entry->Group;
1071         for(int i=0;i<entries.size();i++){
1072                 int j;
1073                 for(j=0;j<Entries.size();j++){
1074                         if(&Entries[j]==((EntryHandle*)entries[i])->Entry)
1075                                 break;
1076                 }
1077                 Group->Children.removeAt(Entries[j].Index);
1078                 Entries[j].Handle->invalidate();
1079                 Entries.removeAt(j);
1080         }
1081
1082         for(int i=0;i<Group->Children.size();i++){
1083                 Group->Children[i]->Index=i;
1084         }
1085 };
1086
1087 QList<IGroupHandle*> Kdb3Database::groups(){
1088         QList<IGroupHandle*> handles;
1089         for(int i=0; i<GroupHandles.size(); i++){
1090                 if(GroupHandles[i].isValid())handles.append(&GroupHandles[i]);
1091         }
1092         return handles;
1093 }
1094
1095 quint32 Kdb3Database::getNewGroupId(){
1096         quint32 id;
1097         bool used;
1098         do{
1099                 used=false;
1100                 randomize(&id,4);
1101                 if(!id)continue; //group IDs must not be 0
1102                 for(int j=0;j<Groups.size();j++){
1103                         if(Groups[j].Id==id){
1104                                 used=true;
1105                                 break;
1106                         }
1107                 }
1108         } while(used);
1109         return id;
1110 }
1111
1112 IGroupHandle* Kdb3Database::addGroup(const CGroup* group,IGroupHandle* ParentHandle){
1113         GroupHandles.append(GroupHandle(this));
1114         Groups.append(*group);
1115         Groups.back().Id=getNewGroupId();
1116         Groups.back().Handle=&GroupHandles.back();
1117         GroupHandles.back().Group=&Groups.back();
1118         if(ParentHandle){
1119                 Groups.back().Parent=((GroupHandle*)ParentHandle)->Group;
1120                 Groups.back().Index=Groups.back().Parent->Children.size();
1121                 Groups.back().Parent->Children.append(&Groups.back());
1122         }
1123         else{
1124                 // Insert to root group. Try to keep Backup group at the end.
1125                 Groups.back().Parent=&RootGroup;
1126                 Groups.back().Index=RootGroup.Children.size();
1127                 int position = RootGroup.Children.size();
1128                 if (group->Title!="Backup" && !RootGroup.Children.isEmpty() && RootGroup.Children.last()->Title=="Backup"){
1129                         RootGroup.Children.last()->Index = Groups.back().Index;
1130                         Groups.back().Index--;
1131                         position--;
1132                 }
1133                 RootGroup.Children.insert(position, &Groups.back());
1134         }
1135         return &GroupHandles.back();
1136 }
1137
1138 IGroupHandle* Kdb3Database::backupGroup(bool create){
1139         IGroupHandle* group = NULL;
1140         QList<IGroupHandle*> allGroups = groups();
1141         for (int i=0; i<allGroups.size(); i++){
1142                 if (allGroups[i]->parent()==NULL && allGroups[i]->title()=="Backup"){
1143                         group = allGroups[i];
1144                         break;
1145                 }
1146         }
1147         
1148         if (group==NULL && create){
1149                 CGroup newGroup;
1150                 newGroup.Title = "Backup";
1151                 newGroup.Image = 4;
1152                 group = addGroup(&newGroup, NULL);
1153         }
1154         
1155         return group;
1156 }
1157
1158 Kdb3Database::StdEntry::StdEntry(){
1159         Handle = NULL;
1160         Group = NULL;
1161 }
1162
1163 Kdb3Database::StdGroup::StdGroup(){
1164         Index=0;
1165         Id=0;
1166         Parent=NULL;
1167         Handle=NULL;
1168 }
1169
1170 Kdb3Database::StdGroup::StdGroup(const CGroup& other){
1171         Index=0;
1172         Id=other.Id;
1173         Image=other.Image;
1174         Title=other.Title;
1175         Parent=NULL;
1176         Handle=NULL;
1177 }
1178
1179 void Kdb3Database::EntryHandle::setTitle(const QString& Title){Entry->Title=Title; }
1180 void Kdb3Database::EntryHandle::setUsername(const QString& Username){Entry->Username=Username;}
1181 void Kdb3Database::EntryHandle::setUrl(const QString& Url){Entry->Url=Url;}
1182 void Kdb3Database::EntryHandle::setPassword(const SecString& Password){Entry->Password=Password;}
1183 void Kdb3Database::EntryHandle::setExpire(const KpxDateTime& s){Entry->Expire=s;}
1184 void Kdb3Database::EntryHandle::setCreation(const KpxDateTime& s){Entry->Creation=s;}
1185 void Kdb3Database::EntryHandle::setLastAccess(const KpxDateTime& s){Entry->LastAccess=s;}
1186 void Kdb3Database::EntryHandle::setLastMod(const KpxDateTime& s){Entry->LastMod=s;}
1187 void Kdb3Database::EntryHandle::setBinaryDesc(const QString& s){Entry->BinaryDesc=s;}
1188 void Kdb3Database::EntryHandle::setComment(const QString& s){Entry->Comment=s;}
1189 void Kdb3Database::EntryHandle::setBinary(const QByteArray& s){Entry->Binary=s;}
1190 void Kdb3Database::EntryHandle::setImage(const quint32& s){Entry->Image=s;}
1191 KpxUuid Kdb3Database::EntryHandle::uuid()const{return Entry->Uuid;}
1192 IGroupHandle* Kdb3Database::EntryHandle::group()const{return Entry->Group->Handle;}
1193 quint32 Kdb3Database::EntryHandle::image()const{return Entry->Image;}
1194 QString Kdb3Database::EntryHandle::title()const{return Entry->Title;}
1195 QString Kdb3Database::EntryHandle::url()const{return Entry->Url;}
1196 QString Kdb3Database::EntryHandle::username()const{return Entry->Username;}
1197 SecString Kdb3Database::EntryHandle::password()const{return Entry->Password;}
1198 QString Kdb3Database::EntryHandle::comment()const{return Entry->Comment;}
1199 QString Kdb3Database::EntryHandle::binaryDesc()const{return Entry->BinaryDesc;}
1200 KpxDateTime     Kdb3Database::EntryHandle::creation()const{return Entry->Creation;}
1201 KpxDateTime     Kdb3Database::EntryHandle::lastMod()const{return Entry->LastMod;}
1202 KpxDateTime     Kdb3Database::EntryHandle::lastAccess()const{return Entry->LastAccess;}
1203 KpxDateTime     Kdb3Database::EntryHandle::expire()const{return Entry->Expire;}
1204 QByteArray Kdb3Database::EntryHandle::binary()const{return Entry->Binary;}
1205 quint32 Kdb3Database::EntryHandle::binarySize()const{return Entry->Binary.size();}
1206
1207 QString Kdb3Database::EntryHandle::friendlySize()const
1208 {
1209     quint32 binsize = binarySize();
1210     QString unit;
1211     uint    faktor;
1212     int     prec;
1213
1214     if (binsize < 1024)
1215     {
1216         unit = tr("Bytes");
1217         faktor = 1;
1218         prec = 0;
1219     }
1220     else
1221     {
1222         if (binsize < 1048576)
1223         {
1224             unit = tr("KiB");
1225             faktor = 1024;
1226         }
1227         else
1228             if (binsize < 1073741824)
1229             {
1230                 unit = tr("MiB");
1231                 faktor = 1048576;
1232             }
1233             else
1234             {
1235                 unit = tr("GiB");
1236                 faktor = 1073741824;
1237             }
1238         prec = 1;
1239     }
1240     return (QString::number((float)binsize / (float)faktor, 'f', prec) + " " + unit);
1241 }
1242
1243 int Kdb3Database::EntryHandle::visualIndex()const{return Entry->Index;}
1244 void Kdb3Database::EntryHandle::setVisualIndexDirectly(int i){Entry->Index=i;}
1245 bool Kdb3Database::EntryHandle::isValid()const{return valid;}
1246
1247 CEntry Kdb3Database::EntryHandle::data()const{
1248         return *this->Entry;
1249 }
1250
1251 void Kdb3Database::EntryHandle::setVisualIndex(int index){
1252         QList<IEntryHandle*>Entries=pDB->entries(Entry->Group->Handle);
1253         Entries.move(visualIndex(),index);
1254         for(int i=0;i<Entries.size();i++){
1255                 dynamic_cast<Kdb3Database::EntryHandle*>(Entries[i])->Entry->Index=index;
1256         }
1257 }
1258
1259 Kdb3Database::EntryHandle::EntryHandle(Kdb3Database* db){
1260         pDB=db;
1261         valid=true;
1262         Entry=NULL;
1263 }
1264
1265
1266 bool Kdb3Database::GroupHandle::isValid(){return valid;}
1267 QString Kdb3Database::GroupHandle::title(){return Group->Title;}
1268 quint32 Kdb3Database::GroupHandle::image(){return Group->Image;}
1269 int Kdb3Database::GroupHandle::index(){return Group->Index;}
1270 void Kdb3Database::GroupHandle::setTitle(const QString& Title){Group->Title=Title;}
1271 void Kdb3Database::GroupHandle::setExpanded(bool IsExpanded){Group->IsExpanded=IsExpanded;}
1272 bool Kdb3Database::GroupHandle::expanded(){return Group->IsExpanded;}
1273 void Kdb3Database::GroupHandle::setImage(const quint32& New){Group->Image=New;}
1274
1275
1276 Kdb3Database::GroupHandle::GroupHandle(Kdb3Database* db){
1277         pDB=db;
1278         valid=true;
1279         Group=NULL;
1280 }
1281
1282 IGroupHandle* Kdb3Database::GroupHandle::parent(){
1283         return (IGroupHandle*)Group->Parent->Handle;
1284 }
1285
1286 int Kdb3Database::GroupHandle::level(){
1287         int i=0;
1288         StdGroup* group=Group;
1289         while(group->Parent){
1290                 group=group->Parent;
1291                 i++;
1292         }
1293         i--;
1294         return i;
1295 }
1296
1297
1298 QList<IGroupHandle*> Kdb3Database::GroupHandle::children(){
1299         QList<IGroupHandle*> children;
1300         for(int i=0; i < Group->Children.size(); i++){
1301                 children.append(Group->Children[i]->Handle);
1302         }
1303         return children;
1304 }
1305
1306
1307 void memcpyFromLEnd32(quint32* dst,const char* src){
1308         if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1309                 memcpy(((char*)dst)+3,src+0,1);
1310                 memcpy(((char*)dst)+2,src+1,1);
1311                 memcpy(((char*)dst)+1,src+2,1);
1312                 memcpy(((char*)dst)+0,src+3,1);
1313         }
1314         else{
1315                 memcpy(dst,src,4);
1316         }
1317 }
1318
1319 void memcpyFromLEnd16(quint16* dst,const char* src){
1320         if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1321                 memcpy(((char*)dst)+1,src+0,1);
1322                 memcpy(((char*)dst)+0,src+1,1);
1323         }
1324         else{
1325                 memcpy(dst,src,2);
1326         }
1327 }
1328
1329 void memcpyToLEnd32(char* dst,const quint32* src){
1330         if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1331                 memcpy(dst+0,((char*)src)+3,1);
1332                 memcpy(dst+1,((char*)src)+2,1);
1333                 memcpy(dst+2,((char*)src)+1,1);
1334                 memcpy(dst+3,((char*)src)+0,1);
1335         }
1336         else{
1337                 memcpy(dst,src,4);
1338         }
1339 }
1340
1341 void memcpyToLEnd16(char* dst,const quint16* src){
1342         if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1343                 memcpy(dst+0,((char*)src)+1,1);
1344                 memcpy(dst+1,((char*)src)+0,1);
1345         }
1346         else{
1347                 memcpy(dst,src,2);
1348         }
1349 }
1350
1351 bool Kdb3Database::save(){
1352         if(!Groups.size()){
1353                 error=tr("The database must contain at least one group.");
1354                 return false;
1355         }
1356         
1357         if (!File->isOpen()) {
1358                 if(!File->open(QIODevice::ReadWrite)){
1359                         error=tr("Could not open file.");
1360                         return false;
1361                 }
1362         }
1363         
1364         if(!(File->openMode() & QIODevice::WriteOnly)){
1365                 error = tr("The database has been opened read-only.");
1366                 return false;
1367         }
1368         
1369         //Delete old backup entries
1370         if (config->backup() && config->backupDelete() && config->backupDeleteAfter()>0 && backupGroup()){
1371                 QDateTime time = QDateTime::currentDateTime().addDays(-config->backupDeleteAfter());
1372                 QList<IEntryHandle*> backupEntries = entries(backupGroup());
1373                 for (int i=0; i<backupEntries.size(); i++){
1374                         if (backupEntries[i]->lastMod()<time)
1375                                 deleteEntry(backupEntries[i]);
1376                 }
1377         }
1378         
1379         quint32 NumGroups,NumEntries,Signature1,Signature2,Flags,Version;
1380         quint8 FinalRandomSeed[16];
1381         quint8 ContentsHash[32];
1382         quint8 EncryptionIV[16];
1383
1384         unsigned int FileSize;
1385
1386         QList<StdEntry> MetaStreams;
1387         MetaStreams << StdEntry();
1388         createCustomIconsMetaStream(&MetaStreams.back());
1389         MetaStreams << StdEntry();
1390         createGroupTreeStateMetaStream(&MetaStreams.back());
1391
1392         FileSize=DB_HEADER_SIZE;
1393         // Get the size of all groups (94 Byte + length of the name string)
1394         for(int i = 0; i < Groups.size(); i++){
1395                 FileSize += 94 + Groups[i].Title.toUtf8().length()+1;
1396         }
1397         // Get the size of all entries
1398         for(int i = 0; i < Entries.size(); i++){
1399                 FileSize
1400                         += 134
1401                         +Entries[i].Title.toUtf8().length()+1
1402                         +Entries[i].Username.toUtf8().length()+1
1403                         +Entries[i].Url.toUtf8().length()+1
1404                         +Entries[i].Password.length()+1
1405                         +Entries[i].Comment.toUtf8().length()+1
1406                         +Entries[i].BinaryDesc.toUtf8().length()+1
1407                         +Entries[i].Binary.length();
1408         }
1409
1410         for(int i=0; i < UnknownMetaStreams.size(); i++){
1411                 FileSize
1412                         +=165
1413                         +UnknownMetaStreams[i].Comment.toUtf8().length()+1
1414                         +UnknownMetaStreams[i].Binary.length();
1415         }
1416
1417         for(int i=0; i < MetaStreams.size(); i++){
1418                 FileSize
1419                                 +=165
1420                                 +MetaStreams[i].Comment.toUtf8().length()+1
1421                                 +MetaStreams[i].Binary.length();
1422         }
1423
1424
1425         // Round up filesize to 16-byte boundary for Rijndael/Twofish
1426         FileSize = (FileSize + 16) - (FileSize % 16);
1427         char* buffer=new char[FileSize+16];
1428
1429         Signature1 = PWM_DBSIG_1;
1430         Signature2 = PWM_DBSIG_2;
1431         Flags = PWM_FLAG_SHA2;
1432         if(Algorithm == Rijndael_Cipher) Flags |= PWM_FLAG_RIJNDAEL;
1433         else if(Algorithm == Twofish_Cipher) Flags |= PWM_FLAG_TWOFISH;
1434         Version = PWM_DBVER_DW;
1435         NumGroups = Groups.size();
1436         NumEntries = Entries.size()+UnknownMetaStreams.size()+MetaStreams.size();
1437
1438         QList<StdEntry> saveEntries = Entries;
1439         qSort(saveEntries.begin(),saveEntries.end(),StdEntryLessThan);
1440
1441         randomize(FinalRandomSeed,16);
1442         randomize(EncryptionIV,16);
1443
1444         unsigned int pos=DB_HEADER_SIZE; // Skip the header, it will be written later
1445
1446         serializeGroups(buffer,pos);
1447         serializeEntries(saveEntries,buffer,pos);
1448         serializeEntries(UnknownMetaStreams,buffer,pos);
1449         serializeEntries(MetaStreams,buffer,pos);
1450         SHA256::hashBuffer(buffer+DB_HEADER_SIZE,ContentsHash,pos-DB_HEADER_SIZE);
1451         memcpyToLEnd32(buffer,&Signature1);
1452         memcpyToLEnd32(buffer+4,&Signature2);
1453         memcpyToLEnd32(buffer+8,&Flags);
1454         memcpyToLEnd32(buffer+12,&Version);
1455         memcpy(buffer+16,FinalRandomSeed,16);
1456         memcpy(buffer+32,EncryptionIV,16);
1457         memcpyToLEnd32(buffer+48,&NumGroups);
1458         memcpyToLEnd32(buffer+52,&NumEntries);
1459         memcpy(buffer+56,ContentsHash,32);
1460         memcpy(buffer+88,TransfRandomSeed,32);
1461         memcpyToLEnd32(buffer+120,&KeyTransfRounds);
1462         quint8 FinalKey[32];
1463
1464         SHA256 sha;
1465         sha.update(FinalRandomSeed,16);
1466         MasterKey.unlock();
1467         sha.update(*MasterKey,32);
1468         MasterKey.lock();
1469         sha.finish(FinalKey);
1470
1471         unsigned long EncryptedPartSize;
1472
1473         if(Algorithm == Rijndael_Cipher){
1474                 EncryptedPartSize=((pos-DB_HEADER_SIZE)/16+1)*16;
1475                 quint8 PadLen=EncryptedPartSize-(pos-DB_HEADER_SIZE);
1476                 for(int i=0;i<PadLen;i++)
1477                         ((quint8*)buffer)[DB_HEADER_SIZE+EncryptedPartSize-1-i]=PadLen;
1478                 AESencrypt aes;
1479                 aes.key256(FinalKey);
1480                 aes.cbc_encrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,EncryptedPartSize,(unsigned char*)EncryptionIV);
1481         }
1482         else{ // Algorithm == Twofish_Cipher
1483                 CTwofish twofish;
1484                 if(twofish.init(FinalKey, 32, EncryptionIV) == false){
1485                         UNEXP_ERROR
1486                         delete [] buffer;
1487                         return false;
1488                 }
1489                 EncryptedPartSize = (unsigned long)twofish.padEncrypt((quint8*)buffer+DB_HEADER_SIZE,
1490                         pos - DB_HEADER_SIZE,(quint8*)buffer+DB_HEADER_SIZE);
1491         }
1492         if((EncryptedPartSize > (0xFFFFFFE - 202)) || (!EncryptedPartSize && Groups.size())){
1493                 UNEXP_ERROR
1494                 delete [] buffer;
1495                 return false;
1496         }
1497         
1498         int size = EncryptedPartSize+DB_HEADER_SIZE;
1499         
1500         if (!saveFileTransactional(buffer, size)) {
1501                 error=decodeFileError(File->error());
1502                 delete [] buffer;
1503                 return false;
1504         }
1505
1506         delete [] buffer;
1507         //if(SearchGroupID!=-1)Groups.push_back(SearchGroup);
1508         return true;
1509 }
1510
1511 bool Kdb3Database::saveFileTransactional(char* buffer, int size) {
1512         QString orgFilename = File->fileName();
1513         QFile* tmpFile = new QFile(orgFilename + ".tmp");
1514         if (!tmpFile->open(QIODevice::WriteOnly|QIODevice::Truncate)) {
1515                 tmpFile->remove();
1516                 delete tmpFile;
1517                 return false;
1518         }
1519         if (tmpFile->write(buffer,size) != size) {
1520                 tmpFile->remove();
1521                 delete tmpFile;
1522                 return false;
1523         }
1524         if (!syncFile(tmpFile))
1525                 qWarning("Unable to flush file to disk");
1526         tmpFile->close();
1527         if (!File->remove()) {
1528                 delete tmpFile;
1529                 return false;
1530         }
1531         delete File;
1532         File = NULL;
1533         if (!tmpFile->rename(orgFilename)) {
1534                 delete tmpFile;
1535                 File = new QFile(orgFilename);
1536                 return false;
1537         }
1538         File = tmpFile;
1539         if (!tmpFile->open(QIODevice::ReadWrite)) {
1540                 delete tmpFile;
1541                 return false;
1542         }
1543         
1544         return true;
1545 }
1546
1547 void Kdb3Database::createCustomIconsMetaStream(StdEntry* e){
1548         /* Rev 3 */
1549         e->BinaryDesc="bin-stream";
1550         e->Title="Meta-Info";
1551         e->Username="SYSTEM";
1552         e->Comment="KPX_CUSTOM_ICONS_4";
1553         e->Url="$";
1554         if(Groups.size())e->GroupId=Groups[0].Id;
1555         int Size=12;
1556         quint32 NumEntries=0;
1557         for(quint32 i=0;i<Entries.size();i++){
1558                 if (Entries[i].Image>=BUILTIN_ICONS)
1559                         NumEntries++;
1560         }
1561         quint32 NumGroups=0;
1562         for(quint32 i=0;i<Groups.size();i++){
1563                 if (Groups[i].Image>=BUILTIN_ICONS)
1564                         NumGroups++;
1565         }
1566         Size+=8*NumGroups+20*NumEntries;
1567         Size+=CustomIcons.size()*1000; // 1KB
1568         e->Binary.reserve(Size);
1569         e->Binary.resize(12);
1570         quint32 NumIcons=CustomIcons.size();
1571
1572         memcpyToLEnd32(e->Binary.data(),&NumIcons);
1573         memcpyToLEnd32(e->Binary.data()+4,&NumEntries);
1574         memcpyToLEnd32(e->Binary.data()+8,&NumGroups);
1575         for(int i=0;i<CustomIcons.size();i++){
1576                 quint32 ImgSize;
1577                 char ImgSizeBin[4];
1578                 QByteArray png;
1579                 png.reserve(1000);
1580                 QBuffer buffer(&png);
1581                 CustomIcons[i].save(&buffer,"PNG",0);
1582                 ImgSize=png.size();
1583                 memcpyToLEnd32(ImgSizeBin,&ImgSize);
1584                 e->Binary.append(QByteArray::fromRawData(ImgSizeBin,4));
1585                 e->Binary.append(png);
1586         }
1587         
1588         for(quint32 i=0;i<Entries.size();i++){
1589                 if (Entries[i].Image>=BUILTIN_ICONS){
1590                         char Bin[20];
1591                         Entries[i].Uuid.toRaw(Bin);
1592                         quint32 id=Entries[i].Image-BUILTIN_ICONS;
1593                         memcpyToLEnd32(Bin+16,&id);
1594                         e->Binary.append(QByteArray::fromRawData(Bin,20));
1595                 }
1596         }
1597         for(quint32 i=0;i<Groups.size();i++){
1598                 if (Groups[i].Image>=BUILTIN_ICONS){
1599                         char Bin[8];
1600                         memcpyToLEnd32(Bin,&Groups[i].Id);
1601                         quint32 id=Groups[i].Image-BUILTIN_ICONS;
1602                         memcpyToLEnd32(Bin+4,&id);
1603                         e->Binary.append(QByteArray::fromRawData(Bin,8));
1604                 }
1605         }
1606 }
1607
1608 QList<IGroupHandle*> Kdb3Database::sortedGroups(){
1609         QList<IGroupHandle*> SortedGroups;
1610         appendChildrenToGroupList(SortedGroups,RootGroup);
1611         return SortedGroups;
1612 }
1613
1614
1615 void Kdb3Database::appendChildrenToGroupList(QList<IGroupHandle*>& list,StdGroup& group){
1616         for(int i=0;i<group.Children.size();i++){
1617                 list << group.Children[i]->Handle;
1618                 appendChildrenToGroupList(list,*group.Children[i]);
1619         }
1620 }
1621
1622
1623 void Kdb3Database::appendChildrenToGroupList(QList<StdGroup*>& list,StdGroup& group){
1624         for(int i=0;i<group.Children.size();i++){
1625                 list << group.Children[i];
1626                 appendChildrenToGroupList(list,*group.Children[i]);
1627         }
1628 }
1629
1630
1631 void Kdb3Database::serializeGroups(char* buffer,unsigned int& pos){
1632         quint16 FieldType;
1633         quint32 FieldSize;
1634         quint32 Flags=0; //unused
1635         QList<StdGroup*>SortedGroups;
1636         appendChildrenToGroupList(SortedGroups,RootGroup);
1637
1638         for(int i=0; i < SortedGroups.size(); i++){
1639                 unsigned char Date[5];
1640                 dateToPackedStruct5(Date_Never,Date);
1641                 quint16 Level=0;
1642                 StdGroup* group=SortedGroups[i];
1643                 while(group->Parent){
1644                         Level++;
1645                         group=group->Parent;
1646                 }
1647                 Level--;
1648
1649                 FieldType = 0x0001; FieldSize = 4;
1650                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1651                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1652                 memcpyToLEnd32(buffer+pos, &SortedGroups[i]->Id); pos += 4;
1653
1654                 FieldType = 0x0002; FieldSize = SortedGroups[i]->Title.toUtf8().length() + 1;
1655                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1656                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1657                 memcpy(buffer+pos, SortedGroups[i]->Title.toUtf8(),FieldSize); pos += FieldSize;
1658
1659                 FieldType = 0x0003; FieldSize = 5; //Creation
1660                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1661                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1662                 memcpy(buffer+pos, Date,5); pos+=5;
1663
1664                 FieldType = 0x0004; FieldSize = 5; //LastMod
1665                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1666                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1667                 memcpy(buffer+pos, Date,5); pos+=5;
1668
1669                 FieldType = 0x0005; FieldSize = 5; //LastAccess
1670                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1671                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1672                 memcpy(buffer+pos, Date,5); pos+=5;
1673
1674                 FieldType = 0x0006; FieldSize = 5; //Expire
1675                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1676                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1677                 memcpy(buffer+pos, Date,5); pos+=5;
1678
1679                 FieldType = 0x0007; FieldSize = 4;
1680                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1681                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1682                 memcpyToLEnd32(buffer+pos, &SortedGroups[i]->Image); pos += 4;
1683
1684                 FieldType = 0x0008; FieldSize = 2;
1685                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1686                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1687                 memcpyToLEnd16(buffer+pos, &Level); pos += 2;
1688
1689                 FieldType = 0x0009; FieldSize = 4;
1690                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1691                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1692                 memcpyToLEnd32(buffer+pos, &Flags); pos += 4;
1693
1694                 FieldType = 0xFFFF; FieldSize = 0;
1695                 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1696                 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1697         }
1698
1699 }
1700
1701
1702 void Kdb3Database::serializeEntries(QList<StdEntry>& EntryList,char* buffer,unsigned int& pos){
1703          quint16 FieldType;
1704          quint32 FieldSize;
1705          for(int i = 0; i < EntryList.size(); i++){
1706                  FieldType = 0x0001; FieldSize = 16;
1707                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1708                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1709                  EntryList[i].Uuid.toRaw(buffer+pos);           pos += 16;
1710
1711                  FieldType = 0x0002; FieldSize = 4;
1712                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1713                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1714                  memcpyToLEnd32(buffer+pos, &EntryList[i].GroupId); pos += 4;
1715
1716                  FieldType = 0x0003; FieldSize = 4;
1717                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1718                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1719                  memcpyToLEnd32(buffer+pos,&EntryList[i].Image); pos += 4;
1720
1721
1722                  FieldType = 0x0004;
1723                  FieldSize = EntryList[i].Title.toUtf8().length() + 1; // Add terminating NULL character space
1724                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1725                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1726                  memcpy(buffer+pos, EntryList[i].Title.toUtf8(),FieldSize);  pos += FieldSize;
1727
1728                  FieldType = 0x0005;
1729                  FieldSize = EntryList[i].Url.toUtf8().length() + 1; // Add terminating NULL character space
1730                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1731                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1732                  memcpy(buffer+pos, EntryList[i].Url.toUtf8(),FieldSize);  pos += FieldSize;
1733
1734                  FieldType = 0x0006;
1735                  FieldSize = EntryList[i].Username.toUtf8().length() + 1; // Add terminating NULL character space
1736                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1737                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1738                  memcpy(buffer+pos, EntryList[i].Username.toUtf8(),FieldSize);  pos += FieldSize;
1739
1740                  FieldType = 0x0007;
1741                  FieldSize = EntryList[i].Password.length() + 1; // Add terminating NULL character space
1742                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1743                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1744                  EntryList[i].Password.unlock();
1745                  memcpy(buffer+pos, EntryList[i].Password.string().toUtf8(),FieldSize);  pos += FieldSize;
1746                  EntryList[i].Password.lock();
1747
1748                  FieldType = 0x0008;
1749                  FieldSize = EntryList[i].Comment.toUtf8().length() + 1; // Add terminating NULL character space
1750                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1751                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1752                  memcpy(buffer+pos, EntryList[i].Comment.toUtf8(),FieldSize);  pos += FieldSize;
1753
1754                  FieldType = 0x0009; FieldSize = 5;
1755                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1756                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1757                  dateToPackedStruct5(EntryList[i].Creation,(unsigned char*)buffer+pos); pos+=5;
1758
1759
1760                  FieldType = 0x000A; FieldSize = 5;
1761                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1762                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1763                  dateToPackedStruct5(EntryList[i].LastMod,(unsigned char*)buffer+pos); pos+=5;
1764
1765                  FieldType = 0x000B; FieldSize = 5;
1766                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1767                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1768                  dateToPackedStruct5(EntryList[i].LastAccess,(unsigned char*)buffer+pos); pos+=5;
1769
1770                  FieldType = 0x000C; FieldSize = 5;
1771                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1772                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1773                  dateToPackedStruct5(EntryList[i].Expire,(unsigned char*)buffer+pos); pos+=5;
1774
1775                  FieldType = 0x000D;
1776                  FieldSize = EntryList[i].BinaryDesc.toUtf8().length() + 1; // Add terminating NULL character space
1777                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1778                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1779                  memcpy(buffer+pos, EntryList[i].BinaryDesc.toUtf8(),FieldSize);  pos += FieldSize;
1780
1781                  FieldType = 0x000E; FieldSize = EntryList[i].Binary.length();
1782                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1783                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1784                  if((!EntryList[i].Binary.isNull()) && (FieldSize != 0))
1785                          memcpy(buffer+pos, EntryList[i].Binary.data(), FieldSize);
1786                  pos += FieldSize;
1787
1788                  FieldType = 0xFFFF; FieldSize = 0;
1789                  memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1790                  memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1791          }
1792  }
1793
1794 bool Kdb3Database::close(){
1795         if (File!=NULL)
1796                 delete File;
1797         return true;
1798 }
1799
1800 void Kdb3Database::create(){
1801         File=NULL;
1802         RootGroup.Title="$ROOT$";
1803         RootGroup.Parent=NULL;
1804         RootGroup.Handle=NULL;
1805         Algorithm=Rijndael_Cipher;
1806         KeyTransfRounds=50000;
1807         KeyError=false;
1808 }
1809
1810 bool Kdb3Database::isKeyError(){
1811         if(KeyError){
1812                 KeyError=false;
1813                 return true;
1814         }
1815         else
1816                 return false;
1817 }
1818
1819 IEntryHandle* Kdb3Database::cloneEntry(const IEntryHandle* entry){
1820         StdEntry dolly;
1821         dolly=*((EntryHandle*)entry)->Entry;
1822         dolly.Uuid.generate();
1823         Entries.append(dolly);
1824         EntryHandles.append(EntryHandle(this));
1825         EntryHandles.back().Entry=&Entries.back();
1826         Entries.back().Handle=&EntryHandles.back();
1827         return &EntryHandles.back();
1828 }
1829
1830 IEntryHandle* Kdb3Database::newEntry(IGroupHandle* group){
1831         StdEntry Entry;
1832         Entry.Uuid.generate();
1833         Entry.Group=((GroupHandle*)group)->Group;
1834         Entry.GroupId=Entry.Group->Id;
1835         Entries.append(Entry);
1836         EntryHandles.append(EntryHandle(this));
1837         EntryHandles.back().Entry=&Entries.back();
1838         Entries.back().Handle=&EntryHandles.back();
1839         return &EntryHandles.back();
1840 }
1841
1842 IEntryHandle* Kdb3Database::addEntry(const CEntry* NewEntry, IGroupHandle* Group){
1843         StdEntry Entry(*((StdEntry*)NewEntry));
1844         Entry.Uuid.generate();
1845         Entry.Group=((GroupHandle*)Group)->Group;
1846         Entry.GroupId=Entry.Group->Id;
1847         Entries.append(Entry);
1848         EntryHandles.append(EntryHandle(this));
1849         EntryHandles.back().Entry=&Entries.back();
1850         Entries.back().Handle=&EntryHandles.back();
1851         return &EntryHandles.back();
1852 }
1853
1854 void Kdb3Database::deleteLastEntry(){
1855         Entries.removeAt(Entries.size()-1);
1856         EntryHandles.back().invalidate();
1857 }
1858
1859 bool Kdb3Database::isParent(IGroupHandle* parent, IGroupHandle* child){
1860         StdGroup* group=((GroupHandle*)child)->Group;
1861         while(group->Parent!=&RootGroup){
1862                 if(group->Parent==((GroupHandle*)parent)->Group)return true;
1863                 group=group->Parent;
1864         }
1865         return false;
1866 }
1867
1868
1869
1870 void Kdb3Database::cleanUpHandles(){}
1871
1872 bool Kdb3Database::searchStringContains(const QString& search, const QString& string,bool Cs, bool RegExp){
1873         if(RegExp){
1874                 QRegExp exp(search,Cs ? Qt::CaseSensitive : Qt::CaseInsensitive);
1875                 if(string.contains(exp)==0)return false;}
1876                 else
1877                         if(string.contains(search,Cs ? Qt::CaseSensitive : Qt::CaseInsensitive)==0)return false;
1878
1879                 return true;
1880 }
1881
1882 void Kdb3Database::getEntriesRecursive(IGroupHandle* Group, QList<IEntryHandle*>& EntryList){
1883         EntryList<<entries(Group);
1884         for(int i=0;i<((GroupHandle*)Group)->Group->Children.size();    i++){
1885                 getEntriesRecursive(((GroupHandle*)Group)->Group->Children[i]->Handle,EntryList);
1886         }
1887 }
1888
1889 QList<IEntryHandle*> Kdb3Database::search(IGroupHandle* Group,const QString& search, bool CaseSensitive, bool RegExp, bool Recursive,bool* Fields){
1890         bool fields[6]={true,true,true,false,true,true};
1891         if(!Fields)
1892                 Fields=fields;
1893         QList<IEntryHandle*> SearchEntries;
1894         if(search==QString())return Group ? entries(Group) : entries();
1895         if(Group){
1896                 if(Recursive)
1897                         getEntriesRecursive(Group,SearchEntries);
1898                 else
1899                         SearchEntries=entries(Group);
1900         }
1901         else
1902                 SearchEntries=entries();
1903         
1904         IGroupHandle* bGroup = backupGroup();
1905         
1906         QList<IEntryHandle*> ResultEntries;
1907         for(int i=0; i<SearchEntries.size(); i++){
1908                 IGroupHandle* entryGroup = SearchEntries[i]->group();
1909                 while (entryGroup->parent())
1910                         entryGroup = entryGroup->parent();
1911                 if (entryGroup == bGroup)
1912                         continue;
1913                 
1914                 bool match=false;
1915                 if(Fields[0])match=match||searchStringContains(search,SearchEntries[i]->title(),CaseSensitive,RegExp);
1916                 if(Fields[1])match=match||searchStringContains(search,SearchEntries[i]->username(),CaseSensitive,RegExp);
1917                 if(Fields[2])match=match||searchStringContains(search,SearchEntries[i]->url(),CaseSensitive,RegExp);
1918                 SecString Password=SearchEntries[i]->password();
1919                 Password.unlock();
1920                 if(Fields[3])match=match||searchStringContains(search,Password.string(),CaseSensitive,RegExp);
1921                 Password.lock();
1922                 if(Fields[4])match=match||searchStringContains(search,SearchEntries[i]->comment(),CaseSensitive,RegExp);
1923                 if(Fields[5])match=match||searchStringContains(search,SearchEntries[i]->binaryDesc(),CaseSensitive,RegExp);
1924                 if(match)
1925                         ResultEntries << SearchEntries[i];
1926         }
1927
1928         return ResultEntries;
1929 }
1930
1931 void Kdb3Database::rebuildIndices(QList<StdGroup*>& list){
1932         for(int i=0;i<list.size();i++){
1933                 list[i]->Index=i;
1934         }
1935 }
1936
1937
1938 void Kdb3Database::moveGroup(IGroupHandle* groupHandle,IGroupHandle* NewParent,int Pos){
1939         StdGroup* Parent;
1940         StdGroup* Group=((GroupHandle*)groupHandle)->Group;
1941         if(NewParent)
1942                 Parent=((GroupHandle*)NewParent)->Group;
1943         else
1944                 Parent=&RootGroup;
1945         Group->Parent->Children.removeAt(Group->Index);
1946         rebuildIndices(Group->Parent->Children);
1947         Group->Parent=Parent;
1948         if(Pos==-1){
1949                 Parent->Children.append(Group);
1950         }
1951         else
1952         {
1953                 Q_ASSERT(Parent->Children.size()>=Pos);
1954                 Parent->Children.insert(Pos,Group);
1955         }
1956         rebuildIndices(Parent->Children);
1957 }
1958
1959 bool Kdb3Database::changeFile(const QString& filename){
1960         QFile* tmpFile = new QFile(filename);
1961         if(!tmpFile->open(QIODevice::ReadWrite)){
1962                 error = decodeFileError(File->error());
1963                 delete tmpFile;
1964                 return false;
1965         }
1966         
1967         if (File)
1968                 delete File;
1969         
1970         File = tmpFile;
1971
1972         return true;
1973 }
1974
1975 void Kdb3Database::generateMasterKey(){
1976         randomize(TransfRandomSeed,32);
1977         RawMasterKey.unlock();
1978         MasterKey.unlock();
1979         KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds);
1980         RawMasterKey.lock();
1981         MasterKey.lock();
1982 }
1983
1984 /*void Kdb3Database::copyTree(Kdb3Database* db, GroupHandle* orgGroup, IGroupHandle* parent) {
1985         IGroupHandle* newParent = db->addGroup(orgGroup->Group, parent);
1986         
1987         QList<IEntryHandle*> entryList = entries(orgGroup);
1988         for (int i=0; i<entryList.size(); i++) {
1989                 EntryHandle* entry = static_cast<EntryHandle*>(entryList[i]);
1990                 db->addEntry(entry->Entry, newParent);
1991         }
1992         
1993         QList<IGroupHandle*> children = orgGroup->children();
1994         for (int i=0; i<children.size(); i++) {
1995                 GroupHandle* child = static_cast<GroupHandle*>(children[i]);
1996                 copyTree(db, child, newParent);
1997         }
1998 }
1999
2000 IDatabase* Kdb3Database::groupToNewDb(IGroupHandle* group){
2001         Kdb3Database* db = new Kdb3Database();
2002         db->create();
2003         copyTree(db, static_cast<GroupHandle*>(group), NULL);
2004         
2005         db->changeFile("/ramtmp/test.kdb");
2006         if (!db->save())
2007                 qWarning("%s", CSTR(db->error));
2008         
2009         return db;
2010 }*/
2011
2012
2013 void KeyTransform::transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds){
2014         KeyTransform* ktLeft = new KeyTransform(&src[0], &dst[0], KeySeed, rounds);
2015         KeyTransform* ktRight = new KeyTransform(&src[16], &dst[16], KeySeed, rounds);
2016         ktLeft->start();
2017         ktRight->start();
2018         ktLeft->wait();
2019         ktRight->wait();
2020         SHA256::hashBuffer(dst,dst,32);
2021         delete ktLeft;
2022         delete ktRight;
2023 }
2024
2025 KeyTransform::KeyTransform(quint8* pSrc, quint8* pDst, quint8* pKeySeed, int pRounds){
2026         src = pSrc;
2027         dst = pDst;
2028         KeySeed = pKeySeed;
2029         rounds = pRounds;
2030 }
2031
2032 void KeyTransform::run(){
2033         AESencrypt aes;
2034         aes.key256(KeySeed);
2035         memcpy(dst,src,16);
2036         for (int i=0; i<rounds; i++){
2037                 aes.ecb_encrypt(dst,dst,16);
2038         }
2039 }
2040
2041
2042 int KeyTransformBenchmark::benchmark(int pMSecs){
2043         KeyTransformBenchmark* ktbLeft = new KeyTransformBenchmark(pMSecs);
2044         KeyTransformBenchmark* ktbRight = new KeyTransformBenchmark(pMSecs);
2045         ktbLeft->start();
2046         ktbRight->start();
2047         ktbLeft->wait();
2048         ktbRight->wait();
2049         int num = std::min(ktbLeft->rounds, ktbRight->rounds);
2050         delete ktbLeft;
2051         delete ktbRight;
2052         
2053         return num;
2054 }
2055
2056 KeyTransformBenchmark::KeyTransformBenchmark(int pMSecs){
2057         msecs = pMSecs;
2058         rounds = 0;
2059 }
2060
2061 void KeyTransformBenchmark::run(){
2062         quint8 KeySeed[32];
2063         memset(KeySeed, 0x4B, 32);
2064         quint8 dst[16];
2065         memset(dst, 0x7E, 16);
2066         
2067         QTime t;
2068         t.start();
2069         
2070         AESencrypt aes;
2071         aes.key256(KeySeed);
2072         
2073         do {
2074                 for (int i=0; i<64; i++){
2075                         aes.ecb_encrypt(dst,dst,16);
2076                 }
2077                 rounds += 64;
2078         } while (t.elapsed() < msecs);
2079 }