~ubuntu-branches/ubuntu/saucy/kopete/saucy-proposed

« back to all changes in this revision

Viewing changes to plugins/history2/history2import.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-06-21 02:22:39 UTC
  • Revision ID: package-import@ubuntu.com-20130621022239-63l3zc8p0nf26pt6
Tags: upstream-4.10.80
ImportĀ upstreamĀ versionĀ 4.10.80

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    history2import.cpp
 
3
 
 
4
    Copyright (c) 2012 by Volker HƤrtel
 
5
    Copyright (c) 2010 by Timo Schluessler
 
6
    Kopete    (c) 2010 by the Kopete developers  <kopete-devel@kde.org>
 
7
 
 
8
    *************************************************************************
 
9
    *                                                                       *
 
10
    * This program is free software; you can redistribute it and/or modify  *
 
11
    * it under the terms of the GNU General Public License as published by  *
 
12
    * the Free Software Foundation; either version 2 of the License, or     *
 
13
    * (at your option) any later version.                                   *
 
14
    *                                                                       *
 
15
    *************************************************************************
 
16
*/
 
17
 
 
18
#include "history2import.h"
 
19
 
 
20
#include <QtCore/QStack>
 
21
#include <QtCore/QDir>
 
22
#include <QtGui/QTextEdit>
 
23
#include <QtGui/QTreeView>
 
24
#include <QtGui/QPushButton>
 
25
#include <QtGui/QCheckBox>
 
26
#include <QtGui/QGridLayout>
 
27
#include <QtGui/QStandardItemModel>
 
28
#include <QtGui/QProgressDialog>
 
29
#include <QtGui/QMessageBox>
 
30
#include <QtGui/QFileDialog>
 
31
#include <QtGui/QApplication>
 
32
#include <QtXml/QXmlStreamReader>
 
33
#include <QXmlQuery>
 
34
#include <QFile>
 
35
 
 
36
#include <kdebug.h>
 
37
#include <klocale.h>
 
38
#include <KStandardDirs>
 
39
 
 
40
#include <kopetecontactlist.h>
 
41
#include <kopetemetacontact.h>
 
42
#include <kopeteprotocol.h>
 
43
#include <kopeteaccount.h>
 
44
#include <kopetecontact.h>
 
45
#include <kopetemessage.h>
 
46
 
 
47
#include "history2logger.h"
 
48
 
 
49
 
 
50
History2Import::History2Import(QWidget *parent)
 
51
        : KDialog(parent) {
 
52
 
 
53
        // set dialog settings
 
54
        setButtons(KDialog::Ok | KDialog::Details | KDialog::Cancel);
 
55
        setWindowTitle(KDialog::makeStandardCaption(i18n("Import History")));
 
56
        setButtonText(KDialog::Ok, i18n("Import Listed Logs"));
 
57
 
 
58
        // create widgets
 
59
        QWidget *w = new QWidget(this);
 
60
        QGridLayout *l = new QGridLayout(w);
 
61
 
 
62
        display = new QTextEdit(w);
 
63
        display->setReadOnly(true);
 
64
        treeView = new QTreeView(w);
 
65
 
 
66
        QPushButton *fromPidgin = new QPushButton(i18n("Get History From &Pidgin..."), w);
 
67
 
 
68
        QPushButton *fromKopete = new QPushButton(i18n("Get History From &Kopete..."), w);
 
69
 
 
70
        l->addWidget(treeView, 0, 0, 1, 3);
 
71
        l->addWidget(display, 0, 4, 1, 10);
 
72
        l->addWidget(fromPidgin, 1, 0);
 
73
        l->addWidget(fromKopete, 1, 1);
 
74
 
 
75
        setMainWidget(w);
 
76
 
 
77
 
 
78
        // create details widget
 
79
        QWidget *details = new QWidget(w);
 
80
        QVBoxLayout *dL = new QVBoxLayout(details);
 
81
 
 
82
        QTextEdit *detailsEdit = new QTextEdit(details);
 
83
        detailsEdit->setReadOnly(true);
 
84
        selectByHand = new QCheckBox(i18n("Select log directory by hand"), details);
 
85
 
 
86
        dL->addWidget(selectByHand);
 
87
        dL->addWidget(detailsEdit);
 
88
 
 
89
        setDetailsWidget(details);
 
90
        detailsCursor = QTextCursor(detailsEdit->document());
 
91
 
 
92
        // create model for treeView
 
93
        QStandardItemModel *model = new QStandardItemModel(treeView);
 
94
        treeView->setModel(model);
 
95
        model->setHorizontalHeaderLabels(QStringList(i18n("Parsed History")));
 
96
 
 
97
        // connect everything
 
98
        connect(treeView, SIGNAL(clicked(QModelIndex)), this, SLOT(itemClicked(QModelIndex)));
 
99
        connect(fromPidgin, SIGNAL(clicked()), this, SLOT(importPidgin()));
 
100
        connect(fromKopete, SIGNAL(clicked()), this, SLOT(importKopete()));
 
101
        connect(this, SIGNAL(okClicked()), this, SLOT(save()));
 
102
 
 
103
        // define variables
 
104
        amount = 0;
 
105
        cancel = false;
 
106
        pidginImported = false;
 
107
        cacheContact = 0;
 
108
 
 
109
        timeFormats << "(MM/dd/yyyy hh:mm:ss)" << "(MM/dd/yyyy hh:mm:ss AP)"
 
110
                    << "(MM/dd/yy hh:mm:ss)" << "(MM/dd/yy hh:mm:ss AP)"
 
111
                    << "(dd.MM.yyyy hh:mm:ss)" << "(dd.MM.yyyy hh:mm:ss AP)"
 
112
                    << "(dd.MM.yy hh:mm:ss)" << "(dd.MM.yyyy hh:mm:ss AP)"
 
113
                    << "(dd/MM/yyyy hh:mm:ss)" << "(dd/MM/yyyy hh:mm:ss AP)"
 
114
                    << "(dd/MM/yy hh:mm:ss)" << "(dd/MM/yy hh:mm:ss AP)"
 
115
                    << "(yyyy-MM-dd hh:mm:ss)" << "(yyyy-MM-dd hh:mm:ss AP)";
 
116
        show();
 
117
}
 
