1 /* vi: set sw=4 ts=4:
2 *
3 * Copyright (C) 2001 - 2012 Christian Hohnstaedt.
4 *
5 * All rights reserved.
6 */
7
8 #include "db.h"
9 #include "base.h"
10 #include "func.h"
11 #include "exception.h"
12 #include <QStringList>
13 #include <QDebug>
14 #include <QDateTime>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <errno.h>
23
24 #define XNUM(n) CCHAR(QString::number((n), 16))
25
db(const QString & filename,QFlags<QFile::Permission> perm)26 db::db(const QString &filename, QFlags<QFile::Permission> perm)
27 {
28 name = filename;
29 file.setFileName(filename);
30 bool newFile = !file.exists();
31
32 if (!file.open(QIODevice::ReadWrite)) {
33 fileIOerr("open");
34 } else {
35 first();
36 if (newFile)
37 file.setPermissions(perm);
38 else if (!verify_magic()) {
39 file.close();
40 throw errorEx("Unknown database format", filename);
41 }
42 }
43 }
44
~db()45 db::~db()
46 {
47 file.close();
48 }
49
fileIOerr(QString s)50 void db::fileIOerr(QString s)
51 {
52 errstr = QString("DB ") + s + "() '" + file.fileName() + "'";
53 dberrno = errno;
54 throw errorEx(errstr, strerror(errno));
55 }
56
convert_header(db_header_t * h)57 void db::convert_header(db_header_t *h)
58 {
59 h->magic = xntohl(head.magic);
60 h->len = xntohl(head.len);
61 h->headver = xntohs(head.headver);
62 h->type = xntohs(head.type);
63 h->version = xntohs(head.version);
64 h->flags = xntohs(head.flags);
65 memcpy(h->name, head.name, NAMELEN);
66 }
67
verify_magic(void)68 bool db::verify_magic(void)
69 {
70 if (!eof())
71 if (xntohl(head.magic) != XCA_MAGIC) {
72 return false;
73 }
74 return true;
75 }
76
eof()77 bool db::eof()
78 {
79 return head_offset == file.size();
80 }
81
find(enum pki_type type,QString name)82 int db::find(enum pki_type type, QString name)
83 {
84 while (!eof()) {
85 if (xntohs(head.type) == type) {
86 if (name.isEmpty()) { /* only compare type */
87 return 0;
88 } else if (QString::fromUtf8(head.name) == name) {
89 return 0;
90 }
91 }
92 if (!verify_magic()) {
93 return -1;
94 }
95 next();
96 }
97 return 1;
98 }
99
first(int flag)100 void db::first(int flag)
101 {
102 int ret;
103 memset(&head, 0, sizeof(db_header_t) );
104 head_offset = 0;
105 file.seek(0);
106 ret = file.read((char*)&head, sizeof(db_header_t) );
107 if (ret < 0 )
108 fileIOerr("read");
109 if (ret==0) {
110 head_offset = file.size();
111 return;
112 }
113 if (!verify_magic())
114 return;
115 if (xntohs(head.flags) & flag)
116 next(flag);
117 }
118
next(int flag)119 int db::next(int flag)
120 {
121 qint64 ret;
122 qint64 garbage = -1;
123 int result = 1;
124
125 if (eof())
126 return 1;
127
128 head_offset += xntohl(head.len);
129 if (head_offset >= file.size()) {
130 head_offset = file.size();
131 return 1;
132 }
133 while (1) {
134 file.seek(head_offset);
135 ret = file.read((char*)&head, sizeof head);
136 if (ret==0) {
137 head_offset = file.size();
138 break;
139 }
140 if (ret < 0) {
141 fileIOerr("read");
142 return -1;
143 }
144 if (ret != sizeof head) {
145 qWarning("next(): Short read: 0x%s of 0x%s @ 0x%s",
146 XNUM(ret), XNUM(sizeof head),
147 XNUM(head_offset));
148 if (garbage != -1) {
149 ret += head_offset - garbage;
150 head_offset = garbage;
151 }
152 qWarning("next(): Truncating 0x%s garbage bytes @ 0x%s",
153 XNUM(ret), XNUM(head_offset));
154 if (backup())
155 file.resize(head_offset);
156 head_offset = file.size();
157 return -1;
158 }
159 qint64 hlen = xntohl(head.len);
160 if (!verify_magic()) {
161 if (garbage == -1)
162 garbage = head_offset;
163 head_offset += 1;
164 continue;
165 } else {
166 if (garbage != -1) {
167 qWarning("next(): 0x%s bytes garbage skipped at 0x%s",
168 XNUM(head_offset - garbage),
169 XNUM(garbage));
170 }
171 garbage = -1;
172 if (file.size() < head_offset + hlen) {
173 qWarning("next(): Short item (%s of %s) at 0x%s",
174 XNUM(xntohl(head.len)),
175 XNUM(file.size() - head_offset),
176 XNUM(head_offset));
177 garbage = head_offset;
178 /* invalidate the header */
179 qWarning("Invalidate short item @ 0x%s\n",
180 XNUM(head_offset));
181 file.seek(head_offset);
182 char inval = 0xcb; // 0xca +1
183 file.write(&inval, 1);
184 head_offset += 4;
185 continue;
186 }
187 }
188 if (!(xntohs(head.flags) & flag)) {
189 result = 0;
190 break;
191 } else {
192 head_offset += hlen;
193 }
194 }
195 if (garbage != -1) {
196 qWarning("next(): 0x%s bytes garbage skipped at 0x%s",
197 XNUM(head_offset - garbage), XNUM(garbage));
198 }
199 return result;
200 }
201
load(db_header_t * u_header)202 unsigned char *db::load(db_header_t *u_header)
203 {
204 uint32_t size;
205 qint64 ret;
206 unsigned char *data;
207
208 if (eof())
209 return NULL;
210 size = xntohl(head.len) - sizeof(db_header_t);
211 data = (unsigned char *)malloc(size);
212 file.seek(head_offset + sizeof(db_header_t));
213 ret = file.read((char*)data, size);
214 if (ret == (qint64)size) {
215 if (u_header)
216 convert_header(u_header);
217 return data;
218 } else {
219 free(data);
220 if (ret < 0)
221 fileIOerr("read");
222 return NULL;
223 }
224 }
225
get_header(db_header_t * u_header)226 bool db::get_header(db_header_t *u_header)
227 {
228 if (eof())
229 return false;
230 convert_header(u_header);
231 return true;
232 }
233
backup_name()234 QString db::backup_name()
235 {
236 return file.fileName() + "_backup_" +
237 QDateTime::currentDateTime()
238 .toString("yyyyMMdd_hhmmss") + ".xdb";
239 }
240
backup()241 bool db::backup()
242 {
243 QFile this_file, new_file;
244 QString backup = backup_name();
245 qint64 ret, wret;
246 char buf[BUFSIZ];
247
248 this_file.setFileName(file.fileName());
249 if (!this_file.open(QIODevice::ReadOnly)) {
250 return false;
251 }
252 new_file.setFileName(backup);
253 if (!new_file.open(QIODevice::ReadWrite)) {
254 this_file.close();
255 return false;
256 }
257 while (1) {
258 ret = this_file.read(buf, sizeof buf);
259 if (ret <= 0)
260 break;
261 wret = new_file.write(buf, ret);
262 if (wret != ret)
263 break;
264 }
265 this_file.close();
266 new_file.close();
267 return ret == 0;
268 }
269
intToData(uint32_t val)270 QByteArray db::intToData(uint32_t val)
271 {
272 uint32_t v = xhtonl(val);
273 return QByteArray((char*)&v, sizeof(uint32_t));
274 }
275
intFromData(QByteArray & ba)276 uint32_t db::intFromData(QByteArray &ba)
277 {
278 uint32_t ret;
279 if ((unsigned)(ba.count()) < sizeof(uint32_t)) {
280 throw errorEx(QObject::tr("Out of data"));
281 }
282 memcpy(&ret, ba.constData(), sizeof(uint32_t));
283 ba = ba.mid(sizeof(uint32_t));
284 return xntohl(ret);
285 }
286
boolToData(bool val)287 QByteArray db::boolToData(bool val)
288 {
289 char c = val ? 1 : 0;
290 return QByteArray(&c, 1);
291 }
292
boolFromData(QByteArray & ba)293 bool db::boolFromData(QByteArray &ba)
294 {
295 unsigned char c;
296 if (ba.count() < 1)
297 throw errorEx(QObject::tr("Out of data"));
298
299 c = ba.constData()[0];
300 ba = ba.mid(1);
301 return c ? true : false;
302 }
303
stringToData(const QString val)304 QByteArray db::stringToData(const QString val)
305 {
306 QByteArray ba = val.toUtf8();
307 int idx = ba.indexOf('\0');
308 if (idx == -1)
309 ba += '\0';
310 else
311 ba.truncate(idx +1);
312 return ba;
313 }
314
stringFromData(QByteArray & ba)315 QString db::stringFromData(QByteArray &ba)
316 {
317 int idx = ba.indexOf('\0');
318
319 if (idx == -1)
320 throw errorEx(QObject::tr("Error finding endmarker of string"));
321
322 QString ret = QString::fromUtf8(ba.constData(), idx);
323 ba = ba.mid(idx+1);
324 return ret;
325 }
326