summaryrefslogtreecommitdiffabout
path: root/pwmanager/pwmanager/pwmdoc.cpp
Unidiff
Diffstat (limited to 'pwmanager/pwmanager/pwmdoc.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--pwmanager/pwmanager/pwmdoc.cpp2775
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
59using namespace std;
60
61
62void 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
83void 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
97void 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
111bool 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
129DocTimer::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
146DocTimer::~DocTimer()
147{
148 delete mpwTimer;
149 delete autoLockTimer;
150 delete metaCheckTimer;
151}
152
153void 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
176void 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
191void 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
206void 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
224void DocTimer::mpwTimeout()
225{
226 if (mpwLock) {
227 mpwTimer->start(1000, true);
228 return;
229 }
230 doc->unsetDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW);
231}
232
233void 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
248void 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
285PwMDocList PwMDoc::openDocList;
286unsigned int PwMDocList::unnamedDocCnt = 1;
287
288PwMDoc::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
306PwMDoc::~PwMDoc()
307{
308 emit docClosed(this);
309 getOpenDocList()->del(this);
310 delete _timer;
311}
312
313PwMerror 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 &currentPw, &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, &currentPw, &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;
483out_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 }
498out:
499 return ret;
500}
501
502PwMerror 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, &currentPw, &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, &currentPw, 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);
584out_success:
585 openDocList.edit(this, getTitle().latin1());
586 emit docOpened(this);
587 return e_success;
588}
589
590PwMerror 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
680PwMerror 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
847PwMerror 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
899bool 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
913bool 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
968PwMerror 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
1017PwMerror 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
1038bool 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
1050bool 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
1086bool 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
1100bool 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
1128unsigned 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
1140bool 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
1152bool 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
1167bool 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
1181bool 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
1208PwMerror 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
1230bool 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
1249bool 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
1266PwMerror 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
1340PwMerror 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
1426PwMerror 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
1475bool 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
1517bool 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
1530bool 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
1588bool 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
1600bool 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
1794PwMerror 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
1845void PwMDoc::_deepUnlock()
1846{
1847 deepLock(false);
1848}
1849
1850void 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
1860void 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(&currentPw, &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
1875void 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
1887void 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
1901int 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
1913void 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
1992void 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
2008bool 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
2022bool 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
2038bool 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
2048bool 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
2061bool 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
2071bool 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
2090void 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
2103void 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
2115void 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
2131void 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
2142void 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
2153void 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
2166void 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
2183bool 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
2193bool 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
2235bool 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
2245bool 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
2280PwMerror 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
2403PwMerror 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
2420PwMerror 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
2555bool 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
2570PwMerror 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
2647PwMerror 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
2688void 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
2730QString 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
2749bool 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 }
2765out_accept:
2766 deleted = true;
2767 delete this;
2768 return true;
2769out_ignore:
2770 return false;
2771}
2772
2773#ifndef PWM_EMBEDDED
2774#include "pwmdoc.moc"
2775#endif