118
 
 
119
History2Import::~History2Import(void) {
 
120
        qDeleteAll(logs);
 
121
}
 
122
 
 
123
void History2Import::save(void) {
 
124
        QProgressDialog progress(i18n("Saving logs to disk ..."), i18n("Abort Saving"), 0, amount, this);
 
125
        progress.setWindowTitle(i18n("Saving"));
 
126
 
 
127
        Log *log;
 
128
        History2Logger::instance()->beginTransaction();
 
129
        foreach (log, logs) {
 
130
                Message message;
 
131
                foreach (message, log->messages) {
 
132
                        Kopete::Message kMessage;
 
133
                        if (message.incoming) {
 
134
                                kMessage = Kopete::Message(log->other, log->me);
 
135
                                kMessage.setDirection(Kopete::Message::Inbound);
 
136
                        } else {
 
137
                                kMessage = Kopete::Message(log->me, log->other);
 
138
                                kMessage.setDirection(Kopete::Message::Outbound);
 
139
                        }
 
140
                        kMessage.setPlainBody(message.text);
 
141
                        kMessage.setTimestamp(message.timestamp);
 
142
                        History2Logger::instance()->appendMessage(kMessage, log->other, true);
 
143
 
 
144
                        progress.setValue(progress.value()+1);
 
145
                        qApp->processEvents();
 
146
                        if (progress.wasCanceled()) {
 
147
                                cancel = true;
 
148
                                break;
 
149
                        }
 
150
                }
 
151
                if (cancel)
 
152
                        break;
 
153
        }
 
154
        History2Logger::instance()->commitTransaction();
 
155
}
 
156
 
 
157
void History2Import::displayLog(struct Log *log) {
 
158
        Message message;
 
159
 
 
160
        QList<QStandardItem*> items;
 
161
        QStringList strings;
 
162
 
 
163
        items << static_cast<QStandardItemModel*>(treeView->model())->invisibleRootItem();
 
164
        items << NULL << NULL << NULL;
 
165
        strings << "" << "" << "";
 
166
 
 
167
        foreach(message, log->messages) {
 
168
                amount++; // for QProgressDialog in save()
 
169
 
 
170
                strings[0] = log->other->protocol()->pluginId() + " (" + log->other->account()->accountId() + ')';
 
171
                strings[1] = log->other->nickName();
 
172
                strings[2] = message.timestamp.toString("yyyy-MM-dd");
 
173
 
 
174
                bool update = false;
 
175
                int i;
 
176
                for (i=1; i<4; i++) {
 
177
                        if (update || !items.at(i) || items.at(i)->data(Qt::DisplayRole) != strings.at(i-1)) {
 
178
                                items[i] = findItem(strings.at(i-1), items.at(i-1));
 
179
                                update = true;
 
180
                        } //else
 
181
                        //kDebug(14310) << "using cached item";
 
182
                }
 
183
 
 
184
                if (!items.at(3)->data(Qt::UserRole).isValid())
 
185
                        items[3]->setData((int)logs.indexOf(log), Qt::UserRole);
 
186
        }
 
187
 
 
188
}
 
