summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--pwmanager/pwmanager/csv.cpp428
-rw-r--r--pwmanager/pwmanager/csv.h91
-rw-r--r--pwmanager/pwmanager/pwm.cpp85
-rw-r--r--pwmanager/pwmanager/pwm.h4
-rw-r--r--pwmanager/pwmanager/pwmanagerE.pro2
5 files changed, 608 insertions, 2 deletions
diff --git a/pwmanager/pwmanager/csv.cpp b/pwmanager/pwmanager/csv.cpp
new file mode 100644
index 0000000..194edf2
--- a/dev/null
+++ b/pwmanager/pwmanager/csv.cpp
@@ -0,0 +1,428 @@
+/***************************************************************************
+ * *
+ * copyright (C) 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 1.1 of pwmanager
+ * and was modified to run on embedded devices that run microkde
+ * The original file version was 1.2
+ * $Id$
+ **************************************************************************/
+
+#include "csv.h"
+#include "pwmdoc.h"
+#include "pwmexception.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#define MAX_CSV_FILE_SIZE (50 * 1024 * 1024) // bytes
+
+
+Csv::Csv(QWidget *_parent)
+ : parent (_parent)
+{
+}
+
+Csv::~Csv()
+{
+}
+
+bool Csv::importData(const QString &filepath,
+ PwMDoc *doc)
+{
+ bool ret = true;
+ QByteArray d;
+ QFile f(filepath);
+ if (!f.open(IO_ReadOnly)) {
+ KMessageBox::error(parent,
+ i18n("Could not open file.\n"
+ "Does the file exist?"),
+ i18n("Open error."));
+ ret = false;
+ goto out;
+ }
+ if (f.size() > MAX_CSV_FILE_SIZE) {
+ KMessageBox::error(parent,
+ i18n("File too big.\nMaximum file size is 1 Byte.", "File too big.\nMaximum file size is %n Bytes.", MAX_CSV_FILE_SIZE),
+ i18n("File too big."));
+ ret = false;
+ goto out_close;
+ }
+ d = f.readAll();
+ if (d.isEmpty()) {
+ KMessageBox::error(parent,
+ i18n("Could not read file or file empty."),
+ i18n("Reading failed."));
+ ret = false;
+ goto out_close;
+ }
+ if (!doImport(d, doc)) {
+ KMessageBox::error(parent,
+ i18n("Import failed.\n"
+ "Corrupt CSV data format."),
+ i18n("File corrupt."));
+ ret = false;
+ goto out_close;
+ }
+
+out_close:
+ f.close();
+out:
+ return ret;
+}
+
+bool Csv::doImport(const QByteArray &d,
+ PwMDoc *doc)
+{
+ PwMDataItem di;
+ //US ENH: initialize all members:
+ di.clear();
+
+ int refIndex = 0;
+ int ret;
+ QCString s, curCat;
+ int fieldIndex = 0;
+ bool inRecord = false;
+ /* fieldIndex is a reference count to see which
+ * value we are attaching to di.
+ * Valid counts are:
+ * 0 -> category
+ * 1 -> desc
+ * 2 -> name
+ * 3 -> pw
+ * 4 -> url
+ * 5 -> launcher
+ * 6 -> comment
+ */
+
+ while (1) {
+ ret = nextField(&s, d, inRecord, &refIndex);
+ switch (ret) {
+ case 0:
+ // successfully got next field.
+ inRecord = true;
+ switch (fieldIndex) {
+ case 0: // category
+ if (s.isEmpty()) {
+ /* This is special case. It's the category
+ * list terminating empty field.
+ */
+ ++fieldIndex;
+ } else
+ curCat = s;
+ break;
+ case 1: // desc
+ di.desc = s;
+ ++fieldIndex;
+ break;
+ case 2: // name
+ di.name = s;
+ ++fieldIndex;
+ break;
+ case 3: // pw
+ di.pw = s;
+ ++fieldIndex;
+ break;
+ case 4: // url
+ di.url = s;
+ ++fieldIndex;
+ break;
+ case 5: // launcher
+ di.launcher = s;
+ ++fieldIndex;
+ break;
+ case 6: // comment
+ di.comment = s;
+ ++fieldIndex;
+ break;
+ default:
+ /* Too many fields in a record.
+ * We simply throw it away.
+ */
+ break;
+ }
+ break;
+ case 1:
+ // record complete.
+ if (fieldIndex == 6)
+ di.comment = s;
+ inRecord = false;
+ fieldIndex = 0;
+ doc->addEntry(curCat, &di, true);
+ //US ENH: clear di for the next row
+ di.clear();
+ break;
+ case 2:
+ // data completely parsed.
+ doc->flagDirty();
+ return true;
+ case -1:
+ // parse error
+ doc->flagDirty();
+ return false;
+ }
+ }
+ BUG();
+ return false;
+}
+
+int Csv::nextField(QCString *ret,
+ const QByteArray &in,
+ bool inRecord,
+ int *_refIndex)
+{
+ int rv = -2;
+ char c;
+ bool inField = false;
+ bool isQuoted = false;
+ bool searchingTerminator = false;
+ int refIndex;
+ int inSize = static_cast<int>(in.size());
+ ret->truncate(0);
+
+ for (refIndex = *_refIndex; refIndex < inSize; ++refIndex) {
+ c = in.at(refIndex);
+ if (!inField) {
+ // we have to search the field beginning, now.
+ switch (c) {
+ case ' ': // space
+ case ' ': // tab
+ // hm, still not the beginning. Go on..
+ break;
+ case '\r':
+ case '\n':
+ if (inRecord) {
+ /* This is the end of the last field in
+ * the record.
+ */
+ PWM_ASSERT(ret->isEmpty());
+ rv = 1; // record end
+ goto out;
+ } else {
+ // hm, still not the beginning. Go on..
+ break;
+ }
+ case ',':
+ // Oh, an empty field. How sad.
+ PWM_ASSERT(ret->isEmpty());
+ rv = 0; // field end
+ goto out;
+ case '\"':
+ // this is the quoted beginning.
+ inField = true;
+ isQuoted = true;
+ if (refIndex + 1 >= inSize)
+ goto unexp_eof;
+ break;
+ default:
+ // this is the unquoted beginning.
+ inField = true;
+ isQuoted = false;
+ *ret += c;
+ break;
+ }
+ } else {
+ // we are in the field now. Search the end.
+ if (isQuoted) {
+ if (searchingTerminator) {
+ switch (c) {
+ case '\r':
+ case '\n':
+ rv = 1; // record end
+ goto out;
+ case ',':
+ // found it.
+ rv = 0; // field end
+ goto out;
+ default:
+ // go on.
+ continue;
+ }
+ }
+ switch (c) {
+ case '\"':
+ /* check if this is the end of the
+ * entry, or just an inline escaped quote.
+ */
+ char next;
+ if (refIndex + 1 >= inSize) {
+ // This is the last char, so it's the end.
+ rv = 2; // data end
+ goto out;
+ }
+ next = in.at(refIndex + 1);
+ switch (next) {
+ case '\"':
+ // This is an escaped double quote.
+ // So skip next iteration.
+ refIndex += 1;
+ *ret += c;
+ break;
+ default:
+ /* end of field.
+ * We have to search the comma (or newline...),
+ * which officially terminates the entry.
+ */
+ searchingTerminator = true;
+ break;
+ }
+ break;
+ default:
+ // nothing special about the char. Go on!
+ *ret += c;
+ break;
+ }
+ } else {
+ switch (c) {
+ case '\"':
+ // This is not allowed here.
+ return -1; // parser error
+ case '\r':
+ case '\n':
+ rv = 1; // record end
+ goto out;
+ case ',':
+ rv = 0; // field end
+ goto out;
+ default:
+ // nothing special about the char. Go on!
+ *ret += c;
+ break;
+ }
+ }
+ }
+ }
+ // we are at the end of the stream, now!
+ if (searchingTerminator) {
+ /* Ok, there's no terminating comma (or newline...),
+ * because we are at the end. That's perfectly fine.
+ */
+ PWM_ASSERT(inField);
+ rv = 2; // data end
+ goto out;
+ }
+ if (!isQuoted && inField) {
+ // That's the end of the last unquoted field.
+ rv = 2; // data end
+ goto out;
+ }
+ if (!inField) {
+ // This is expected EOF
+ rv = 2; // data end
+ goto out;
+ }
+
+unexp_eof:
+ printDebug("unexpected EOF :(");
+ return -1; // parser error
+
+out:
+ if (!isQuoted)
+ *ret = ret->stripWhiteSpace();
+ *_refIndex = refIndex + 1;
+ return rv;
+}
+
+bool Csv::exportData(const QString &filepath,
+ PwMDoc *doc)
+{
+ PWM_ASSERT(!doc->isDocEmpty());
+ bool ret = true;
+ if (QFile::exists(filepath)) {
+ int ret;
+ ret = KMessageBox::questionYesNo(parent,
+ i18n("This file does already exist.\n"
+ "Do you want to overwrite it?"),
+ i18n("Overwrite file?"));
+ if (ret == KMessageBox::No)
+ return false;
+ if (!QFile::remove(filepath)) {
+ KMessageBox::error(parent,
+ i18n("Could not delete the old file."),
+ i18n("Delete error."));
+ return false;
+ }
+ }
+ QFile f(filepath);
+ if (!f.open(IO_ReadWrite)) {
+ KMessageBox::error(parent,
+ i18n("Could not open file for writing."),
+ i18n("Open error."));
+ ret = false;
+ goto out;
+ }
+ doc->unlockAll_tempoary();
+ if (!doExport(f, doc))
+ ret = false;
+ doc->unlockAll_tempoary(true);
+ f.close();
+out:
+ return ret;
+}
+
+bool Csv::doExport(QFile &f,
+ PwMDoc *doc)
+{
+ unsigned int numCat = doc->numCategories();
+ unsigned int numEntr;
+ unsigned int i, j;
+ PwMDataItem d;
+ QCString s, catName;
+ QByteArray b;
+
+ for (i = 0; i < numCat; ++i) {
+ numEntr = doc->numEntries(i);
+ catName = newField(doc->getCategory(i)->c_str());
+ for (j = 0; j < numEntr; ++j) {
+ doc->getEntry(i, j, &d);
+ s = catName;
+ s += ",,";
+ s += newField(d.desc.c_str());
+ s += ",";
+ s += newField(d.name.c_str());
+ s += ",";
+ s += newField(d.pw.c_str());
+ s += ",";
+ s += newField(d.url.c_str());
+ s += ",";
+ s += newField(d.launcher.c_str());
+ s += ",";
+ s += newField(d.comment.c_str());
+ s += "\r\n";
+ b = s;
+ // remove \0 termination
+#ifndef PWM_EMBEDDED
+ b.resize(b.size() - 1, QGArray::SpeedOptim);
+#else
+ b.resize(b.size() - 1);
+#endif
+ if (!f.writeBlock(b))
+ return false;
+ }
+ }
+ return true;
+}
+
+QCString Csv::newField(QCString s)
+{
+ if (s.isEmpty())
+ return QCString();
+ QCString ret("\"");
+#ifndef PWM_EMBEDDED
+ s.replace('\"', "\"\"");
+#else
+ s.replace(QRegExp("\""), "\"\"");
+#endif
+ ret += s;
+ ret += "\"";
+ return ret;
+}
diff --git a/pwmanager/pwmanager/csv.h b/pwmanager/pwmanager/csv.h
new file mode 100644
index 0000000..6f3c1e1
--- a/dev/null
+++ b/pwmanager/pwmanager/csv.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * *
+ * copyright (C) 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 1.1 of pwmanager
+ * and was modified to run on embedded devices that run microkde
+ * The original file version was 1.2
+ * $Id$
+ **************************************************************************/
+
+
+#ifndef __PWMANAGER_CSV_H
+#define __PWMANAGER_CSV_H
+
+#include <qcstring.h>
+#include <qfile.h>
+
+
+class PwMDoc;
+class QString;
+class QWidget;
+
+/** class for CSV (Comma Separated Value) export and import.
+ *
+ * http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
+ * http://en.wikipedia.org/wiki/Comma-separated_values
+ *
+ * There are two types of CSV output we can produce.
+ * One with Category support (recommended):
+ * "Category 1",, "Desc 1", "Username 1", "Password 1", "URL 1", "Launcher 1", "Comment 1"
+ * "Category 1",, "Desc 2", "Username 2", "Password 2", "URL 2", "Launcher 2", "Comment 2"
+ * ...
+ * The empty "" is neccessary, because in future versions we will
+ * support nested Categories. We want to be future compatible, now.
+ *
+ * And one without Category support:
+ *FIXME: it's not implemented, yet. ;)
+ * "Desc 1", "Username 1", "Password 1", "URL 1", "Launcher 1", "Comment 1"
+ * "Desc 2", "Username 2", "Password 2", "URL 2", "Launcher 2", "Comment 2"
+ * ...
+ *
+ */
+class Csv
+{
+public:
+ Csv(QWidget *_parent);
+ ~Csv();
+
+ /** import data from "filepath" into "doc" */
+ bool importData(const QString &filepath,
+ PwMDoc *doc);
+ /** export data from "doc" to "filepath" */
+ bool exportData(const QString &filepath,
+ PwMDoc *doc);
+
+protected:
+ /** do the import process. */
+ bool doImport(const QByteArray &d,
+ PwMDoc *doc);
+ /** parse for the next field.
+ * @return Return values are:
+ * 0 -> successfully got the next field.
+ * 1 -> record end.
+ * 2 -> data end.
+ * -1 -> parser error.
+ */
+ int nextField(QCString *ret,
+ const QByteArray &in,
+ bool inRecord,
+ int *_refIndex);
+ /** do the export process. */
+ bool doExport(QFile &f,
+ PwMDoc *doc);
+ /** generate a new data field string. */
+ QCString newField(QCString s);
+
+protected:
+ /** current parent widget. */
+ QWidget *parent;
+};
+
+#endif // __PWMANAGER_CSV_H
diff --git a/pwmanager/pwmanager/pwm.cpp b/pwmanager/pwmanager/pwm.cpp
index 66d26d6..ac0c978 100644
--- a/pwmanager/pwmanager/pwm.cpp
+++ b/pwmanager/pwmanager/pwm.cpp
@@ -58,2 +58,3 @@
#include "findwndimpl.h"
+#include "csv.h"
@@ -115,3 +116,4 @@ enum {
BUTTON_POPUP_EXPORT_TEXT = 0,
- BUTTON_POPUP_EXPORT_GPASMAN
+ BUTTON_POPUP_EXPORT_GPASMAN,
+ BUTTON_POPUP_EXPORT_CSV
#ifdef CONFIG_KWALLETIF
@@ -123,3 +125,4 @@ enum {
BUTTON_POPUP_IMPORT_TEXT = 0,
- BUTTON_POPUP_IMPORT_GPASMAN
+ BUTTON_POPUP_IMPORT_GPASMAN,
+ BUTTON_POPUP_IMPORT_CSV
#ifdef CONFIG_KWALLETIF
@@ -183,2 +186,3 @@ PwM::~PwM()
{
+ //qDebug("PwM::~PwM()");
disconnect(curDoc(), SIGNAL(docClosed(PwMDoc *)),
@@ -187,2 +191,3 @@ PwM::~PwM()
emit closed(this);
+ //qDebug("PwM::~PwM() emited closed(this)");
delete view;
@@ -243,2 +248,4 @@ void PwM::initMenubar()
SLOT(exportToGpasman()), 0, BUTTON_POPUP_EXPORT_GPASMAN);
+ exportPopup->insertItem(i18n("&CSV (Comma Separated Value) ..."), this,
+ SLOT(exportToCsv()), 0, BUTTON_POPUP_EXPORT_CSV);
#ifdef CONFIG_KWALLETIF
@@ -255,2 +262,4 @@ void PwM::initMenubar()
SLOT(importFromGpasman()), 0, BUTTON_POPUP_IMPORT_GPASMAN);
+ importPopup->insertItem(i18n("&CSV (Comma Separated Value) ..."), this,
+ SLOT(importCsv()), 0, BUTTON_POPUP_IMPORT_CSV);
#ifdef CONFIG_KWALLETIF
@@ -1018,2 +1027,74 @@ void PwM::exportToGpasman()
+
+
+void PwM::exportToCsv()
+{
+ PWM_ASSERT(curDoc());
+ if (curDoc()->isDocEmpty()) {
+ KMessageBox::information(this,
+ i18n
+ ("Sorry, there is nothing to export;\n"
+ "please add some passwords first."),
+ i18n("Nothing to Do"));
+ return;
+ }
+
+ curDoc()->timer()->getLock(DocTimer::id_autoLockTimer);
+ QString fn(KFileDialog::getSaveFileName("*.csv", i18n("*|CSV Text File"), this));
+ if (fn.isEmpty()) {
+ curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
+ return;
+ }
+
+ Csv csv(this);
+ if (!csv.exportData(fn, curDoc())) {
+ curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
+ showStatMsg(i18n("CSV file export failed."));
+ return;
+ }
+ showStatMsg(i18n("Successfully exported data."));
+ curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
+}
+
+bool PwM::importCsv()
+{
+ Csv csv(this);
+ if (!isVirgin()) {
+ if (KMessageBox::questionYesNo(this,
+ i18n("Do you want to import the data\n"
+ "into the current document? (If you\n"
+ "select \"no\", a new document will be\n"
+ "opened.)"),
+ i18n("Import into This Document?"))
+ == KMessageBox::No) {
+ // import the data to a new window.
+ PwM *newInstance = init->createMainWnd();
+ bool ok = newInstance->importCsv();
+ if (!ok) {
+ newInstance->setForceQuit(true);
+ delete_and_null(newInstance);
+ }
+ return ok;
+ }
+ }
+
+ QString filename = KFileDialog::getOpenFileName("*.csv", i18n("*|CSV Text File"), this);
+ if (filename.isEmpty())
+ return false;
+ curDoc()->timer()->getLock(DocTimer::id_autoLockTimer);
+ if (!csv.importData(filename, curDoc())) {
+ curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
+ showStatMsg(i18n("CSV file import failed."));
+ return false;
+ }
+ curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
+ KMessageBox::information(this,
+ i18n("Successfully imported the CSV data\n"
+ "into the current document."), i18n("Successfully Imported"));
+ showStatMsg(i18n("Successfully imported"));
+ setVirgin(false);
+ return true;
+}
+
+
void PwM::exportToKWallet()
diff --git a/pwmanager/pwmanager/pwm.h b/pwmanager/pwmanager/pwm.h
index 6ab9d6b..5822d59 100644
--- a/pwmanager/pwmanager/pwm.h
+++ b/pwmanager/pwmanager/pwm.h
@@ -126,2 +126,4 @@ public slots:
void exportToKWallet();
+ /** file/export/csv triggered */
+ void exportToCsv();
/** file/import/text triggered */
@@ -132,2 +134,4 @@ public slots:
bool importKWallet();
+ /** file/import/csv triggered */
+ bool importCsv();
/** file/print triggered */
diff --git a/pwmanager/pwmanager/pwmanagerE.pro b/pwmanager/pwmanager/pwmanagerE.pro
index 1445bcf..c46e937 100644
--- a/pwmanager/pwmanager/pwmanagerE.pro
+++ b/pwmanager/pwmanager/pwmanagerE.pro
@@ -64,2 +64,3 @@ compiler.h \
compressgzip.h \
+csv.h \
findwnd_emb.h \
@@ -129,2 +130,3 @@ commentbox.cpp \
compressgzip.cpp \
+csv.cpp \
findwnd_emb.cpp \