1 /*
2  *  Copyright (C) 2016 Felix Geyer <debfx@fobos.de>
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 2 or (at your option)
7  *  version 3 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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "KeePass2Repair.h"
19 
20 #include <QBuffer>
21 #include <QRegExp>
22 
23 #include "format/KeePass2RandomStream.h"
24 #include "format/KeePass2Reader.h"
25 #include "format/KeePass2XmlReader.h"
26 
KeePass2Repair()27 KeePass2Repair::KeePass2Repair()
28     : m_db(nullptr)
29 {
30 }
31 
repairDatabase(QIODevice * device,const CompositeKey & key)32 KeePass2Repair::RepairResult KeePass2Repair::repairDatabase(QIODevice* device, const CompositeKey& key)
33 {
34     m_db = nullptr;
35     m_errorStr.clear();
36 
37     KeePass2Reader reader;
38     reader.setSaveXml(true);
39 
40     Database* db = reader.readDatabase(device, key, true);
41     if (!reader.hasError()) {
42         delete db;
43         return NothingTodo;
44     }
45 
46     QByteArray xmlData = reader.xmlData();
47     if (!db || xmlData.isEmpty()) {
48         delete db;
49         m_errorStr = reader.errorString();
50         return UnableToOpen;
51     }
52 
53     bool repairAction = false;
54 
55     QString xmlStart = QString::fromLatin1(xmlData.constData(), qMin(100, xmlData.size()));
56     QRegExp encodingRegExp("encoding=\"([^\"]+)\"", Qt::CaseInsensitive, QRegExp::RegExp2);
57     if (encodingRegExp.indexIn(xmlStart) != -1) {
58         if (encodingRegExp.cap(1).compare("utf-8", Qt::CaseInsensitive) != 0
59                 && encodingRegExp.cap(1).compare("utf8", Qt::CaseInsensitive) != 0)
60         {
61             // database is not utf-8 encoded, we don't support repairing that
62             delete db;
63             return RepairFailed;
64         }
65     }
66 
67     // try to fix broken databases because of bug #392
68     for (int i = (xmlData.size() - 1); i >= 0; i--) {
69         quint8 ch = static_cast<quint8>(xmlData.at(i));
70         if (ch < 0x20 && ch != 0x09 && ch != 0x0A && ch != 0x0D) {
71             xmlData.remove(i, 1);
72             repairAction = true;
73         }
74     }
75 
76     if (!repairAction) {
77         // we were unable to find the problem
78         delete db;
79         return RepairFailed;
80     }
81 
82     KeePass2RandomStream randomStream;
83     randomStream.init(reader.streamKey());
84     KeePass2XmlReader xmlReader;
85     QBuffer buffer(&xmlData);
86     buffer.open(QIODevice::ReadOnly);
87     xmlReader.readDatabase(&buffer, db, &randomStream);
88 
89     if (xmlReader.hasError()) {
90         delete db;
91         return RepairFailed;
92     }
93     else {
94         m_db = db;
95         return RepairSuccess;
96     }
97 }
98 
database() const99 Database* KeePass2Repair::database() const
100 {
101     return m_db;
102 }
103 
errorString() const104 QString KeePass2Repair::errorString() const
105 {
106     return m_errorStr;
107 }
108