189
 
 
190
QStandardItem * History2Import::findItem(const QString &text, QStandardItem *parent) {
 
191
        int i;
 
192
        bool found = false;
 
193
        QStandardItem *child = 0L;
 
194
 
 
195
        for (i=0; i < parent->rowCount(); i++) {
 
196
                child = parent->child(i, 0);
 
197
                if (child->data(Qt::DisplayRole) == text) {
 
198
                        found = true;
 
199
                        break;
 
200
                }
 
201
        }
 
202
        if (!found) {
 
203
                child = new QStandardItem(text);
 
204
                parent->appendRow(child);
 
205
        }
 
206
 
 
207
        return child;
 
208
}
 
209
 
 
210
void History2Import::itemClicked(const QModelIndex &index) {
 
211
        QVariant id = index.data(Qt::UserRole);
 
212
 
 
213
        if (id.canConvert<int>()) {
 
214
                Log *log = logs.at(id.toInt());
 
215
                display->document()->clear();
 
216
                QTextCursor cursor(display->document());
 
217
 
 
218
                Message message;
 
219
                QDate date = QDate::fromString(index.data(Qt::DisplayRole).toString(), "yyyy-MM-dd");
 
220
                foreach (message, log->messages) {
 
221
                        if (date != message.timestamp.date())
 
222
                                continue;
 
223
                        cursor.insertHtml(message.timestamp.toString("hh:mm:ss "));
 
224
                        if (message.incoming)
 
225
                                cursor.insertHtml("<font color=\"blue\">" + log->other->nickName().append(": </font>"));
 
226
                        else
 
227
                                cursor.insertHtml("<font color=\"green\">" + log->me->nickName().append(": </font>"));
 
228
                        cursor.insertHtml(message.text);
 
229
                        cursor.insertBlock();
 
230
                }
 
231
        }
 
232
}
 
233
 
 
234
int History2Import::countLogs(QDir dir, int depth) {
 
235
        int res = 0;
 
236
        QStack<int> pos;
 
237
        QStringList files;
 
238
        pos.push(0);
 
239
 
 
240
        depth++;
 
241
 
 
242
        forever {
 
243
                files = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
 
244
 
 
245
                if (pos.size() == depth) {
 
246
                        res += dir.entryList(QDir::Files).size();
 
247
                }
 
248
                if (files.isEmpty() || files.size() <= pos.top() || pos.size() == depth) {
 
249
                        dir.cdUp();
 
250
                        pos.pop();
 
251
                        if (pos.isEmpty())
 
252
                                break;
 
253
                        pos.top()++;
 
254
                } else if (pos.size() != depth) {
 
255
                        dir.cd(files.at(pos.top()));
 
256
                        pos.push(0);
 
257
                }
 
258
        }
 
259
 
 
260
        return res;
 
261
}
 
262
 
 
263
void History2Import::importKopete() {
 
264
 
 
265
        cancel = false;
 
266
        QString logDir = KStandardDirs::locateLocal("data",QString("kopete/logs/"));
 
267
        QDir dd(logDir);
 
268
        if (selectByHand->isChecked() || !dd.exists()) {
 
269
                logDir = QFileDialog::getExistingDirectory(mainWidget(), i18n("Select Log Directory"), QDir::homePath());
 
270
        }
 
271
        int total = countLogs(logDir, 3);
 
272
        QProgressDialog progress(i18n("Parsing history from kopete ..."), i18n("Abort parsing"), 0, total, mainWidget());
 
273
        progress.setWindowTitle(i18n("Parsing history"));
 
274
        progress.show();
 
275
//      qDebug() << "logdir=" << logDir;
 
276
        QDir ld(logDir);
 
277
        ld.setFilter( QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot );
 
278
        ld.setSorting( QDir::Name );
 
279
        QString protocol;
 
280
        QString account;
 
281
 
 
282
        const QFileInfoList protocols = ld.entryInfoList();
 
283
        foreach (const QFileInfo protocolDir, protocols) {
 
284
//              qDebug() << "protocoldir=" << protocolDir.absoluteFilePath();
 
285
                QDir pd(protocolDir.absoluteFilePath());
 
286
                pd.setFilter( QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot );
 
287
                pd.setSorting( QDir::Name );
 
288
                const QFileInfoList accounts = pd.entryInfoList();
 
289
                foreach (const QFileInfo accountDir, accounts) {
 
290
//                      qDebug() << "accountdir=" << accountDir.absoluteFilePath();
 
291
                        QDir d(accountDir.absoluteFilePath());
 
292
                        d.setFilter( QDir::Files | QDir::NoSymLinks );
 
293
                        d.setSorting( QDir::Name );
 
294
                        const QFileInfoList list = d.entryInfoList();
 
295
                        foreach( const QFileInfo &fi, list ) {
 
296
//                              qDebug() << "file=" << fi.absoluteFilePath();
 
297
                                Log *log = new Log();
 
298
                                protocol = protocolDir.fileName().replace("-", ".");
 
299
                                account = accountDir.fileName().replace("-", ".");
 
300
                                readKopeteMessages(protocol,account, fi.absoluteFilePath(), log);
 
301
                                logs.append(log);
 
302
                                displayLog(log);
 
303
                                progress.setValue(progress.value()+1);
 
304
                                qApp->processEvents();
 
305
                                if (cancel || progress.wasCanceled()) {
 
306
                                        cancel = true;
 
307
                                        break;
 
308
                                }
 
309
                        }
 
310
                        if (cancel) {
 
311
                                break;
 
312
                        }
 
313
                }
 
314
                if (cancel) {
 
315
                        break;
 
316
                }
 
317
        }
 
318
}
 
