summaryrefslogtreecommitdiffabout
path: root/pwmanager/pwmanager/csv.cpp
Unidiff
Diffstat (limited to 'pwmanager/pwmanager/csv.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--pwmanager/pwmanager/csv.cpp428
1 files changed, 428 insertions, 0 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}