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