319
 
 
320
void History2Import::importPidgin() {
 
321
        if (pidginImported) {
 
322
                if (QMessageBox::question(this,
 
323
                                          i18n("Are You Sure?"),
 
324
                                          i18n("You already imported logs from pidgin. If you do it twice, each message is imported twice.\nAre you sure you want to continue?"),
 
325
                                          QMessageBox::Yes | QMessageBox::No,
 
326
                                          QMessageBox::No) != QMessageBox::Yes)
 
327
                        return;
 
328
        }
 
329
        pidginImported = true;
 
330
 
 
331
        QDir logDir = QDir::homePath();
 
332
        if (selectByHand->isChecked() || !logDir.cd(".purple/logs"))
 
333
                logDir = QFileDialog::getExistingDirectory(mainWidget(), i18n("Select Log Directory"), QDir::homePath());
 
334
 
 
335
        int total = countLogs(logDir, 3);
 
336
        QProgressDialog progress(i18n("Parsing history from pidgin ..."), i18n("Abort parsing"), 0, total, mainWidget());
 
337
        progress.setWindowTitle(i18n("Parsing history"));
 
338
        progress.show();
 
339
        cancel = false;
 
340
 
 
341
        // protocolMap maps pidgin account-names to kopete protocol names (as in Kopete::Contact::protocol()->pluginId())
 
342
        QHash<QString, QString> protocolMap;
 
343
        protocolMap.insert("msn", "WlmProtocol");
 
344
        protocolMap.insert("icq", "ICQProtocol");
 
345
        protocolMap.insert("aim", "AIMProtocol");
 
346
        protocolMap.insert("jabber", "JabberProtocol");
 
347
        protocolMap.insert("yahoo", "YahooProtocol");
 
348
        protocolMap.insert("qq", "QQProtocol");
 
349
        protocolMap.insert("irc", "IRCProtocol");
 
350
        protocolMap.insert("gadu-gadu", "GaduProtocol");
 
351
        protocolMap.insert("bonjour", "BonjourProtocol");
 
352
        protocolMap.insert("meanwhile", "MeanwhileProtocol");
 
353
 
 
354
        QString protocolFolder;
 
355
        foreach (protocolFolder, logDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
 
356
                logDir.cd(protocolFolder);
 
357
 
 
358
                // check if we can map the protocol
 
359
                if (!protocolMap.contains(protocolFolder)) {
 
360
                        detailsCursor.insertText(i18n("WARNING: There is no equivalent for protocol %1 in kopete.\n", protocolFolder));
 
361
                        logDir.cdUp();
 
362
                        continue;
 
363
                }
 
364
                const QString & protocol = protocolMap.value(protocolFolder);
 
365
 
 
366
                if (protocolFolder == "gadu-gadu")
 
367
                        protocolFolder = "gadu";
 
368
                else if (protocolFolder == "msn")
 
369
                        protocolFolder = "wlm";
 
370
 
 
371
                QString accountFolder;
 
372
                foreach (accountFolder, logDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
 
373
                        logDir.cd(accountFolder);
 
374
 
 
375
                        // TODO use findContact?
 
376
                        Kopete::ContactList * cList = Kopete::ContactList::self();
 
377
                        QList<Kopete::Contact *> meList = cList->myself()->contacts();
 
378
                        Kopete::Contact *me;
 
379
                        bool found = false;
 
380
                        foreach (me, meList) {
 
381
                                if (me->protocol()->pluginId() == protocol && me->account()->accountId().contains(accountFolder, Qt::CaseInsensitive)) {
 
382
                                        found = true;
 
383
                                        break;
 
384
                                }
 
385
                        }
 
386
                        if (!found) {
 
387
                                detailsCursor.insertText(i18n("WARNING: Cannot find matching account for %1 (%2).\n", accountFolder, protocolFolder));
 
388
                                logDir.cdUp();
 
389
                                continue;
 
390
                        }
 
391
 
 
392
                        QString chatPartner;
 
393
                        foreach (chatPartner, logDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
 
394
                                logDir.cd(chatPartner);
 
395
 
 
396
                                Kopete::Contact *other = cList->findContact(me->protocol()->pluginId(), me->account()->accountId(), chatPartner);
 
397
                                Log *log =  new Log();
 
398
                                if (!other) {
 
399
                                        detailsCursor.insertText(i18n("WARNING: Cannot find %1 (%2) in your contact list. Found logs will not be imported.\n", chatPartner, protocolFolder));
 
400
                                        logDir.cdUp();
 
401
                                        continue;
 
402
                                } else {
 
403
                                        log->me = me;
 
404
                                        log->other = other;
 
405
                                }
 
406
 
 
407
                                QString logFile;
 
408
                                QStringList filter;
 
409
                                filter << "*.html" << "*.txt";
 
410
                                foreach(logFile, logDir.entryList(filter, QDir::Files)) {
 
411
                                        QFile file(logDir.filePath(logFile));
 
412
                                        if (!file.open(QIODevice::ReadOnly)) {
 
413
                                                detailsCursor.insertText(i18n("WARNING: Cannot open file %1. Skipping.\n", logDir.filePath(logFile)));
 
414
                                                continue;
 
415
                                        }
 
416
 
 
417
                                        if (logFile.endsWith(".html"))
 
418
                                                parsePidginXml(file, log, QDate::fromString(logFile.left(10), "yyyy-MM-dd"));
 
419
                                        else if (logFile.endsWith(".txt"))
 
420
                                                parsePidginTxt(file, log, QDate::fromString(logFile.left(10), "yyyy-MM-dd"));
 
421
 
 
422
                                        file.close();
 
423
 
 
424
                                        progress.setValue(progress.value()+1);
 
425
                                        qApp->processEvents();
 
426
                                        if (cancel || progress.wasCanceled()) {
 
427
                                                cancel = true;
 
428
                                                break;
 
429
                                        }
 
430
                                }
 
431
 
 
432
                                logs.append(log);
 
433
                                displayLog(log);
 
434
 
 
435
                                if (cancel)
 
436
                                        break;
 
437
                                logDir.cdUp();
 
438
                        }
 
439
                        if (cancel)
 
440
                                break;
 
441
                        logDir.cdUp();
 
442
                }
 
443
                if (cancel)
 
444
                        break;
 
445
                logDir.cdUp();
 
446
        }
 
447
 
 
448
}
 
