summaryrefslogtreecommitdiffabout
authorulf69 <ulf69>2004-10-22 18:48:35 (UTC)
committer ulf69 <ulf69>2004-10-22 18:48:35 (UTC)
commita5274f27dc71e1a0ffae73f32f84f4dd013b4b76 (patch) (unidiff)
tree5c2e3e105fa9df8999752a314455d0f424bf474b
parent163b74a23d102074fc0adefddba5b4fa9d4dd2a5 (diff)
downloadkdepimpi-a5274f27dc71e1a0ffae73f32f84f4dd013b4b76.zip
kdepimpi-a5274f27dc71e1a0ffae73f32f84f4dd013b4b76.tar.gz
kdepimpi-a5274f27dc71e1a0ffae73f32f84f4dd013b4b76.tar.bz2
added csv import/export
Diffstat (more/less context) (ignore 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 @@
1/***************************************************************************
2 * *
3 * copyright (C) 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 1.1 of pwmanager
15 * and was modified to run on embedded devices that run microkde
16 * The original file version was 1.2
17 * $Id$
18 **************************************************************************/
19
20#include "csv.h"
21#include "pwmdoc.h"
22#include "pwmexception.h"
23
24#include <kmessagebox.h>
25#include <klocale.h>
26
27 #define MAX_CSV_FILE_SIZE(50 * 1024 * 1024) // bytes
28
29
30Csv::Csv(QWidget *_parent)
31 : parent (_parent)
32{
33}
34
35Csv::~Csv()
36{
37}
38
39bool Csv::importData(const QString &filepath,
40 PwMDoc *doc)
41{
42 bool ret = true;
43 QByteArray d;
44 QFile f(filepath);
45 if (!f.open(IO_ReadOnly)) {
46 KMessageBox::error(parent,
47 i18n("Could not open file.\n"
48 "Does the file exist?"),
49 i18n("Open error."));
50 ret = false;
51 goto out;
52 }
53 if (f.size() > MAX_CSV_FILE_SIZE) {
54 KMessageBox::error(parent,
55 i18n("File too big.\nMaximum file size is 1 Byte.", "File too big.\nMaximum file size is %n Bytes.", MAX_CSV_FILE_SIZE),
56 i18n("File too big."));
57 ret = false;
58 goto out_close;
59 }
60 d = f.readAll();
61 if (d.isEmpty()) {
62 KMessageBox::error(parent,
63 i18n("Could not read file or file empty."),
64 i18n("Reading failed."));
65 ret = false;
66 goto out_close;
67 }
68 if (!doImport(d, doc)) {
69 KMessageBox::error(parent,
70 i18n("Import failed.\n"
71 "Corrupt CSV data format."),
72 i18n("File corrupt."));
73 ret = false;
74 goto out_close;
75 }
76
77out_close:
78 f.close();
79out:
80 return ret;
81}
82
83bool Csv::doImport(const QByteArray &d,
84 PwMDoc *doc)
85{
86 PwMDataItem di;
87 //US ENH: initialize all members:
88 di.clear();
89
90 int refIndex = 0;
91 int ret;
92 QCString s, curCat;
93 int fieldIndex = 0;
94 bool inRecord = false;
95 /* fieldIndex is a reference count to see which
96 * value we are attaching to di.
97 * Valid counts are:
98 * 0 -> category
99 * 1 -> desc
100 * 2 -> name
101 * 3 -> pw
102 * 4 -> url
103 * 5 -> launcher
104 * 6 -> comment
105 */
106
107 while (1) {
108 ret = nextField(&s, d, inRecord, &refIndex);
109 switch (ret) {
110 case 0:
111 // successfully got next field.
112 inRecord = true;
113 switch (fieldIndex) {
114 case 0: // category
115 if (s.isEmpty()) {
116 /* This is special case. It's the category
117 * list terminating empty field.
118 */
119 ++fieldIndex;
120 } else
121 curCat = s;
122 break;
123 case 1:// desc
124 di.desc = s;
125 ++fieldIndex;
126 break;
127 case 2: // name
128 di.name = s;
129 ++fieldIndex;
130 break;
131 case 3: // pw
132 di.pw = s;
133 ++fieldIndex;
134 break;
135 case 4: // url
136 di.url = s;
137 ++fieldIndex;
138 break;
139 case 5: // launcher
140 di.launcher = s;
141 ++fieldIndex;
142 break;
143 case 6: // comment
144 di.comment = s;
145 ++fieldIndex;
146 break;
147 default:
148 /* Too many fields in a record.
149 * We simply throw it away.
150 */
151 break;
152 }
153 break;
154 case 1:
155 // record complete.
156 if (fieldIndex == 6)
157 di.comment = s;
158 inRecord = false;
159 fieldIndex = 0;
160 doc->addEntry(curCat, &di, true);
161 //US ENH: clear di for the next row
162 di.clear();
163 break;
164 case 2:
165 // data completely parsed.
166 doc->flagDirty();
167 return true;
168 case -1:
169 // parse error
170 doc->flagDirty();
171 return false;
172 }
173 }
174 BUG();
175 return false;
176}
177
178int Csv::nextField(QCString *ret,
179 const QByteArray &in,
180 bool inRecord,
181 int *_refIndex)
182{
183 int rv = -2;
184 char c;
185 bool inField = false;
186 bool isQuoted = false;
187 bool searchingTerminator = false;
188 int refIndex;
189 int inSize = static_cast<int>(in.size());
190 ret->truncate(0);
191
192 for (refIndex = *_refIndex; refIndex < inSize; ++refIndex) {
193 c = in.at(refIndex);
194 if (!inField) {
195 // we have to search the field beginning, now.
196 switch (c) {
197 case ' ': // space
198 case '': // tab
199 // hm, still not the beginning. Go on..
200 break;
201 case '\r':
202 case '\n':
203 if (inRecord) {
204 /* This is the end of the last field in
205 * the record.
206 */
207 PWM_ASSERT(ret->isEmpty());
208 rv = 1; // record end
209 goto out;
210 } else {
211 // hm, still not the beginning. Go on..
212 break;
213 }
214 case ',':
215 // Oh, an empty field. How sad.
216 PWM_ASSERT(ret->isEmpty());
217 rv = 0; // field end
218 goto out;
219 case '\"':
220 // this is the quoted beginning.
221 inField = true;
222 isQuoted = true;
223 if (refIndex + 1 >= inSize)
224 goto unexp_eof;
225 break;
226 default:
227 // this is the unquoted beginning.
228 inField = true;
229 isQuoted = false;
230 *ret += c;
231 break;
232 }
233 } else {
234 // we are in the field now. Search the end.
235 if (isQuoted) {
236 if (searchingTerminator) {
237 switch (c) {
238 case '\r':
239 case '\n':
240 rv = 1; // record end
241 goto out;
242 case ',':
243 // found it.
244 rv = 0; // field end
245 goto out;
246 default:
247 // go on.
248 continue;
249 }
250 }
251 switch (c) {
252 case '\"':
253 /* check if this is the end of the
254 * entry, or just an inline escaped quote.
255 */
256 char next;
257 if (refIndex + 1 >= inSize) {
258 // This is the last char, so it's the end.
259 rv = 2; // data end
260 goto out;
261 }
262 next = in.at(refIndex + 1);
263 switch (next) {
264 case '\"':
265 // This is an escaped double quote.
266 // So skip next iteration.
267 refIndex += 1;
268 *ret += c;
269 break;
270 default:
271 /* end of field.
272 * We have to search the comma (or newline...),
273 * which officially terminates the entry.
274 */
275 searchingTerminator = true;
276 break;
277 }
278 break;
279 default:
280 // nothing special about the char. Go on!
281 *ret += c;
282 break;
283 }
284 } else {
285 switch (c) {
286 case '\"':
287 // This is not allowed here.
288 return -1; // parser error
289 case '\r':
290 case '\n':
291 rv = 1; // record end
292 goto out;
293 case ',':
294 rv = 0; // field end
295 goto out;
296 default:
297 // nothing special about the char. Go on!
298 *ret += c;
299 break;
300 }
301 }
302 }
303 }
304 // we are at the end of the stream, now!
305 if (searchingTerminator) {
306 /* Ok, there's no terminating comma (or newline...),
307 * because we are at the end. That's perfectly fine.
308 */
309 PWM_ASSERT(inField);
310 rv = 2; // data end
311 goto out;
312 }
313 if (!isQuoted && inField) {
314 // That's the end of the last unquoted field.
315 rv = 2; // data end
316 goto out;
317 }
318 if (!inField) {
319 // This is expected EOF
320 rv = 2; // data end
321 goto out;
322 }
323
324unexp_eof:
325 printDebug("unexpected EOF :(");
326 return -1; // parser error
327
328out:
329 if (!isQuoted)
330 *ret = ret->stripWhiteSpace();
331 *_refIndex = refIndex + 1;
332 return rv;
333}
334
335bool Csv::exportData(const QString &filepath,
336 PwMDoc *doc)
337{
338 PWM_ASSERT(!doc->isDocEmpty());
339 bool ret = true;
340 if (QFile::exists(filepath)) {
341 int ret;
342 ret = KMessageBox::questionYesNo(parent,
343 i18n("This file does already exist.\n"
344 "Do you want to overwrite it?"),
345 i18n("Overwrite file?"));
346 if (ret == KMessageBox::No)
347 return false;
348 if (!QFile::remove(filepath)) {
349 KMessageBox::error(parent,
350 i18n("Could not delete the old file."),
351 i18n("Delete error."));
352 return false;
353 }
354 }
355 QFile f(filepath);
356 if (!f.open(IO_ReadWrite)) {
357 KMessageBox::error(parent,
358 i18n("Could not open file for writing."),
359 i18n("Open error."));
360 ret = false;
361 goto out;
362 }
363 doc->unlockAll_tempoary();
364 if (!doExport(f, doc))
365 ret = false;
366 doc->unlockAll_tempoary(true);
367 f.close();
368out:
369 return ret;
370}
371
372bool Csv::doExport(QFile &f,
373 PwMDoc *doc)
374{
375 unsigned int numCat = doc->numCategories();
376 unsigned int numEntr;
377 unsigned int i, j;
378 PwMDataItem d;
379 QCString s, catName;
380 QByteArray b;
381
382 for (i = 0; i < numCat; ++i) {
383 numEntr = doc->numEntries(i);
384 catName = newField(doc->getCategory(i)->c_str());
385 for (j = 0; j < numEntr; ++j) {
386 doc->getEntry(i, j, &d);
387 s = catName;
388 s += ",,";
389 s += newField(d.desc.c_str());
390 s += ",";
391 s += newField(d.name.c_str());
392 s += ",";
393 s += newField(d.pw.c_str());
394 s += ",";
395 s += newField(d.url.c_str());
396 s += ",";
397 s += newField(d.launcher.c_str());
398 s += ",";
399 s += newField(d.comment.c_str());
400 s += "\r\n";
401 b = s;
402 // remove \0 termination
403#ifndef PWM_EMBEDDED
404 b.resize(b.size() - 1, QGArray::SpeedOptim);
405#else
406 b.resize(b.size() - 1);
407#endif
408 if (!f.writeBlock(b))
409 return false;
410 }
411 }
412 return true;
413}
414
415QCString Csv::newField(QCString s)
416{
417 if (s.isEmpty())
418 return QCString();
419 QCString ret("\"");
420#ifndef PWM_EMBEDDED
421 s.replace('\"', "\"\"");
422#else
423 s.replace(QRegExp("\""), "\"\"");
424#endif
425 ret += s;
426 ret += "\"";
427 return ret;
428}
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 @@
1/***************************************************************************
2 * *
3 * copyright (C) 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 1.1 of pwmanager
15 * and was modified to run on embedded devices that run microkde
16 * The original file version was 1.2
17 * $Id$
18 **************************************************************************/
19
20
21#ifndef __PWMANAGER_CSV_H
22#define __PWMANAGER_CSV_H
23
24#include <qcstring.h>
25#include <qfile.h>
26
27
28class PwMDoc;
29class QString;
30class QWidget;
31
32/** class for CSV (Comma Separated Value) export and import.
33 *
34 * http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
35 * http://en.wikipedia.org/wiki/Comma-separated_values
36 *
37 * There are two types of CSV output we can produce.
38 * One with Category support (recommended):
39 * "Category 1",, "Desc 1", "Username 1", "Password 1", "URL 1", "Launcher 1", "Comment 1"
40 * "Category 1",, "Desc 2", "Username 2", "Password 2", "URL 2", "Launcher 2", "Comment 2"
41 * ...
42 * The empty "" is neccessary, because in future versions we will
43 * support nested Categories. We want to be future compatible, now.
44 *
45 * And one without Category support:
46 *FIXME: it's not implemented, yet. ;)
47 * "Desc 1", "Username 1", "Password 1", "URL 1", "Launcher 1", "Comment 1"
48 * "Desc 2", "Username 2", "Password 2", "URL 2", "Launcher 2", "Comment 2"
49 * ...
50 *
51 */
52class Csv
53{
54public:
55 Csv(QWidget *_parent);
56 ~Csv();
57
58 /** import data from "filepath" into "doc" */
59 bool importData(const QString &filepath,
60 PwMDoc *doc);
61 /** export data from "doc" to "filepath" */
62 bool exportData(const QString &filepath,
63 PwMDoc *doc);
64
65protected:
66 /** do the import process. */
67 bool doImport(const QByteArray &d,
68 PwMDoc *doc);
69 /** parse for the next field.
70 * @return Return values are:
71 * 0 -> successfully got the next field.
72 * 1 -> record end.
73 * 2 -> data end.
74 * -1 -> parser error.
75 */
76 int nextField(QCString *ret,
77 const QByteArray &in,
78 bool inRecord,
79 int *_refIndex);
80 /** do the export process. */
81 bool doExport(QFile &f,
82 PwMDoc *doc);
83 /** generate a new data field string. */
84 QCString newField(QCString s);
85
86protected:
87 /** current parent widget. */
88 QWidget *parent;
89};
90
91#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 @@
58#include "findwndimpl.h" 58#include "findwndimpl.h"
59#include "csv.h"
59 60
@@ -115,3 +116,4 @@ enum {
115 BUTTON_POPUP_EXPORT_TEXT = 0, 116 BUTTON_POPUP_EXPORT_TEXT = 0,
116 BUTTON_POPUP_EXPORT_GPASMAN 117 BUTTON_POPUP_EXPORT_GPASMAN,
118 BUTTON_POPUP_EXPORT_CSV
117#ifdef CONFIG_KWALLETIF 119#ifdef CONFIG_KWALLETIF
@@ -123,3 +125,4 @@ enum {
123 BUTTON_POPUP_IMPORT_TEXT = 0, 125 BUTTON_POPUP_IMPORT_TEXT = 0,
124 BUTTON_POPUP_IMPORT_GPASMAN 126 BUTTON_POPUP_IMPORT_GPASMAN,
127 BUTTON_POPUP_IMPORT_CSV
125#ifdef CONFIG_KWALLETIF 128#ifdef CONFIG_KWALLETIF
@@ -183,2 +186,3 @@ PwM::~PwM()
183{ 186{
187 //qDebug("PwM::~PwM()");
184 disconnect(curDoc(), SIGNAL(docClosed(PwMDoc *)), 188 disconnect(curDoc(), SIGNAL(docClosed(PwMDoc *)),
@@ -187,2 +191,3 @@ PwM::~PwM()
187 emit closed(this); 191 emit closed(this);
192 //qDebug("PwM::~PwM() emited closed(this)");
188 delete view; 193 delete view;
@@ -243,2 +248,4 @@ void PwM::initMenubar()
243 SLOT(exportToGpasman()), 0, BUTTON_POPUP_EXPORT_GPASMAN); 248 SLOT(exportToGpasman()), 0, BUTTON_POPUP_EXPORT_GPASMAN);
249 exportPopup->insertItem(i18n("&CSV (Comma Separated Value) ..."), this,
250 SLOT(exportToCsv()), 0, BUTTON_POPUP_EXPORT_CSV);
244#ifdef CONFIG_KWALLETIF 251#ifdef CONFIG_KWALLETIF
@@ -255,2 +262,4 @@ void PwM::initMenubar()
255 SLOT(importFromGpasman()), 0, BUTTON_POPUP_IMPORT_GPASMAN); 262 SLOT(importFromGpasman()), 0, BUTTON_POPUP_IMPORT_GPASMAN);
263 importPopup->insertItem(i18n("&CSV (Comma Separated Value) ..."), this,
264 SLOT(importCsv()), 0, BUTTON_POPUP_IMPORT_CSV);
256#ifdef CONFIG_KWALLETIF 265#ifdef CONFIG_KWALLETIF
@@ -1018,2 +1027,74 @@ void PwM::exportToGpasman()
1018 1027
1028
1029
1030void PwM::exportToCsv()
1031{
1032 PWM_ASSERT(curDoc());
1033 if (curDoc()->isDocEmpty()) {
1034 KMessageBox::information(this,
1035 i18n
1036 ("Sorry, there is nothing to export;\n"
1037 "please add some passwords first."),
1038 i18n("Nothing to Do"));
1039 return;
1040 }
1041
1042 curDoc()->timer()->getLock(DocTimer::id_autoLockTimer);
1043 QString fn(KFileDialog::getSaveFileName("*.csv", i18n("*|CSV Text File"), this));
1044 if (fn.isEmpty()) {
1045 curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
1046 return;
1047 }
1048
1049 Csv csv(this);
1050 if (!csv.exportData(fn, curDoc())) {
1051 curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
1052 showStatMsg(i18n("CSV file export failed."));
1053 return;
1054 }
1055 showStatMsg(i18n("Successfully exported data."));
1056 curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
1057}
1058
1059bool PwM::importCsv()
1060{
1061 Csv csv(this);
1062 if (!isVirgin()) {
1063 if (KMessageBox::questionYesNo(this,
1064 i18n("Do you want to import the data\n"
1065 "into the current document? (If you\n"
1066 "select \"no\", a new document will be\n"
1067 "opened.)"),
1068 i18n("Import into This Document?"))
1069 == KMessageBox::No) {
1070 // import the data to a new window.
1071 PwM *newInstance = init->createMainWnd();
1072 bool ok = newInstance->importCsv();
1073 if (!ok) {
1074 newInstance->setForceQuit(true);
1075 delete_and_null(newInstance);
1076 }
1077 return ok;
1078 }
1079 }
1080
1081 QString filename = KFileDialog::getOpenFileName("*.csv", i18n("*|CSV Text File"), this);
1082 if (filename.isEmpty())
1083 return false;
1084 curDoc()->timer()->getLock(DocTimer::id_autoLockTimer);
1085 if (!csv.importData(filename, curDoc())) {
1086 curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
1087 showStatMsg(i18n("CSV file import failed."));
1088 return false;
1089 }
1090 curDoc()->timer()->putLock(DocTimer::id_autoLockTimer);
1091 KMessageBox::information(this,
1092 i18n("Successfully imported the CSV data\n"
1093 "into the current document."), i18n("Successfully Imported"));
1094 showStatMsg(i18n("Successfully imported"));
1095 setVirgin(false);
1096 return true;
1097}
1098
1099
1019void PwM::exportToKWallet() 1100void 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:
126 void exportToKWallet(); 126 void exportToKWallet();
127 /** file/export/csv triggered */
128 void exportToCsv();
127 /** file/import/text triggered */ 129 /** file/import/text triggered */
@@ -132,2 +134,4 @@ public slots:
132 bool importKWallet(); 134 bool importKWallet();
135 /** file/import/csv triggered */
136 bool importCsv();
133 /** file/print triggered */ 137 /** 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 \
64compressgzip.h \ 64compressgzip.h \
65csv.h \
65findwnd_emb.h \ 66findwnd_emb.h \
@@ -129,2 +130,3 @@ commentbox.cpp \
129compressgzip.cpp \ 130compressgzip.cpp \
131csv.cpp \
130findwnd_emb.cpp \ 132findwnd_emb.cpp \