summaryrefslogtreecommitdiffabout
path: root/pwmanager/pwmanager/pwmdoc.cpp
authorulf69 <ulf69>2004-09-15 17:53:22 (UTC)
committer ulf69 <ulf69>2004-09-15 17:53:22 (UTC)
commitd3925ba5bd25224bc4a60d3d6a107c464994a1ea (patch) (side-by-side diff)
tree60f69da1d2b79ee3081e7ef5c09a46470ca6eda0 /pwmanager/pwmanager/pwmdoc.cpp
parentce83a3479d23b9e8a59c745ccd0a0b14f64ef4e8 (diff)
downloadkdepimpi-d3925ba5bd25224bc4a60d3d6a107c464994a1ea.zip
kdepimpi-d3925ba5bd25224bc4a60d3d6a107c464994a1ea.tar.gz
kdepimpi-d3925ba5bd25224bc4a60d3d6a107c464994a1ea.tar.bz2
initial revision
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 @@
+/***************************************************************************
+ * *
+ * copyright (C) 2003, 2004 by Michael Buesch *
+ * email: mbuesch@freenet.de *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License version 2 *
+ * as published by the Free Software Foundation. *
+ * *
+ ***************************************************************************/
+
+/***************************************************************************
+ * copyright (C) 2004 by Ulf Schenk
+ * This file is originaly based on version 2.0 of pwmanager
+ * and was modified to run on embedded devices that run microkde
+ *
+ * $Id$
+ **************************************************************************/
+
+#include "pwmdoc.h"
+#include "pwmview.h"
+#include "blowfish.h"
+#include "sha1.h"
+#include "globalstuff.h"
+#include "gpasmanfile.h"
+#include "serializer.h"
+#include "compressgzip.h"
+#include "compressbzip2.h"
+#include "randomizer.h"
+#include "pwminit.h"
+#ifndef PWM_EMBEDDED
+//US #include "libgryptif.h"
+#else
+#endif
+
+#ifdef CONFIG_KWALLETIF
+# include "kwalletemu.h"
+#endif // CONFIG_KWALLETIF
+
+#include <qdatetime.h>
+#include <qsize.h>
+#include <qfileinfo.h>
+#include <qfile.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iostream>
+#include <algorithm>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdint.h>
+
+//TODO: reset to its normal value.
+#define META_CHECK_TIMER_INTERVAL 10/*300*/ /* sek */
+
+using namespace std;
+
+
+void PwMDocList::add(PwMDoc *doc, const string &id)
+{
+#ifdef PWM_DEBUG
+ // check for existance of object in debug mode only.
+ vector<listItem>::iterator begin = docList.begin(),
+ end = docList.end(),
+ i = begin;
+ while (i != end) {
+ if (i->doc == doc) {
+ BUG();
+ return;
+ }
+ ++i;
+ }
+#endif
+ listItem newItem;
+ newItem.doc = doc;
+ newItem.docId = id;
+ docList.push_back(newItem);
+}
+
+void PwMDocList::edit(PwMDoc *doc, const string &newId)
+{
+ vector<listItem>::iterator begin = docList.begin(),
+ end = docList.end(),
+ i = begin;
+ while (i != end) {
+ if (i->doc == doc) {
+ i->docId = newId;
+ return;
+ }
+ ++i;
+ }
+}
+
+void PwMDocList::del(PwMDoc *doc)
+{
+ vector<listItem>::iterator begin = docList.begin(),
+ end = docList.end(),
+ i = begin;
+ while (i != end) {
+ if (i->doc == doc) {
+ docList.erase(i);
+ return;
+ }
+ ++i;
+ }
+}
+
+bool PwMDocList::find(const string &id, listItem *ret)
+{
+ vector<listItem>::iterator begin = docList.begin(),
+ end = docList.end(),
+ i = begin;
+ while (i != end) {
+ if (i->docId == id) {
+ if (ret)
+ *ret = *i;
+ return true;
+ }
+ ++i;
+ }
+ return false;
+}
+
+
+
+DocTimer::DocTimer(PwMDoc *_doc)
+ : doc (_doc)
+ , mpwLock (0)
+ , autoLockLock (0)
+ , metaCheckLock (0)
+{
+ mpwTimer = new QTimer;
+ autoLockTimer = new QTimer;
+ metaCheckTimer = new QTimer;
+ connect(mpwTimer, SIGNAL(timeout()),
+ this, SLOT(mpwTimeout()));
+ connect(autoLockTimer, SIGNAL(timeout()),
+ this, SLOT(autoLockTimeout()));
+ connect(metaCheckTimer, SIGNAL(timeout()),
+ this, SLOT(metaCheckTimeout()));
+}
+
+DocTimer::~DocTimer()
+{
+ delete mpwTimer;
+ delete autoLockTimer;
+ delete metaCheckTimer;
+}
+
+void DocTimer::start(TimerIDs timer)
+{
+ switch (timer) {
+ case id_mpwTimer:
+ if (mpwTimer->isActive())
+ mpwTimer->stop();
+ doc->setDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW);
+ mpwTimer->start(conf()->confGlobPwTimeout() * 1000, true);
+ break;
+ case id_autoLockTimer:
+ if (autoLockTimer->isActive())
+ autoLockTimer->stop();
+ if (conf()->confGlobLockTimeout() > 0)
+ autoLockTimer->start(conf()->confGlobLockTimeout() * 1000, true);
+ break;
+ case id_metaCheckTimer:
+ if (metaCheckTimer->isActive())
+ metaCheckTimer->stop();
+ metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true);
+ break;
+ }
+}
+
+void DocTimer::stop(TimerIDs timer)
+{
+ switch (timer) {
+ case id_mpwTimer:
+ mpwTimer->stop();
+ break;
+ case id_autoLockTimer:
+ autoLockTimer->stop();
+ break;
+ case id_metaCheckTimer:
+ metaCheckTimer->stop();
+ break;
+ }
+}
+
+void DocTimer::getLock(TimerIDs timer)
+{
+ switch (timer) {
+ case id_mpwTimer:
+ ++mpwLock;
+ break;
+ case id_autoLockTimer:
+ ++autoLockLock;
+ break;
+ case id_metaCheckTimer:
+ ++metaCheckLock;
+ break;
+ }
+}
+
+void DocTimer::putLock(TimerIDs timer)
+{
+ switch (timer) {
+ case id_mpwTimer:
+ if (mpwLock)
+ --mpwLock;
+ break;
+ case id_autoLockTimer:
+ if (autoLockLock)
+ --autoLockLock;
+ break;
+ case id_metaCheckTimer:
+ if (metaCheckLock)
+ --metaCheckLock;
+ break;
+ }
+}
+
+void DocTimer::mpwTimeout()
+{
+ if (mpwLock) {
+ mpwTimer->start(1000, true);
+ return;
+ }
+ doc->unsetDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW);
+}
+
+void DocTimer::autoLockTimeout()
+{
+ if (autoLockLock) {
+ autoLockTimer->start(1000, true);
+ return;
+ }
+ if (conf()->confGlobAutoDeepLock() &&
+ doc->filename != QString::null &&
+ doc->filename != "") {
+ doc->deepLock(true);
+ } else {
+ doc->lockAll(true);
+ }
+}
+
+void DocTimer::metaCheckTimeout()
+{
+ if (metaCheckLock) {
+ // check again in one second.
+ metaCheckTimer->start(1000, true);
+ return;
+ }
+ if (doc->isDeepLocked()) {
+ metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true);
+ return;
+ }
+ if (doc->isDocEmpty()) {
+ metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true);
+ return;
+ }
+#ifdef CONFIG_KWALLETIF
+ KWalletEmu *kwlEmu = doc->init->kwalletEmu();
+ if (kwlEmu)
+ kwlEmu->suspendDocSignals();
+#endif // CONFIG_KWALLETIF
+ /* We simply trigger all views to update their
+ * displayed values. This way they have a chance
+ * to get notified when some meta changes over time.
+ * (for example an entry expired).
+ * The _view_ is responsive for not updating its
+ * contents if nothing really changed!
+ */
+ emit doc->dataChanged(doc);
+#ifdef CONFIG_KWALLETIF
+ if (kwlEmu)
+ kwlEmu->resumeDocSignals();
+#endif // CONFIG_KWALLETIF
+ metaCheckTimer->start(META_CHECK_TIMER_INTERVAL * 1000, true);
+}
+
+
+
+PwMDocList PwMDoc::openDocList;
+unsigned int PwMDocList::unnamedDocCnt = 1;
+
+PwMDoc::PwMDoc(QObject *parent, const char *name)
+ : PwMDocUi(parent, name)
+ , dataChangedLock (0)
+{
+ deleted = false;
+ unnamedNum = 0;
+ getOpenDocList()->add(this, getTitle().latin1());
+ curDocStat = 0;
+ setMaxNumEntries();
+ _timer = new DocTimer(this);
+ timer()->start(DocTimer::id_mpwTimer);
+ timer()->start(DocTimer::id_autoLockTimer);
+ timer()->start(DocTimer::id_metaCheckTimer);
+ addCategory(DEFAULT_CATEGORY, 0, false);
+ listView = 0;
+ emit docCreated(this);
+}
+
+PwMDoc::~PwMDoc()
+{
+ emit docClosed(this);
+ getOpenDocList()->del(this);
+ delete _timer;
+}
+
+PwMerror PwMDoc::saveDoc(char compress, const QString *file)
+{
+ PwMerror ret, e;
+ if (!file) {
+ if (filename == "")
+ return e_filename;
+ } else {
+ if (*file == "" && filename == "")
+ return e_filename;
+ if (*file != "")
+ filename = *file;
+ }
+
+ bool wasDeepLocked = isDeepLocked();
+ if (wasDeepLocked) {
+ if (deepLock(false) != e_success)
+ return e_noPw;
+ }
+
+ if (!isPwAvailable()) {
+ /* password is not available. This means, the
+ * document wasn't saved, yet.
+ */
+ bool useChipcard = getDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ QString pw(requestNewMpw(&useChipcard));
+ if (pw != "") {
+ currentPw = pw;
+ } else {
+ return e_noPw;
+ }
+ if (useChipcard) {
+ setDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ } else {
+ unsetDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ }
+ }
+#ifndef PWM_EMBEDDED
+ int _cryptAlgo = conf()->confGlobCryptAlgo();
+ int _hashAlgo = conf()->confGlobHashAlgo();
+#else
+ int _cryptAlgo = PWM_CRYPT_BLOWFISH;
+ int _hashAlgo = PWM_HASH_SHA1;
+#endif
+
+ // sanity check for the selected algorithms
+ if (_cryptAlgo < PWM_CRYPT_BLOWFISH ||
+ _cryptAlgo > PWM_CRYPT_TWOFISH128) {
+ printWarn("Invalid Crypto-Algorithm selected! "
+ "Config-file seems to be corrupt. "
+ "Falling back to Blowfish.");
+ _cryptAlgo = PWM_CRYPT_BLOWFISH;
+ }
+ if (_hashAlgo < PWM_HASH_SHA1 ||
+ _hashAlgo > PWM_HASH_TIGER) {
+ printWarn("Invalid Hash-Algorithm selected! "
+ "Config-file seems to be corrupt. "
+ "Falling back to SHA1.");
+ _hashAlgo = PWM_HASH_SHA1;
+ }
+ char cryptAlgo = static_cast<char>(_cryptAlgo);
+ char hashAlgo = static_cast<char>(_hashAlgo);
+
+ if (conf()->confGlobMakeFileBackup()) {
+ if (!backupFile(filename))
+ return e_fileBackup;
+ }
+ QString tmpFileMoved(QString::null);
+ if (QFile::exists(filename)) {
+ /* Move the existing file to some tmp file.
+ * When saving file succeeds, delete tmp file. Otherwise
+ * move tmp file back. See below.
+ */
+ Randomizer *rnd = Randomizer::obj();
+ char rnd_buf[5];
+ sprintf(rnd_buf, "%X%X%X%X", rnd->genRndChar() & 0xFF, rnd->genRndChar() & 0xFF,
+ rnd->genRndChar() & 0xFF, rnd->genRndChar() & 0xFF);
+ tmpFileMoved = filename + "." + rnd_buf + ".mv";
+ if (!copyFile(filename, tmpFileMoved))
+ return e_openFile;
+ if (!QFile::remove(filename)) {
+ printWarn(string("removing orig file ")
+ + filename.latin1()
+ + " failed!");
+ }
+ }
+ QFile f(filename);
+ string serialized;
+ if (!f.open(IO_ReadWrite)) {
+ ret = e_openFile;
+ goto out_moveback;
+ }
+ e = writeFileHeader(hashAlgo, hashAlgo,
+ cryptAlgo, compress,
+ &currentPw, &f);
+ if (e == e_hashNotImpl) {
+ printDebug("PwMDoc::saveDoc(): writeFileHeader() failed: e_hashNotImpl");
+ f.close();
+ ret = e_hashNotImpl;
+ goto out_moveback;
+ } else if (e != e_success) {
+ printDebug("PwMDoc::saveDoc(): writeFileHeader() failed");
+ f.close();
+ ret = e_writeHeader;
+ goto out_moveback;
+ }
+ if (!serializeDta(&serialized)) {
+ printDebug("PwMDoc::saveDoc(): serializeDta() failed");
+ f.close();
+ ret = e_serializeDta;
+ goto out_moveback;
+ }
+ e = writeDataHash(hashAlgo, &serialized, &f);
+ if (e == e_hashNotImpl) {
+ printDebug("PwMDoc::saveDoc(): writeDataHash() failed: e_hashNotImpl");
+ f.close();
+ ret = e_hashNotImpl;
+ goto out_moveback;
+ } else if (e != e_success) {
+ printDebug("PwMDoc::saveDoc(): writeDataHash() failed");
+ f.close();
+ ret = e_writeHeader;
+ goto out_moveback;
+ }
+ if (!compressDta(&serialized, compress)) {
+ printDebug("PwMDoc::saveDoc(): compressDta() failed");
+ f.close();
+ ret = e_enc;
+ goto out_moveback;
+ }
+ e = encrypt(&serialized, &currentPw, &f, cryptAlgo);
+ if (e == e_weakPw) {
+ printDebug("PwMDoc::saveDoc(): encrypt() failed: e_weakPw");
+ f.close();
+ ret = e_weakPw;
+ goto out_moveback;
+ } else if (e == e_cryptNotImpl) {
+ printDebug("PwMDoc::saveDoc(): encrypt() failed: e_cryptNotImpl");
+ f.close();
+ ret = e_cryptNotImpl;
+ goto out_moveback;
+ } else if (e != e_success) {
+ printDebug("PwMDoc::saveDoc(): encrypt() failed");
+ f.close();
+ ret = e_enc;
+ goto out_moveback;
+ }
+ unsetDocStatFlag(DOC_STAT_DISK_DIRTY);
+ f.close();
+ if (chmod(filename.latin1(),
+ conf()->confGlobFilePermissions())) {
+ printWarn(string("chmod failed: ") + strerror(errno));
+ }
+ openDocList.edit(this, getTitle().latin1());
+ if (wasDeepLocked)
+ deepLock(true);
+ if (tmpFileMoved != QString::null) {
+ // now remove the moved file.
+ if (!QFile::remove(tmpFileMoved)) {
+ printWarn(string("removing file ")
+ + tmpFileMoved.latin1()
+ + " failed!");
+ }
+ }
+ ret = e_success;
+ printDebug(string("writing file { compress: ")
+ + tostr(static_cast<int>(compress)) + " cryptAlgo: "
+ + tostr(static_cast<int>(cryptAlgo)) + " hashAlgo: "
+ + tostr(static_cast<int>(hashAlgo))
+ + " }");
+ goto out;
+out_moveback:
+ if (tmpFileMoved != QString::null) {
+ if (copyFile(tmpFileMoved, filename)) {
+ if (!QFile::remove(tmpFileMoved)) {
+ printWarn(string("removing tmp file ")
+ + filename.latin1()
+ + " failed!");
+ }
+ } else {
+ printWarn(string("couldn't copy file ")
+ + tmpFileMoved.latin1()
+ + " back to "
+ + filename.latin1());
+ }
+ }
+out:
+ return ret;
+}
+
+PwMerror PwMDoc::openDoc(const QString *file, int openLocked)
+{
+ PWM_ASSERT(file);
+ PWM_ASSERT(openLocked == 0 || openLocked == 1 || openLocked == 2);
+ string decrypted, dataHash;
+ PwMerror ret;
+ char cryptAlgo, dataHashType, compress;
+ unsigned int headerLen;
+
+ if (*file == "")
+ return e_readFile;
+ filename = *file;
+ /* check if this file is already open.
+ * This does not catch symlinks!
+ */
+ if (!isDeepLocked()) {
+ if (getOpenDocList()->find(filename.latin1()))
+ return e_alreadyOpen;
+ }
+ QFile f(filename);
+
+ if (openLocked == 2) {
+ // open deep-locked
+ if (!QFile::exists(filename))
+ return e_openFile;
+ if (deepLock(true, false) != e_success)
+ return e_openFile;
+ goto out_success;
+ }
+
+ if (!f.open(IO_ReadOnly))
+ return e_openFile;
+
+ ret = checkHeader(&cryptAlgo, &currentPw, &compress, &headerLen,
+ &dataHashType, &dataHash, &f);
+ if (ret != e_success) {
+ printDebug("PwMDoc::openDoc(): checkHeader() failed");
+ f.close();
+ if (ret == e_wrongPw) {
+ wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ return ret;
+ } else if (ret == e_noPw ||
+ ret == e_fileVer ||
+ ret == e_fileFormat ||
+ ret == e_hashNotImpl) {
+ return ret;
+ } else
+ return e_readFile;
+ }
+ ret = decrypt(&decrypted, headerLen, &currentPw, cryptAlgo, &f);
+ if (ret == e_cryptNotImpl) {
+ printDebug("PwMDoc::openDoc(): decrypt() failed: e_cryptNotImpl");
+ f.close();
+ return e_cryptNotImpl;
+ } else if (ret != e_success) {
+ printDebug("PwMDoc::openDoc(): decrypt() failed");
+ f.close();
+ return e_readFile;
+ }
+ if (!decompressDta(&decrypted, compress)) {
+ printDebug("PwMDoc::openDoc(): decompressDta() failed");
+ f.close();
+ return e_fileCorrupt;
+ }
+ ret = checkDataHash(dataHashType, &dataHash, &decrypted);
+ if (ret == e_hashNotImpl) {
+ printDebug("PwMDoc::openDoc(): checkDataHash() failed: e_hashNotImpl");
+ f.close();
+ return e_hashNotImpl;
+ } else if (ret != e_success) {
+ printDebug("PwMDoc::openDoc(): checkDataHash() failed");
+ f.close();
+ return e_fileCorrupt;
+ }
+ if (!deSerializeDta(&decrypted, openLocked == 1)) {
+ printDebug("PwMDoc::openDoc(): deSerializeDta() failed");
+ f.close();
+ return e_readFile;
+ }
+ f.close();
+ timer()->start(DocTimer::id_mpwTimer);
+ timer()->start(DocTimer::id_autoLockTimer);
+out_success:
+ openDocList.edit(this, getTitle().latin1());
+ emit docOpened(this);
+ return e_success;
+}
+
+PwMerror PwMDoc::writeFileHeader(char keyHash, char dataHash, char crypt, char compress,
+ QString *pw, QFile *f)
+{
+ PWM_ASSERT(pw);
+ PWM_ASSERT(f);
+ PWM_ASSERT(listView);
+#ifndef PWM_EMBEDDED
+ if (f->writeBlock(FILE_ID_HEADER, strlen(FILE_ID_HEADER)) !=
+ static_cast<Q_LONG>(strlen(FILE_ID_HEADER))) {
+ return e_writeFile;
+ }
+ if (f->putch(PWM_FILE_VER) == -1 ||
+ f->putch(keyHash) == -1 ||
+ f->putch(dataHash) == -1 ||
+ f->putch(crypt) == -1 ||
+ f->putch(compress) == -1 ||
+ f->putch((getDocStatFlag(DOC_STAT_USE_CHIPCARD)) ?
+ (static_cast<char>(0x01)) : (static_cast<char>(0x00))) == -1) {
+ return e_writeFile;
+ }
+
+#else
+ if (f->writeBlock(FILE_ID_HEADER, strlen(FILE_ID_HEADER)) !=
+ (long)(strlen(FILE_ID_HEADER))) {
+ return e_writeFile;
+ }
+ if (f->putch(PWM_FILE_VER) == -1 ||
+ f->putch(keyHash) == -1 ||
+ f->putch(dataHash) == -1 ||
+ f->putch(crypt) == -1 ||
+ f->putch(compress) == -1 ||
+ f->putch((getDocStatFlag(DOC_STAT_USE_CHIPCARD)) ?
+ ((char)(0x01)) : ((char)(0x00))) == -1) {
+ return e_writeFile;
+ }
+#endif
+ // write bytes of NUL-data. These bytes are reserved for future-use.
+ const int bufSize = 64;
+ char tmp_buf[bufSize];
+ memset(tmp_buf, 0x00, bufSize);
+ if (f->writeBlock(tmp_buf, bufSize) != bufSize)
+ return e_writeFile;
+
+ switch (keyHash) {
+ case PWM_HASH_SHA1: {
+ const int hashlen = SHA1_HASH_LEN_BYTE;
+ Sha1 hash;
+ hash.sha1_write(reinterpret_cast<const byte *>(pw->latin1()), pw->length());
+ string ret = hash.sha1_read();
+ if (f->writeBlock(ret.c_str(), hashlen) != hashlen)
+ return e_writeFile;
+ break;
+ }
+#ifndef PWM_EMBEDDED
+ case PWM_HASH_SHA256:
+ /*... fall through */
+ case PWM_HASH_SHA384:
+ case PWM_HASH_SHA512:
+ case PWM_HASH_MD5:
+ case PWM_HASH_RMD160:
+ case PWM_HASH_TIGER:
+ {
+ if (!LibGCryptIf::available())
+ return e_hashNotImpl;
+ LibGCryptIf gc;
+ PwMerror err;
+ unsigned char *buf;
+ size_t hashLen;
+ err = gc.hash(&buf,
+ &hashLen,
+ reinterpret_cast<const unsigned char *>(pw->latin1()),
+ pw->length(),
+ keyHash);
+ if (err != e_success)
+ return e_hashNotImpl;
+ if (f->writeBlock(reinterpret_cast<const char *>(buf), hashLen)
+ != static_cast<Q_LONG>(hashLen)) {
+ delete [] buf;
+ return e_hashNotImpl;
+ }
+ delete [] buf;
+ break;
+ }
+#endif
+ default: {
+ return e_hashNotImpl;
+ } }
+ return e_success;
+}
+
+PwMerror PwMDoc::checkHeader(char *cryptAlgo, QString *pw, char *compress,
+ unsigned int *headerLength, char *dataHashType,
+ string *dataHash, QFile *f)
+{
+ PWM_ASSERT(cryptAlgo);
+ PWM_ASSERT(pw);
+ PWM_ASSERT(headerLength);
+ PWM_ASSERT(dataHashType);
+ PWM_ASSERT(dataHash);
+ PWM_ASSERT(f);
+ int tmpRet;
+ // check "magic" header
+ const char magicHdr[] = FILE_ID_HEADER;
+ const int hdrLen = array_size(magicHdr) - 1;
+ char tmp[hdrLen];
+ if (f->readBlock(tmp, hdrLen) != hdrLen)
+ return e_readFile;
+ if (memcmp(tmp, magicHdr, hdrLen) != 0)
+ return e_fileFormat;
+ // read and check file ver
+ int fileV = f->getch();
+ if (fileV == -1)
+ return e_fileFormat;
+ if (fileV != PWM_FILE_VER)
+ return e_fileVer;
+ // read hash hash type
+ int keyHash = f->getch();
+ if (keyHash == -1)
+ return e_fileFormat;
+ // read data hash type
+ tmpRet = f->getch();
+ if (tmpRet == -1)
+ return e_fileFormat;
+ *dataHashType = tmpRet;
+ // read crypt algo
+ tmpRet = f->getch();
+ if (tmpRet == -1)
+ return e_fileFormat;
+ *cryptAlgo = tmpRet;
+ // get compression-algo
+ tmpRet = f->getch();
+ if (tmpRet == -1)
+ return e_fileFormat;
+ *compress = tmpRet;
+ // get the MPW-flag
+ int mpw_flag = f->getch();
+ if (mpw_flag == -1)
+ return e_fileFormat;
+ if (mpw_flag == 0x01)
+ setDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ else
+ unsetDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ // skip the "RESERVED"-bytes
+ if (!(f->at(f->at() + 64)))
+ return e_fileFormat;
+
+ *pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ if (*pw == "") {
+ /* the user didn't give a master-password
+ * or didn't insert a chipcard
+ */
+ return e_noPw;
+ }
+ // verify key-hash
+ switch (keyHash) {
+ case PWM_HASH_SHA1: {
+ // read hash from header
+ const int hashLen = SHA1_HASH_LEN_BYTE;
+ string readHash;
+ int i;
+ for (i = 0; i < hashLen; ++i)
+ readHash.push_back(f->getch());
+ Sha1 hash;
+ hash.sha1_write(reinterpret_cast<const byte *>(pw->latin1()), pw->length());
+ string ret = hash.sha1_read();
+ if (ret != readHash)
+ return e_wrongPw; // hash doesn't match (wrong key)
+ break;
+ }
+#ifndef PWM_EMBEDDED
+ case PWM_HASH_SHA256:
+ /*... fall through */
+ case PWM_HASH_SHA384:
+ case PWM_HASH_SHA512:
+ case PWM_HASH_MD5:
+ case PWM_HASH_RMD160:
+ case PWM_HASH_TIGER: {
+ if (!LibGCryptIf::available())
+ return e_hashNotImpl;
+ LibGCryptIf gc;
+ PwMerror err;
+ unsigned char *buf;
+ size_t hashLen;
+ err = gc.hash(&buf,
+ &hashLen,
+ reinterpret_cast<const unsigned char *>(pw->latin1()),
+ pw->length(),
+ keyHash);
+ if (err != e_success)
+ return e_hashNotImpl;
+ string calcHash(reinterpret_cast<const char *>(buf),
+ static_cast<string::size_type>(hashLen));
+ delete [] buf;
+ // read hash from header
+ string readHash;
+ size_t i;
+ for (i = 0; i < hashLen; ++i)
+ readHash.push_back(f->getch());
+ if (calcHash != readHash)
+ return e_wrongPw; // hash doesn't match (wrong key)
+ break;
+ }
+#endif
+ default: {
+ return e_hashNotImpl;
+ } }
+ // read the data-hash from the file
+ unsigned int hashLen, i;
+ switch (*dataHashType) {
+ case PWM_HASH_SHA1:
+ hashLen = SHA1_HASH_LEN_BYTE;
+ break;
+#ifndef PWM_EMBEDDED
+ case PWM_HASH_SHA256:
+ /*... fall through */
+ case PWM_HASH_SHA384:
+ case PWM_HASH_SHA512:
+ case PWM_HASH_MD5:
+ case PWM_HASH_RMD160:
+ case PWM_HASH_TIGER: {
+ if (!LibGCryptIf::available())
+ return e_hashNotImpl;
+ LibGCryptIf gc;
+ hashLen = gc.hashLength(*dataHashType);
+ if (hashLen == 0)
+ return e_hashNotImpl;
+ break;
+ }
+#endif
+ default:
+ return e_hashNotImpl;
+ }
+ *dataHash = "";
+ for (i = 0; i < hashLen; ++i) {
+ tmpRet = f->getch();
+ if (tmpRet == -1)
+ return e_fileFormat;
+ dataHash->push_back(static_cast<char>(tmpRet));
+ }
+ *headerLength = f->at();
+#ifndef PWM_EMBEDDED
+ printDebug(string("opening file { compress: ")
+ + tostr(static_cast<int>(*compress)) + " cryptAlgo: "
+ + tostr(static_cast<int>(*cryptAlgo)) + " keyHashAlgo: "
+ + tostr(static_cast<int>(keyHash))
+ + " }");
+#else
+ printDebug(string("opening file { compress: ")
+ + tostr((int)(*compress)) + " cryptAlgo: "
+ + tostr((int)(*cryptAlgo)) + " keyHashAlgo: "
+ + tostr((int)(keyHash))
+ + " }");
+#endif
+
+ return e_success;
+}
+
+PwMerror PwMDoc::writeDataHash(char dataHash, string *d, QFile *f)
+{
+ PWM_ASSERT(d);
+ PWM_ASSERT(f);
+
+ switch (dataHash) {
+ case PWM_HASH_SHA1: {
+ const int hashLen = SHA1_HASH_LEN_BYTE;
+ Sha1 h;
+ h.sha1_write(reinterpret_cast<const byte *>(d->c_str()), d->size());
+ string hRet = h.sha1_read();
+ if (f->writeBlock(hRet.c_str(), hashLen) != hashLen)
+ return e_writeFile;
+ break;
+ }
+#ifndef PWM_EMBEDDED
+ case PWM_HASH_SHA256:
+ /*... fall through */
+ case PWM_HASH_SHA384:
+ case PWM_HASH_SHA512:
+ case PWM_HASH_MD5:
+ case PWM_HASH_RMD160:
+ case PWM_HASH_TIGER: {
+ if (!LibGCryptIf::available())
+ return e_hashNotImpl;
+ LibGCryptIf gc;
+ PwMerror err;
+ unsigned char *buf;
+ size_t hashLen;
+ err = gc.hash(&buf,
+ &hashLen,
+ reinterpret_cast<const unsigned char *>(d->c_str()),
+ d->size(),
+ dataHash);
+ if (err != e_success)
+ return e_hashNotImpl;
+ if (f->writeBlock(reinterpret_cast<const char *>(buf), hashLen)
+ != static_cast<Q_LONG>(hashLen)) {
+ delete [] buf;
+ return e_hashNotImpl;
+ }
+ delete [] buf;
+ break;
+ }
+#endif
+ default: {
+ return e_hashNotImpl;
+ } }
+
+ return e_success;
+}
+
+bool PwMDoc::backupFile(const QString &filePath)
+{
+ QFileInfo fi(filePath);
+ if (!fi.exists())
+ return true; // Yes, true is correct.
+ QString pathOnly(fi.dirPath(true));
+ QString nameOnly(fi.fileName());
+ QString backupPath = pathOnly
+ + "/~"
+ + nameOnly
+ + ".backup";
+ return copyFile(filePath, backupPath);
+}
+
+bool PwMDoc::copyFile(const QString &src, const QString &dst)
+{
+ QFileInfo fi(src);
+ if (!fi.exists())
+ return false;
+ if (QFile::exists(dst)) {
+ if (!QFile::remove(dst))
+ return false;
+ }
+ QFile srcFd(src);
+ if (!srcFd.open(IO_ReadOnly))
+ return false;
+ QFile dstFd(dst);
+ if (!dstFd.open(IO_ReadWrite)) {
+ srcFd.close();
+ return false;
+ }
+ const int tmpBuf_size = 512;
+ char tmpBuf[tmpBuf_size];
+#ifndef PWM_EMBEDDED
+ Q_LONG bytesRead, bytesWritten;
+#else
+ long bytesRead, bytesWritten;
+#endif
+ while (!srcFd.atEnd()) {
+#ifndef PWM_EMBEDDED
+ bytesRead = srcFd.readBlock(tmpBuf,
+ static_cast<Q_ULONG>(tmpBuf_size));
+#else
+ bytesRead = srcFd.readBlock(tmpBuf,
+ (unsigned long)(tmpBuf_size));
+#endif
+ if (bytesRead == -1) {
+ srcFd.close();
+ dstFd.close();
+ return false;
+ }
+#ifndef PWM_EMBEDDED
+ bytesWritten = dstFd.writeBlock(tmpBuf,
+ static_cast<Q_ULONG>(bytesRead));
+#else
+ bytesWritten = dstFd.writeBlock(tmpBuf,
+ (unsigned long)(bytesRead));
+#endif
+ if (bytesWritten != bytesRead) {
+ srcFd.close();
+ dstFd.close();
+ return false;
+ }
+ }
+ srcFd.close();
+ dstFd.close();
+ return true;
+}
+
+PwMerror PwMDoc::addEntry(const QString &category, PwMDataItem *d,
+ bool dontFlagDirty, bool updateMeta)
+{
+ PWM_ASSERT(d);
+ unsigned int cat = 0;
+
+ if (isDeepLocked()) {
+ PwMerror ret;
+ ret = deepLock(false);
+ if (ret != e_success)
+ return e_lock;
+ }
+
+ addCategory(category, &cat);
+
+ if (numEntries(category) >= maxEntries)
+ return e_maxAllowedEntr;
+
+ vector<unsigned int> foundPositions;
+ /* historically this was:
+ * const int searchIn = SEARCH_IN_DESC | SEARCH_IN_NAME |
+ * SEARCH_IN_URL | SEARCH_IN_LAUNCHER;
+ * But for now we only search in desc.
+ * That's a tweak to be KWallet compatible. But it should not add
+ * usability-drop onto PwManager, does it?
+ * (And yes, "int" was a bug. Correct is "unsigned int")
+ */
+ const unsigned int searchIn = SEARCH_IN_DESC;
+ findEntry(cat, *d, searchIn, &foundPositions, true);
+ if (foundPositions.size()) {
+ // DOH! We found this entry.
+ return e_entryExists;
+ }
+
+ d->listViewPos = -1;
+ d->lockStat = conf()->confGlobNewEntrLockStat();
+ if (updateMeta) {
+ d->meta.create = QDateTime::currentDateTime();
+ d->meta.update = d->meta.create;
+ }
+ dta[cat].d.push_back(*d);
+
+ delAllEmptyCat(true);
+
+ if (!dontFlagDirty)
+ flagDirty();
+ return e_success;
+}
+
+PwMerror PwMDoc::addCategory(const QString &category, unsigned int *categoryIndex,
+ bool checkIfExist)
+{
+ if (isDeepLocked()) {
+ PwMerror ret;
+ ret = deepLock(false);
+ if (ret != e_success)
+ return e_lock;
+ }
+ if (checkIfExist) {
+ if (findCategory(category, categoryIndex))
+ return e_categoryExists;
+ }
+ PwMCategoryItem item;
+ item.name = category.latin1();
+ dta.push_back(item);
+ if (categoryIndex)
+ *categoryIndex = dta.size() - 1;
+ return e_success;
+}
+
+bool PwMDoc::delEntry(const QString &category, unsigned int index, bool dontFlagDirty)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return false;
+ }
+
+ return delEntry(cat, index, dontFlagDirty);
+}
+
+bool PwMDoc::delEntry(unsigned int category, unsigned int index, bool dontFlagDirty)
+{
+ if (isDeepLocked())
+ return false;
+ if (index > dta[category].d.size() - 1)
+ return false;
+ getDataChangedLock();
+ if (!lockAt(category, index, false)) {
+ putDataChangedLock();
+ return false;
+ }
+ putDataChangedLock();
+ int lvPos = dta[category].d[index].listViewPos;
+
+ // delete entry
+ dta[category].d.erase(dta[category].d.begin() + index);
+
+ unsigned int i, entries = numEntries(category);
+ if (!entries) {
+ // no more entries in this category, so
+ // we can delete it, too.
+ BUG_ON(!delCategory(category));
+ // delCategory() flags it dirty, so we need not to do so.
+ return true;
+ }
+ for (i = 0; i < entries; ++i) {
+ // decrement all listViewPositions that are greater than the deleted.
+ if (dta[category].d[i].listViewPos > lvPos)
+ --dta[category].d[i].listViewPos;
+ }
+
+ if (!dontFlagDirty)
+ flagDirty();
+ return true;
+}
+
+bool PwMDoc::editEntry(const QString &oldCategory, const QString &newCategory,
+ unsigned int index, PwMDataItem *d, bool updateMeta)
+{
+ PWM_ASSERT(d);
+ unsigned int oldCat = 0;
+
+ if (!findCategory(oldCategory, &oldCat)) {
+ BUG();
+ return false;
+ }
+
+ return editEntry(oldCat, newCategory, index, d, updateMeta);
+}
+
+bool PwMDoc::editEntry(unsigned int oldCategory, const QString &newCategory,
+ unsigned int index, PwMDataItem *d, bool updateMeta)
+{
+ if (isDeepLocked())
+ return false;
+ if (updateMeta) {
+ d->meta.update = QDateTime::currentDateTime();
+ if (d->meta.create.isNull()) {
+ d->meta.create = d->meta.update;
+ }
+ }
+ if (dta[oldCategory].name != newCategory.latin1()) {
+ // the user changed the category.
+ PwMerror ret;
+ d->rev = 0;
+ ret = addEntry(newCategory, d, true, false);
+ if (ret != e_success)
+ return false;
+ if (!delEntry(oldCategory, index, true))
+ return false;
+ } else {
+ d->rev = dta[oldCategory].d[index].rev + 1; // increment revision counter.
+ dta[oldCategory].d[index] = *d;
+ }
+ flagDirty();
+ return true;
+}
+
+unsigned int PwMDoc::numEntries(const QString &category)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return 0;
+ }
+
+ return numEntries(cat);
+}
+
+bool PwMDoc::serializeDta(string *d)
+{
+ PWM_ASSERT(d);
+ Serializer ser;
+ if (!ser.serialize(dta))
+ return false;
+ d->assign(ser.getXml());
+ if (!d->size())
+ return false;
+ return true;
+}
+
+bool PwMDoc::deSerializeDta(const string *d, bool entriesLocked)
+{
+ PWM_ASSERT(d);
+ try {
+ Serializer ser(d->c_str());
+ ser.setDefaultLockStat(entriesLocked);
+ if (!ser.deSerialize(&dta))
+ return false;
+ } catch (PwMException) {
+ return false;
+ }
+ emitDataChanged(this);
+ return true;
+}
+
+bool PwMDoc::getEntry(const QString &category, unsigned int index,
+ PwMDataItem * d, bool unlockIfLocked)
+{
+ PWM_ASSERT(d);
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return false;
+ }
+
+ return getEntry(cat, index, d, unlockIfLocked);
+}
+
+bool PwMDoc::getEntry(unsigned int category, unsigned int index,
+ PwMDataItem *d, bool unlockIfLocked)
+{
+ if (index > dta[category].d.size() - 1)
+ return false;
+
+ bool locked = isLocked(category, index);
+ if (locked) {
+ /* this entry is locked. We don't return a password,
+ * until it's unlocked by the user by inserting
+ * chipcard or entering the mpw
+ */
+ if (unlockIfLocked) {
+ if (!lockAt(category, index, false)) {
+ return false;
+ }
+ locked = false;
+ }
+ }
+
+ *d = dta[category].d[index];
+ if (locked)
+ d->pw = LOCKED_STRING.latin1();
+
+ return true;
+}
+
+PwMerror PwMDoc::getCommentByLvp(const QString &category, int listViewPos,
+ string *foundComment)
+{
+ PWM_ASSERT(foundComment);
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat))
+ return e_invalidArg;
+
+ unsigned int i, entries = numEntries(cat);
+ for (i = 0; i < entries; ++i) {
+ if (dta[cat].d[i].listViewPos == listViewPos) {
+ *foundComment = dta[cat].d[i].comment;
+ if (dta[cat].d[i].binary)
+ return e_binEntry;
+ return e_normalEntry;
+ }
+ }
+ BUG();
+ return e_generic;
+}
+
+bool PwMDoc::compressDta(string *d, char algo)
+{
+ PWM_ASSERT(d);
+ switch (algo) {
+ case PWM_COMPRESS_GZIP: {
+ CompressGzip comp;
+ return comp.compress(d);
+ } case PWM_COMPRESS_BZIP2: {
+ CompressBzip2 comp;
+ return comp.compress(d);
+ } case PWM_COMPRESS_NONE: {
+ return true;
+ } default: {
+ BUG();
+ }
+ }
+ return false;
+}
+
+bool PwMDoc::decompressDta(string *d, char algo)
+{
+ PWM_ASSERT(d);
+ switch (algo) {
+ case PWM_COMPRESS_GZIP: {
+ CompressGzip comp;
+ return comp.decompress(d);
+ } case PWM_COMPRESS_BZIP2: {
+ CompressBzip2 comp;
+ return comp.decompress(d);
+ } case PWM_COMPRESS_NONE: {
+ return true;
+ }
+ }
+ return false;
+}
+
+PwMerror PwMDoc::encrypt(string *d, const QString *pw, QFile *f, char algo)
+{
+ PWM_ASSERT(d);
+ PWM_ASSERT(pw);
+ PWM_ASSERT(f);
+
+ size_t encSize;
+ byte *encrypted = 0;
+
+ switch (algo) {
+ case PWM_CRYPT_BLOWFISH: {
+ Blowfish::padNull(d);
+ encSize = d->length();
+ encrypted = new byte[encSize];
+ Blowfish bf;
+ if (bf.bf_setkey((byte *) pw->latin1(), pw->length())) {
+ delete [] encrypted;
+ return e_weakPw;
+ }
+ bf.bf_encrypt((byte *) encrypted, (byte *) d->c_str(), encSize);
+ break;
+ }
+#ifndef PWM_EMBEDDED
+ case PWM_CRYPT_AES128:
+ /*... fall through */
+ case PWM_CRYPT_AES192:
+ case PWM_CRYPT_AES256:
+ case PWM_CRYPT_3DES:
+ case PWM_CRYPT_TWOFISH:
+ case PWM_CRYPT_TWOFISH128: {
+ if (!LibGCryptIf::available())
+ return e_cryptNotImpl;
+ LibGCryptIf gc;
+ PwMerror err;
+ unsigned char *plain = new unsigned char[d->length() + 1024];
+ memcpy(plain, d->c_str(), d->length());
+ err = gc.encrypt(&encrypted,
+ &encSize,
+ plain,
+ d->length(),
+ reinterpret_cast<const unsigned char *>(pw->latin1()),
+ pw->length(),
+ algo);
+ delete [] plain;
+ if (err != e_success)
+ return e_cryptNotImpl;
+ break;
+ }
+#endif
+ default: {
+ delete_ifnot_null_array(encrypted);
+ return e_cryptNotImpl;
+ } }
+
+ // write encrypted data to file
+#ifndef PWM_EMBEDDED
+ if (f->writeBlock(reinterpret_cast<const char *>(encrypted),
+ static_cast<Q_ULONG>(encSize))
+ != static_cast<Q_LONG>(encSize)) {
+ delete_ifnot_null_array(encrypted);
+ return e_writeFile;
+ }
+#else
+ if (f->writeBlock((const char *)(encrypted),
+ (unsigned long)(encSize))
+ != (long)(encSize)) {
+ delete_ifnot_null_array(encrypted);
+ return e_writeFile;
+ }
+#endif
+ delete_ifnot_null_array(encrypted);
+ return e_success;
+}
+
+PwMerror PwMDoc::decrypt(string *d, unsigned int pos, const QString *pw,
+ char algo, QFile *f)
+{
+ PWM_ASSERT(d);
+ PWM_ASSERT(pw);
+ PWM_ASSERT(f);
+
+ unsigned int cryptLen = f->size() - pos;
+ byte *encrypted = new byte[cryptLen];
+ byte *decrypted = new byte[cryptLen];
+
+ f->at(pos);
+#ifndef PWM_EMBEDDED
+ if (f->readBlock(reinterpret_cast<char *>(encrypted),
+ static_cast<Q_ULONG>(cryptLen))
+ != static_cast<Q_LONG>(cryptLen)) {
+ delete [] encrypted;
+ delete [] decrypted;
+ return e_readFile;
+ }
+#else
+ if (f->readBlock((char *)(encrypted),
+ (unsigned long)(cryptLen))
+ != (long)(cryptLen)) {
+ delete [] encrypted;
+ delete [] decrypted;
+ return e_readFile;
+ }
+#endif
+ switch (algo) {
+ case PWM_CRYPT_BLOWFISH: {
+ Blowfish bf;
+ bf.bf_setkey((byte *) pw->latin1(), pw->length());
+ bf.bf_decrypt(decrypted, encrypted, cryptLen);
+ break;
+ }
+#ifndef PWM_EMBEDDED
+ case PWM_CRYPT_AES128:
+ /*... fall through */
+ case PWM_CRYPT_AES192:
+ case PWM_CRYPT_AES256:
+ case PWM_CRYPT_3DES:
+ case PWM_CRYPT_TWOFISH:
+ case PWM_CRYPT_TWOFISH128: {
+ if (!LibGCryptIf::available())
+ return e_cryptNotImpl;
+ LibGCryptIf gc;
+ PwMerror err;
+ err = gc.decrypt(&decrypted,
+ &cryptLen,
+ encrypted,
+ cryptLen,
+ reinterpret_cast<const unsigned char *>(pw->latin1()),
+ pw->length(),
+ algo);
+ if (err != e_success) {
+ delete [] encrypted;
+ delete [] decrypted;
+ return e_cryptNotImpl;
+ }
+ break;
+ }
+#endif
+ default: {
+ delete [] encrypted;
+ delete [] decrypted;
+ return e_cryptNotImpl;
+ } }
+ delete [] encrypted;
+#ifndef PWM_EMBEDDED
+ d->assign(reinterpret_cast<const char *>(decrypted),
+ static_cast<string::size_type>(cryptLen));
+#else
+ d->assign((const char *)(decrypted),
+ (string::size_type)(cryptLen));
+#endif
+ delete [] decrypted;
+ if (algo == PWM_CRYPT_BLOWFISH) {
+ if (!Blowfish::unpadNull(d)) {
+ BUG();
+ return e_readFile;
+ }
+ }
+ return e_success;
+}
+
+PwMerror PwMDoc::checkDataHash(char dataHashType, const string *dataHash,
+ const string *dataStream)
+{
+ PWM_ASSERT(dataHash);
+ PWM_ASSERT(dataStream);
+ switch(dataHashType) {
+ case PWM_HASH_SHA1: {
+ Sha1 hash;
+ hash.sha1_write((byte*)dataStream->c_str(), dataStream->length());
+ string ret = hash.sha1_read();
+ if (ret != *dataHash)
+ return e_fileCorrupt;
+ break;
+ }
+#ifndef PWM_EMBEDDED
+ case PWM_HASH_SHA256:
+ /*... fall through */
+ case PWM_HASH_SHA384:
+ case PWM_HASH_SHA512:
+ case PWM_HASH_MD5:
+ case PWM_HASH_RMD160:
+ case PWM_HASH_TIGER: {
+ if (!LibGCryptIf::available())
+ return e_hashNotImpl;
+ LibGCryptIf gc;
+ PwMerror err;
+ unsigned char *buf;
+ size_t hashLen;
+ err = gc.hash(&buf,
+ &hashLen,
+ reinterpret_cast<const unsigned char *>(dataStream->c_str()),
+ dataStream->length(),
+ dataHashType);
+ if (err != e_success)
+ return e_hashNotImpl;
+ string calcHash(reinterpret_cast<const char *>(buf),
+ static_cast<string::size_type>(hashLen));
+ delete [] buf;
+ if (calcHash != *dataHash)
+ return e_fileCorrupt;
+ break;
+ }
+#endif
+ default:
+ return e_hashNotImpl;
+ }
+ return e_success;
+}
+
+bool PwMDoc::lockAt(unsigned int category, unsigned int index,
+ bool lock)
+{
+ if (index >= numEntries(category)) {
+ BUG();
+ return false;
+ }
+ if (lock == dta[category].d[index].lockStat)
+ return true;
+
+ if (!lock && currentPw != "") {
+ // "unlocking" and "password is already set"
+ if (!getDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW)) {
+ // unlocking without pw not allowed
+ QString pw;
+ pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ if (pw != "") {
+ if (pw != currentPw) {
+ wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ return false;
+ } else {
+ timer()->start(DocTimer::id_mpwTimer);
+ }
+ } else {
+ return false;
+ }
+ } else {
+ timer()->start(DocTimer::id_mpwTimer);
+ }
+ }
+
+ dta[category].d[index].lockStat = lock;
+ dta[category].d[index].rev++; // increment revision counter.
+
+ emitDataChanged(this);
+ if (!lock)
+ timer()->start(DocTimer::id_autoLockTimer);
+
+ return true;
+
+}
+
+bool PwMDoc::lockAt(const QString &category,unsigned int index,
+ bool lock)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return false;
+ }
+
+ return lockAt(cat, index, lock);
+}
+
+bool PwMDoc::lockAll(bool lock)
+{
+ if (!lock && isDeepLocked()) {
+ PwMerror ret;
+ ret = deepLock(false);
+ if (ret != e_success)
+ return false;
+ return true;
+ }
+ if (isDocEmpty()) {
+ return true;
+ }
+ if (!lock && currentPw != "") {
+ // unlocking and password is already set
+ if (!getDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW)) {
+ // unlocking without pw not allowed
+ QString pw;
+ pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ if (pw != "") {
+ if (pw != currentPw) {
+ wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ return false;
+ } else {
+ timer()->start(DocTimer::id_mpwTimer);
+ }
+ } else {
+ return false;
+ }
+ } else {
+ timer()->start(DocTimer::id_mpwTimer);
+ }
+ }
+
+ vector<PwMCategoryItem>::iterator catBegin = dta.begin(),
+ catEnd = dta.end(),
+ catI = catBegin;
+ vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI;
+ while (catI != catEnd) {
+ entrBegin = catI->d.begin();
+ entrEnd = catI->d.end();
+ entrI = entrBegin;
+ while (entrI != entrEnd) {
+ entrI->lockStat = lock;
+ entrI->rev++; // increment revision counter.
+ ++entrI;
+ }
+ ++catI;
+ }
+
+ emitDataChanged(this);
+ if (lock)
+ timer()->stop(DocTimer::id_autoLockTimer);
+ else
+ timer()->start(DocTimer::id_autoLockTimer);
+
+ return true;
+}
+
+bool PwMDoc::isLocked(const QString &category, unsigned int index)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return false;
+ }
+
+ return isLocked(cat, index);
+}
+
+bool PwMDoc::unlockAll_tempoary(bool revert)
+{
+ static vector< vector<bool> > *oldLockStates = 0;
+ static bool wasDeepLocked;
+
+ if (revert) { // revert the unlocking
+ if (oldLockStates) {
+ /* we actually _have_ unlocked something, because
+ * we have allocated space for the oldLockStates.
+ * So, go on and revert them!
+ */
+ if (wasDeepLocked) {
+ PwMerror ret = deepLock(true);
+ if (ret == e_success) {
+ /* deep-lock succeed. We are save.
+ * (but if it failed, just go on
+ * lock them normally)
+ */
+ delete_and_null(oldLockStates);
+ timer()->start(DocTimer::id_autoLockTimer);
+ printDebug("tempoary unlocking of dta "
+ "reverted by deep-locking.");
+ return true;
+ }
+ printDebug("deep-lock failed while reverting! "
+ "Falling back to normal-lock.");
+ }
+ if (unlikely(!wasDeepLocked &&
+ numCategories() != oldLockStates->size())) {
+ /* DOH! We have modified "dta" while
+ * it was unlocked tempoary. DON'T DO THIS!
+ */
+ BUG();
+ delete_and_null(oldLockStates);
+ timer()->start(DocTimer::id_autoLockTimer);
+ return false;
+ }
+ vector<PwMCategoryItem>::iterator catBegin = dta.begin(),
+ catEnd = dta.end(),
+ catI = catBegin;
+ vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI;
+ vector< vector<bool> >::iterator oldCatStatI = oldLockStates->begin();
+ vector<bool>::iterator oldEntrStatBegin,
+ oldEntrStatEnd,
+ oldEntrStatI;
+ while (catI != catEnd) {
+ entrBegin = catI->d.begin();
+ entrEnd = catI->d.end();
+ entrI = entrBegin;
+ if (likely(!wasDeepLocked)) {
+ oldEntrStatBegin = oldCatStatI->begin();
+ oldEntrStatEnd = oldCatStatI->end();
+ oldEntrStatI = oldEntrStatBegin;
+ if (unlikely(catI->d.size() != oldCatStatI->size())) {
+ /* DOH! We have modified "dta" while
+ * it was unlocked tempoary. DON'T DO THIS!
+ */
+ BUG();
+ delete_and_null(oldLockStates);
+ timer()->start(DocTimer::id_autoLockTimer);
+ return false;
+ }
+ }
+ while (entrI != entrEnd) {
+ if (wasDeepLocked) {
+ /* this is an error-fallback if
+ * deeplock didn't succeed
+ */
+ entrI->lockStat = true;
+ } else {
+ entrI->lockStat = *oldEntrStatI;
+ }
+ ++entrI;
+ if (likely(!wasDeepLocked))
+ ++oldEntrStatI;
+ }
+ ++catI;
+ if (likely(!wasDeepLocked))
+ ++oldCatStatI;
+ }
+ delete_and_null(oldLockStates);
+ if (unlikely(wasDeepLocked)) {
+ /* error fallback... */
+ unsetDocStatFlag(DOC_STAT_DEEPLOCKED);
+ emitDataChanged(this);
+ printDebug("WARNING: unlockAll_tempoary(true) "
+ "deeplock fallback!");
+ }
+ printDebug("tempoary unlocking of dta reverted.");
+ } else {
+ printDebug("unlockAll_tempoary(true): nothing to do.");
+ }
+ timer()->start(DocTimer::id_autoLockTimer);
+ } else { // unlock all data tempoary
+ if (unlikely(oldLockStates != 0)) {
+ /* DOH! We have already unlocked the data tempoarly.
+ * No need to do it twice. ;)
+ */
+ BUG();
+ return false;
+ }
+ wasDeepLocked = false;
+ bool mustUnlock = false;
+ if (isDeepLocked()) {
+ PwMerror ret;
+ while (1) {
+ ret = deepLock(false);
+ if (ret == e_success) {
+ break;
+ } else if (ret == e_wrongPw) {
+ wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ } else {
+ printDebug("deep-unlocking failed while "
+ "tempoary unlocking!");
+ return false;
+ }
+ }
+ wasDeepLocked = true;
+ mustUnlock = true;
+ } else {
+ // first check if it's needed to unlock some entries
+ vector<PwMCategoryItem>::iterator catBegin = dta.begin(),
+ catEnd = dta.end(),
+ catI = catBegin;
+ vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI;
+ while (catI != catEnd) {
+ entrBegin = catI->d.begin();
+ entrEnd = catI->d.end();
+ entrI = entrBegin;
+ while (entrI != entrEnd) {
+ if (entrI->lockStat == true) {
+ mustUnlock = true;
+ break;
+ }
+ ++entrI;
+ }
+ if (mustUnlock)
+ break;
+ ++catI;
+ }
+ }
+ if (!mustUnlock) {
+ // nothing to do.
+ timer()->stop(DocTimer::id_autoLockTimer);
+ printDebug("unlockAll_tempoary(): nothing to do.");
+ return true;
+ } else if (!wasDeepLocked) {
+ if (!getDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW) &&
+ currentPw != "") {
+ /* we can't unlock without mpw, so
+ * we need to ask for it.
+ */
+ QString pw;
+ while (1) {
+ pw = requestMpw(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ if (pw == "") {
+ return false;
+ } else if (pw == currentPw) {
+ break;
+ }
+ wrongMpwMsgBox(getDocStatFlag(DOC_STAT_USE_CHIPCARD));
+ }
+ }
+ }
+ timer()->stop(DocTimer::id_autoLockTimer);
+ oldLockStates = new vector< vector<bool> >;
+ vector<bool> tmp_vec;
+ vector<PwMCategoryItem>::iterator catBegin = dta.begin(),
+ catEnd = dta.end(),
+ catI = catBegin;
+ vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI;
+ while (catI != catEnd) {
+ entrBegin = catI->d.begin();
+ entrEnd = catI->d.end();
+ entrI = entrBegin;
+ while (entrI != entrEnd) {
+ if (!wasDeepLocked) {
+ tmp_vec.push_back(entrI->lockStat);
+ }
+ entrI->lockStat = false;
+ ++entrI;
+ }
+ if (!wasDeepLocked) {
+ oldLockStates->push_back(tmp_vec);
+ tmp_vec.clear();
+ }
+ ++catI;
+ }
+ printDebug("tempoary unlocked dta.");
+ }
+
+ return true;
+}
+
+PwMerror PwMDoc::deepLock(bool lock, bool saveToFile)
+{
+ PwMerror ret;
+
+ if (lock) {
+ if (isDeepLocked())
+ return e_lock;
+ if (saveToFile) {
+ if (isDocEmpty())
+ return e_docIsEmpty;
+ ret = saveDoc(conf()->confGlobCompression());
+ if (ret == e_filename) {
+ /* the doc wasn't saved to a file
+ * by the user, yet.
+ */
+ cantDeeplock_notSavedMsgBox();
+ return e_docNotSaved;
+ } else if (ret != e_success) {
+ return e_lock;
+ }
+ }
+ timer()->stop(DocTimer::id_autoLockTimer);
+ clearDoc();
+ PwMDataItem d;
+ d.desc = IS_DEEPLOCKED_SHORTMSG.latin1();
+ d.comment = IS_DEEPLOCKED_MSG.latin1();
+ d.listViewPos = 0;
+ addEntry(DEFAULT_CATEGORY, &d, true);
+ lockAt(DEFAULT_CATEGORY, 0, true);
+ unsetDocStatFlag(DOC_STAT_DISK_DIRTY);
+ setDocStatFlag(DOC_STAT_DEEPLOCKED);
+ } else {
+ if (!isDeepLocked())
+ return e_lock;
+ ret = openDoc(&filename, (conf()->confGlobUnlockOnOpen())
+ ? 0 : 1);
+ if (ret == e_wrongPw) {
+ return e_wrongPw;
+ } else if (ret != e_success) {
+ printDebug(string("PwMDoc::deepLock(false): ERR! openDoc() == ")
+ + tostr(static_cast<int>(ret)));
+ return e_lock;
+ }
+ unsetDocStatFlag(DOC_STAT_DEEPLOCKED);
+ timer()->start(DocTimer::id_autoLockTimer);
+ }
+
+ emitDataChanged(this);
+ return e_success;
+}
+
+void PwMDoc::_deepUnlock()
+{
+ deepLock(false);
+}
+
+void PwMDoc::clearDoc()
+{
+ dta.clear();
+ PwMCategoryItem d;
+ d.name = DEFAULT_CATEGORY.latin1();
+ dta.push_back(d);
+ currentPw = "";
+ unsetDocStatFlag(DOC_STAT_UNLOCK_WITHOUT_PW);
+}
+
+void PwMDoc::changeCurrentPw()
+{
+ if (currentPw == "")
+ return; // doc hasn't been saved. No mpw available.
+ bool useChipcard = getDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ QString pw = requestMpwChange(&currentPw, &useChipcard);
+ if (pw == "")
+ return;
+ if (useChipcard)
+ setDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ else
+ unsetDocStatFlag(DOC_STAT_USE_CHIPCARD);
+ setCurrentPw(pw);
+}
+
+void PwMDoc::setListViewPos(const QString &category, unsigned int index,
+ int pos)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return;
+ }
+ setListViewPos(cat, index, pos);
+}
+
+void PwMDoc::setListViewPos(unsigned int category, unsigned int index,
+ int pos)
+{
+ dta[category].d[index].listViewPos = pos;
+
+/* FIXME workaround: don't flag dirty, because this function sometimes
+ * get's called when it shouldn't. It's because PwMView assumes
+ * the user resorted the UI on behalf of signal layoutChanged().
+ * This is somewhat broken and incorrect, but I've no other
+ * solution for now.
+ */
+// setDocStatFlag(DOC_STAT_DISK_DIRTY);
+}
+
+int PwMDoc::getListViewPos(const QString &category, unsigned int index)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ BUG();
+ return -1;
+ }
+
+ return dta[cat].d[index].listViewPos;
+}
+
+void PwMDoc::findEntry(unsigned int category, PwMDataItem find, unsigned int searchIn,
+ vector<unsigned int> *foundPositions, bool breakAfterFound,
+ bool caseSensitive, bool exactWordMatch, bool sortByLvp)
+{
+ PWM_ASSERT(foundPositions);
+ PWM_ASSERT(searchIn);
+ foundPositions->clear();
+
+ unsigned int i, entries = numEntries(category);
+ for (i = 0; i < entries; ++i) {
+ if (searchIn & SEARCH_IN_DESC) {
+ if (!compareString(find.desc, dta[category].d[i].desc,
+ caseSensitive, exactWordMatch)) {
+ continue;
+ }
+ }
+ if (searchIn & SEARCH_IN_NAME) {
+ if (!compareString(find.name, dta[category].d[i].name,
+ caseSensitive, exactWordMatch)) {
+ continue;
+ }
+ }
+ if (searchIn & SEARCH_IN_PW) {
+ bool wasLocked = isLocked(category, i);
+ getDataChangedLock();
+ lockAt(category, i, false);
+ if (!compareString(find.pw, dta[category].d[i].pw,
+ caseSensitive, exactWordMatch)) {
+ lockAt(category, i, wasLocked);
+ putDataChangedLock();
+ continue;
+ }
+ lockAt(category, i, wasLocked);
+ putDataChangedLock();
+ }
+ if (searchIn & SEARCH_IN_COMMENT) {
+ if (!compareString(find.comment, dta[category].d[i].comment,
+ caseSensitive, exactWordMatch)) {
+ continue;
+ }
+ }
+ if (searchIn & SEARCH_IN_URL) {
+ if (!compareString(find.url, dta[category].d[i].url,
+ caseSensitive, exactWordMatch)) {
+ continue;
+ }
+ }
+ if (searchIn & SEARCH_IN_LAUNCHER) {
+ if (!compareString(find.launcher, dta[category].d[i].launcher,
+ caseSensitive, exactWordMatch)) {
+ continue;
+ }
+ }
+
+ // all selected "searchIn" matched.
+ foundPositions->push_back(i);
+ if (breakAfterFound)
+ break;
+ }
+
+ if (sortByLvp && foundPositions->size() > 1) {
+ vector< pair<unsigned int /* foundPosition (real doc pos) */,
+ unsigned int /* lvp-pos */> > tmp_vec;
+
+ unsigned int i, items = foundPositions->size();
+ pair<unsigned int, unsigned int> tmp_pair;
+ for (i = 0; i < items; ++i) {
+ tmp_pair.first = (*foundPositions)[i];
+ tmp_pair.second = dta[category].d[(*foundPositions)[i]].listViewPos;
+ tmp_vec.push_back(tmp_pair);
+ }
+ sort(tmp_vec.begin(), tmp_vec.end(), dta_lvp_greater());
+ foundPositions->clear();
+ for (i = 0; i < items; ++i) {
+ foundPositions->push_back(tmp_vec[i].first);
+ }
+ }
+}
+
+void PwMDoc::findEntry(const QString &category, PwMDataItem find, unsigned int searchIn,
+ vector<unsigned int> *foundPositions, bool breakAfterFound,
+ bool caseSensitive, bool exactWordMatch, bool sortByLvp)
+{
+ PWM_ASSERT(foundPositions);
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat)) {
+ foundPositions->clear();
+ return;
+ }
+
+ findEntry(cat, find, searchIn, foundPositions, breakAfterFound,
+ caseSensitive, exactWordMatch, sortByLvp);
+}
+
+bool PwMDoc::compareString(const string &s1, const string &s2, bool caseSensitive,
+ bool exactWordMatch)
+{
+ QString _s1(s1.c_str());
+ QString _s2(s2.c_str());
+ if (!caseSensitive) {
+ _s1 = _s1.lower();
+ _s2 = _s2.lower();
+ }
+ if (exactWordMatch ? (_s1 == _s2) : (_s2.find(_s1) != -1))
+ return true;
+ return false;
+}
+
+bool PwMDoc::findCategory(const QString &name, unsigned int *index)
+{
+ vector<PwMCategoryItem>::iterator i = dta.begin(),
+ end = dta.end();
+ while (i != end) {
+ if ((*i).name == name.latin1()) {
+ if (index) {
+ *index = i - dta.begin();
+ }
+ return true;
+ }
+ ++i;
+ }
+ return false;
+}
+
+bool PwMDoc::renameCategory(const QString &category, const QString &newName)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat))
+ return false;
+
+ return renameCategory(cat, newName);
+}
+
+bool PwMDoc::renameCategory(unsigned int category, const QString &newName,
+ bool dontFlagDirty)
+{
+ if (category > numCategories() - 1)
+ return false;
+
+ dta[category].name = newName.latin1();
+ if (!dontFlagDirty)
+ flagDirty();
+
+ return true;
+}
+
+bool PwMDoc::delCategory(const QString &category)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat))
+ return false;
+
+ return delCategory(cat);
+}
+
+bool PwMDoc::delCategory(unsigned int category, bool dontFlagDirty)
+{
+ if (category > numCategories() - 1)
+ return false;
+
+ // We don't delete it, if it is the last existing
+ // category! Instead we rename it to "Default".
+ if (numCategories() > 1) {
+ dta.erase(dta.begin() + category);
+ } else {
+ renameCategory(category, DEFAULT_CATEGORY, dontFlagDirty);
+ return true;
+ }
+ if (!dontFlagDirty)
+ flagDirty();
+
+ return true;
+}
+
+void PwMDoc::delAllEmptyCat(bool dontFlagDirty)
+{
+ vector<PwMCategoryItem>::iterator begin = dta.begin(),
+ end = dta.end(),
+ i = begin;
+ while (i != end) {
+ if (i->d.empty()) {
+ delCategory(begin - i, dontFlagDirty);
+ }
+ ++i;
+ }
+}
+
+void PwMDoc::getCategoryList(vector<string> *list)
+{
+ PWM_ASSERT(list);
+ list->clear();
+ vector<PwMCategoryItem>::iterator i = dta.begin(),
+ end = dta.end();
+ while (i != end) {
+ list->push_back(i->name);
+ ++i;
+ }
+}
+
+void PwMDoc::getCategoryList(QStringList *list)
+{
+ PWM_ASSERT(list);
+ list->clear();
+ vector<PwMCategoryItem>::iterator i = dta.begin(),
+ end = dta.end();
+ while (i != end) {
+#ifndef PWM_EMBEDDED
+ list->push_back(i->name.c_str());
+#else
+ list->append(i->name.c_str());
+#endif
+ ++i;
+ }
+}
+
+void PwMDoc::getEntryList(const QString &category, QStringList *list)
+{
+ PWM_ASSERT(list);
+ unsigned int cat = 0;
+ if (!findCategory(category, &cat)) {
+ list->clear();
+ return;
+ }
+ getEntryList(cat, list);
+}
+
+void PwMDoc::getEntryList(const QString &category, vector<string> *list)
+{
+ PWM_ASSERT(list);
+ unsigned int cat = 0;
+ if (!findCategory(category, &cat)) {
+ list->clear();
+ return;
+ }
+ getEntryList(cat, list);
+}
+
+void PwMDoc::getEntryList(unsigned int category, vector<string> *list)
+{
+ PWM_ASSERT(list);
+ list->clear();
+ vector<PwMDataItem>::iterator begin = dta[category].d.begin(),
+ end = dta[category].d.end(),
+ i = begin;
+ while (i != end) {
+ list->push_back(i->desc);
+ ++i;
+ }
+}
+
+void PwMDoc::getEntryList(unsigned int category, QStringList *list)
+{
+ PWM_ASSERT(list);
+ list->clear();
+ vector<PwMDataItem>::iterator begin = dta[category].d.begin(),
+ end = dta[category].d.end(),
+ i = begin;
+ while (i != end) {
+#ifndef PWM_EMBEDDED
+ list->push_back(i->desc.c_str());
+#else
+ list->append(i->desc.c_str());
+#endif
+ ++i;
+ }
+}
+
+bool PwMDoc::execLauncher(const QString &category, unsigned int entryIndex)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat))
+ return false;
+
+ return execLauncher(cat, entryIndex);
+}
+
+bool PwMDoc::execLauncher(unsigned int category, unsigned int entryIndex)
+{
+ if (geteuid() == 0) {
+ rootAlertMsgBox();
+ return false;
+ }
+ QString command(dta[category].d[entryIndex].launcher.c_str());
+ bool wasLocked = isLocked(category, entryIndex);
+
+ if (command.find("$p") != -1) {
+ /* the user requested the password to be included
+ * into the command. We have to ask for the password,
+ * if it's locked. We do that by unlocking the entry
+ */
+ if (!lockAt(category, entryIndex, false))
+ return false;
+ }
+#ifndef PWM_EMBEDDED
+ command.replace("$d", dta[category].d[entryIndex].desc.c_str());
+ command.replace("$n", dta[category].d[entryIndex].name.c_str());
+ command.replace("$p", dta[category].d[entryIndex].pw.c_str());
+ command.replace("$u", dta[category].d[entryIndex].url.c_str());
+ command.replace("$c", dta[category].d[entryIndex].comment.c_str());
+#else
+ command.replace(QRegExp("$d"), dta[category].d[entryIndex].desc.c_str());
+ command.replace(QRegExp("$n"), dta[category].d[entryIndex].name.c_str());
+ command.replace(QRegExp("$p"), dta[category].d[entryIndex].pw.c_str());
+ command.replace(QRegExp("$u"), dta[category].d[entryIndex].url.c_str());
+ command.replace(QRegExp("$c"), dta[category].d[entryIndex].comment.c_str());
+#endif
+ command.append(" &");
+
+ QString customXterm(conf()->confGlobXtermCommand());
+ if (!customXterm.isEmpty())
+ command = customXterm + " " + command;
+
+ system(command.latin1());
+
+ lockAt(category, entryIndex, wasLocked);
+ return true;
+}
+
+bool PwMDoc::goToURL(const QString &category, unsigned int entryIndex)
+{
+ unsigned int cat = 0;
+
+ if (!findCategory(category, &cat))
+ return false;
+
+ return goToURL(cat, entryIndex);
+}
+
+bool PwMDoc::goToURL(unsigned int category, unsigned int entryIndex)
+{
+ if (geteuid() == 0) {
+ rootAlertMsgBox();
+ return false;
+ }
+ QString url(dta[category].d[entryIndex].url.c_str());
+ if (url.isEmpty())
+ return false;
+
+ QString customBrowser(conf()->confGlobBrowserCommand());
+ if (!customBrowser.isEmpty()) {
+ browserProc.clearArguments();
+ browserProc << customBrowser << url;
+ if (browserProc.start(KProcess::DontCare))
+ return true;
+ }
+
+ browserProc.clearArguments();
+ browserProc << "konqueror" << url;
+ if (browserProc.start(KProcess::DontCare))
+ return true;
+
+ browserProc.clearArguments();
+ browserProc << "mozilla" << url;
+ if (browserProc.start(KProcess::DontCare))
+ return true;
+
+ browserProc.clearArguments();
+ browserProc << "opera" << url;
+ if (browserProc.start(KProcess::DontCare))
+ return true;
+ return false;
+}
+
+PwMerror PwMDoc::exportToText(const QString *file)
+{
+ PWM_ASSERT(file);
+ if (QFile::exists(*file)) {
+ if (!QFile::remove(*file))
+ return e_accessFile;
+ }
+ QFile f(*file);
+ if (!f.open(IO_ReadWrite))
+ return e_openFile;
+
+ if (!unlockAll_tempoary()) {
+ f.close();
+ return e_lock;
+ }
+
+ // write header
+ string header = i18n("Password table generated by\nPwM v").latin1();
+ header += PACKAGE_VER;
+ header += i18n("\non ").latin1();
+ QDate currDate = QDate::currentDate();
+ QTime currTime = QTime::currentTime();
+
+#ifndef PWM_EMBEDDED
+ header += currDate.toString("ddd MMMM d ").latin1();
+ header += currTime.toString("hh:mm:ss ").latin1();
+#else
+ QString dfs = KGlobal::locale()->dateFormatShort();
+ bool ampm = KGlobal::locale()->use12Clock();
+ KGlobal::locale()->setDateFormatShort("%A %B %d");
+ KGlobal::locale()->setHore24Format(true);
+
+ header += KGlobal::locale()->formatDate(currDate, true, KLocale::Userdefined);
+ header += KGlobal::locale()->formatTime(currTime, true);
+ KGlobal::locale()->setDateFormatShort(dfs);
+ KGlobal::locale()->setHore24Format(!ampm);
+
+#endif
+ header += tostr(currDate.year());
+ header += "\n==============================\n\n";
+
+
+#ifndef PWM_EMBEDDED
+ if (f.writeBlock(header.c_str(), header.length()) != (Q_LONG)header.length()) {
+ unlockAll_tempoary(true);
+ f.close();
+ return e_writeFile;
+ }
+#else
+ if (f.writeBlock(header.c_str(), header.length()) != (long)header.length()) {
+ unlockAll_tempoary(true);
+ f.close();
+ return e_writeFile;
+ }
+#endif
+ unsigned int i, numCat = numCategories();
+ unsigned int j, numEnt;
+ string exp;
+ for (i = 0; i < numCat; ++i) {
+ numEnt = numEntries(i);
+
+ exp = "\n== Category: ";
+ exp += dta[i].name;
+ exp += " ==\n";
+#ifndef PWM_EMBEDDED
+ if (f.writeBlock(exp.c_str(), exp.length()) != (Q_LONG)exp.length()) {
+ unlockAll_tempoary(true);
+ f.close();
+ return e_writeFile;
+ }
+#else
+ if (f.writeBlock(exp.c_str(), exp.length()) != (long)exp.length()) {
+ unlockAll_tempoary(true);
+ f.close();
+ return e_writeFile;
+ }
+#endif
+ for (j = 0; j < numEnt; ++j) {
+ exp = "\n-- ";
+ exp += dta[i].d[j].desc;
+ exp += " --\n";
+
+ exp += i18n("Username: ").latin1();
+ exp += dta[i].d[j].name;
+ exp += "\n";
+
+ exp += i18n("Password: ").latin1();
+ exp += dta[i].d[j].pw;
+ exp += "\n";
+
+ exp += i18n("Comment: ").latin1();
+ exp += dta[i].d[j].comment;
+ exp += "\n";
+
+ exp += i18n("URL: ").latin1();
+ exp += dta[i].d[j].url;
+ exp += "\n";
+
+ exp += i18n("Launcher: ").latin1();
+ exp += dta[i].d[j].launcher;
+ exp += "\n";
+
+#ifndef PWM_EMBEDDED
+ if (f.writeBlock(exp.c_str(), exp.length()) != (Q_LONG)exp.length()) {
+ unlockAll_tempoary(true);
+ f.close();
+ return e_writeFile;
+ }
+#else
+ if (f.writeBlock(exp.c_str(), exp.length()) != (long)exp.length()) {
+ unlockAll_tempoary(true);
+ f.close();
+ return e_writeFile;
+ }
+#endif
+ }
+ }
+ unlockAll_tempoary(true);
+ f.close();
+
+ return e_success;
+}
+
+PwMerror PwMDoc::importFromText(const QString *file, int format)
+{
+ PWM_ASSERT(file);
+ if (format == 0)
+ return importText_PwM(file);
+ else if (format == -1) {
+ // probe for all formats
+ if (importText_PwM(file) == e_success)
+ return e_success;
+ dta.clear();
+ emitDataChanged(this);
+ // add next format here...
+ return e_fileFormat;
+ }
+ return e_invalidArg;
+}
+
+PwMerror PwMDoc::importText_PwM(const QString *file)
+{
+ PWM_ASSERT(file);
+ FILE *f;
+ int tmp;
+ ssize_t ret;
+ string curCat;
+ unsigned int entriesRead = 0;
+ PwMDataItem currItem;
+ f = fopen(file->latin1(), "r");
+ if (!f)
+ return e_openFile;
+ size_t ch_tmp_size = 1024;
+ char *ch_tmp = (char*)malloc(ch_tmp_size);
+ if (!ch_tmp) {
+ fclose(f);
+ return e_outOfMem;
+ }
+
+ // - check header
+ if (getline(&ch_tmp, &ch_tmp_size, f) == -1) // skip first line.
+ goto formatError;
+ // check version-string and return version in "ch_tmp".
+ if (fscanf(f, "PwM v%s", ch_tmp) != 1) {
+ // header not recognized as PwM generated header
+ goto formatError;
+ }
+ // set filepointer behind version-string-line previously checked
+ if (getline(&ch_tmp, &ch_tmp_size, f) == -1)
+ goto formatError;
+ // skip next line containing the build-date
+ if (getline(&ch_tmp, &ch_tmp_size, f) == -1)
+ goto formatError;
+ // read header termination line
+ if (getline(&ch_tmp, &ch_tmp_size, f) == -1)
+ goto formatError;
+ if (strcmp(ch_tmp, "==============================\n"))
+ goto formatError;
+
+ // - read entries
+ do {
+ // find beginning of next category
+ do {
+ tmp = fgetc(f);
+ } while (tmp == '\n' && tmp != EOF);
+ if (tmp == EOF)
+ break;
+
+ // decrement filepos by one
+ fseek(f, -1, SEEK_CUR);
+ // read cat-name
+ if (getline(&ch_tmp, &ch_tmp_size, f) == -1)
+ goto formatError;
+ // check cat-name format
+ if (memcmp(ch_tmp, "== Category: ", 13) != 0)
+ goto formatError;
+ if (memcmp(ch_tmp + (strlen(ch_tmp) - 1 - 3), " ==", 3) != 0)
+ goto formatError;
+ // copy cat-name
+ curCat.assign(ch_tmp + 13, strlen(ch_tmp) - 1 - 16);
+
+ do {
+ // find beginning of next entry
+ do {
+ tmp = fgetc(f);
+ } while (tmp == '\n' && tmp != EOF && tmp != '=');
+ if (tmp == EOF)
+ break;
+ if (tmp == '=') {
+ fseek(f, -1, SEEK_CUR);
+ break;
+ }
+ // decrement filepos by one
+ fseek(f, -1, SEEK_CUR);
+ // read desc-line
+ if (getline(&ch_tmp, &ch_tmp_size, f) == -1)
+ goto formatError;
+ // check desc-line format
+ if (memcmp(ch_tmp, "-- ", 3) != 0)
+ goto formatError;
+ if (memcmp(ch_tmp + (strlen(ch_tmp) - 1 - 3), " --", 3) != 0)
+ goto formatError;
+ // add desc-line
+ currItem.desc.assign(ch_tmp + 3, strlen(ch_tmp) - 1 - 6);
+
+ // read username-line
+ if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1)
+ goto formatError;
+ if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.name))
+ goto formatError;
+
+ // read pw-line
+ if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1)
+ goto formatError;
+ if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.pw))
+ goto formatError;
+
+ // read comment-line
+ if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1)
+ goto formatError;
+ if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.comment))
+ goto formatError;
+
+ // read URL-line
+ if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1)
+ goto formatError;
+ if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.url))
+ goto formatError;
+
+ // read launcher-line
+ if ((ret = getline(&ch_tmp, &ch_tmp_size, f)) == -1)
+ goto formatError;
+ if (!textExtractEntry_PwM(ch_tmp, ret, &currItem.launcher))
+ goto formatError;
+
+ currItem.lockStat = true;
+ currItem.listViewPos = -1;
+ addEntry(curCat.c_str(), &currItem, true);
+ ++entriesRead;
+ } while (1);
+ } while (1);
+ if (!entriesRead)
+ goto formatError;
+
+ free(ch_tmp);
+ fclose(f);
+ flagDirty();
+ return e_success;
+
+ formatError:
+ free(ch_tmp);
+ fclose(f);
+ return e_fileFormat;
+}
+
+bool PwMDoc::textExtractEntry_PwM(const char *in, ssize_t in_size, string *out)
+{
+ PWM_ASSERT(in && out);
+ ssize_t i = 0, len = in_size - 1;
+ while (i < len) {
+ if (in[i] == ':')
+ break;
+ ++i;
+ }
+ i += 2;
+ *out = "";
+ out->append(in + i, in_size - i - 1);
+ return true;
+}
+
+PwMerror PwMDoc::exportToGpasman(const QString *file)
+{
+ PWM_ASSERT(file);
+ GpasmanFile gp;
+ int ret;
+
+ if (!unlockAll_tempoary())
+ return e_lock;
+
+ QString gpmPassword;
+ while (1) {
+ gpmPassword = requestNewMpw(0);
+ if (gpmPassword == "") {
+ unlockAll_tempoary(true);
+ return e_noPw;
+ }
+ if (gpmPassword.length() < 4) {
+ gpmPwLenErrMsgBox();
+ } else {
+ break;
+ }
+ }
+
+ ret = gp.save_init(file->latin1(), gpmPassword.latin1());
+ if (ret != 1) {
+ unlockAll_tempoary(true);
+ return e_accessFile;
+ }
+
+ char *entry[4];
+ unsigned int numCat = numCategories(), i;
+ unsigned int numEntr, j;
+ int descLen, nameLen, pwLen, commentLen;
+ for (i = 0; i < numCat; ++i) {
+ numEntr = numEntries(i);
+ for (j = 0; j < numEntr; ++j) {
+ descLen = dta[i].d[j].desc.length();
+ nameLen = dta[i].d[j].name.length();
+ pwLen = dta[i].d[j].pw.length();
+ commentLen = dta[i].d[j].comment.length();
+ entry[0] = new char[descLen + 1];
+ entry[1] = new char[nameLen + 1];
+ entry[2] = new char[pwLen + 1];
+ entry[3] = new char[commentLen + 1];
+ strcpy(entry[0], descLen == 0 ? " " : dta[i].d[j].desc.c_str());
+ strcpy(entry[1], nameLen == 0 ? " " : dta[i].d[j].name.c_str());
+ strcpy(entry[2], pwLen == 0 ? " " : dta[i].d[j].pw.c_str());
+ strcpy(entry[3], commentLen == 0 ? " " : dta[i].d[j].comment.c_str());
+ entry[0][descLen == 0 ? descLen + 1 : descLen] = '\0';
+ entry[1][nameLen == 0 ? nameLen + 1 : nameLen] = '\0';
+ entry[2][pwLen == 0 ? pwLen + 1 : pwLen] = '\0';
+ entry[3][commentLen == 0 ? commentLen + 1 : commentLen] = '\0';
+
+ ret = gp.save_entry(entry);
+ if (ret == -1){
+ delete [] entry[0];
+ delete [] entry[1];
+ delete [] entry[2];
+ delete [] entry[3];
+ gp.save_finalize();
+ unlockAll_tempoary(true);
+ return e_writeFile;
+ }
+
+ delete [] entry[0];
+ delete [] entry[1];
+ delete [] entry[2];
+ delete [] entry[3];
+ }
+ }
+ unlockAll_tempoary(true);
+ if (gp.save_finalize() == -1)
+ return e_writeFile;
+
+ return e_success;
+}
+
+PwMerror PwMDoc::importFromGpasman(const QString *file)
+{
+ PWM_ASSERT(file);
+ QString pw = requestMpw(false);
+ if (pw == "")
+ return e_noPw;
+ GpasmanFile gp;
+ int ret, i;
+ PwMerror ret2;
+ char *entry[4];
+ PwMDataItem tmpData;
+ ret = gp.load_init(file->latin1(), pw.latin1());
+ if (ret != 1)
+ return e_accessFile;
+
+ do {
+ ret = gp.load_entry(entry);
+ if(ret != 1)
+ break;
+ tmpData.desc = entry[0];
+ tmpData.name = entry[1];
+ tmpData.pw = entry[2];
+ tmpData.comment = entry[3];
+ tmpData.lockStat = true;
+ tmpData.listViewPos = -1;
+ ret2 = addEntry(DEFAULT_CATEGORY, &tmpData, true);
+ for (i = 0; i < 4; ++i)
+ free(entry[i]);
+ if (ret2 == e_maxAllowedEntr) {
+ gp.load_finalize();
+ return e_maxAllowedEntr;
+ }
+ } while (1);
+ gp.load_finalize();
+ if (isDocEmpty())
+ return e_wrongPw; // we assume this.
+
+ flagDirty();
+ return e_success;
+}
+
+void PwMDoc::ensureLvp()
+{
+ if (isDocEmpty())
+ return;
+
+ vector< vector<PwMDataItem>::iterator > undefined;
+ vector< vector<PwMDataItem>::iterator >::iterator undefBegin,
+ undefEnd,
+ undefI;
+ vector<PwMCategoryItem>::iterator catBegin = dta.begin(),
+ catEnd = dta.end(),
+ catI = catBegin;
+ vector<PwMDataItem>::iterator entrBegin, entrEnd, entrI;
+ int lvpTop, tmpLvp;
+
+ while (catI != catEnd) {
+ lvpTop = -1;
+ undefined.clear();
+
+ entrBegin = catI->d.begin();
+ entrEnd = catI->d.end();
+ entrI = entrBegin;
+
+ while (entrI != entrEnd) {
+ tmpLvp = entrI->listViewPos;
+ if (tmpLvp == -1)
+ undefined.push_back(entrI);
+ else if (tmpLvp > lvpTop)
+ lvpTop = tmpLvp;
+ ++entrI;
+ }
+ undefBegin = undefined.begin();
+ undefEnd = undefined.end();
+ undefI = undefBegin;
+ while (undefI != undefEnd) {
+ (*undefI)->listViewPos = ++lvpTop;
+ ++undefI;
+ }
+ ++catI;
+ }
+}
+
+QString PwMDoc::getTitle()
+{
+ /* NOTE: We have to ensure, that the returned title
+ * is unique and not reused somewhere else while
+ * this document is valid (open).
+ */
+ QString title(getFilename());
+ if (title.isEmpty()) {
+ if (unnamedNum == 0) {
+ unnamedNum = PwMDocList::getNewUnnamedNumber();
+ PWM_ASSERT(unnamedNum != 0);
+ }
+ title = DEFAULT_TITLE;
+ title += " ";
+ title += tostr(unnamedNum).c_str();
+ }
+ return title;
+}
+
+bool PwMDoc::tryDelete()
+{
+ if (deleted)
+ return true;
+ int ret;
+ if (isDirty()) {
+ ret = dirtyAskSave(getTitle());
+ if (ret == 0) { // save to disk
+ if (!saveDocUi(this))
+ goto out_ignore;
+ } else if (ret == 1) { // don't save and delete
+ goto out_accept;
+ } else { // cancel operation
+ goto out_ignore;
+ }
+ }
+out_accept:
+ deleted = true;
+ delete this;
+ return true;
+out_ignore:
+ return false;
+}
+
+#ifndef PWM_EMBEDDED
+#include "pwmdoc.moc"
+#endif