449
 
 
450
QDateTime History2Import::extractTime(const QString &string, QDate ref) {
 
451
        QDateTime dateTime;
 
452
        QTime time;
 
453
 
 
454
        // try some formats used by pidgin
 
455
        if      ((time = QTime::fromString(string, "(hh:mm:ss)"))    .isValid());
 
456
        else if ((time = QTime::fromString(string, "(hh:mm:ss AP)")) .isValid());
 
457
        else {
 
458
                QString format;
 
459
                foreach (format, timeFormats) {
 
460
                        if ((dateTime = QDateTime::fromString(string, format)).isValid())
 
461
                                break;
 
462
                }
 
463
        }
 
464
 
 
465
        // check if the century in dateTime is equal to that of our date reference
 
466
        if (dateTime.isValid()) {
 
467
                int diff = ref.year() - dateTime.date().year();
 
468
                dateTime = dateTime.addYears(diff - (diff % 100));
 
469
        }
 
470
 
 
471
        // if string contains only a time we use ref as date
 
472
        if (time.isValid())
 
473
                dateTime = QDateTime(ref, time);
 
474
 
 
475
        // inform the user about the date problems
 
476
        // TODO ask the user for date format to enter
 
477
        if (!dateTime.isValid())
 
478
                detailsCursor.insertText(i18n("WARNING: Cannot parse date \"%1\". You may want to edit the file containing this date manually. (Example recognized date strings: \"05/31/2008 15:24:30\".)\n", string, dateTime.toString("yyyy-MM-dd hh:mm:ss")));
 
479
 
 
480
 
 
481
        return dateTime;
 
482
}
 
