Diffstat (limited to 'pwmanager/pwmanager/pwmdoc.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | pwmanager/pwmanager/pwmdoc.cpp | 2775 |
1 files changed, 2775 insertions, 0 deletions
diff --git a/pwmanager/pwmanager/pwmdoc.cpp b/pwmanager/pwmanager/pwmdoc.cpp new file mode 100644 index 0000000..04af360 --- a/dev/null +++ b/pwmanager/pwmanager/pwmdoc.cpp | |||
@@ -0,0 +1,2775 @@ | |||
1 | /*************************************************************************** | ||
2 | * * | ||
3 | * copyright (C) 2003, 2004 by Michael Buesch * | ||
4 | * email: mbuesch@freenet.de * | ||
5 | * * | ||
6 | * This program is free software; you can redistribute it and/or modify * | ||
7 | * it under the terms of the GNU General Public License version 2 * | ||
8 | * as published by the Free Software Foundation. * | ||
9 | * * | ||
10 | ***************************************************************************/ | ||
11 | |||
12 | /*************************************************************************** | ||
13 | * copyright (C) 2004 by Ulf Schenk | ||
14 | * This file is originaly based on version 2.0 of pwmanager | ||
15 | * and was modified to run on embedded devices that run microkde | ||
16 | * | ||
17 | * $Id$ | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include "pwmdoc.h" | ||
21 | #include "pwmview.h" | ||
22 | #include "blowfish.h" | ||
23 | #include "sha1.h" | ||
24 | #include "globalstuff.h" | ||
25 | #include "gpasmanfile.h" | ||
26 | #include "serializer.h" | ||
27 | #include "compressgzip.h" | ||
28 | #include "compressbzip2.h" | ||
29 | #include "randomizer.h" | ||
30 | #include "pwminit.h" | ||
31 | #ifndef PWM_EMBEDDED | ||
32 | //US #include "libgryptif.h" | ||
33 | #else | ||
34 | #endif | ||
35 | |||
36 | #ifdef CONFIG_KWALLETIF | ||
37 | # include "kwalletemu.h" | ||
38 | #endif // CONFIG_KWALLETIF | ||
39 | |||
40 | #include <qdatetime.h> | ||
41 | #include <qsize.h> | ||
42 | #include <qfileinfo.h> | ||
43 | #include <qfile.h> | ||
44 | |||
45 | #include <stdio.h> | ||
46 | #include <stdlib.h> | ||
47 | #include <errno.h> | ||
48 | #include <string.h> | ||
49 | #include <iostream> | ||
50 | #include <algorithm> | ||
51 | #include <sys/types.h> | ||
52 | #include <sys/stat.h> | ||
53 | #include <unistd.h> | ||
54 | #include <stdint.h> | ||
55 | |||
56 | //TODO: reset to its normal value. | ||
57 | #define META_CHECK_TIMER_INTERVAL10/*300*/ /* sek */ | ||
58 | |||
59 | using namespace std; | ||
60 | |||
61 | |||
62 | void PwMDocList::add(PwMDoc *doc, const string &id) | ||
63 | { | ||
64 | #ifdef PWM_DEBUG | ||
65 | // check for existance of object in debug mode only. | ||
66 | vector<listItem>::iterator begin = docList.begin(), | ||
67 | end = docList.end(), | ||
68 | i = begin; | ||
69 | while (i != end) { | ||
70 | if (i->doc == doc) { | ||
71 | BUG(); | ||
72 | return; | ||
73 | } | ||
74 | ++i; | ||
75 | } | ||
76 | #endif | ||
77 | listItem newItem; | ||
78 | newItem.doc = doc; | ||
79 | newItem.docId = id; | ||
80 | docList.push_back(newItem); | ||
81 | } | ||
82 | |||
83 | void PwMDocList::edit(PwMDoc *doc, const string &newId) | ||
84 | { | ||
85 | vector<listItem>::iterator begin = docList.begin(), | ||
86 | end = docList.end(), | ||
87 | i = begin; | ||
88 | while (i != end) { | ||
89 | if (i->doc == doc) { | ||
90 | i->docId = newId; | ||
91 | return; | ||
92 | } | ||
93 | ++i; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | void PwMDocList::del(PwMDoc *doc) | ||
98 | { | ||
99 | vector<listItem>::iterator begin = docList.begin(), | ||
100 | end = docList.end(), | ||
101 | i = begin; | ||
102 | while (i != end) { | ||
103 | if (i->doc == doc) { | ||
104 | docList.erase(i); | ||
105 | return; | ||
106 | } | ||
107 | ++i; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | bool PwMDocList::find(const string &id, listItem *ret) | ||
112 | { | ||
113 | vector<listItem>::iterator begin = docList.begin(), | ||
114 | end = docList.end(), | ||
115 | i = begin; | ||
116 | while (i != end) { | ||
117 | if (i->docId == id) { | ||
118 | if (ret) | ||
119 | *ret = *i; | ||
120 | return true; | ||
121 | } | ||
122 | ++i; | ||
123 | } | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | |||
128 | |||
129 | DocTimer::DocTimer(PwMDoc *_doc) | ||
130 | : doc (_doc) | ||
131 | , mpwLock (0) | ||
132 | , autoLockLock (0) | ||
133 | , metaCheckLock (0) | ||
134 | { | ||
135 | mpwTimer = new QTimer; | ||
136 | autoLockTimer = new QTimer; | ||
137 | metaCheckTimer = new QTimer; | ||
138 | connect(mpwTimer, SIGNAL(timeout()), | ||
139 | this, SLOT(mpwTimeout())); | ||
140 | connect(autoLockTimer, SIGNAL(timeout()), | ||
141 | this, SLOT(autoLockTimeout())); | ||
142 | connect(metaCheckTimer, SIGNAL(timeout()), | ||
143 | this, SLOT(metaCheckTimeout())); | ||
144 | } | ||
145 | |||
146 | DocTimer::~DocTimer() | ||
147 | { | ||
148 | delete mpwTimer; | ||
149 | delete autoLockTimer; | ||
150 | delete metaCheckTimer; | ||
151 | } | ||
152 | |||
153 | void DocTimer::start(TimerIDs timer) | ||
154 | { | ||
155 | switch (timer) { | ||
156 | case id_mpwTimer: | ||
157 | if (mpwTimer->isActive()) | ||
158 | mpwTimer->stop(); | ||
159 | doc->setDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW); | ||
160 | mpwTimer->start(conf()->confGlobPwTimeout() * 1000, true); | ||
161 | break; | ||
162 | case id_autoLockTimer: | ||
163 | if (autoLockTimer->isActive()) | ||
164 | autoLockTimer->stop(); | ||
165 | if (conf()->confGlobLockTimeout() > 0) | ||
166 | autoLockTimer->start(conf()->confGlobLockTimeout() * 1000, true); | ||
167 | break; | ||
168 | case id_metaCheckTimer: | ||
169 | if (metaCheckTimer->isActive()) | ||
170 | metaCheckTimer->stop(); | ||
171 | metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true); | ||
172 | break; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | void DocTimer::stop(TimerIDs timer) | ||
177 | { | ||
178 | switch (timer) { | ||
179 | case id_mpwTimer: | ||
180 | mpwTimer->stop(); | ||
181 | break; | ||
182 | case id_autoLockTimer: | ||
183 | autoLockTimer->stop(); | ||
184 | break; | ||
185 | case id_metaCheckTimer: | ||
186 | metaCheckTimer->stop(); | ||
187 | break; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | void DocTimer::getLock(TimerIDs timer) | ||
192 | { | ||
193 | switch (timer) { | ||
194 | case id_mpwTimer: | ||
195 | ++mpwLock; | ||
196 | break; | ||
197 | case id_autoLockTimer: | ||
198 | ++autoLockLock; | ||
199 | break; | ||
200 | case id_metaCheckTimer: | ||
201 | ++metaCheckLock; | ||
202 | break; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | void DocTimer::putLock(TimerIDs timer) | ||
207 | { | ||
208 | switch (timer) { | ||
209 | case id_mpwTimer: | ||
210 | if (mpwLock) | ||
211 | --mpwLock; | ||
212 | break; | ||
213 | case id_autoLockTimer: | ||
214 | if (autoLockLock) | ||
215 | --autoLockLock; | ||
216 | break; | ||
217 | case id_metaCheckTimer: | ||
218 | if (metaCheckLock) | ||
219 | --metaCheckLock; | ||
220 | break; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | void DocTimer::mpwTimeout() | ||
225 | { | ||
226 | if (mpwLock) { | ||
227 | mpwTimer->start(1000, true); | ||
228 | return; | ||
229 | } | ||
230 | doc->unsetDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW); | ||
231 | } | ||
232 | |||
233 | void DocTimer::autoLockTimeout() | ||
234 | { | ||
235 | if (autoLockLock) { | ||
236 | autoLockTimer->start(1000, true); | ||
237 | return; | ||
238 | } | ||
239 | if (conf()->confGlobAutoDeepLock() && | ||
240 | doc->filename != QString::null && | ||
241 | doc->filename != "") { | ||
242 | doc->deepLock(true); | ||
243 | } else { | ||
244 | doc->lockAll(true); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | void DocTimer::metaCheckTimeout() | ||
249 | { | ||
250 | if (metaCheckLock) { | ||
251 | // check again in one second. | ||
252 | metaCheckTimer->start(1000, true); | ||
253 | return; | ||
254 | } | ||
255 | if (doc->isDeepLocked()) { | ||
256 | metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true); | ||
257 | return; | ||
258 | } | ||
259 | if (doc->isDocEmpty()) { | ||
260 | metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true); | ||
261 | return; | ||
262 | } | ||
263 | #ifdef CONFIG_KWALLETIF | ||
264 | KWalletEmu *kwlEmu = doc->init->kwalletEmu(); | ||
265 | if (kwlEmu) | ||
266 | kwlEmu->suspendDocSignals(); | ||
267 | #endif // CONFIG_KWALLETIF | ||
268 | /* We simply trigger all views to update their | ||
269 | * displayed values. This way they have a chance | ||
270 | * to get notified when some meta changes over time. | ||
271 | * (for example an entry expired). | ||
272 | * The _view_ is responsive for not updating its | ||
273 | * contents if nothing really changed! | ||
274 | */ | ||
275 | emit doc->dataChanged(doc); | ||
276 | #ifdef CONFIG_KWALLETIF | ||
277 | if (kwlEmu) | ||
278 | kwlEmu->resumeDocSignals(); | ||
279 | #endif // CONFIG_KWALLETIF | ||
280 | metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true); | ||
281 | } | ||
282 | |||
283 | |||
284 | |||
285 | PwMDocList PwMDoc::openDocList; | ||
286 | unsigned int PwMDocList::unnamedDocCnt = 1; | ||
287 | |||
288 | PwMDoc::PwMDoc(QObject *parent, const char *name) | ||
289 | : PwMDocUi(parent, name) | ||
290 | , dataChangedLock (0) | ||
291 | { | ||
292 | deleted = false; | ||
293 | unnamedNum = 0; | ||
294 | getOpenDocList()->add(this, getTitle().latin1()); | ||
295 | curDocStat = 0; | ||
296 | setMaxNumEntries(); | ||
297 | _timer = new DocTimer(this); | ||
298 | timer()->start(DocTimer::id_mpwTimer); | ||
299 | timer()->start(DocTimer::id_autoLockTimer); | ||
300 | timer()->start(DocTimer::id_metaCheckTimer); | ||
301 | addCategory(DEFAULT_CATEGORY, 0, false); | ||
302 | listView = 0; | ||
303 | emit docCreated(this); | ||
304 | } | ||
305 | |||
306 | PwMDoc::~PwMDoc() | ||
307 | { | ||
308 | emit docClosed(this); | ||
309 | getOpenDocList()->del(this); | ||
310 | delete _timer; | ||
311 | } | ||
312 | |||
313 | PwMerror PwMDoc::saveDoc(char compress, const QString *file) | ||
314 | { | ||
315 | PwMerror ret, e; | ||
316 | if (!file) { | ||
317 | if (filename == "") | ||
318 | return e_filename; | ||
319 | } else { | ||
320 | if (*file == "" && filename == "") | ||
321 | return e_filename; | ||
322 | if (*file != "") | ||
323 | filename = *file; | ||
324 | } | ||
325 | |||
326 | bool wasDeepLocked = isDeepLocked(); | ||
327 | if (wasDeepLocked) { | ||
328 | if (deepLock(false) != e_success) | ||
329 | return e_noPw; | ||
330 | } | ||
331 | |||
332 | if (!isPwAvailable()) { | ||
333 | /* password is not available. This means, the | ||
334 | * document wasn't saved, yet. | ||
335 | */ | ||
336 | bool useChipcard = getDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
337 | QString pw(requestNewMpw(&useChipcard)); | ||
338 | if (pw != "") { | ||
339 | currentPw = pw; | ||
340 | } else { | ||
341 | return e_noPw; | ||
342 | } | ||
343 | if (useChipcard) { | ||
344 | setDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
345 | } else { | ||
346 | unsetDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
347 | } | ||
348 | } | ||
349 | #ifndef PWM_EMBEDDED | ||
350 | int _cryptAlgo = conf()->confGlobCryptAlgo(); | ||
351 | int _hashAlgo = conf()->confGlobHashAlgo(); | ||
352 | #else | ||
353 | int _cryptAlgo = PWM_CRYPT_BLOWFISH; | ||
354 | int _hashAlgo = PWM_HASH_SHA1; | ||
355 | #endif | ||
356 | |||
357 | // sanity check for the selected algorithms | ||
358 | if (_cryptAlgo < PWM_CRYPT_BLOWFISH || | ||
359 | _cryptAlgo > PWM_CRYPT_TWOFISH128) { | ||
360 | printWarn("Invalid Crypto-Algorithm selected! " | ||
361 | "Config-file seems to be corrupt. " | ||
362 | "Falling back to Blowfish."); | ||
363 | _cryptAlgo = PWM_CRYPT_BLOWFISH; | ||
364 | } | ||
365 | if (_hashAlgo < PWM_HASH_SHA1 || | ||
366 | _hashAlgo > PWM_HASH_TIGER) { | ||
367 | printWarn("Invalid Hash-Algorithm selected! " | ||
368 | "Config-file seems to be corrupt. " | ||
369 | "Falling back to SHA1."); | ||
370 | _hashAlgo = PWM_HASH_SHA1; | ||
371 | } | ||
372 | char cryptAlgo = static_cast<char>(_cryptAlgo); | ||
373 | char hashAlgo = static_cast<char>(_hashAlgo); | ||
374 | |||
375 | if (conf()->confGlobMakeFileBackup()) { | ||
376 | if (!backupFile(filename)) | ||
377 | return e_fileBackup; | ||
378 | } | ||
379 | QString tmpFileMoved(QString::null); | ||
380 | if (QFile::exists(filename)) { | ||
381 | /* Move the existing file to some tmp file. | ||
382 | * When saving file succeeds, delete tmp file. Otherwise | ||
383 | * move tmp file back. See below. | ||
384 | */ | ||
385 | Randomizer *rnd = Randomizer::obj(); | ||
386 | char rnd_buf[5]; | ||
387 | sprintf(rnd_buf, "%X%X%X%X", rnd->genRndChar() & 0xFF, rnd->genRndChar() & 0xFF, | ||
388 | rnd->genRndChar() & 0xFF, rnd->genRndChar() & 0xFF); | ||
389 | tmpFileMoved = filename + "." + rnd_buf + ".mv"; | ||
390 | if (!copyFile(filename, tmpFileMoved)) | ||
391 | return e_openFile; | ||
392 | if (!QFile::remove(filename)) { | ||
393 | printWarn(string("removing orig file ") | ||
394 | + filename.latin1() | ||
395 | + " failed!"); | ||
396 | } | ||
397 | } | ||
398 | QFile f(filename); | ||
399 | string serialized; | ||
400 | if (!f.open(IO_ReadWrite)) { | ||
401 | ret = e_openFile; | ||
402 | goto out_moveback; | ||
403 | } | ||
404 | e = writeFileHeader(hashAlgo, hashAlgo, | ||
405 | cryptAlgo, compress, | ||
406 | ¤tPw, &f); | ||
407 | if (e == e_hashNotImpl) { | ||
408 | printDebug("PwMDoc::saveDoc(): writeFileHeader() failed: e_hashNotImpl"); | ||
409 | f.close(); | ||
410 | ret = e_hashNotImpl; | ||
411 | goto out_moveback; | ||
412 | } else if (e != e_success) { | ||
413 | printDebug("PwMDoc::saveDoc(): writeFileHeader() failed"); | ||
414 | f.close(); | ||
415 | ret = e_writeHeader; | ||
416 | goto out_moveback; | ||
417 | } | ||
418 | if (!serializeDta(&serialized)) { | ||
419 | printDebug("PwMDoc::saveDoc(): serializeDta() failed"); | ||
420 | f.close(); | ||
421 | ret = e_serializeDta; | ||
422 | goto out_moveback; | ||
423 | } | ||
424 | e = writeDataHash(hashAlgo, &serialized, &f); | ||
425 | if (e == e_hashNotImpl) { | ||
426 | printDebug("PwMDoc::saveDoc(): writeDataHash() failed: e_hashNotImpl"); | ||
427 | f.close(); | ||
428 | ret = e_hashNotImpl; | ||
429 | goto out_moveback; | ||
430 | } else if (e != e_success) { | ||
431 | printDebug("PwMDoc::saveDoc(): writeDataHash() failed"); | ||
432 | f.close(); | ||
433 | ret = e_writeHeader; | ||
434 | goto out_moveback; | ||
435 | } | ||
436 | if (!compressDta(&serialized, compress)) { | ||
437 | printDebug("PwMDoc::saveDoc(): compressDta() failed"); | ||
438 | f.close(); | ||
439 | ret = e_enc; | ||
440 | goto out_moveback; | ||
441 | } | ||
442 | e = encrypt(&serialized, ¤tPw, &f, cryptAlgo); | ||
443 | if (e == e_weakPw) { | ||
444 | printDebug("PwMDoc::saveDoc(): encrypt() failed: e_weakPw"); | ||
445 | f.close(); | ||
446 | ret = e_weakPw; | ||
447 | goto out_moveback; | ||
448 | } else if (e == e_cryptNotImpl) { | ||
449 | printDebug("PwMDoc::saveDoc(): encrypt() failed: e_cryptNotImpl"); | ||
450 | f.close(); | ||
451 | ret = e_cryptNotImpl; | ||
452 | goto out_moveback; | ||
453 | } else if (e != e_success) { | ||
454 | printDebug("PwMDoc::saveDoc(): encrypt() failed"); | ||
455 | f.close(); | ||
456 | ret = e_enc; | ||
457 | goto out_moveback; | ||
458 | } | ||
459 | unsetDocStatFlag(DOC_STAT_DISK_DIRTY); | ||
460 | f.close(); | ||
461 | if (chmod(filename.latin1(), | ||
462 | conf()->confGlobFilePermissions())) { | ||
463 | printWarn(string("chmod failed: ") + strerror(errno)); | ||
464 | } | ||
465 | openDocList.edit(this, getTitle().latin1()); | ||
466 | if (wasDeepLocked) | ||
467 | deepLock(true); | ||
468 | if (tmpFileMoved != QString::null) { | ||
469 | // now remove the moved file. | ||
470 | if (!QFile::remove(tmpFileMoved)) { | ||
471 | printWarn(string("removing file ") | ||
472 | + tmpFileMoved.latin1() | ||
473 | + " failed!"); | ||
474 | } | ||
475 | } | ||
476 | ret = e_success; | ||
477 | printDebug(string("writing file { compress: ") | ||
478 | + tostr(static_cast<int>(compress)) + " cryptAlgo: " | ||
479 | + tostr(static_cast<int>(cryptAlgo)) + " hashAlgo: " | ||
480 | + tostr(static_cast<int>(hashAlgo)) | ||
481 | + " }"); | ||
482 | goto out; | ||
483 | out_moveback: | ||
484 | if (tmpFileMoved != QString::null) { | ||
485 | if (copyFile(tmpFileMoved, filename)) { | ||
486 | if (!QFile::remove(tmpFileMoved)) { | ||
487 | printWarn(string("removing tmp file ") | ||
488 | + filename.latin1() | ||
489 | + " failed!"); | ||
490 | } | ||
491 | } else { | ||
492 | printWarn(string("couldn't copy file ") | ||
493 | + tmpFileMoved.latin1() | ||
494 | + " back to " | ||
495 | + filename.latin1()); | ||
496 | } | ||
497 | } | ||
498 | out: | ||
499 | return ret; | ||
500 | } | ||
501 | |||
502 | PwMerror PwMDoc::openDoc(const QString *file, int openLocked) | ||
503 | { | ||
504 | PWM_ASSERT(file); | ||
505 | PWM_ASSERT(openLocked == 0 || openLocked == 1 || openLocked == 2); | ||
506 | string decrypted, dataHash; | ||
507 | PwMerror ret; | ||
508 | char cryptAlgo, dataHashType, compress; | ||
509 | unsigned int headerLen; | ||
510 | |||
511 | if (*file == "") | ||
512 | return e_readFile; | ||
513 | filename = *file; | ||
514 | /* check if this file is already open. | ||
515 | * This does not catch symlinks! | ||
516 | */ | ||
517 | if (!isDeepLocked()) { | ||
518 | if (getOpenDocList()->find(filename.latin1())) | ||
519 | return e_alreadyOpen; | ||
520 | } | ||
521 | QFile f(filename); | ||
522 | |||
523 | if (openLocked == 2) { | ||
524 | // open deep-locked | ||
525 | if (!QFile::exists(filename)) | ||
526 | return e_openFile; | ||
527 | if (deepLock(true, false) != e_success) | ||
528 | return e_openFile; | ||
529 | goto out_success; | ||
530 | } | ||
531 | |||
532 | if (!f.open(IO_ReadOnly)) | ||
533 | return e_openFile; | ||
534 | |||
535 | ret = checkHeader(&cryptAlgo, ¤tPw, &compress, &headerLen, | ||
536 | &dataHashType, &dataHash, &f); | ||
537 | if (ret != e_success) { | ||
538 | printDebug("PwMDoc::openDoc(): checkHeader() failed"); | ||
539 | f.close(); | ||
540 | if (ret == e_wrongPw) { | ||
541 | wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
542 | return ret; | ||
543 | } else if (ret == e_noPw || | ||
544 | ret == e_fileVer || | ||
545 | ret == e_fileFormat || | ||
546 | ret == e_hashNotImpl) { | ||
547 | return ret; | ||
548 | } else | ||
549 | return e_readFile; | ||
550 | } | ||
551 | ret = decrypt(&decrypted, headerLen, ¤tPw, cryptAlgo, &f); | ||
552 | if (ret == e_cryptNotImpl) { | ||
553 | printDebug("PwMDoc::openDoc(): decrypt() failed: e_cryptNotImpl"); | ||
554 | f.close(); | ||
555 | return e_cryptNotImpl; | ||
556 | } else if (ret != e_success) { | ||
557 | printDebug("PwMDoc::openDoc(): decrypt() failed"); | ||
558 | f.close(); | ||
559 | return e_readFile; | ||
560 | } | ||
561 | if (!decompressDta(&decrypted, compress)) { | ||
562 | printDebug("PwMDoc::openDoc(): decompressDta() failed"); | ||
563 | f.close(); | ||
564 | return e_fileCorrupt; | ||
565 | } | ||
566 | ret = checkDataHash(dataHashType, &dataHash, &decrypted); | ||
567 | if (ret == e_hashNotImpl) { | ||
568 | printDebug("PwMDoc::openDoc(): checkDataHash() failed: e_hashNotImpl"); | ||
569 | f.close(); | ||
570 | return e_hashNotImpl; | ||
571 | } else if (ret != e_success) { | ||
572 | printDebug("PwMDoc::openDoc(): checkDataHash() failed"); | ||
573 | f.close(); | ||
574 | return e_fileCorrupt; | ||
575 | } | ||
576 | if (!deSerializeDta(&decrypted, openLocked == 1)) { | ||
577 | printDebug("PwMDoc::openDoc(): deSerializeDta() failed"); | ||
578 | f.close(); | ||
579 | return e_readFile; | ||
580 | } | ||
581 | f.close(); | ||
582 | timer()->start(DocTimer::id_mpwTimer); | ||
583 | timer()->start(DocTimer::id_autoLockTimer); | ||
584 | out_success: | ||
585 | openDocList.edit(this, getTitle().latin1()); | ||
586 | emit docOpened(this); | ||
587 | return e_success; | ||
588 | } | ||
589 | |||
590 | PwMerror PwMDoc::writeFileHeader(char keyHash, char dataHash, char crypt, char compress, | ||
591 | QString *pw, QFile *f) | ||
592 | { | ||
593 | PWM_ASSERT(pw); | ||
594 | PWM_ASSERT(f); | ||
595 | PWM_ASSERT(listView); | ||
596 | #ifndef PWM_EMBEDDED | ||
597 | if (f->writeBlock(FILE_ID_HEADER, strlen(FILE_ID_HEADER)) != | ||
598 | static_cast<Q_LONG>(strlen(FILE_ID_HEADER))) { | ||
599 | return e_writeFile; | ||
600 | } | ||
601 | if (f->putch(PWM_FILE_VER) == -1 || | ||
602 | f->putch(keyHash) == -1 || | ||
603 | f->putch(dataHash) == -1 || | ||
604 | f->putch(crypt) == -1 || | ||
605 | f->putch(compress) == -1 || | ||
606 | f->putch((getDocStatFlag(DOC_STAT_USE_CHIPCARD)) ? | ||
607 | (static_cast<char>(0x01)) : (static_cast<char>(0x00))) == -1) { | ||
608 | return e_writeFile; | ||
609 | } | ||
610 | |||
611 | #else | ||
612 | if (f->writeBlock(FILE_ID_HEADER, strlen(FILE_ID_HEADER)) != | ||
613 | (long)(strlen(FILE_ID_HEADER))) { | ||
614 | return e_writeFile; | ||
615 | } | ||
616 | if (f->putch(PWM_FILE_VER) == -1 || | ||
617 | f->putch(keyHash) == -1 || | ||
618 | f->putch(dataHash) == -1 || | ||
619 | f->putch(crypt) == -1 || | ||
620 | f->putch(compress) == -1 || | ||
621 | f->putch((getDocStatFlag(DOC_STAT_USE_CHIPCARD)) ? | ||
622 | ((char)(0x01)) : ((char)(0x00))) == -1) { | ||
623 | return e_writeFile; | ||
624 | } | ||
625 | #endif | ||
626 | // write bytes of NUL-data. These bytes are reserved for future-use. | ||
627 | const int bufSize = 64; | ||
628 | char tmp_buf[bufSize]; | ||
629 | memset(tmp_buf, 0x00, bufSize); | ||
630 | if (f->writeBlock(tmp_buf, bufSize) != bufSize) | ||
631 | return e_writeFile; | ||
632 | |||
633 | switch (keyHash) { | ||
634 | case PWM_HASH_SHA1: { | ||
635 | const int hashlen = SHA1_HASH_LEN_BYTE; | ||
636 | Sha1 hash; | ||
637 | hash.sha1_write(reinterpret_cast<const byte *>(pw->latin1()), pw->length()); | ||
638 | string ret = hash.sha1_read(); | ||
639 | if (f->writeBlock(ret.c_str(), hashlen) != hashlen) | ||
640 | return e_writeFile; | ||
641 | break; | ||
642 | } | ||
643 | #ifndef PWM_EMBEDDED | ||
644 | case PWM_HASH_SHA256: | ||
645 | /*... fall through */ | ||
646 | case PWM_HASH_SHA384: | ||
647 | case PWM_HASH_SHA512: | ||
648 | case PWM_HASH_MD5: | ||
649 | case PWM_HASH_RMD160: | ||
650 | case PWM_HASH_TIGER: | ||
651 | { | ||
652 | if (!LibGCryptIf::available()) | ||
653 | return e_hashNotImpl; | ||
654 | LibGCryptIf gc; | ||
655 | PwMerror err; | ||
656 | unsigned char *buf; | ||
657 | size_t hashLen; | ||
658 | err = gc.hash(&buf, | ||
659 | &hashLen, | ||
660 | reinterpret_cast<const unsigned char *>(pw->latin1()), | ||
661 | pw->length(), | ||
662 | keyHash); | ||
663 | if (err != e_success) | ||
664 | return e_hashNotImpl; | ||
665 | if (f->writeBlock(reinterpret_cast<const char *>(buf), hashLen) | ||
666 | != static_cast<Q_LONG>(hashLen)) { | ||
667 | delete [] buf; | ||
668 | return e_hashNotImpl; | ||
669 | } | ||
670 | delete [] buf; | ||
671 | break; | ||
672 | } | ||
673 | #endif | ||
674 | default: { | ||
675 | return e_hashNotImpl; | ||
676 | } } | ||
677 | return e_success; | ||
678 | } | ||
679 | |||
680 | PwMerror PwMDoc::checkHeader(char *cryptAlgo, QString *pw, char *compress, | ||
681 | unsigned int *headerLength, char *dataHashType, | ||
682 | string *dataHash, QFile *f) | ||
683 | { | ||
684 | PWM_ASSERT(cryptAlgo); | ||
685 | PWM_ASSERT(pw); | ||
686 | PWM_ASSERT(headerLength); | ||
687 | PWM_ASSERT(dataHashType); | ||
688 | PWM_ASSERT(dataHash); | ||
689 | PWM_ASSERT(f); | ||
690 | int tmpRet; | ||
691 | // check "magic" header | ||
692 | const char magicHdr[] = FILE_ID_HEADER; | ||
693 | const int hdrLen = array_size(magicHdr) - 1; | ||
694 | char tmp[hdrLen]; | ||
695 | if (f->readBlock(tmp, hdrLen) != hdrLen) | ||
696 | return e_readFile; | ||
697 | if (memcmp(tmp, magicHdr, hdrLen) != 0) | ||
698 | return e_fileFormat; | ||
699 | // read and check file ver | ||
700 | int fileV = f->getch(); | ||
701 | if (fileV == -1) | ||
702 | return e_fileFormat; | ||
703 | if (fileV != PWM_FILE_VER) | ||
704 | return e_fileVer; | ||
705 | // read hash hash type | ||
706 | int keyHash = f->getch(); | ||
707 | if (keyHash == -1) | ||
708 | return e_fileFormat; | ||
709 | // read data hash type | ||
710 | tmpRet = f->getch(); | ||
711 | if (tmpRet == -1) | ||
712 | return e_fileFormat; | ||
713 | *dataHashType = tmpRet; | ||
714 | // read crypt algo | ||
715 | tmpRet = f->getch(); | ||
716 | if (tmpRet == -1) | ||
717 | return e_fileFormat; | ||
718 | *cryptAlgo = tmpRet; | ||
719 | // get compression-algo | ||
720 | tmpRet = f->getch(); | ||
721 | if (tmpRet == -1) | ||
722 | return e_fileFormat; | ||
723 | *compress = tmpRet; | ||
724 | // get the MPW-flag | ||
725 | int mpw_flag = f->getch(); | ||
726 | if (mpw_flag == -1) | ||
727 | return e_fileFormat; | ||
728 | if (mpw_flag == 0x01) | ||
729 | setDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
730 | else | ||
731 | unsetDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
732 | // skip the "RESERVED"-bytes | ||
733 | if (!(f->at(f->at() + 64))) | ||
734 | return e_fileFormat; | ||
735 | |||
736 | *pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
737 | if (*pw == "") { | ||
738 | /* the user didn't give a master-password | ||
739 | * or didn't insert a chipcard | ||
740 | */ | ||
741 | return e_noPw; | ||
742 | } | ||
743 | // verify key-hash | ||
744 | switch (keyHash) { | ||
745 | case PWM_HASH_SHA1: { | ||
746 | // read hash from header | ||
747 | const int hashLen = SHA1_HASH_LEN_BYTE; | ||
748 | string readHash; | ||
749 | int i; | ||
750 | for (i = 0; i < hashLen; ++i) | ||
751 | readHash.push_back(f->getch()); | ||
752 | Sha1 hash; | ||
753 | hash.sha1_write(reinterpret_cast<const byte *>(pw->latin1()), pw->length()); | ||
754 | string ret = hash.sha1_read(); | ||
755 | if (ret != readHash) | ||
756 | return e_wrongPw;// hash doesn't match (wrong key) | ||
757 | break; | ||
758 | } | ||
759 | #ifndef PWM_EMBEDDED | ||
760 | case PWM_HASH_SHA256: | ||
761 | /*... fall through */ | ||
762 | case PWM_HASH_SHA384: | ||
763 | case PWM_HASH_SHA512: | ||
764 | case PWM_HASH_MD5: | ||
765 | case PWM_HASH_RMD160: | ||
766 | case PWM_HASH_TIGER: { | ||
767 | if (!LibGCryptIf::available()) | ||
768 | return e_hashNotImpl; | ||
769 | LibGCryptIf gc; | ||
770 | PwMerror err; | ||
771 | unsigned char *buf; | ||
772 | size_t hashLen; | ||
773 | err = gc.hash(&buf, | ||
774 | &hashLen, | ||
775 | reinterpret_cast<const unsigned char *>(pw->latin1()), | ||
776 | pw->length(), | ||
777 | keyHash); | ||
778 | if (err != e_success) | ||
779 | return e_hashNotImpl; | ||
780 | string calcHash(reinterpret_cast<const char *>(buf), | ||
781 | static_cast<string::size_type>(hashLen)); | ||
782 | delete [] buf; | ||
783 | // read hash from header | ||
784 | string readHash; | ||
785 | size_t i; | ||
786 | for (i = 0; i < hashLen; ++i) | ||
787 | readHash.push_back(f->getch()); | ||
788 | if (calcHash != readHash) | ||
789 | return e_wrongPw;// hash doesn't match (wrong key) | ||
790 | break; | ||
791 | } | ||
792 | #endif | ||
793 | default: { | ||
794 | return e_hashNotImpl; | ||
795 | } } | ||
796 | // read the data-hash from the file | ||
797 | unsigned int hashLen, i; | ||
798 | switch (*dataHashType) { | ||
799 | case PWM_HASH_SHA1: | ||
800 | hashLen = SHA1_HASH_LEN_BYTE; | ||
801 | break; | ||
802 | #ifndef PWM_EMBEDDED | ||
803 | case PWM_HASH_SHA256: | ||
804 | /*... fall through */ | ||
805 | case PWM_HASH_SHA384: | ||
806 | case PWM_HASH_SHA512: | ||
807 | case PWM_HASH_MD5: | ||
808 | case PWM_HASH_RMD160: | ||
809 | case PWM_HASH_TIGER: { | ||
810 | if (!LibGCryptIf::available()) | ||
811 | return e_hashNotImpl; | ||
812 | LibGCryptIf gc; | ||
813 | hashLen = gc.hashLength(*dataHashType); | ||
814 | if (hashLen == 0) | ||
815 | return e_hashNotImpl; | ||
816 | break; | ||
817 | } | ||
818 | #endif | ||
819 | default: | ||
820 | return e_hashNotImpl; | ||
821 | } | ||
822 | *dataHash = ""; | ||
823 | for (i = 0; i < hashLen; ++i) { | ||
824 | tmpRet = f->getch(); | ||
825 | if (tmpRet == -1) | ||
826 | return e_fileFormat; | ||
827 | dataHash->push_back(static_cast<char>(tmpRet)); | ||
828 | } | ||
829 | *headerLength = f->at(); | ||
830 | #ifndef PWM_EMBEDDED | ||
831 | printDebug(string("opening file { compress: ") | ||
832 | + tostr(static_cast<int>(*compress)) + " cryptAlgo: " | ||
833 | + tostr(static_cast<int>(*cryptAlgo)) + " keyHashAlgo: " | ||
834 | + tostr(static_cast<int>(keyHash)) | ||
835 | + " }"); | ||
836 | #else | ||
837 | printDebug(string("opening file { compress: ") | ||
838 | + tostr((int)(*compress)) + " cryptAlgo: " | ||
839 | + tostr((int)(*cryptAlgo)) + " keyHashAlgo: " | ||
840 | + tostr((int)(keyHash)) | ||
841 | + " }"); | ||
842 | #endif | ||
843 | |||
844 | return e_success; | ||
845 | } | ||
846 | |||
847 | PwMerror PwMDoc::writeDataHash(char dataHash, string *d, QFile *f) | ||
848 | { | ||
849 | PWM_ASSERT(d); | ||
850 | PWM_ASSERT(f); | ||
851 | |||
852 | switch (dataHash) { | ||
853 | case PWM_HASH_SHA1: { | ||
854 | const int hashLen = SHA1_HASH_LEN_BYTE; | ||
855 | Sha1 h; | ||
856 | h.sha1_write(reinterpret_cast<const byte *>(d->c_str()), d->size()); | ||
857 | string hRet = h.sha1_read(); | ||
858 | if (f->writeBlock(hRet.c_str(), hashLen) != hashLen) | ||
859 | return e_writeFile; | ||
860 | break; | ||
861 | } | ||
862 | #ifndef PWM_EMBEDDED | ||
863 | case PWM_HASH_SHA256: | ||
864 | /*... fall through */ | ||
865 | case PWM_HASH_SHA384: | ||
866 | case PWM_HASH_SHA512: | ||
867 | case PWM_HASH_MD5: | ||
868 | case PWM_HASH_RMD160: | ||
869 | case PWM_HASH_TIGER: { | ||
870 | if (!LibGCryptIf::available()) | ||
871 | return e_hashNotImpl; | ||
872 | LibGCryptIf gc; | ||
873 | PwMerror err; | ||
874 | unsigned char *buf; | ||
875 | size_t hashLen; | ||
876 | err = gc.hash(&buf, | ||
877 | &hashLen, | ||
878 | reinterpret_cast<const unsigned char *>(d->c_str()), | ||
879 | d->size(), | ||
880 | dataHash); | ||
881 | if (err != e_success) | ||
882 | return e_hashNotImpl; | ||
883 | if (f->writeBlock(reinterpret_cast<const char *>(buf), hashLen) | ||
884 | != static_cast<Q_LONG>(hashLen)) { | ||
885 | delete [] buf; | ||
886 | return e_hashNotImpl; | ||
887 | } | ||
888 | delete [] buf; | ||
889 | break; | ||
890 | } | ||
891 | #endif | ||
892 | default: { | ||
893 | return e_hashNotImpl; | ||
894 | } } | ||
895 | |||
896 | return e_success; | ||
897 | } | ||
898 | |||
899 | bool PwMDoc::backupFile(const QString &filePath) | ||
900 | { | ||
901 | QFileInfo fi(filePath); | ||
902 | if (!fi.exists()) | ||
903 | return true; // Yes, true is correct. | ||
904 | QString pathOnly(fi.dirPath(true)); | ||
905 | QString nameOnly(fi.fileName()); | ||
906 | QString backupPath = pathOnly | ||
907 | + "/~" | ||
908 | + nameOnly | ||
909 | + ".backup"; | ||
910 | return copyFile(filePath, backupPath); | ||
911 | } | ||
912 | |||
913 | bool PwMDoc::copyFile(const QString &src, const QString &dst) | ||
914 | { | ||
915 | QFileInfo fi(src); | ||
916 | if (!fi.exists()) | ||
917 | return false; | ||
918 | if (QFile::exists(dst)) { | ||
919 | if (!QFile::remove(dst)) | ||
920 | return false; | ||
921 | } | ||
922 | QFile srcFd(src); | ||
923 | if (!srcFd.open(IO_ReadOnly)) | ||
924 | return false; | ||
925 | QFile dstFd(dst); | ||
926 | if (!dstFd.open(IO_ReadWrite)) { | ||
927 | srcFd.close(); | ||
928 | return false; | ||
929 | } | ||
930 | const int tmpBuf_size = 512; | ||
931 | char tmpBuf[tmpBuf_size]; | ||
932 | #ifndef PWM_EMBEDDED | ||
933 | Q_LONG bytesRead, bytesWritten; | ||
934 | #else | ||
935 | long bytesRead, bytesWritten; | ||
936 | #endif | ||
937 | while (!srcFd.atEnd()) { | ||
938 | #ifndef PWM_EMBEDDED | ||
939 | bytesRead = srcFd.readBlock(tmpBuf, | ||
940 | static_cast<Q_ULONG>(tmpBuf_size)); | ||
941 | #else | ||
942 | bytesRead = srcFd.readBlock(tmpBuf, | ||
943 | (unsigned long)(tmpBuf_size)); | ||
944 | #endif | ||
945 | if (bytesRead == -1) { | ||
946 | srcFd.close(); | ||
947 | dstFd.close(); | ||
948 | return false; | ||
949 | } | ||
950 | #ifndef PWM_EMBEDDED | ||
951 | bytesWritten = dstFd.writeBlock(tmpBuf, | ||
952 | static_cast<Q_ULONG>(bytesRead)); | ||
953 | #else | ||
954 | bytesWritten = dstFd.writeBlock(tmpBuf, | ||
955 | (unsigned long)(bytesRead)); | ||
956 | #endif | ||
957 | if (bytesWritten != bytesRead) { | ||
958 | srcFd.close(); | ||
959 | dstFd.close(); | ||
960 | return false; | ||
961 | } | ||
962 | } | ||
963 | srcFd.close(); | ||
964 | dstFd.close(); | ||
965 | return true; | ||
966 | } | ||
967 | |||
968 | PwMerror PwMDoc::addEntry(const QString &category, PwMDataItem *d, | ||
969 | bool dontFlagDirty, bool updateMeta) | ||
970 | { | ||
971 | PWM_ASSERT(d); | ||
972 | unsigned int cat = 0; | ||
973 | |||
974 | if (isDeepLocked()) { | ||
975 | PwMerror ret; | ||
976 | ret = deepLock(false); | ||
977 | if (ret != e_success) | ||
978 | return e_lock; | ||
979 | } | ||
980 | |||
981 | addCategory(category, &cat); | ||
982 | |||
983 | if (numEntries(category) >= maxEntries) | ||
984 | return e_maxAllowedEntr; | ||
985 | |||
986 | vector<unsigned int> foundPositions; | ||
987 | /* historically this was: | ||
988 | *const int searchIn = SEARCH_IN_DESC | SEARCH_IN_NAME | | ||
989 | * SEARCH_IN_URL | SEARCH_IN_LAUNCHER; | ||
990 | * But for now we only search in desc. | ||
991 | * That's a tweak to be KWallet compatible. But it should not add | ||
992 | * usability-drop onto PwManager, does it? | ||
993 | * (And yes, "int" was a bug. Correct is "unsigned int") | ||
994 | */ | ||
995 | const unsigned int searchIn = SEARCH_IN_DESC; | ||
996 | findEntry(cat, *d, searchIn, &foundPositions, true); | ||
997 | if (foundPositions.size()) { | ||
998 | // DOH! We found this entry. | ||
999 | return e_entryExists; | ||
1000 | } | ||
1001 | |||
1002 | d->listViewPos = -1; | ||
1003 | d->lockStat = conf()->confGlobNewEntrLockStat(); | ||
1004 | if (updateMeta) { | ||
1005 | d->meta.create = QDateTime::currentDateTime(); | ||
1006 | d->meta.update = d->meta.create; | ||
1007 | } | ||
1008 | dta[cat].d.push_back(*d); | ||
1009 | |||
1010 | delAllEmptyCat(true); | ||
1011 | |||
1012 | if (!dontFlagDirty) | ||
1013 | flagDirty(); | ||
1014 | return e_success; | ||
1015 | } | ||
1016 | |||
1017 | PwMerror PwMDoc::addCategory(const QString &category, unsigned int *categoryIndex, | ||
1018 | bool checkIfExist) | ||
1019 | { | ||
1020 | if (isDeepLocked()) { | ||
1021 | PwMerror ret; | ||
1022 | ret = deepLock(false); | ||
1023 | if (ret != e_success) | ||
1024 | return e_lock; | ||
1025 | } | ||
1026 | if (checkIfExist) { | ||
1027 | if (findCategory(category, categoryIndex)) | ||
1028 | return e_categoryExists; | ||
1029 | } | ||
1030 | PwMCategoryItem item; | ||
1031 | item.name = category.latin1(); | ||
1032 | dta.push_back(item); | ||
1033 | if (categoryIndex) | ||
1034 | *categoryIndex = dta.size() - 1; | ||
1035 | return e_success; | ||
1036 | } | ||
1037 | |||
1038 | bool PwMDoc::delEntry(const QString &category, unsigned int index, bool dontFlagDirty) | ||
1039 | { | ||
1040 | unsigned int cat = 0; | ||
1041 | |||
1042 | if (!findCategory(category, &cat)) { | ||
1043 | BUG(); | ||
1044 | return false; | ||
1045 | } | ||
1046 | |||
1047 | return delEntry(cat, index, dontFlagDirty); | ||
1048 | } | ||
1049 | |||
1050 | bool PwMDoc::delEntry(unsigned int category, unsigned int index, bool dontFlagDirty) | ||
1051 | { | ||
1052 | if (isDeepLocked()) | ||
1053 | return false; | ||
1054 | if (index > dta[category].d.size() - 1) | ||
1055 | return false; | ||
1056 | getDataChangedLock(); | ||
1057 | if (!lockAt(category, index, false)) { | ||
1058 | putDataChangedLock(); | ||
1059 | return false; | ||
1060 | } | ||
1061 | putDataChangedLock(); | ||
1062 | int lvPos = dta[category].d[index].listViewPos; | ||
1063 | |||
1064 | // delete entry | ||
1065 | dta[category].d.erase(dta[category].d.begin() + index); | ||
1066 | |||
1067 | unsigned int i, entries = numEntries(category); | ||
1068 | if (!entries) { | ||
1069 | // no more entries in this category, so | ||
1070 | // we can delete it, too. | ||
1071 | BUG_ON(!delCategory(category)); | ||
1072 | // delCategory() flags it dirty, so we need not to do so. | ||
1073 | return true; | ||
1074 | } | ||
1075 | for (i = 0; i < entries; ++i) { | ||
1076 | // decrement all listViewPositions that are greater than the deleted. | ||
1077 | if (dta[category].d[i].listViewPos > lvPos) | ||
1078 | --dta[category].d[i].listViewPos; | ||
1079 | } | ||
1080 | |||
1081 | if (!dontFlagDirty) | ||
1082 | flagDirty(); | ||
1083 | return true; | ||
1084 | } | ||
1085 | |||
1086 | bool PwMDoc::editEntry(const QString &oldCategory, const QString &newCategory, | ||
1087 | unsigned int index, PwMDataItem *d, bool updateMeta) | ||
1088 | { | ||
1089 | PWM_ASSERT(d); | ||
1090 | unsigned int oldCat = 0; | ||
1091 | |||
1092 | if (!findCategory(oldCategory, &oldCat)) { | ||
1093 | BUG(); | ||
1094 | return false; | ||
1095 | } | ||
1096 | |||
1097 | return editEntry(oldCat, newCategory, index, d, updateMeta); | ||
1098 | } | ||
1099 | |||
1100 | bool PwMDoc::editEntry(unsigned int oldCategory, const QString &newCategory, | ||
1101 | unsigned int index, PwMDataItem *d, bool updateMeta) | ||
1102 | { | ||
1103 | if (isDeepLocked()) | ||
1104 | return false; | ||
1105 | if (updateMeta) { | ||
1106 | d->meta.update = QDateTime::currentDateTime(); | ||
1107 | if (d->meta.create.isNull()) { | ||
1108 | d->meta.create = d->meta.update; | ||
1109 | } | ||
1110 | } | ||
1111 | if (dta[oldCategory].name != newCategory.latin1()) { | ||
1112 | // the user changed the category. | ||
1113 | PwMerror ret; | ||
1114 | d->rev = 0; | ||
1115 | ret = addEntry(newCategory, d, true, false); | ||
1116 | if (ret != e_success) | ||
1117 | return false; | ||
1118 | if (!delEntry(oldCategory, index, true)) | ||
1119 | return false; | ||
1120 | } else { | ||
1121 | d->rev = dta[oldCategory].d[index].rev + 1; // increment revision counter. | ||
1122 | dta[oldCategory].d[index] = *d; | ||
1123 | } | ||
1124 | flagDirty(); | ||
1125 | return true; | ||
1126 | } | ||
1127 | |||
1128 | unsigned int PwMDoc::numEntries(const QString &category) | ||
1129 | { | ||
1130 | unsigned int cat = 0; | ||
1131 | |||
1132 | if (!findCategory(category, &cat)) { | ||
1133 | BUG(); | ||
1134 | return 0; | ||
1135 | } | ||
1136 | |||
1137 | return numEntries(cat); | ||
1138 | } | ||
1139 | |||
1140 | bool PwMDoc::serializeDta(string *d) | ||
1141 | { | ||
1142 | PWM_ASSERT(d); | ||
1143 | Serializer ser; | ||
1144 | if (!ser.serialize(dta)) | ||
1145 | return false; | ||
1146 | d->assign(ser.getXml()); | ||
1147 | if (!d->size()) | ||
1148 | return false; | ||
1149 | return true; | ||
1150 | } | ||
1151 | |||
1152 | bool PwMDoc::deSerializeDta(const string *d, bool entriesLocked) | ||
1153 | { | ||
1154 | PWM_ASSERT(d); | ||
1155 | try { | ||
1156 | Serializer ser(d->c_str()); | ||
1157 | ser.setDefaultLockStat(entriesLocked); | ||
1158 | if (!ser.deSerialize(&dta)) | ||
1159 | return false; | ||
1160 | } catch (PwMException) { | ||
1161 | return false; | ||
1162 | } | ||
1163 | emitDataChanged(this); | ||
1164 | return true; | ||
1165 | } | ||
1166 | |||
1167 | bool PwMDoc::getEntry(const QString &category, unsigned int index, | ||
1168 | PwMDataItem * d, bool unlockIfLocked) | ||
1169 | { | ||
1170 | PWM_ASSERT(d); | ||
1171 | unsigned int cat = 0; | ||
1172 | |||
1173 | if (!findCategory(category, &cat)) { | ||
1174 | BUG(); | ||
1175 | return false; | ||
1176 | } | ||
1177 | |||
1178 | return getEntry(cat, index, d, unlockIfLocked); | ||
1179 | } | ||
1180 | |||
1181 | bool PwMDoc::getEntry(unsigned int category, unsigned int index, | ||
1182 | PwMDataItem *d, bool unlockIfLocked) | ||
1183 | { | ||
1184 | if (index > dta[category].d.size() - 1) | ||
1185 | return false; | ||
1186 | |||
1187 | bool locked = isLocked(category, index); | ||
1188 | if (locked) { | ||
1189 | /* this entry is locked. We don't return a password, | ||
1190 | * until it's unlocked by the user by inserting | ||
1191 | * chipcard or entering the mpw | ||
1192 | */ | ||
1193 | if (unlockIfLocked) { | ||
1194 | if (!lockAt(category, index, false)) { | ||
1195 | return false; | ||
1196 | } | ||
1197 | locked = false; | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | *d = dta[category].d[index]; | ||
1202 | if (locked) | ||
1203 | d->pw = LOCKED_STRING.latin1(); | ||
1204 | |||
1205 | return true; | ||
1206 | } | ||
1207 | |||
1208 | PwMerror PwMDoc::getCommentByLvp(const QString &category, int listViewPos, | ||
1209 | string *foundComment) | ||
1210 | { | ||
1211 | PWM_ASSERT(foundComment); | ||
1212 | unsigned int cat = 0; | ||
1213 | |||
1214 | if (!findCategory(category, &cat)) | ||
1215 | return e_invalidArg; | ||
1216 | |||
1217 | unsigned int i, entries = numEntries(cat); | ||
1218 | for (i = 0; i < entries; ++i) { | ||
1219 | if (dta[cat].d[i].listViewPos == listViewPos) { | ||
1220 | *foundComment = dta[cat].d[i].comment; | ||
1221 | if (dta[cat].d[i].binary) | ||
1222 | return e_binEntry; | ||
1223 | return e_normalEntry; | ||
1224 | } | ||
1225 | } | ||
1226 | BUG(); | ||
1227 | return e_generic; | ||
1228 | } | ||
1229 | |||
1230 | bool PwMDoc::compressDta(string *d, char algo) | ||
1231 | { | ||
1232 | PWM_ASSERT(d); | ||
1233 | switch (algo) { | ||
1234 | case PWM_COMPRESS_GZIP: { | ||
1235 | CompressGzip comp; | ||
1236 | return comp.compress(d); | ||
1237 | } case PWM_COMPRESS_BZIP2: { | ||
1238 | CompressBzip2 comp; | ||
1239 | return comp.compress(d); | ||
1240 | } case PWM_COMPRESS_NONE: { | ||
1241 | return true; | ||
1242 | } default: { | ||
1243 | BUG(); | ||
1244 | } | ||
1245 | } | ||
1246 | return false; | ||
1247 | } | ||
1248 | |||
1249 | bool PwMDoc::decompressDta(string *d, char algo) | ||
1250 | { | ||
1251 | PWM_ASSERT(d); | ||
1252 | switch (algo) { | ||
1253 | case PWM_COMPRESS_GZIP: { | ||
1254 | CompressGzip comp; | ||
1255 | return comp.decompress(d); | ||
1256 | } case PWM_COMPRESS_BZIP2: { | ||
1257 | CompressBzip2 comp; | ||
1258 | return comp.decompress(d); | ||
1259 | } case PWM_COMPRESS_NONE: { | ||
1260 | return true; | ||
1261 | } | ||
1262 | } | ||
1263 | return false; | ||
1264 | } | ||
1265 | |||
1266 | PwMerror PwMDoc::encrypt(string *d, const QString *pw, QFile *f, char algo) | ||
1267 | { | ||
1268 | PWM_ASSERT(d); | ||
1269 | PWM_ASSERT(pw); | ||
1270 | PWM_ASSERT(f); | ||
1271 | |||
1272 | size_t encSize; | ||
1273 | byte *encrypted = 0; | ||
1274 | |||
1275 | switch (algo) { | ||
1276 | case PWM_CRYPT_BLOWFISH: { | ||
1277 | Blowfish::padNull(d); | ||
1278 | encSize = d->length(); | ||
1279 | encrypted = new byte[encSize]; | ||
1280 | Blowfish bf; | ||
1281 | if (bf.bf_setkey((byte *) pw->latin1(), pw->length())) { | ||
1282 | delete [] encrypted; | ||
1283 | return e_weakPw; | ||
1284 | } | ||
1285 | bf.bf_encrypt((byte *) encrypted, (byte *) d->c_str(), encSize); | ||
1286 | break; | ||
1287 | } | ||
1288 | #ifndef PWM_EMBEDDED | ||
1289 | case PWM_CRYPT_AES128: | ||
1290 | /*... fall through */ | ||
1291 | case PWM_CRYPT_AES192: | ||
1292 | case PWM_CRYPT_AES256: | ||
1293 | case PWM_CRYPT_3DES: | ||
1294 | case PWM_CRYPT_TWOFISH: | ||
1295 | case PWM_CRYPT_TWOFISH128: { | ||
1296 | if (!LibGCryptIf::available()) | ||
1297 | return e_cryptNotImpl; | ||
1298 | LibGCryptIf gc; | ||
1299 | PwMerror err; | ||
1300 | unsigned char *plain = new unsigned char[d->length() + 1024]; | ||
1301 | memcpy(plain, d->c_str(), d->length()); | ||
1302 | err = gc.encrypt(&encrypted, | ||
1303 | &encSize, | ||
1304 | plain, | ||
1305 | d->length(), | ||
1306 | reinterpret_cast<const unsigned char *>(pw->latin1()), | ||
1307 | pw->length(), | ||
1308 | algo); | ||
1309 | delete [] plain; | ||
1310 | if (err != e_success) | ||
1311 | return e_cryptNotImpl; | ||
1312 | break; | ||
1313 | } | ||
1314 | #endif | ||
1315 | default: { | ||
1316 | delete_ifnot_null_array(encrypted); | ||
1317 | return e_cryptNotImpl; | ||
1318 | } } | ||
1319 | |||
1320 | // write encrypted data to file | ||
1321 | #ifndef PWM_EMBEDDED | ||
1322 | if (f->writeBlock(reinterpret_cast<const char *>(encrypted), | ||
1323 | static_cast<Q_ULONG>(encSize)) | ||
1324 | != static_cast<Q_LONG>(encSize)) { | ||
1325 | delete_ifnot_null_array(encrypted); | ||
1326 | return e_writeFile; | ||
1327 | } | ||
1328 | #else | ||
1329 | if (f->writeBlock((const char *)(encrypted), | ||
1330 | (unsigned long)(encSize)) | ||
1331 | != (long)(encSize)) { | ||
1332 | delete_ifnot_null_array(encrypted); | ||
1333 | return e_writeFile; | ||
1334 | } | ||
1335 | #endif | ||
1336 | delete_ifnot_null_array(encrypted); | ||
1337 | return e_success; | ||
1338 | } | ||
1339 | |||
1340 | PwMerror PwMDoc::decrypt(string *d, unsigned int pos, const QString *pw, | ||
1341 | char algo, QFile *f) | ||
1342 | { | ||
1343 | PWM_ASSERT(d); | ||
1344 | PWM_ASSERT(pw); | ||
1345 | PWM_ASSERT(f); | ||
1346 | |||
1347 | unsigned int cryptLen = f->size() - pos; | ||
1348 | byte *encrypted = new byte[cryptLen]; | ||
1349 | byte *decrypted = new byte[cryptLen]; | ||
1350 | |||
1351 | f->at(pos); | ||
1352 | #ifndef PWM_EMBEDDED | ||
1353 | if (f->readBlock(reinterpret_cast<char *>(encrypted), | ||
1354 | static_cast<Q_ULONG>(cryptLen)) | ||
1355 | != static_cast<Q_LONG>(cryptLen)) { | ||
1356 | delete [] encrypted; | ||
1357 | delete [] decrypted; | ||
1358 | return e_readFile; | ||
1359 | } | ||
1360 | #else | ||
1361 | if (f->readBlock((char *)(encrypted), | ||
1362 | (unsigned long)(cryptLen)) | ||
1363 | != (long)(cryptLen)) { | ||
1364 | delete [] encrypted; | ||
1365 | delete [] decrypted; | ||
1366 | return e_readFile; | ||
1367 | } | ||
1368 | #endif | ||
1369 | switch (algo) { | ||
1370 | case PWM_CRYPT_BLOWFISH: { | ||
1371 | Blowfish bf; | ||
1372 | bf.bf_setkey((byte *) pw->latin1(), pw->length()); | ||
1373 | bf.bf_decrypt(decrypted, encrypted, cryptLen); | ||
1374 | break; | ||
1375 | } | ||
1376 | #ifndef PWM_EMBEDDED | ||
1377 | case PWM_CRYPT_AES128: | ||
1378 | /*... fall through */ | ||
1379 | case PWM_CRYPT_AES192: | ||
1380 | case PWM_CRYPT_AES256: | ||
1381 | case PWM_CRYPT_3DES: | ||
1382 | case PWM_CRYPT_TWOFISH: | ||
1383 | case PWM_CRYPT_TWOFISH128: { | ||
1384 | if (!LibGCryptIf::available()) | ||
1385 | return e_cryptNotImpl; | ||
1386 | LibGCryptIf gc; | ||
1387 | PwMerror err; | ||
1388 | err = gc.decrypt(&decrypted, | ||
1389 | &cryptLen, | ||
1390 | encrypted, | ||
1391 | cryptLen, | ||
1392 | reinterpret_cast<const unsigned char *>(pw->latin1()), | ||
1393 | pw->length(), | ||
1394 | algo); | ||
1395 | if (err != e_success) { | ||
1396 | delete [] encrypted; | ||
1397 | delete [] decrypted; | ||
1398 | return e_cryptNotImpl; | ||
1399 | } | ||
1400 | break; | ||
1401 | } | ||
1402 | #endif | ||
1403 | default: { | ||
1404 | delete [] encrypted; | ||
1405 | delete [] decrypted; | ||
1406 | return e_cryptNotImpl; | ||
1407 | } } | ||
1408 | delete [] encrypted; | ||
1409 | #ifndef PWM_EMBEDDED | ||
1410 | d->assign(reinterpret_cast<const char *>(decrypted), | ||
1411 | static_cast<string::size_type>(cryptLen)); | ||
1412 | #else | ||
1413 | d->assign((const char *)(decrypted), | ||
1414 | (string::size_type)(cryptLen)); | ||
1415 | #endif | ||
1416 | delete [] decrypted; | ||
1417 | if (algo == PWM_CRYPT_BLOWFISH) { | ||
1418 | if (!Blowfish::unpadNull(d)) { | ||
1419 | BUG(); | ||
1420 | return e_readFile; | ||
1421 | } | ||
1422 | } | ||
1423 | return e_success; | ||
1424 | } | ||
1425 | |||
1426 | PwMerror PwMDoc::checkDataHash(char dataHashType, const string *dataHash, | ||
1427 | const string *dataStream) | ||
1428 | { | ||
1429 | PWM_ASSERT(dataHash); | ||
1430 | PWM_ASSERT(dataStream); | ||
1431 | switch(dataHashType) { | ||
1432 | case PWM_HASH_SHA1: { | ||
1433 | Sha1 hash; | ||
1434 | hash.sha1_write((byte*)dataStream->c_str(), dataStream->length()); | ||
1435 | string ret = hash.sha1_read(); | ||
1436 | if (ret != *dataHash) | ||
1437 | return e_fileCorrupt; | ||
1438 | break; | ||
1439 | } | ||
1440 | #ifndef PWM_EMBEDDED | ||
1441 | case PWM_HASH_SHA256: | ||
1442 | /*... fall through */ | ||
1443 | case PWM_HASH_SHA384: | ||
1444 | case PWM_HASH_SHA512: | ||
1445 | case PWM_HASH_MD5: | ||
1446 | case PWM_HASH_RMD160: | ||
1447 | case PWM_HASH_TIGER: { | ||
1448 | if (!LibGCryptIf::available()) | ||
1449 | return e_hashNotImpl; | ||
1450 | LibGCryptIf gc; | ||
1451 | PwMerror err; | ||
1452 | unsigned char *buf; | ||
1453 | size_t hashLen; | ||
1454 | err = gc.hash(&buf, | ||
1455 | &hashLen, | ||
1456 | reinterpret_cast<const unsigned char *>(dataStream->c_str()), | ||
1457 | dataStream->length(), | ||
1458 | dataHashType); | ||
1459 | if (err != e_success) | ||
1460 | return e_hashNotImpl; | ||
1461 | string calcHash(reinterpret_cast<const char *>(buf), | ||
1462 | static_cast<string::size_type>(hashLen)); | ||
1463 | delete [] buf; | ||
1464 | if (calcHash != *dataHash) | ||
1465 | return e_fileCorrupt; | ||
1466 | break; | ||
1467 | } | ||
1468 | #endif | ||
1469 | default: | ||
1470 | return e_hashNotImpl; | ||
1471 | } | ||
1472 | return e_success; | ||
1473 | } | ||
1474 | |||
1475 | bool PwMDoc::lockAt(unsigned int category, unsigned int index, | ||
1476 | bool lock) | ||
1477 | { | ||
1478 | if (index >= numEntries(category)) { | ||
1479 | BUG(); | ||
1480 | return false; | ||
1481 | } | ||
1482 | if (lock == dta[category].d[index].lockStat) | ||
1483 | return true; | ||
1484 | |||
1485 | if (!lock && currentPw != "") { | ||
1486 | // "unlocking" and "password is already set" | ||
1487 | if (!getDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW)) { | ||
1488 | // unlocking without pw not allowed | ||
1489 | QString pw; | ||
1490 | pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1491 | if (pw != "") { | ||
1492 | if (pw != currentPw) { | ||
1493 | wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1494 | return false; | ||
1495 | } else { | ||
1496 | timer()->start(DocTimer::id_mpwTimer); | ||
1497 | } | ||
1498 | } else { | ||
1499 | return false; | ||
1500 | } | ||
1501 | } else { | ||
1502 | timer()->start(DocTimer::id_mpwTimer); | ||
1503 | } | ||
1504 | } | ||
1505 | |||
1506 | dta[category].d[index].lockStat = lock; | ||
1507 | dta[category].d[index].rev++; // increment revision counter. | ||
1508 | |||
1509 | emitDataChanged(this); | ||
1510 | if (!lock) | ||
1511 | timer()->start(DocTimer::id_autoLockTimer); | ||
1512 | |||
1513 | return true; | ||
1514 | |||
1515 | } | ||
1516 | |||
1517 | bool PwMDoc::lockAt(const QString &category,unsigned int index, | ||
1518 | bool lock) | ||
1519 | { | ||
1520 | unsigned int cat = 0; | ||
1521 | |||
1522 | if (!findCategory(category, &cat)) { | ||
1523 | BUG(); | ||
1524 | return false; | ||
1525 | } | ||
1526 | |||
1527 | return lockAt(cat, index, lock); | ||
1528 | } | ||
1529 | |||
1530 | bool PwMDoc::lockAll(bool lock) | ||
1531 | { | ||
1532 | if (!lock && isDeepLocked()) { | ||
1533 | PwMerror ret; | ||
1534 | ret = deepLock(false); | ||
1535 | if (ret != e_success) | ||
1536 | return false; | ||
1537 | return true; | ||
1538 | } | ||
1539 | if (isDocEmpty()) { | ||
1540 | return true; | ||
1541 | } | ||
1542 | if (!lock && currentPw != "") { | ||
1543 | // unlocking and password is already set | ||
1544 | if (!getDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW)) { | ||
1545 | // unlocking without pw not allowed | ||
1546 | QString pw; | ||
1547 | pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1548 | if (pw != "") { | ||
1549 | if (pw != currentPw) { | ||
1550 | wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1551 | return false; | ||
1552 | } else { | ||
1553 | timer()->start(DocTimer::id_mpwTimer); | ||
1554 | } | ||
1555 | } else { | ||
1556 | return false; | ||
1557 | } | ||
1558 | } else { | ||
1559 | timer()->start(DocTimer::id_mpwTimer); | ||
1560 | } | ||
1561 | } | ||
1562 | |||
1563 | vector<PwMCategoryItem>::iterator catBegin = dta.begin(), | ||
1564 | catEnd = dta.end(), | ||
1565 | catI = catBegin; | ||
1566 | vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI; | ||
1567 | while (catI != catEnd) { | ||
1568 | entrBegin = catI->d.begin(); | ||
1569 | entrEnd = catI->d.end(); | ||
1570 | entrI = entrBegin; | ||
1571 | while (entrI != entrEnd) { | ||
1572 | entrI->lockStat = lock; | ||
1573 | entrI->rev++; // increment revision counter. | ||
1574 | ++entrI; | ||
1575 | } | ||
1576 | ++catI; | ||
1577 | } | ||
1578 | |||
1579 | emitDataChanged(this); | ||
1580 | if (lock) | ||
1581 | timer()->stop(DocTimer::id_autoLockTimer); | ||
1582 | else | ||
1583 | timer()->start(DocTimer::id_autoLockTimer); | ||
1584 | |||
1585 | return true; | ||
1586 | } | ||
1587 | |||
1588 | bool PwMDoc::isLocked(const QString &category, unsigned int index) | ||
1589 | { | ||
1590 | unsigned int cat = 0; | ||
1591 | |||
1592 | if (!findCategory(category, &cat)) { | ||
1593 | BUG(); | ||
1594 | return false; | ||
1595 | } | ||
1596 | |||
1597 | return isLocked(cat, index); | ||
1598 | } | ||
1599 | |||
1600 | bool PwMDoc::unlockAll_tempoary(bool revert) | ||
1601 | { | ||
1602 | static vector< vector<bool> > *oldLockStates = 0; | ||
1603 | static bool wasDeepLocked; | ||
1604 | |||
1605 | if (revert) {// revert the unlocking | ||
1606 | if (oldLockStates) { | ||
1607 | /* we actually _have_ unlocked something, because | ||
1608 | * we have allocated space for the oldLockStates. | ||
1609 | * So, go on and revert them! | ||
1610 | */ | ||
1611 | if (wasDeepLocked) { | ||
1612 | PwMerror ret = deepLock(true); | ||
1613 | if (ret == e_success) { | ||
1614 | /* deep-lock succeed. We are save. | ||
1615 | * (but if it failed, just go on | ||
1616 | * lock them normally) | ||
1617 | */ | ||
1618 | delete_and_null(oldLockStates); | ||
1619 | timer()->start(DocTimer::id_autoLockTimer); | ||
1620 | printDebug("tempoary unlocking of dta " | ||
1621 | "reverted by deep-locking."); | ||
1622 | return true; | ||
1623 | } | ||
1624 | printDebug("deep-lock failed while reverting! " | ||
1625 | "Falling back to normal-lock."); | ||
1626 | } | ||
1627 | if (unlikely(!wasDeepLocked && | ||
1628 | numCategories() != oldLockStates->size())) { | ||
1629 | /* DOH! We have modified "dta" while | ||
1630 | * it was unlocked tempoary. DON'T DO THIS! | ||
1631 | */ | ||
1632 | BUG(); | ||
1633 | delete_and_null(oldLockStates); | ||
1634 | timer()->start(DocTimer::id_autoLockTimer); | ||
1635 | return false; | ||
1636 | } | ||
1637 | vector<PwMCategoryItem>::iterator catBegin = dta.begin(), | ||
1638 | catEnd = dta.end(), | ||
1639 | catI = catBegin; | ||
1640 | vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI; | ||
1641 | vector< vector<bool> >::iterator oldCatStatI = oldLockStates->begin(); | ||
1642 | vector<bool>::iterator oldEntrStatBegin, | ||
1643 | oldEntrStatEnd, | ||
1644 | oldEntrStatI; | ||
1645 | while (catI != catEnd) { | ||
1646 | entrBegin = catI->d.begin(); | ||
1647 | entrEnd = catI->d.end(); | ||
1648 | entrI = entrBegin; | ||
1649 | if (likely(!wasDeepLocked)) { | ||
1650 | oldEntrStatBegin = oldCatStatI->begin(); | ||
1651 | oldEntrStatEnd = oldCatStatI->end(); | ||
1652 | oldEntrStatI = oldEntrStatBegin; | ||
1653 | if (unlikely(catI->d.size() != oldCatStatI->size())) { | ||
1654 | /* DOH! We have modified "dta" while | ||
1655 | * it was unlocked tempoary. DON'T DO THIS! | ||
1656 | */ | ||
1657 | BUG(); | ||
1658 | delete_and_null(oldLockStates); | ||
1659 | timer()->start(DocTimer::id_autoLockTimer); | ||
1660 | return false; | ||
1661 | } | ||
1662 | } | ||
1663 | while (entrI != entrEnd) { | ||
1664 | if (wasDeepLocked) { | ||
1665 | /* this is an error-fallback if | ||
1666 | * deeplock didn't succeed | ||
1667 | */ | ||
1668 | entrI->lockStat = true; | ||
1669 | } else { | ||
1670 | entrI->lockStat = *oldEntrStatI; | ||
1671 | } | ||
1672 | ++entrI; | ||
1673 | if (likely(!wasDeepLocked)) | ||
1674 | ++oldEntrStatI; | ||
1675 | } | ||
1676 | ++catI; | ||
1677 | if (likely(!wasDeepLocked)) | ||
1678 | ++oldCatStatI; | ||
1679 | } | ||
1680 | delete_and_null(oldLockStates); | ||
1681 | if (unlikely(wasDeepLocked)) { | ||
1682 | /* error fallback... */ | ||
1683 | unsetDocStatFlag(DOC_STAT_DEEPLOCKED); | ||
1684 | emitDataChanged(this); | ||
1685 | printDebug("WARNING: unlockAll_tempoary(true) " | ||
1686 | "deeplock fallback!"); | ||
1687 | } | ||
1688 | printDebug("tempoary unlocking of dta reverted."); | ||
1689 | } else { | ||
1690 | printDebug("unlockAll_tempoary(true): nothing to do."); | ||
1691 | } | ||
1692 | timer()->start(DocTimer::id_autoLockTimer); | ||
1693 | } else {// unlock all data tempoary | ||
1694 | if (unlikely(oldLockStates != 0)) { | ||
1695 | /* DOH! We have already unlocked the data tempoarly. | ||
1696 | * No need to do it twice. ;) | ||
1697 | */ | ||
1698 | BUG(); | ||
1699 | return false; | ||
1700 | } | ||
1701 | wasDeepLocked = false; | ||
1702 | bool mustUnlock = false; | ||
1703 | if (isDeepLocked()) { | ||
1704 | PwMerror ret; | ||
1705 | while (1) { | ||
1706 | ret = deepLock(false); | ||
1707 | if (ret == e_success) { | ||
1708 | break; | ||
1709 | } else if (ret == e_wrongPw) { | ||
1710 | wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1711 | } else { | ||
1712 | printDebug("deep-unlocking failed while " | ||
1713 | "tempoary unlocking!"); | ||
1714 | return false; | ||
1715 | } | ||
1716 | } | ||
1717 | wasDeepLocked = true; | ||
1718 | mustUnlock = true; | ||
1719 | } else { | ||
1720 | // first check if it's needed to unlock some entries | ||
1721 | vector<PwMCategoryItem>::iterator catBegin = dta.begin(), | ||
1722 | catEnd = dta.end(), | ||
1723 | catI = catBegin; | ||
1724 | vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI; | ||
1725 | while (catI != catEnd) { | ||
1726 | entrBegin = catI->d.begin(); | ||
1727 | entrEnd = catI->d.end(); | ||
1728 | entrI = entrBegin; | ||
1729 | while (entrI != entrEnd) { | ||
1730 | if (entrI->lockStat == true) { | ||
1731 | mustUnlock = true; | ||
1732 | break; | ||
1733 | } | ||
1734 | ++entrI; | ||
1735 | } | ||
1736 | if (mustUnlock) | ||
1737 | break; | ||
1738 | ++catI; | ||
1739 | } | ||
1740 | } | ||
1741 | if (!mustUnlock) { | ||
1742 | // nothing to do. | ||
1743 | timer()->stop(DocTimer::id_autoLockTimer); | ||
1744 | printDebug("unlockAll_tempoary(): nothing to do."); | ||
1745 | return true; | ||
1746 | } else if (!wasDeepLocked) { | ||
1747 | if (!getDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW) && | ||
1748 | currentPw != "") { | ||
1749 | /* we can't unlock without mpw, so | ||
1750 | * we need to ask for it. | ||
1751 | */ | ||
1752 | QString pw; | ||
1753 | while (1) { | ||
1754 | pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1755 | if (pw == "") { | ||
1756 | return false; | ||
1757 | } else if (pw == currentPw) { | ||
1758 | break; | ||
1759 | } | ||
1760 | wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD)); | ||
1761 | } | ||
1762 | } | ||
1763 | } | ||
1764 | timer()->stop(DocTimer::id_autoLockTimer); | ||
1765 | oldLockStates = new vector< vector<bool> >; | ||
1766 | vector<bool> tmp_vec; | ||
1767 | vector<PwMCategoryItem>::iterator catBegin = dta.begin(), | ||
1768 | catEnd = dta.end(), | ||
1769 | catI = catBegin; | ||
1770 | vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI; | ||
1771 | while (catI != catEnd) { | ||
1772 | entrBegin = catI->d.begin(); | ||
1773 | entrEnd = catI->d.end(); | ||
1774 | entrI = entrBegin; | ||
1775 | while (entrI != entrEnd) { | ||
1776 | if (!wasDeepLocked) { | ||
1777 | tmp_vec.push_back(entrI->lockStat); | ||
1778 | } | ||
1779 | entrI->lockStat = false; | ||
1780 | ++entrI; | ||
1781 | } | ||
1782 | if (!wasDeepLocked) { | ||
1783 | oldLockStates->push_back(tmp_vec); | ||
1784 | tmp_vec.clear(); | ||
1785 | } | ||
1786 | ++catI; | ||
1787 | } | ||
1788 | printDebug("tempoary unlocked dta."); | ||
1789 | } | ||
1790 | |||
1791 | return true; | ||
1792 | } | ||
1793 | |||
1794 | PwMerror PwMDoc::deepLock(bool lock, bool saveToFile) | ||
1795 | { | ||
1796 | PwMerror ret; | ||
1797 | |||
1798 | if (lock) { | ||
1799 | if (isDeepLocked()) | ||
1800 | return e_lock; | ||
1801 | if (saveToFile) { | ||
1802 | if (isDocEmpty()) | ||
1803 | return e_docIsEmpty; | ||
1804 | ret = saveDoc(conf()->confGlobCompression()); | ||
1805 | if (ret == e_filename) { | ||
1806 | /* the doc wasn't saved to a file | ||
1807 | * by the user, yet. | ||
1808 | */ | ||
1809 | cantDeeplock_notSavedMsgBox(); | ||
1810 | return e_docNotSaved; | ||
1811 | } else if (ret != e_success) { | ||
1812 | return e_lock; | ||
1813 | } | ||
1814 | } | ||
1815 | timer()->stop(DocTimer::id_autoLockTimer); | ||
1816 | clearDoc(); | ||
1817 | PwMDataItem d; | ||
1818 | d.desc = IS_DEEPLOCKED_SHORTMSG.latin1(); | ||
1819 | d.comment = IS_DEEPLOCKED_MSG.latin1(); | ||
1820 | d.listViewPos = 0; | ||
1821 | addEntry(DEFAULT_CATEGORY, &d, true); | ||
1822 | lockAt(DEFAULT_CATEGORY, 0, true); | ||
1823 | unsetDocStatFlag(DOC_STAT_DISK_DIRTY); | ||
1824 | setDocStatFlag(DOC_STAT_DEEPLOCKED); | ||
1825 | } else { | ||
1826 | if (!isDeepLocked()) | ||
1827 | return e_lock; | ||
1828 | ret = openDoc(&filename, (conf()->confGlobUnlockOnOpen()) | ||
1829 | ? 0 : 1); | ||
1830 | if (ret == e_wrongPw) { | ||
1831 | return e_wrongPw; | ||
1832 | } else if (ret != e_success) { | ||
1833 | printDebug(string("PwMDoc::deepLock(false): ERR! openDoc() == ") | ||
1834 | + tostr(static_cast<int>(ret))); | ||
1835 | return e_lock; | ||
1836 | } | ||
1837 | unsetDocStatFlag(DOC_STAT_DEEPLOCKED); | ||
1838 | timer()->start(DocTimer::id_autoLockTimer); | ||
1839 | } | ||
1840 | |||
1841 | emitDataChanged(this); | ||
1842 | return e_success; | ||
1843 | } | ||
1844 | |||
1845 | void PwMDoc::_deepUnlock() | ||
1846 | { | ||
1847 | deepLock(false); | ||
1848 | } | ||
1849 | |||
1850 | void PwMDoc::clearDoc() | ||
1851 | { | ||
1852 | dta.clear(); | ||
1853 | PwMCategoryItem d; | ||
1854 | d.name = DEFAULT_CATEGORY.latin1(); | ||
1855 | dta.push_back(d); | ||
1856 | currentPw = ""; | ||
1857 | unsetDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW); | ||
1858 | } | ||
1859 | |||
1860 | void PwMDoc::changeCurrentPw() | ||
1861 | { | ||
1862 | if (currentPw == "") | ||
1863 | return; // doc hasn't been saved. No mpw available. | ||
1864 | bool useChipcard = getDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
1865 | QString pw = requestMpwChange(¤tPw, &useChipcard); | ||
1866 | if (pw == "") | ||
1867 | return; | ||
1868 | if (useChipcard) | ||
1869 | setDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
1870 | else | ||
1871 | unsetDocStatFlag(DOC_STAT_USE_CHIPCARD); | ||
1872 | setCurrentPw(pw); | ||
1873 | } | ||
1874 | |||
1875 | void PwMDoc::setListViewPos(const QString &category, unsigned int index, | ||
1876 | int pos) | ||
1877 | { | ||
1878 | unsigned int cat = 0; | ||
1879 | |||
1880 | if (!findCategory(category, &cat)) { | ||
1881 | BUG(); | ||
1882 | return; | ||
1883 | } | ||
1884 | setListViewPos(cat, index, pos); | ||
1885 | } | ||
1886 | |||
1887 | void PwMDoc::setListViewPos(unsigned int category, unsigned int index, | ||
1888 | int pos) | ||
1889 | { | ||
1890 | dta[category].d[index].listViewPos = pos; | ||
1891 | |||
1892 | /* FIXME workaround: don't flag dirty, because this function sometimes | ||
1893 | * get's called when it shouldn't. It's because PwMView assumes | ||
1894 | * the user resorted the UI on behalf of signal layoutChanged(). | ||
1895 | * This is somewhat broken and incorrect, but I've no other | ||
1896 | * solution for now. | ||
1897 | */ | ||
1898 | //setDocStatFlag(DOC_STAT_DISK_DIRTY); | ||
1899 | } | ||
1900 | |||
1901 | int PwMDoc::getListViewPos(const QString &category, unsigned int index) | ||
1902 | { | ||
1903 | unsigned int cat = 0; | ||
1904 | |||
1905 | if (!findCategory(category, &cat)) { | ||
1906 | BUG(); | ||
1907 | return -1; | ||
1908 | } | ||
1909 | |||
1910 | return dta[cat].d[index].listViewPos; | ||
1911 | } | ||
1912 | |||
1913 | void PwMDoc::findEntry(unsigned int category, PwMDataItem find, unsigned int searchIn, | ||
1914 | vector<unsigned int> *foundPositions, bool breakAfterFound, | ||
1915 | bool caseSensitive, bool exactWordMatch, bool sortByLvp) | ||
1916 | { | ||
1917 | PWM_ASSERT(foundPositions); | ||
1918 | PWM_ASSERT(searchIn); | ||
1919 | foundPositions->clear(); | ||
1920 | |||
1921 | unsigned int i, entries = numEntries(category); | ||
1922 | for (i = 0; i < entries; ++i) { | ||
1923 | if (searchIn & SEARCH_IN_DESC) { | ||
1924 | if (!compareString(find.desc, dta[category].d[i].desc, | ||
1925 | caseSensitive, exactWordMatch)) { | ||
1926 | continue; | ||
1927 | } | ||
1928 | } | ||
1929 | if (searchIn & SEARCH_IN_NAME) { | ||
1930 | if (!compareString(find.name, dta[category].d[i].name, | ||
1931 | caseSensitive, exactWordMatch)) { | ||
1932 | continue; | ||
1933 | } | ||
1934 | } | ||
1935 | if (searchIn & SEARCH_IN_PW) { | ||
1936 | bool wasLocked = isLocked(category, i); | ||
1937 | getDataChangedLock(); | ||
1938 | lockAt(category, i, false); | ||
1939 | if (!compareString(find.pw, dta[category].d[i].pw, | ||
1940 | caseSensitive, exactWordMatch)) { | ||
1941 | lockAt(category, i, wasLocked); | ||
1942 | putDataChangedLock(); | ||
1943 | continue; | ||
1944 | } | ||
1945 | lockAt(category, i, wasLocked); | ||
1946 | putDataChangedLock(); | ||
1947 | } | ||
1948 | if (searchIn & SEARCH_IN_COMMENT) { | ||
1949 | if (!compareString(find.comment, dta[category].d[i].comment, | ||
1950 | caseSensitive, exactWordMatch)) { | ||
1951 | continue; | ||
1952 | } | ||
1953 | } | ||
1954 | if (searchIn & SEARCH_IN_URL) { | ||
1955 | if (!compareString(find.url, dta[category].d[i].url, | ||
1956 | caseSensitive, exactWordMatch)) { | ||
1957 | continue; | ||
1958 | } | ||
1959 | } | ||
1960 | if (searchIn & SEARCH_IN_LAUNCHER) { | ||
1961 | if (!compareString(find.launcher, dta[category].d[i].launcher, | ||
1962 | caseSensitive, exactWordMatch)) { | ||
1963 | continue; | ||
1964 | } | ||
1965 | } | ||
1966 | |||
1967 | // all selected "searchIn" matched. | ||
1968 | foundPositions->push_back(i); | ||
1969 | if (breakAfterFound) | ||
1970 | break; | ||
1971 | } | ||
1972 | |||
1973 | if (sortByLvp && foundPositions->size() > 1) { | ||
1974 | vector< pair<unsigned int /* foundPosition (real doc pos) */, | ||
1975 | unsigned int /* lvp-pos */> > tmp_vec; | ||
1976 | |||
1977 | unsigned int i, items = foundPositions->size(); | ||
1978 | pair<unsigned int, unsigned int> tmp_pair; | ||
1979 | for (i = 0; i < items; ++i) { | ||
1980 | tmp_pair.first = (*foundPositions)[i]; | ||
1981 | tmp_pair.second = dta[category].d[(*foundPositions)[i]].listViewPos; | ||
1982 | tmp_vec.push_back(tmp_pair); | ||
1983 | } | ||
1984 | sort(tmp_vec.begin(), tmp_vec.end(), dta_lvp_greater()); | ||
1985 | foundPositions->clear(); | ||
1986 | for (i = 0; i < items; ++i) { | ||
1987 | foundPositions->push_back(tmp_vec[i].first); | ||
1988 | } | ||
1989 | } | ||
1990 | } | ||
1991 | |||
1992 | void PwMDoc::findEntry(const QString &category, PwMDataItem find, unsigned int searchIn, | ||
1993 | vector<unsigned int> *foundPositions, bool breakAfterFound, | ||
1994 | bool caseSensitive, bool exactWordMatch, bool sortByLvp) | ||
1995 | { | ||
1996 | PWM_ASSERT(foundPositions); | ||
1997 | unsigned int cat = 0; | ||
1998 | |||
1999 | if (!findCategory(category, &cat)) { | ||
2000 | foundPositions->clear(); | ||
2001 | return; | ||
2002 | } | ||
2003 | |||
2004 | findEntry(cat, find, searchIn, foundPositions, breakAfterFound, | ||
2005 | caseSensitive, exactWordMatch, sortByLvp); | ||
2006 | } | ||
2007 | |||
2008 | bool PwMDoc::compareString(const string &s1, const string &s2, bool caseSensitive, | ||
2009 | bool exactWordMatch) | ||
2010 | { | ||
2011 | QString _s1(s1.c_str()); | ||
2012 | QString _s2(s2.c_str()); | ||
2013 | if (!caseSensitive) { | ||
2014 | _s1 = _s1.lower(); | ||
2015 | _s2 = _s2.lower(); | ||
2016 | } | ||
2017 | if (exactWordMatch ? (_s1 == _s2) : (_s2.find(_s1) != -1)) | ||
2018 | return true; | ||
2019 | return false; | ||
2020 | } | ||
2021 | |||
2022 | bool PwMDoc::findCategory(const QString &name, unsigned int *index) | ||
2023 | { | ||
2024 | vector<PwMCategoryItem>::iterator i = dta.begin(), | ||
2025 | end = dta.end(); | ||
2026 | while (i != end) { | ||
2027 | if ((*i).name == name.latin1()) { | ||
2028 | if (index) { | ||
2029 | *index = i - dta.begin(); | ||
2030 | } | ||
2031 | return true; | ||
2032 | } | ||
2033 | ++i; | ||
2034 | } | ||
2035 | return false; | ||
2036 | } | ||
2037 | |||
2038 | bool PwMDoc::renameCategory(const QString &category, const QString &newName) | ||
2039 | { | ||
2040 | unsigned int cat = 0; | ||
2041 | |||
2042 | if (!findCategory(category, &cat)) | ||
2043 | return false; | ||
2044 | |||
2045 | return renameCategory(cat, newName); | ||
2046 | } | ||
2047 | |||
2048 | bool PwMDoc::renameCategory(unsigned int category, const QString &newName, | ||
2049 | bool dontFlagDirty) | ||
2050 | { | ||
2051 | if (category > numCategories() - 1) | ||
2052 | return false; | ||
2053 | |||
2054 | dta[category].name = newName.latin1(); | ||
2055 | if (!dontFlagDirty) | ||
2056 | flagDirty(); | ||
2057 | |||
2058 | return true; | ||
2059 | } | ||
2060 | |||
2061 | bool PwMDoc::delCategory(const QString &category) | ||
2062 | { | ||
2063 | unsigned int cat = 0; | ||
2064 | |||
2065 | if (!findCategory(category, &cat)) | ||
2066 | return false; | ||
2067 | |||
2068 | return delCategory(cat); | ||
2069 | } | ||
2070 | |||
2071 | bool PwMDoc::delCategory(unsigned int category, bool dontFlagDirty) | ||
2072 | { | ||
2073 | if (category > numCategories() - 1) | ||
2074 | return false; | ||
2075 | |||
2076 | // We don't delete it, if it is the last existing | ||
2077 | // category! Instead we rename it to "Default". | ||
2078 | if (numCategories() > 1) { | ||
2079 | dta.erase(dta.begin() + category); | ||
2080 | } else { | ||
2081 | renameCategory(category, DEFAULT_CATEGORY, dontFlagDirty); | ||
2082 | return true; | ||
2083 | } | ||
2084 | if (!dontFlagDirty) | ||
2085 | flagDirty(); | ||
2086 | |||
2087 | return true; | ||
2088 | } | ||
2089 | |||
2090 | void PwMDoc::delAllEmptyCat(bool dontFlagDirty) | ||
2091 | { | ||
2092 | vector<PwMCategoryItem>::iterator begin = dta.begin(), | ||
2093 | end = dta.end(), | ||
2094 | i = begin; | ||
2095 | while (i != end) { | ||
2096 | if (i->d.empty()) { | ||
2097 | delCategory(begin - i, dontFlagDirty); | ||
2098 | } | ||
2099 | ++i; | ||
2100 | } | ||
2101 | } | ||
2102 | |||
2103 | void PwMDoc::getCategoryList(vector<string> *list) | ||
2104 | { | ||
2105 | PWM_ASSERT(list); | ||
2106 | list->clear(); | ||
2107 | vector<PwMCategoryItem>::iterator i = dta.begin(), | ||
2108 | end = dta.end(); | ||
2109 | while (i != end) { | ||
2110 | list->push_back(i->name); | ||
2111 | ++i; | ||
2112 | } | ||
2113 | } | ||
2114 | |||
2115 | void PwMDoc::getCategoryList(QStringList *list) | ||
2116 | { | ||
2117 | PWM_ASSERT(list); | ||
2118 | list->clear(); | ||
2119 | vector<PwMCategoryItem>::iterator i = dta.begin(), | ||
2120 | end = dta.end(); | ||
2121 | while (i != end) { | ||
2122 | #ifndef PWM_EMBEDDED | ||
2123 | list->push_back(i->name.c_str()); | ||
2124 | #else | ||
2125 | list->append(i->name.c_str()); | ||
2126 | #endif | ||
2127 | ++i; | ||
2128 | } | ||
2129 | } | ||
2130 | |||
2131 | void PwMDoc::getEntryList(const QString &category, QStringList *list) | ||
2132 | { | ||
2133 | PWM_ASSERT(list); | ||
2134 | unsigned int cat = 0; | ||
2135 | if (!findCategory(category, &cat)) { | ||
2136 | list->clear(); | ||
2137 | return; | ||
2138 | } | ||
2139 | getEntryList(cat, list); | ||
2140 | } | ||
2141 | |||
2142 | void PwMDoc::getEntryList(const QString &category, vector<string> *list) | ||
2143 | { | ||
2144 | PWM_ASSERT(list); | ||
2145 | unsigned int cat = 0; | ||
2146 | if (!findCategory(category, &cat)) { | ||
2147 | list->clear(); | ||
2148 | return; | ||
2149 | } | ||
2150 | getEntryList(cat, list); | ||
2151 | } | ||
2152 | |||
2153 | void PwMDoc::getEntryList(unsigned int category, vector<string> *list) | ||
2154 | { | ||
2155 | PWM_ASSERT(list); | ||
2156 | list->clear(); | ||
2157 | vector<PwMDataItem>::iterator begin = dta[category].d.begin(), | ||
2158 | end = dta[category].d.end(), | ||
2159 | i = begin; | ||
2160 | while (i != end) { | ||
2161 | list->push_back(i->desc); | ||
2162 | ++i; | ||
2163 | } | ||
2164 | } | ||
2165 | |||
2166 | void PwMDoc::getEntryList(unsigned int category, QStringList *list) | ||
2167 | { | ||
2168 | PWM_ASSERT(list); | ||
2169 | list->clear(); | ||
2170 | vector<PwMDataItem>::iterator begin = dta[category].d.begin(), | ||
2171 | end = dta[category].d.end(), | ||
2172 | i = begin; | ||
2173 | while (i != end) { | ||
2174 | #ifndef PWM_EMBEDDED | ||
2175 | list->push_back(i->desc.c_str()); | ||
2176 | #else | ||
2177 | list->append(i->desc.c_str()); | ||
2178 | #endif | ||
2179 | ++i; | ||
2180 | } | ||
2181 | } | ||
2182 | |||
2183 | bool PwMDoc::execLauncher(const QString &category, unsigned int entryIndex) | ||
2184 | { | ||
2185 | unsigned int cat = 0; | ||
2186 | |||
2187 | if (!findCategory(category, &cat)) | ||
2188 | return false; | ||
2189 | |||
2190 | return execLauncher(cat, entryIndex); | ||
2191 | } | ||
2192 | |||
2193 | bool PwMDoc::execLauncher(unsigned int category, unsigned int entryIndex) | ||
2194 | { | ||
2195 | if (geteuid() == 0) { | ||
2196 | rootAlertMsgBox(); | ||
2197 | return false; | ||
2198 | } | ||
2199 | QString command(dta[category].d[entryIndex].launcher.c_str()); | ||
2200 | bool wasLocked = isLocked(category, entryIndex); | ||
2201 | |||
2202 | if (command.find("$p") != -1) { | ||
2203 | /* the user requested the password to be included | ||
2204 | * into the command. We have to ask for the password, | ||
2205 | * if it's locked. We do that by unlocking the entry | ||
2206 | */ | ||
2207 | if (!lockAt(category, entryIndex, false)) | ||
2208 | return false; | ||
2209 | } | ||
2210 | #ifndef PWM_EMBEDDED | ||
2211 | command.replace("$d", dta[category].d[entryIndex].desc.c_str()); | ||
2212 | command.replace("$n", dta[category].d[entryIndex].name.c_str()); | ||
2213 | command.replace("$p", dta[category].d[entryIndex].pw.c_str()); | ||
2214 | command.replace("$u", dta[category].d[entryIndex].url.c_str()); | ||
2215 | command.replace("$c", dta[category].d[entryIndex].comment.c_str()); | ||
2216 | #else | ||
2217 | command.replace(QRegExp("$d"), dta[category].d[entryIndex].desc.c_str()); | ||
2218 | command.replace(QRegExp("$n"), dta[category].d[entryIndex].name.c_str()); | ||
2219 | command.replace(QRegExp("$p"), dta[category].d[entryIndex].pw.c_str()); | ||
2220 | command.replace(QRegExp("$u"), dta[category].d[entryIndex].url.c_str()); | ||
2221 | command.replace(QRegExp("$c"), dta[category].d[entryIndex].comment.c_str()); | ||
2222 | #endif | ||
2223 | command.append(" &"); | ||
2224 | |||
2225 | QString customXterm(conf()->confGlobXtermCommand()); | ||
2226 | if (!customXterm.isEmpty()) | ||
2227 | command = customXterm + " " + command; | ||
2228 | |||
2229 | system(command.latin1()); | ||
2230 | |||
2231 | lockAt(category, entryIndex, wasLocked); | ||
2232 | return true; | ||
2233 | } | ||
2234 | |||
2235 | bool PwMDoc::goToURL(const QString &category, unsigned int entryIndex) | ||
2236 | { | ||
2237 | unsigned int cat = 0; | ||
2238 | |||
2239 | if (!findCategory(category, &cat)) | ||
2240 | return false; | ||
2241 | |||
2242 | return goToURL(cat, entryIndex); | ||
2243 | } | ||
2244 | |||
2245 | bool PwMDoc::goToURL(unsigned int category, unsigned int entryIndex) | ||
2246 | { | ||
2247 | if (geteuid() == 0) { | ||
2248 | rootAlertMsgBox(); | ||
2249 | return false; | ||
2250 | } | ||
2251 | QString url(dta[category].d[entryIndex].url.c_str()); | ||
2252 | if (url.isEmpty()) | ||
2253 | return false; | ||
2254 | |||
2255 | QString customBrowser(conf()->confGlobBrowserCommand()); | ||
2256 | if (!customBrowser.isEmpty()) { | ||
2257 | browserProc.clearArguments(); | ||
2258 | browserProc << customBrowser << url; | ||
2259 | if (browserProc.start(KProcess::DontCare)) | ||
2260 | return true; | ||
2261 | } | ||
2262 | |||
2263 | browserProc.clearArguments(); | ||
2264 | browserProc << "konqueror" << url; | ||
2265 | if (browserProc.start(KProcess::DontCare)) | ||
2266 | return true; | ||
2267 | |||
2268 | browserProc.clearArguments(); | ||
2269 | browserProc << "mozilla" << url; | ||
2270 | if (browserProc.start(KProcess::DontCare)) | ||
2271 | return true; | ||
2272 | |||
2273 | browserProc.clearArguments(); | ||
2274 | browserProc << "opera" << url; | ||
2275 | if (browserProc.start(KProcess::DontCare)) | ||
2276 | return true; | ||
2277 | return false; | ||
2278 | } | ||
2279 | |||
2280 | PwMerror PwMDoc::exportToText(const QString *file) | ||
2281 | { | ||
2282 | PWM_ASSERT(file); | ||
2283 | if (QFile::exists(*file)) { | ||
2284 | if (!QFile::remove(*file)) | ||
2285 | return e_accessFile; | ||
2286 | } | ||
2287 | QFile f(*file); | ||
2288 | if (!f.open(IO_ReadWrite)) | ||
2289 | return e_openFile; | ||
2290 | |||
2291 | if (!unlockAll_tempoary()) { | ||
2292 | f.close(); | ||
2293 | return e_lock; | ||
2294 | } | ||
2295 | |||
2296 | // write header | ||
2297 | string header = i18n("Password table generated by\nPwM v").latin1(); | ||
2298 | header += PACKAGE_VER; | ||
2299 | header += i18n("\non ").latin1(); | ||
2300 | QDate currDate = QDate::currentDate(); | ||
2301 | QTime currTime = QTime::currentTime(); | ||
2302 | |||
2303 | #ifndef PWM_EMBEDDED | ||
2304 | header += currDate.toString("ddd MMMM d ").latin1(); | ||
2305 | header += currTime.toString("hh:mm:ss ").latin1(); | ||
2306 | #else | ||
2307 | QString dfs = KGlobal::locale()->dateFormatShort(); | ||
2308 | bool ampm = KGlobal::locale()->use12Clock(); | ||
2309 | KGlobal::locale()->setDateFormatShort("%A %B %d"); | ||
2310 | KGlobal::locale()->setHore24Format(true); | ||
2311 | |||
2312 | header += KGlobal::locale()->formatDate(currDate, true, KLocale::Userdefined); | ||
2313 | header += KGlobal::locale()->formatTime(currTime, true); | ||
2314 | KGlobal::locale()->setDateFormatShort(dfs); | ||
2315 | KGlobal::locale()->setHore24Format(!ampm); | ||
2316 | |||
2317 | #endif | ||
2318 | header += tostr(currDate.year()); | ||
2319 | header += "\n==============================\n\n"; | ||
2320 | |||
2321 | |||
2322 | #ifndef PWM_EMBEDDED | ||
2323 | if (f.writeBlock(header.c_str(), header.length()) != (Q_LONG)header.length()) { | ||
2324 | unlockAll_tempoary(true); | ||
2325 | f.close(); | ||
2326 | return e_writeFile; | ||
2327 | } | ||
2328 | #else | ||
2329 | if (f.writeBlock(header.c_str(), header.length()) != (long)header.length()) { | ||
2330 | unlockAll_tempoary(true); | ||
2331 | f.close(); | ||
2332 | return e_writeFile; | ||
2333 | } | ||
2334 | #endif | ||
2335 | unsigned int i, numCat = numCategories(); | ||
2336 | unsigned int j, numEnt; | ||
2337 | string exp; | ||
2338 | for (i = 0; i < numCat; ++i) { | ||
2339 | numEnt = numEntries(i); | ||
2340 | |||
2341 | exp = "\n== Category: "; | ||
2342 | exp += dta[i].name; | ||
2343 | exp += " ==\n"; | ||
2344 | #ifndef PWM_EMBEDDED | ||
2345 | if (f.writeBlock(exp.c_str(), exp.length()) != (Q_LONG)exp.length()) { | ||
2346 | unlockAll_tempoary(true); | ||
2347 | f.close(); | ||
2348 | return e_writeFile; | ||
2349 | } | ||
2350 | #else | ||
2351 | if (f.writeBlock(exp.c_str(), exp.length()) != (long)exp.length()) { | ||
2352 | unlockAll_tempoary(true); | ||
2353 | f.close(); | ||
2354 | return e_writeFile; | ||
2355 | } | ||
2356 | #endif | ||
2357 | for (j = 0; j < numEnt; ++j) { | ||
2358 | exp = "\n-- "; | ||
2359 | exp += dta[i].d[j].desc; | ||
2360 | exp += " --\n"; | ||
2361 | |||
2362 | exp += i18n("Username: ").latin1(); | ||
2363 | exp += dta[i].d[j].name; | ||
2364 | exp += "\n"; | ||
2365 | |||
2366 | exp += i18n("Password: ").latin1(); | ||
2367 | exp += dta[i].d[j].pw; | ||
2368 | exp += "\n"; | ||
2369 | |||
2370 | exp += i18n("Comment: ").latin1(); | ||
2371 | exp += dta[i].d[j].comment; | ||
2372 | exp += "\n"; | ||
2373 | |||
2374 | exp += i18n("URL: ").latin1(); | ||
2375 | exp += dta[i].d[j].url; | ||
2376 | exp += "\n"; | ||
2377 | |||
2378 | exp += i18n("Launcher: ").latin1(); | ||
2379 | exp += dta[i].d[j].launcher; | ||
2380 | exp += "\n"; | ||
2381 | |||
2382 | #ifndef PWM_EMBEDDED | ||
2383 | if (f.writeBlock(exp.c_str(), exp.length()) != (Q_LONG)exp.length()) { | ||
2384 | unlockAll_tempoary(true); | ||
2385 | f.close(); | ||
2386 | return e_writeFile; | ||
2387 | } | ||
2388 | #else | ||
2389 | if (f.writeBlock(exp.c_str(), exp.length()) != (long)exp.length()) { | ||
2390 | unlockAll_tempoary(true); | ||
2391 | f.close(); | ||
2392 | return e_writeFile; | ||
2393 | } | ||
2394 | #endif | ||
2395 | } | ||
2396 | } | ||
2397 | unlockAll_tempoary(true); | ||
2398 | f.close(); | ||
2399 | |||
2400 | return e_success; | ||
2401 | } | ||
2402 | |||
2403 | PwMerror PwMDoc::importFromText(const QString *file, int format) | ||
2404 | { | ||
2405 | PWM_ASSERT(file); | ||
2406 | if (format == 0) | ||
2407 | return importText_PwM(file); | ||
2408 | else if (format == -1) { | ||
2409 | // probe for all formats | ||
2410 | if (importText_PwM(file) == e_success) | ||
2411 | return e_success; | ||
2412 | dta.clear(); | ||
2413 | emitDataChanged(this); | ||
2414 | // add next format here... | ||
2415 | return e_fileFormat; | ||
2416 | } | ||
2417 | return e_invalidArg; | ||
2418 | } | ||
2419 | |||
2420 | PwMerror PwMDoc::importText_PwM(const QString *file) | ||
2421 | { | ||
2422 | PWM_ASSERT(file); | ||
2423 | FILE *f; | ||
2424 | int tmp; | ||
2425 | ssize_t ret; | ||
2426 | string curCat; | ||
2427 | unsigned int entriesRead = 0; | ||
2428 | PwMDataItem currItem; | ||
2429 | f = fopen(file->latin1(), "r"); | ||
2430 | if (!f) | ||
2431 | return e_openFile; | ||
2432 | size_t ch_tmp_size = 1024; | ||
2433 | char *ch_tmp = (char*)malloc(ch_tmp_size); | ||
2434 | if (!ch_tmp) { | ||
2435 | fclose(f); | ||
2436 | return e_outOfMem; | ||
2437 | } | ||
2438 | |||
2439 | // - check header | ||
2440 | if (getline(&ch_tmp, &ch_tmp_size, f) == -1) // skip first line. | ||
2441 | goto formatError; | ||
2442 | // check version-string and return version in "ch_tmp". | ||
2443 | if (fscanf(f, "PwM v%s", ch_tmp) != 1) { | ||
2444 | // header not recognized as PwM generated header | ||
2445 | goto formatError; | ||
2446 | } | ||
2447 | // set filepointer behind version-string-line previously checked | ||
2448 | if (getline(&ch_tmp, &ch_tmp_size, f) == -1) | ||
2449 | goto formatError; | ||
2450 | // skip next line containing the build-date | ||
2451 | if (getline(&ch_tmp, &ch_tmp_size, f) == -1) | ||
2452 | goto formatError; | ||
2453 | // read header termination line | ||
2454 | if (getline(&ch_tmp, &ch_tmp_size, f) == -1) | ||
2455 | goto formatError; | ||
2456 | if (strcmp(ch_tmp, "==============================\n")) | ||
2457 | goto formatError; | ||
2458 | |||
2459 | // - read entries | ||
2460 | do { | ||
2461 | // find beginning of next category | ||
2462 | do { | ||
2463 | tmp = fgetc(f); | ||
2464 | } while (tmp == '\n' && tmp != EOF); | ||
2465 | if (tmp == EOF) | ||
2466 | break; | ||
2467 | |||
2468 | // decrement filepos by one | ||
2469 | fseek(f, -1, SEEK_CUR); | ||
2470 | // read cat-name | ||
2471 | if (getline(&ch_tmp, &ch_tmp_size, f) == -1) | ||
2472 | goto formatError; | ||
2473 | // check cat-name format | ||
2474 | if (memcmp(ch_tmp, "== Category: ", 13) != 0) | ||
2475 | goto formatError; | ||
2476 | if (memcmp(ch_tmp + (strlen(ch_tmp) - 1 - 3), " ==", 3) != 0) | ||
2477 | goto formatError; | ||
2478 | // copy cat-name | ||
2479 | curCat.assign(ch_tmp + 13, strlen(ch_tmp) - 1 - 16); | ||
2480 | |||
2481 | do { | ||
2482 | // find beginning of next entry | ||
2483 | do { | ||
2484 | tmp = fgetc(f); | ||
2485 | } while (tmp == '\n' && tmp != EOF && tmp != '='); | ||
2486 | if (tmp == EOF) | ||
2487 | break; | ||
2488 | if (tmp == '=') { | ||
2489 | fseek(f, -1, SEEK_CUR); | ||
2490 | break; | ||
2491 | } | ||
2492 | // decrement filepos by one | ||
2493 | fseek(f, -1, SEEK_CUR); | ||
2494 | // read desc-line | ||
2495 | if (getline(&ch_tmp, &ch_tmp_size, f) == -1) | ||
2496 | goto formatError; | ||
2497 | // check desc-line format | ||
2498 | if (memcmp(ch_tmp, "-- ", 3) != 0) | ||
2499 | goto formatError; | ||
2500 | if (memcmp(ch_tmp + (strlen(ch_tmp) - 1 - 3), " --", 3) != 0) | ||
2501 | goto formatError; | ||
2502 | // add desc-line | ||
2503 | currItem.desc.assign(ch_tmp + 3, strlen(ch_tmp) - 1 - 6); | ||
2504 | |||
2505 | // read username-line | ||
2506 | if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1) | ||
2507 | goto formatError; | ||
2508 | if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.name)) | ||
2509 | goto formatError; | ||
2510 | |||
2511 | // read pw-line | ||
2512 | if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1) | ||
2513 | goto formatError; | ||
2514 | if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.pw)) | ||
2515 | goto formatError; | ||
2516 | |||
2517 | // read comment-line | ||
2518 | if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1) | ||
2519 | goto formatError; | ||
2520 | if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.comment)) | ||
2521 | goto formatError; | ||
2522 | |||
2523 | // read URL-line | ||
2524 | if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1) | ||
2525 | goto formatError; | ||
2526 | if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.url)) | ||
2527 | goto formatError; | ||
2528 | |||
2529 | // read launcher-line | ||
2530 | if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1) | ||
2531 | goto formatError; | ||
2532 | if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.launcher)) | ||
2533 | goto formatError; | ||
2534 | |||
2535 | currItem.lockStat = true; | ||
2536 | currItem.listViewPos = -1; | ||
2537 | addEntry(curCat.c_str(), &currItem, true); | ||
2538 | ++entriesRead; | ||
2539 | } while (1); | ||
2540 | } while (1); | ||
2541 | if (!entriesRead) | ||
2542 | goto formatError; | ||
2543 | |||
2544 | free(ch_tmp); | ||
2545 | fclose(f); | ||
2546 | flagDirty(); | ||
2547 | return e_success; | ||
2548 | |||
2549 | formatError: | ||
2550 | free(ch_tmp); | ||
2551 | fclose(f); | ||
2552 | return e_fileFormat; | ||
2553 | } | ||
2554 | |||
2555 | bool PwMDoc::textExtractEntry_PwM(const char *in, ssize_t in_size, string *out) | ||
2556 | { | ||
2557 | PWM_ASSERT(in && out); | ||
2558 | ssize_t i = 0, len = in_size - 1; | ||
2559 | while (i < len) { | ||
2560 | if (in[i] == ':') | ||
2561 | break; | ||
2562 | ++i; | ||
2563 | } | ||
2564 | i += 2; | ||
2565 | *out = ""; | ||
2566 | out->append(in + i, in_size - i - 1); | ||
2567 | return true; | ||
2568 | } | ||
2569 | |||
2570 | PwMerror PwMDoc::exportToGpasman(const QString *file) | ||
2571 | { | ||
2572 | PWM_ASSERT(file); | ||
2573 | GpasmanFile gp; | ||
2574 | int ret; | ||
2575 | |||
2576 | if (!unlockAll_tempoary()) | ||
2577 | return e_lock; | ||
2578 | |||
2579 | QString gpmPassword; | ||
2580 | while (1) { | ||
2581 | gpmPassword = requestNewMpw(0); | ||
2582 | if (gpmPassword == "") { | ||
2583 | unlockAll_tempoary(true); | ||
2584 | return e_noPw; | ||
2585 | } | ||
2586 | if (gpmPassword.length() < 4) { | ||
2587 | gpmPwLenErrMsgBox(); | ||
2588 | } else { | ||
2589 | break; | ||
2590 | } | ||
2591 | } | ||
2592 | |||
2593 | ret = gp.save_init(file->latin1(), gpmPassword.latin1()); | ||
2594 | if (ret != 1) { | ||
2595 | unlockAll_tempoary(true); | ||
2596 | return e_accessFile; | ||
2597 | } | ||
2598 | |||
2599 | char *entry[4]; | ||
2600 | unsigned int numCat = numCategories(), i; | ||
2601 | unsigned int numEntr, j; | ||
2602 | int descLen, nameLen, pwLen, commentLen; | ||
2603 | for (i = 0; i < numCat; ++i) { | ||
2604 | numEntr = numEntries(i); | ||
2605 | for (j = 0; j < numEntr; ++j) { | ||
2606 | descLen = dta[i].d[j].desc.length(); | ||
2607 | nameLen = dta[i].d[j].name.length(); | ||
2608 | pwLen = dta[i].d[j].pw.length(); | ||
2609 | commentLen = dta[i].d[j].comment.length(); | ||
2610 | entry[0] = new char[descLen + 1]; | ||
2611 | entry[1] = new char[nameLen + 1]; | ||
2612 | entry[2] = new char[pwLen + 1]; | ||
2613 | entry[3] = new char[commentLen + 1]; | ||
2614 | strcpy(entry[0], descLen == 0 ? " " : dta[i].d[j].desc.c_str()); | ||
2615 | strcpy(entry[1], nameLen == 0 ? " " : dta[i].d[j].name.c_str()); | ||
2616 | strcpy(entry[2], pwLen == 0 ? " " : dta[i].d[j].pw.c_str()); | ||
2617 | strcpy(entry[3], commentLen == 0 ? " " : dta[i].d[j].comment.c_str()); | ||
2618 | entry[0][descLen == 0 ? descLen + 1 : descLen] = '\0'; | ||
2619 | entry[1][nameLen == 0 ? nameLen + 1 : nameLen] = '\0'; | ||
2620 | entry[2][pwLen == 0 ? pwLen + 1 : pwLen] = '\0'; | ||
2621 | entry[3][commentLen == 0 ? commentLen + 1 : commentLen] = '\0'; | ||
2622 | |||
2623 | ret = gp.save_entry(entry); | ||
2624 | if (ret == -1){ | ||
2625 | delete [] entry[0]; | ||
2626 | delete [] entry[1]; | ||
2627 | delete [] entry[2]; | ||
2628 | delete [] entry[3]; | ||
2629 | gp.save_finalize(); | ||
2630 | unlockAll_tempoary(true); | ||
2631 | return e_writeFile; | ||
2632 | } | ||
2633 | |||
2634 | delete [] entry[0]; | ||
2635 | delete [] entry[1]; | ||
2636 | delete [] entry[2]; | ||
2637 | delete [] entry[3]; | ||
2638 | } | ||
2639 | } | ||
2640 | unlockAll_tempoary(true); | ||
2641 | if (gp.save_finalize() == -1) | ||
2642 | return e_writeFile; | ||
2643 | |||
2644 | return e_success; | ||
2645 | } | ||
2646 | |||
2647 | PwMerror PwMDoc::importFromGpasman(const QString *file) | ||
2648 | { | ||
2649 | PWM_ASSERT(file); | ||
2650 | QString pw = requestMpw(false); | ||
2651 | if (pw == "") | ||
2652 | return e_noPw; | ||
2653 | GpasmanFile gp; | ||
2654 | int ret, i; | ||
2655 | PwMerror ret2; | ||
2656 | char *entry[4]; | ||
2657 | PwMDataItem tmpData; | ||
2658 | ret = gp.load_init(file->latin1(), pw.latin1()); | ||
2659 | if (ret != 1) | ||
2660 | return e_accessFile; | ||
2661 | |||
2662 | do { | ||
2663 | ret = gp.load_entry(entry); | ||
2664 | if(ret != 1) | ||
2665 | break; | ||
2666 | tmpData.desc = entry[0]; | ||
2667 | tmpData.name = entry[1]; | ||
2668 | tmpData.pw = entry[2]; | ||
2669 | tmpData.comment = entry[3]; | ||
2670 | tmpData.lockStat = true; | ||
2671 | tmpData.listViewPos = -1; | ||
2672 | ret2 = addEntry(DEFAULT_CATEGORY, &tmpData, true); | ||
2673 | for (i = 0; i < 4; ++i) | ||
2674 | free(entry[i]); | ||
2675 | if (ret2 == e_maxAllowedEntr) { | ||
2676 | gp.load_finalize(); | ||
2677 | return e_maxAllowedEntr; | ||
2678 | } | ||
2679 | } while (1); | ||
2680 | gp.load_finalize(); | ||
2681 | if (isDocEmpty()) | ||
2682 | return e_wrongPw; // we assume this. | ||
2683 | |||
2684 | flagDirty(); | ||
2685 | return e_success; | ||
2686 | } | ||
2687 | |||
2688 | void PwMDoc::ensureLvp() | ||
2689 | { | ||
2690 | if (isDocEmpty()) | ||
2691 | return; | ||
2692 | |||
2693 | vector< vector<PwMDataItem>::iterator > undefined; | ||
2694 | vector< vector<PwMDataItem>::iterator >::iterator undefBegin, | ||
2695 | undefEnd, | ||
2696 | undefI; | ||
2697 | vector<PwMCategoryItem>::iterator catBegin = dta.begin(), | ||
2698 | catEnd = dta.end(), | ||
2699 | catI = catBegin; | ||
2700 | vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI; | ||
2701 | int lvpTop, tmpLvp; | ||
2702 | |||
2703 | while (catI != catEnd) { | ||
2704 | lvpTop = -1; | ||
2705 | undefined.clear(); | ||
2706 | |||
2707 | entrBegin = catI->d.begin(); | ||
2708 | entrEnd = catI->d.end(); | ||
2709 | entrI = entrBegin; | ||
2710 | |||
2711 | while (entrI != entrEnd) { | ||
2712 | tmpLvp = entrI->listViewPos; | ||
2713 | if (tmpLvp == -1) | ||
2714 | undefined.push_back(entrI); | ||
2715 | else if (tmpLvp > lvpTop) | ||
2716 | lvpTop = tmpLvp; | ||
2717 | ++entrI; | ||
2718 | } | ||
2719 | undefBegin = undefined.begin(); | ||
2720 | undefEnd = undefined.end(); | ||
2721 | undefI = undefBegin; | ||
2722 | while (undefI != undefEnd) { | ||
2723 | (*undefI)->listViewPos = ++lvpTop; | ||
2724 | ++undefI; | ||
2725 | } | ||
2726 | ++catI; | ||
2727 | } | ||
2728 | } | ||
2729 | |||
2730 | QString PwMDoc::getTitle() | ||
2731 | { | ||
2732 | /* NOTE: We have to ensure, that the returned title | ||
2733 | * is unique and not reused somewhere else while | ||
2734 | * this document is valid (open). | ||
2735 | */ | ||
2736 | QString title(getFilename()); | ||
2737 | if (title.isEmpty()) { | ||
2738 | if (unnamedNum == 0) { | ||
2739 | unnamedNum = PwMDocList::getNewUnnamedNumber(); | ||
2740 | PWM_ASSERT(unnamedNum != 0); | ||
2741 | } | ||
2742 | title = DEFAULT_TITLE; | ||
2743 | title += " "; | ||
2744 | title += tostr(unnamedNum).c_str(); | ||
2745 | } | ||
2746 | return title; | ||
2747 | } | ||
2748 | |||
2749 | bool PwMDoc::tryDelete() | ||
2750 | { | ||
2751 | if (deleted) | ||
2752 | return true; | ||
2753 | int ret; | ||
2754 | if (isDirty()) { | ||
2755 | ret = dirtyAskSave(getTitle()); | ||
2756 | if (ret == 0) { // save to disk | ||
2757 | if (!saveDocUi(this)) | ||
2758 | goto out_ignore; | ||
2759 | } else if (ret == 1) { // don't save and delete | ||
2760 | goto out_accept; | ||
2761 | } else { // cancel operation | ||
2762 | goto out_ignore; | ||
2763 | } | ||
2764 | } | ||
2765 | out_accept: | ||
2766 | deleted = true; | ||
2767 | delete this; | ||
2768 | return true; | ||
2769 | out_ignore: | ||
2770 | return false; | ||
2771 | } | ||
2772 | |||
2773 | #ifndef PWM_EMBEDDED | ||
2774 | #include "pwmdoc.moc" | ||
2775 | #endif | ||