1 /*
2   filterkmail_maildir.cpp  -  Kmail maildir mail import
3 
4   SPDX-FileCopyrightText: 2005 Danny Kukawka <danny.kukawka@web.de>
5   SPDX-FileCopyrightText: 2012-2021 Laurent Montel <montel@kde.org>
6 
7   SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "filterkmail_maildir.h"
11 
12 #include <KLocalizedString>
13 #include <QFileDialog>
14 
15 using namespace MailImporter;
16 
17 class MailImporter::FilterKMail_maildirPrivate
18 {
19 public:
20     int mImportDirDone = 0;
21     int mTotalDir = 0;
22 };
23 
24 /** Default constructor. */
FilterKMail_maildir()25 FilterKMail_maildir::FilterKMail_maildir()
26     : Filter(i18n("Import KMail Maildirs and Folder Structure"),
27              QStringLiteral("Danny Kukawka"),
28              i18n("<p><b>KMail import filter</b></p>"
29                   "<p>Select the base directory of the KMail mailfolder you want to import.</p>"
30                   "<p><b>Note:</b> Never select your current local KMail maildir (usually "
31                   "~/Mail or ~/.kde/share/apps/kmail/mail ): in this case, ImportWizard may become stuck "
32                   "in a continuous loop. </p>"
33                   "<p>This filter does not import KMail mailfolders with mbox files.</p>"
34                   "<p>Since it is possible to recreate the folder structure, the folders "
35                   "will be stored under: \"KMail-Import\" in your local folder.</p>"))
36     , d(new MailImporter::FilterKMail_maildirPrivate)
37 {
38 }
39 
40 /** Destructor. */
41 FilterKMail_maildir::~FilterKMail_maildir() = default;
42 
43 /** Recursive import of KMail maildir. */
import()44 void FilterKMail_maildir::import()
45 {
46     clearCountDuplicate();
47     const QString homeDir = QDir::homePath();
48     const QString maildir = QFileDialog::getExistingDirectory(nullptr, QString(), homeDir);
49     if (!maildir.isEmpty()) {
50         importMails(maildir);
51     }
52 }
53 
processDirectory(const QString & path)54 void FilterKMail_maildir::processDirectory(const QString &path)
55 {
56     QDir dir(path);
57     const QStringList rootSubDirs = dir.entryList(QStringList(QStringLiteral("*")), QDir::Dirs | QDir::Hidden, QDir::Name);
58     QStringList::ConstIterator end = rootSubDirs.constEnd();
59     for (QStringList::ConstIterator filename = rootSubDirs.constBegin(); filename != end; ++filename) {
60         if (filterInfo()->shouldTerminate()) {
61             break;
62         }
63         if (!(*filename == QLatin1Char('.') || *filename == QLatin1String(".."))) {
64             filterInfo()->setCurrent(0);
65             importDirContents(dir.filePath(*filename));
66             filterInfo()->setOverall((d->mTotalDir > 0) ? (int)((float)d->mImportDirDone / d->mTotalDir * 100) : 0);
67             filterInfo()->setCurrent(100);
68             ++d->mImportDirDone;
69         }
70     }
71 }
72 
importMails(const QString & maildir)73 void FilterKMail_maildir::importMails(const QString &maildir)
74 {
75     if (maildir.isEmpty()) {
76         filterInfo()->alert(i18n("No directory selected."));
77         return;
78     }
79     setMailDir(maildir);
80     /**
81      * If the user only select homedir no import needed because
82      * there should be no files and we surely import wrong files.
83      */
84     if (mailDir() == QDir::homePath() || mailDir() == (QDir::homePath() + QLatin1Char('/'))) {
85         filterInfo()->addErrorLogEntry(i18n("No files found for import."));
86     } else {
87         filterInfo()->setOverall(0);
88         d->mImportDirDone = 0;
89 
90         /** Recursive import of the MailArchives */
91         QDir dir(mailDir());
92         d->mTotalDir = Filter::countDirectory(dir, true /*search hidden directory*/);
93         processDirectory(mailDir());
94 
95         filterInfo()->addInfoLogEntry(i18n("Finished importing emails from %1", mailDir()));
96         if (countDuplicates() > 0) {
97             filterInfo()->addInfoLogEntry(i18np("1 duplicate message not imported", "%1 duplicate messages not imported", countDuplicates()));
98         }
99     }
100     if (filterInfo()->shouldTerminate()) {
101         filterInfo()->addInfoLogEntry(i18n("Finished import, canceled by user."));
102     }
103     filterInfo()->setCurrent(100);
104     filterInfo()->setOverall(100);
105 }
106 
107 /**
108  * Import of a directory contents.
109  * @param info Information storage for the operation.
110  * @param dirName The name of the directory to import.
111  */
importDirContents(const QString & dirName)112 void FilterKMail_maildir::importDirContents(const QString &dirName)
113 {
114     /** Here Import all archives in the current dir */
115     importFiles(dirName);
116 
117     /** If there are subfolders, we import them one by one */
118 
119     processDirectory(dirName);
120 }
121 
122 /**
123  * Import the files within a Folder.
124  * @param info Information storage for the operation.
125  * @param dirName The name of the directory to import.
126  */
importFiles(const QString & dirName)127 void FilterKMail_maildir::importFiles(const QString &dirName)
128 {
129     QDir dir(dirName);
130     QString _path;
131     bool generatedPath = false;
132 
133     QDir importDir(dirName);
134     const QStringList files = importDir.entryList(QStringList(QStringLiteral("[^\\.]*")), QDir::Files, QDir::Name);
135     int currentFile = 1;
136     int numFiles = files.size();
137     QStringList::ConstIterator filesEnd(files.constEnd());
138 
139     for (QStringList::ConstIterator mailFile = files.constBegin(); mailFile != filesEnd; ++mailFile, ++currentFile) {
140         if (filterInfo()->shouldTerminate()) {
141             return;
142         }
143 
144         QString temp_mailfile = *mailFile;
145         if (!(temp_mailfile.endsWith(QLatin1String(".index")) || temp_mailfile.endsWith(QLatin1String(".index.ids"))
146               || temp_mailfile.endsWith(QLatin1String(".index.sorted")) || temp_mailfile.endsWith(QLatin1String(".uidcache")))) {
147             if (!generatedPath) {
148                 _path = QStringLiteral("KMail-Import");
149                 QString _tmp = dir.filePath(*mailFile);
150                 _tmp.remove(mailDir(), Qt::CaseSensitive);
151                 const QStringList subFList = _tmp.split(QLatin1Char('/'), Qt::SkipEmptyParts);
152                 QStringList::ConstIterator end(subFList.end());
153                 for (QStringList::ConstIterator it = subFList.constBegin(); it != end; ++it) {
154                     QString _cat = *it;
155                     if (!(_cat == *mailFile)) {
156                         if (_cat.startsWith(QLatin1Char('.')) && _cat.endsWith(QLatin1String(".directory"))) {
157                             _cat.remove(0, 1);
158                             _cat.remove((_cat.length() - 10), 10);
159                         } else if (_cat.startsWith(QLatin1Char('.'))) {
160                             _cat.remove(0, 1);
161                         }
162                         _path += QLatin1Char('/') + _cat;
163                     }
164                 }
165                 if (_path.endsWith(QLatin1String("cur"))) {
166                     _path.remove(_path.length() - 4, 4);
167                 }
168                 QString _info = _path;
169                 filterInfo()->addInfoLogEntry(i18n("Import folder %1...", _info.remove(0, 12)));
170                 filterInfo()->setFrom(_info);
171                 filterInfo()->setTo(_path);
172                 generatedPath = true;
173             }
174 
175             if (!importMessage(_path, dir.filePath(*mailFile), filterInfo()->removeDupMessage())) {
176                 filterInfo()->addErrorLogEntry(i18n("Could not import %1, duplicated message", *mailFile));
177             }
178             filterInfo()->setCurrent((int)((float)currentFile / numFiles * 100));
179         }
180     }
181 }
182