483
 
 
484
void History2Import::parsePidginTxt(QFile &file, struct Log *log, QDate date) {
 
485
        QString line;
 
486
        QString nick;
 
487
        struct Message message;
 
488
 
 
489
        // this is to collect unknown nicknames (the list stores the index in log->messages of the messages that used the nickname)
 
490
        // the bool says if that nickname is incoming (only used when the list is empty)
 
491
        QHash<QString, QPair<bool, QList<int> > > nicknames;
 
492
 
 
493
        QTextStream str(&file);
 
494
        // utf-8 seems to be default for pidgins-txt logs
 
495
        str.setCodec("UTF-8");
 
496
 
 
497
        while (!str.atEnd()) {
 
498
                line = str.readLine();
 
499
 
 
500
                if (line[0] == '(') {
 
501
                        if (!message.text.isEmpty()) {
 
502
                                log->messages.append(message);
 
503
                                message.text.clear();
 
504
                        }
 
505
 
 
506
                        int endTime = line.indexOf(')')+1;
 
507
                        message.timestamp = extractTime(line.left(endTime), date);
 
508
 
 
509
                        int nickEnd = QRegExp("\\s").indexIn(line, endTime + 1);
 
510
                        // TODO what if a nickname consists of two words? is this possible?
 
511
                        // the following while can't be used because in status logs there is no : after the nickname :(
 
512
                        //while (line[nickEnd-1] != ':')
 
513
                        //      nickEnd = QRegExp("\\").indexIn(line, nickEnd);
 
514
                        if (line[nickEnd -1] != ':') // this line is a status message
 
515
                                continue;
 
516
 
 
517
                        nick = line.mid(endTime+1, nickEnd - endTime - 2); // -2 to delete the colon
 
518
 
 
519
                        // detect if the message is in- or outbound
 
520
                        if (nick == log->me->nickName())
 
521
                                message.incoming = false;
 
522
                        else if (nick == log->other->nickName())
 
523
                                message.incoming = true;
 
524
                        else if (knownNicks.contains(nick))
 
525
                                message.incoming = knownNicks.value(nick);
 
526
                        else {
 
527
                                // store this nick for later decision
 
528
                                nicknames[nick].second.append(log->messages.size());
 
529
                        }
 
530
                        nicknames[nick].first = message.incoming;
 
531
 
 
532
                        if (cancel)
 
533
                                return;
 
534
 
 
535
                        message.text = line.mid(nickEnd + 1);
 
536
                } else if (line[0] == ' ') {
 
537
                        // an already started message is continued in this line
 
538
                        int start = QRegExp("\\S").indexIn(line);
 
539
                        message.text.append('\n' + line.mid(start));
 
540
                }
 
541
        }
 
542
        if (!message.text.isEmpty()) {
 
543
                log->messages.append(message);
 
544
 
 
545
                // check if we can guess which nickname belongs to us
 
546
                QHash<QString, QPair<bool, QList<int> > >::iterator itr;
 
547
                QHash<QString, QPair<bool, QList<int> > >::const_iterator itr2;
 
548
                for (itr = nicknames.begin(); itr != nicknames.end(); ++itr) {
 
549
                        if (itr->second.isEmpty()) // no work for this one
 
550
                                continue;
 
551
                        bool haveAnother = false, lastIncoming = false;
 
552
                        // check against all other nicknames
 
553
                        for (itr2 = nicknames.constBegin(); itr2 != nicknames.constEnd(); ++itr2) {
 
554
                                if (itr2 == itr) // skip ourselve
 
555
                                        continue;
 
556
                                // if there is another unknown nickname, we have no chance to guess which is our
 
557
                                if (!itr2->second.isEmpty())
 
558
                                        break;
 
559
                                if (!haveAnother) {
 
560
                                        lastIncoming = itr2->first;
 
561
                                        haveAnother = true;
 
562
                                } else {
 
563
                                        // when there are more than one known nicknames, but with different incoming-values, we also can't guess which is ours
 
564
                                        if (lastIncoming != itr2->first)
 
565
                                                break;
 
566
                                }
 
567
                        }
 
568
                        // we now can guess the incoming value of itr, namely !lastIncoming
 
569
                        if (haveAnother && itr2 == nicknames.constEnd()) {
 
570
                                // inform the user
 
571
                                if (lastIncoming)
 
572
                                        detailsCursor.insertText(i18n("INFORMATION: Guessed %1 to be one of your nicks.\n", itr.key()));
 
573
                                else
 
574
                                        detailsCursor.insertText(i18n("INFORMATION: Guessed %1 to be one of your buddys nicks.\n", itr.key()));
 
575
                                knownNicks.insert(itr.key(), !lastIncoming);
 
576
                                int i;
 
577
                                for (i = 0; i < itr->second.size(); i++)
 
578
                                        log->messages[itr->second.at(i)].incoming = !lastIncoming;
 
579
                                itr->second.clear(); // we are finished with theese indexes
 
580
                        }
 
581
                }
 
582
                // iterate once again over the nicknames to detect which nicks are still not known. simply ask the user!
 
583
                for (itr = nicknames.begin(); itr != nicknames.end(); ++itr) {
 
584
                        if (itr->second.isEmpty()) // no word for this one
 
585
                                continue;
 
586
                        bool incoming;
 
587
                        int r = QMessageBox::question(NULL,
 
588
                                                      i18n("Cannot map Nickname to Account"),
 
589
                                                      i18n("Did you ever use \"%1\" as nickname in your history?", itr.key()),
 
590
                                                      QMessageBox::Yes | QMessageBox::No | QMessageBox::Abort);
 
591
                        if (r == QMessageBox::Yes) {
 
592
                                knownNicks.insert(itr.key(), false);
 
593
                                incoming = true;
 
594
                        } else if (r == QMessageBox::No) {
 
595
                                knownNicks.insert(itr.key(), true);
 
596
                                incoming = false;
 
597
                        } else {
 
598
                                cancel = true;
 
599
                                return;
 
600
                        }
 
601
                        // set the queried incoming value to our already stored Messages
 
602
                        int i;
 
603
                        for (i = 0; i < itr->second.size(); i++)
 
604
                                log->messages[itr->second.at(i)].incoming = incoming;
 
605
                }
 
606
        }
 
607
}
 
608
 
 
609
void History2Import::parsePidginXml(QFile &file, struct Log * log, QDate date) {
 
610
        int state = 0;
 
611
        struct Message msg;
 
612
 
 
613
        // unfortunately pidgin doesn't write <... /> for the <meta> tag
 
614
        QByteArray data = file.readAll();
 
615
        if (data.contains("<meta")) {
 
616
                int metaEnd = data.indexOf(">", data.indexOf("<meta"));
 
617
                if (data.at(metaEnd-1) != '/')
 
618
                        data.insert(metaEnd, '/');
 
619
        }
 
620
 
 
621
        QXmlStreamReader reader(data);
 
622
 
 
623
        while (!reader.atEnd()) {
 
624
                reader.readNext();
 
625
 
 
626
                // when there is only the color attribute for the font-tag, this must be the beginning of a new message
 
627
                if (state == 0 && reader.isStartElement() && reader.name() == "font" && reader.attributes().size() == 1 && reader.attributes().first().name() == "color") {
 
628
                        if (reader.attributes().value("color") == "#FF0000") // system message, e.g. warning
 
629
                                continue;
 
630
                        state = 1;
 
631
                        msg.incoming = (reader.attributes().value("color") == "#A82F2F");
 
632
                        msg.text.clear();
 
633
                }
 
634
                if (state == 1 && reader.isStartElement() && reader.name() == "font") {
 
635
                        state = 2;
 
636
                }
 
637
                if (state == 2 && reader.isCharacters()) {
 
638
                        msg.timestamp = extractTime(reader.text().toString(), date);
 
639
                        if (msg.timestamp.isValid()) {
 
640
                                state = 3;
 
641
                        }
 
642
                }
 
643
                if (state == 3 && reader.isStartElement() && reader.name() == "b") {
 
644
                        state = 4;
 
645
                }
 
646
                if (state == 4 && reader.isEndElement() && reader.name() == "font") {
 
647
                        state = 5;
 
648
                }
 
649
                if (state == 5 && reader.isCharacters()) {
 
650
                        msg.text += reader.text().toString(); // append text
 
651
                }
 
652
                if (reader.isStartElement() && reader.name() == "br") {
 
653
                        if (state == 5) {
 
654
                                msg.text = msg.text.trimmed();
 
655
                                if (msg.text != "") {
 
656
                                        log->messages.append(msg); // save message for later import via History2Logger (see History2Import::save())
 
657
                                }
 
658
                        }
 
659
                        state = 0;
 
660
                }
 
661
        }
 
662
 
 
663
        if (reader.hasError()) {
 
664
                // we ignore error 4: premature end of document
 
665
                if (reader.error() != 4) {
 
666
                        int i, pos = 0;
 
667
                        for (i=1; i<reader.lineNumber(); i++)
 
668
                                pos = data.indexOf('\n', pos) + 1;
 
669
                        detailsCursor.insertText(i18n("WARNING: XML parser error in %1 at line %2, character %3: %4",
 
670
                                                      file.fileName(), reader.lineNumber(), reader.columnNumber(), reader.errorString()));
 
671
                        detailsCursor.insertBlock();
 
672
                        detailsCursor.insertText(i18n("\t%1", QString(data.mid(pos, data.indexOf('\n', pos) - pos))));
 
673
                        detailsCursor.insertBlock();
 
674
                }
 
675
        }
 
676
        if (state == 5) { // an unsaved message is still pending (this doesn't happen at least for my pidgin-logs - handle it anyway)
 
677
                msg.text = msg.text.trimmed(); // trimm especially unwished newlines and spaces
 
678
                if (msg.text != "") {
 
679
                        log->messages.append(msg); // save message for later import via History2Logger (see History2Import::save())
 
680
                }
 
681
        }
 
682
}
 
683
 
 
684
void History2Import::readKopeteMessages(QString protocol, QString account, QString f, Log *log) {
 
685
//      qDebug() << "read kopete message from file " << f << " for protocol = " << protocol << ", account= " << account;
 
686
        QDomDocument doc( "Kopete-History" );
 
687
        QFile file(f);
 
688
        if ( !file.open( QIODevice::ReadOnly ) ) {
 
689
                detailsCursor.insertText("Could not open file: "+f);
 
690
                detailsCursor.insertBlock();
 
691
                return;
 
692
        }
 
693
        if ( !doc.setContent( &file ) ) {
 
694
                file.close();
 
695
                detailsCursor.insertText("Could not parse file: "+f);
 
696
                detailsCursor.insertBlock();
 
697
                return;
 
698
        }
 
699
        file.close();
 
700
 
 
701
        QRegExp rxTime("(\\d+) (\\d+):(\\d+)($|:)(\\d*)"); //(with a 0.7.x compatibility)
 
702
        QDomElement docElem = doc.documentElement();
 
703
        QDomNode n = docElem.firstChild();
 
704
        Message m;
 
705
        QString meString;
 
706
        QString otherString;
 
707
        int mon = -1;
 
708
        int year = -1;
 
709
 
 
710
        while(!n.isNull()) {
 
711
                QDomElement  msgElem2 = n.toElement();
 
712
                if( !msgElem2.isNull() && msgElem2.tagName()=="head") {
 
713
//                      qDebug() << "found head";
 
714
                        QDomNodeList list = msgElem2.childNodes();
 
715
                        for (int i=0; i<list.size(); i++) {
 
716
                                QDomNode node = list.at(i);
 
717
                                if (node.isElement()) {
 
718
                                        QDomElement h = node.toElement();
 
719
//                                      qDebug() << "found tag " << h.tagName();
 
720
                                        if (h.tagName() == "date") {
 
721
                                                mon = h.attribute("month").toInt();
 
722
                                                year = h.attribute("year").toInt();
 
723
                                        } else if (h.tagName() == "contact") {
 
724
                                                if (h.hasAttribute("type")) {
 
725
                                                        meString = h.attribute("contactId");
 
726
                                                } else {
 
727
                                                        otherString = h.attribute("contactId");
 
728
                                                        log->other = Kopete::ContactList::self()->findContact(protocol, account, otherString);
 
729
                                                        if (log->other) {
 
730
                                                                log->me = log->other->account()->myself();
 
731
                                                        }
 
732
                                                }
 
733
                                        }
 
734
                                }
 
735
                        }
 
736
                        if (!log->me || !log->other || year <0 || mon < 0) {
 
737
                                detailsCursor.insertText("Missing information for: me="+meString+", other="+otherString+", year="+QString("%1").arg(year)+", mon="+QString("%1").arg(mon));
 
738
                                detailsCursor.insertBlock();
 
739
                                return;
 
740
                        }
 
741
                }
 
742
                if( !msgElem2.isNull() && msgElem2.tagName()=="msg") {
 
743
                        rxTime.indexIn(msgElem2.attribute("time"));
 
744
                        QDateTime dt( QDate(year , mon , rxTime.cap(1).toUInt()), QTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt(), rxTime.cap(5).toUInt()  ) );
 
745
 
 
746
                        m.incoming = (msgElem2.attribute("in") == "1");
 
747
 
 
748
                        m.text = msgElem2.text();
 
749
                        m.timestamp = dt;
 
750
                        log->messages.append(m);
 
751
                }
 
752
 
 
753
                n = n.nextSibling();
 
754
        } // end while on messages
 
755
 
 
756
}
 
757
 
 
758
 
 
759
#include "history2import.moc"