summaryrefslogtreecommitdiffstats
path: root/examples/maildirresource/libmaildir/maildir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/maildirresource/libmaildir/maildir.cpp')
-rw-r--r--examples/maildirresource/libmaildir/maildir.cpp865
1 files changed, 865 insertions, 0 deletions
diff --git a/examples/maildirresource/libmaildir/maildir.cpp b/examples/maildirresource/libmaildir/maildir.cpp
new file mode 100644
index 0000000..37bf6ea
--- /dev/null
+++ b/examples/maildirresource/libmaildir/maildir.cpp
@@ -0,0 +1,865 @@
1/*
2 Copyright (c) 2007 Till Adam <adam@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "maildir.h"
21#include "keycache.h"
22
23#include <QDateTime>
24#include <QDir>
25#include <QFileInfo>
26#include <QHostInfo>
27#include <QUuid>
28
29#include <time.h>
30#include <unistd.h>
31
32//Define it to get more debug output to expense of operating speed
33// #define DEBUG_KEYCACHE_CONSITENCY
34
35
36// static void initRandomSeed()
37// {
38// static bool init = false;
39// if (!init) {
40// unsigned int seed;
41// init = true;
42// int fd = KDE_open("/dev/urandom", O_RDONLY);
43// if (fd < 0 || ::read(fd, &seed, sizeof(seed)) != sizeof(seed)) {
44// // No /dev/urandom... try something else.
45// srand(getpid());
46// seed = rand() + time(0);
47// }
48//
49// if (fd >= 0)
50// close(fd);
51//
52// qsrand(seed);
53// }
54// }
55
56
57bool removeDirAndContentsRecursively(const QString & path)
58{
59 bool success = true;
60
61 QDir d;
62 d.setPath(path);
63 d.setFilter(QDir::Files | QDir::Dirs | QDir::Hidden | QDir::NoSymLinks);
64
65 QFileInfoList list = d.entryInfoList();
66
67 Q_FOREACH (const QFileInfo &fi, list) {
68 if (fi.isDir()) {
69 if (fi.fileName() != QLatin1String(".") && fi.fileName() != QLatin1String("..")) {
70 success = success && removeDirAndContentsRecursively(fi.absoluteFilePath());
71 }
72 } else {
73 success = success && d.remove(fi.absoluteFilePath());
74 }
75 }
76
77 if (success) {
78 success = success && d.rmdir(path); // nuke ourselves, we should be empty now
79 }
80 return success;
81}
82
83using namespace KPIM;
84
85Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, statusSeparatorRx, (":|!"))
86
87class Maildir::Private
88{
89public:
90 Private(const QString& p, bool isRoot)
91 :path(p), isRoot(isRoot)
92 {
93 hostName = QHostInfo::localHostName();
94 // The default implementation of QUuid::createUuid() doesn't use
95 // a seed that is random enough. Therefor we use our own initialization
96 // until this issue will be fixed in Qt 4.7.
97 // initRandomSeed();
98
99 //Cache object is created the first time this runs.
100 //It will live throughout the lifetime of the application
101 KeyCache::self()->addKeys(path);
102 }
103
104 Private(const Private& rhs)
105 {
106 path = rhs.path;
107 isRoot = rhs.isRoot;
108 hostName = rhs.hostName;
109 }
110
111 bool operator==(const Private& rhs) const
112 {
113 return path == rhs.path;
114 }
115 bool accessIsPossible(bool createMissingFolders = true);
116 bool canAccess(const QString& path) const;
117
118 QStringList subPaths() const
119 {
120 QStringList paths;
121 paths << path + QString::fromLatin1("/cur");
122 paths << path + QString::fromLatin1("/new");
123 paths << path + QString::fromLatin1("/tmp");
124 return paths;
125 }
126
127 QStringList listNew() const
128 {
129 QDir d(path + QString::fromLatin1("/new"));
130 d.setSorting(QDir::NoSort);
131 return d.entryList(QDir::Files);
132 }
133
134 QStringList listCurrent() const
135 {
136 QDir d(path + QString::fromLatin1("/cur"));
137 d.setSorting(QDir::NoSort);
138 return d.entryList(QDir::Files);
139 }
140
141 QString findRealKey(const QString& key) const
142 {
143 KeyCache* keyCache = KeyCache::self();
144 if (keyCache->isNewKey(path, key)) {
145#ifdef DEBUG_KEYCACHE_CONSITENCY
146 if (!QFile::exists(path + QString::fromLatin1("/new/") + key)) {
147 qDebug() << "WARNING: key is in cache, but the file is gone: " << path + QString::fromLatin1("/new/") + key;
148 }
149#endif
150 return path + QString::fromLatin1("/new/") + key;
151 }
152 if (keyCache->isCurKey(path, key)) {
153#ifdef DEBUG_KEYCACHE_CONSITENCY
154 if (!QFile::exists(path + QString::fromLatin1("/cur/") + key)) {
155 qDebug() << "WARNING: key is in cache, but the file is gone: " << path + QString::fromLatin1("/cur/") + key;
156 }
157#endif
158 return path + QString::fromLatin1("/cur/") + key;
159 }
160 QString realKey = path + QString::fromLatin1("/new/") + key;
161
162 QFile f(realKey);
163 if (f.exists()) {
164 keyCache->addNewKey(path, key);
165 } else { //not in "new", search in "cur"
166 realKey = path + QString::fromLatin1("/cur/") + key;
167 QFile f2(realKey);
168 if (f2.exists()) {
169 keyCache->addCurKey(path, key);
170 } else {
171 realKey.clear(); //not in "cur" either
172 }
173 }
174
175 return realKey;
176 }
177
178 static QString subDirNameForFolderName(const QString &folderName)
179 {
180 return QString::fromLatin1(".%1.directory").arg(folderName);
181 }
182
183 QString subDirPath() const
184 {
185 QDir dir(path);
186 return subDirNameForFolderName(dir.dirName());
187 }
188
189 bool moveAndRename(QDir &dest, const QString &newName)
190 {
191 if (!dest.exists()) {
192 qDebug() << "Destination does not exist";
193 return false;
194 }
195 if (dest.exists(newName) || dest.exists(subDirNameForFolderName(newName))) {
196 qDebug() << "New name already in use";
197 return false;
198 }
199
200 if (!dest.rename(path, newName)) {
201 qDebug() << "Failed to rename maildir";
202 return false;
203 }
204 const QDir subDirs(Maildir::subDirPathForFolderPath(path));
205 if (subDirs.exists() && !dest.rename(subDirs.path(), subDirNameForFolderName(newName))) {
206 qDebug() << "Failed to rename subfolders";
207 return false;
208 }
209
210 path = dest.path() + QDir::separator() + newName;
211 return true;
212 }
213
214 QString path;
215 bool isRoot;
216 QString hostName;
217 QString lastError;
218};
219
220Maildir::Maildir(const QString& path, bool isRoot)
221:d(new Private(path, isRoot))
222{
223}
224
225void Maildir::swap(const Maildir &rhs)
226{
227 Private *p = d;
228 d = new Private(*rhs.d);
229 delete p;
230}
231
232
233Maildir::Maildir(const Maildir & rhs)
234 :d(new Private(*rhs.d))
235
236{
237}
238
239Maildir& Maildir::operator= (const Maildir & rhs)
240{
241 // copy and swap, exception safe, and handles assignment to self
242 Maildir temp(rhs);
243 swap(temp);
244 return *this;
245}
246
247
248bool Maildir::operator== (const Maildir & rhs) const
249{
250 return *d == *rhs.d;
251}
252
253
254Maildir::~Maildir()
255{
256 delete d;
257}
258
259bool Maildir::Private::canAccess(const QString& path) const
260{
261 //return access(QFile::encodeName(path), R_OK | W_OK | X_OK) != 0;
262 // FIXME X_OK?
263 QFileInfo d(path);
264 return d.isReadable() && d.isWritable();
265}
266
267bool Maildir::Private::accessIsPossible(bool createMissingFolders)
268{
269 QStringList paths = subPaths();
270
271 paths.prepend(path);
272
273 Q_FOREACH (const QString &p, paths) {
274 if (!QFile::exists(p)) {
275 if (!createMissingFolders) {
276 // lastError = i18n("Error opening %1; this folder is missing.", p);
277 return false;
278 }
279 QDir().mkpath(p);
280 if (!QFile::exists(p)) {
281 // lastError = i18n("Error opening %1; this folder is missing.", p);
282 return false;
283 }
284 }
285 if (!canAccess(p)) {
286 // lastError = i18n("Error opening %1; either this is not a valid "
287 // "maildir folder, or you do not have sufficient access permissions." ,p);
288 return false;
289 }
290 }
291 return true;
292}
293
294bool Maildir::isValid(bool createMissingFolders) const
295{
296 if (path().isEmpty()) {
297 return false;
298 }
299 if (!d->isRoot) {
300 if (d->accessIsPossible(createMissingFolders)) {
301 return true;
302 }
303 } else {
304 Q_FOREACH (const QString &sf, subFolderList()) {
305 const Maildir subMd = Maildir(path() + QLatin1Char('/') + sf);
306 if (!subMd.isValid()) {
307 d->lastError = subMd.lastError();
308 return false;
309 }
310 }
311 return true;
312 }
313 return false;
314}
315
316bool Maildir::isRoot() const
317{
318 return d->isRoot;
319}
320
321bool Maildir::create()
322{
323 // FIXME: in a failure case, this will leave partially created dirs around
324 // we should clean them up, but only if they didn't previously existed...
325 Q_FOREACH (const QString &p, d->subPaths()) {
326 QDir dir(p);
327 if (!dir.exists(p)) {
328 if (!dir.mkpath(p))
329 return false;
330 }
331 }
332 return true;
333}
334
335QString Maildir::path() const
336{
337 return d->path;
338}
339
340QString Maildir::name() const
341{
342 const QDir dir(d->path);
343 return dir.dirName();
344}
345
346QString Maildir::addSubFolder(const QString& path)
347{
348 if (!isValid())
349 return QString();
350
351 // make the subdir dir
352 QDir dir(d->path);
353 if (!d->isRoot) {
354 dir.cdUp();
355 if (!dir.exists(d->subDirPath()))
356 dir.mkdir(d->subDirPath());
357 dir.cd(d->subDirPath());
358 }
359
360 const QString fullPath = dir.path() + QLatin1Char('/') + path;
361 Maildir subdir(fullPath);
362 if (subdir.create())
363 return fullPath;
364 return QString();
365}
366
367bool Maildir::removeSubFolder(const QString& folderName)
368{
369 if (!isValid()) return false;
370 QDir dir(d->path);
371 if (!d->isRoot) {
372 dir.cdUp();
373 if (!dir.exists(d->subDirPath())) return false;
374 dir.cd(d->subDirPath());
375 }
376 if (!dir.exists(folderName)) return false;
377
378 // remove it recursively
379 bool result = removeDirAndContentsRecursively(dir.absolutePath() + QLatin1Char('/') + folderName);
380 QString subfolderName = subDirNameForFolderName(folderName);
381 if (dir.exists(subfolderName))
382 result &= removeDirAndContentsRecursively(dir.absolutePath() + QLatin1Char('/') + subfolderName);
383 return result;
384}
385
386Maildir Maildir::subFolder(const QString& subFolder) const
387{
388 // make the subdir dir
389 QDir dir(d->path);
390 if (!d->isRoot) {
391 dir.cdUp();
392 if (dir.exists(d->subDirPath())) {
393 dir.cd(d->subDirPath());
394 }
395 }
396 return Maildir(dir.path() + QLatin1Char('/') + subFolder);
397}
398
399Maildir Maildir::parent() const
400{
401 if (!isValid() || d->isRoot)
402 return Maildir();
403 QDir dir(d->path);
404 dir.cdUp();
405 if (!dir.dirName().startsWith(QLatin1Char('.')) || !dir.dirName().endsWith(QLatin1String(".directory")))
406 return Maildir();
407 const QString parentName = dir.dirName().mid(1, dir.dirName().size() - 11);
408 dir.cdUp();
409 dir.cd(parentName);
410 return Maildir (dir.path());
411}
412
413QStringList Maildir::entryList() const
414{
415 QStringList result;
416 if (isValid()) {
417 result += d->listNew();
418 result += d->listCurrent();
419 }
420 // qDebug() <<"Maildir::entryList()" << result;
421 return result;
422}
423
424QStringList Maildir::listCurrent() const
425{
426 QStringList result;
427 if (isValid()) {
428 result += d->listCurrent();
429 }
430 return result;
431}
432
433QString Maildir::findRealKey(const QString& key) const
434{
435 return d->findRealKey(key);
436}
437
438
439QStringList Maildir::listNew() const
440{
441 QStringList result;
442 if (isValid()) {
443 result += d->listNew();
444 }
445 return result;
446}
447
448QString Maildir::pathToNew() const
449{
450 if (isValid()) {
451 return d->path + QString::fromLatin1("/new");
452 }
453 return QString();
454}
455
456QString Maildir::pathToCurrent() const
457{
458 if (isValid()) {
459 return d->path + QString::fromLatin1("/cur");
460 }
461 return QString();
462}
463
464QString Maildir::subDirPath() const
465{
466 QDir dir(d->path);
467 dir.cdUp();
468 return dir.path() + QDir::separator() + d->subDirPath();
469}
470
471
472
473QStringList Maildir::subFolderList() const
474{
475 QDir dir(d->path);
476
477 // the root maildir has its subfolders directly beneath it
478 if (!d->isRoot) {
479 dir.cdUp();
480 if (!dir.exists(d->subDirPath()))
481 return QStringList();
482 dir.cd(d->subDirPath());
483 }
484 dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
485 QStringList entries = dir.entryList();
486 entries.removeAll(QLatin1String("cur"));
487 entries.removeAll(QLatin1String("new"));
488 entries.removeAll(QLatin1String("tmp"));
489 return entries;
490}
491
492QByteArray Maildir::readEntry(const QString& key) const
493{
494 QByteArray result;
495
496 QString realKey(d->findRealKey(key));
497 if (realKey.isEmpty()) {
498 // FIXME error handling?
499 qWarning() << "Maildir::readEntry unable to find: " << key;
500 // d->lastError = i18n("Cannot locate mail file %1." ,key);
501 return result;
502 }
503
504 QFile f(realKey);
505 if (!f.open(QIODevice::ReadOnly)) {
506 // d->lastError = i18n("Cannot open mail file %1.", realKey);
507 return result;
508 }
509
510 // FIXME be safer than this
511 result = f.readAll();
512
513 return result;
514}
515qint64 Maildir::size(const QString& key) const
516{
517 QString realKey(d->findRealKey(key));
518 if (realKey.isEmpty()) {
519 // FIXME error handling?
520 qWarning() << "Maildir::size unable to find: " << key;
521 // d->lastError = i18n("Cannot locate mail file %1." , key);
522 return -1;
523 }
524
525 QFileInfo info(realKey);
526 if (!info.exists()) {
527 // d->lastError = i18n("Cannot open mail file %1." ,realKey);
528 return -1;
529 }
530
531 return info.size();
532}
533
534QDateTime Maildir::lastModified(const QString& key) const
535{
536 const QString realKey(d->findRealKey(key));
537 if (realKey.isEmpty()) {
538 qWarning() << "Maildir::lastModified unable to find: " << key;
539 // d->lastError = i18n("Cannot locate mail file %1." , key);
540 return QDateTime();
541 }
542
543 const QFileInfo info(realKey);
544 if (!info.exists())
545 return QDateTime();
546
547 return info.lastModified();
548}
549
550QByteArray Maildir::readEntryHeadersFromFile(const QString& file) const
551{
552 QByteArray result;
553
554 QFile f(file);
555 if (!f.open(QIODevice::ReadOnly)) {
556 // FIXME error handling?
557 qWarning() << "Maildir::readEntryHeaders unable to find: " << file;
558 // d->lastError = i18n("Cannot locate mail file %1." , file);
559 return result;
560 }
561 f.map(0, qMin((qint64)8000, f.size()));
562 forever {
563 QByteArray line = f.readLine();
564 if (line.isEmpty() || line.startsWith('\n'))
565 break;
566 result.append(line);
567 }
568 return result;
569}
570
571QByteArray Maildir::readEntryHeaders(const QString& key) const
572{
573 const QString realKey(d->findRealKey(key));
574 if (realKey.isEmpty()) {
575 qWarning() << "Maildir::readEntryHeaders unable to find: " << key;
576 // d->lastError = i18n("Cannot locate mail file %1." , key);
577 return QByteArray();
578 }
579
580 return readEntryHeadersFromFile(realKey);
581}
582
583
584static QString createUniqueFileName()
585{
586 qint64 time = QDateTime::currentMSecsSinceEpoch() / 1000;
587 int r = qrand() % 1000;
588 QString identifier = QLatin1String("R") + QString::number(r);
589
590 QString fileName = QString::number(time) + QLatin1Char('.') + identifier + QLatin1Char('.');
591
592 return fileName;
593}
594
595bool Maildir::writeEntry(const QString& key, const QByteArray& data)
596{
597 QString realKey(d->findRealKey(key));
598 if (realKey.isEmpty()) {
599 // FIXME error handling?
600 qWarning() << "Maildir::writeEntry unable to find: " << key;
601 // d->lastError = i18n("Cannot locate mail file %1." ,key);
602 return false;
603 }
604 QFile f(realKey);
605 bool result = f.open(QIODevice::WriteOnly);
606 result = result & (f.write(data) != -1);
607 f.close();
608 if (!result) {
609 // d->lastError = i18n("Cannot write to mail file %1." ,realKey);
610 return false;
611 }
612 return true;
613}
614
615QString Maildir::addEntry(const QByteArray& data)
616{
617 QString uniqueKey;
618 QString key;
619 QString finalKey;
620 QString curKey;
621
622 // QUuid doesn't return globally unique identifiers, therefor we query until we
623 // get one that doesn't exists yet
624 do {
625 uniqueKey = createUniqueFileName() + d->hostName;
626 key = d->path + QLatin1String("/tmp/") + uniqueKey;
627 finalKey = d->path + QLatin1String("/new/") + uniqueKey;
628 curKey = d->path + QLatin1String("/cur/") + uniqueKey;
629 } while (QFile::exists(key) || QFile::exists(finalKey) || QFile::exists(curKey));
630
631 QFile f(key);
632 bool result = f.open(QIODevice::WriteOnly);
633 result = result & (f.write(data) != -1);
634 f.close();
635 if (!result) {
636 // d->lastError = i18n("Cannot write to mail file %1." , key);
637 return QString();
638 }
639 /*
640 * FIXME:
641 *
642 * The whole point of the locking free maildir idea is that the moves between
643 * the internal directories are atomic. Afaik QFile::rename does not guarantee
644 * that, so this will need to be done properly. - ta
645 *
646 * For reference: http://trolltech.com/developer/task-tracker/index_html?method=entry&id=211215
647 */
648 if (!f.rename(finalKey)) {
649 qWarning() << "Maildir: Failed to add entry: " << finalKey << "! Error: " << f.errorString();
650 // d->lastError = i18n("Failed to create mail file %1. The error was: %2" , finalKey, f.errorString());
651 return QString();
652 }
653 KeyCache *keyCache = KeyCache::self();
654 keyCache->removeKey(d->path, key); //remove all keys, be it "cur" or "new" first
655 keyCache->addNewKey(d->path, key); //and add a key for "new", as the mail was moved there
656 return uniqueKey;
657}
658
659bool Maildir::removeEntry(const QString& key)
660{
661 QString realKey(d->findRealKey(key));
662 if (realKey.isEmpty()) {
663 qWarning() << "Maildir::removeEntry unable to find: " << key;
664 return false;
665 }
666 KeyCache *keyCache = KeyCache::self();
667 keyCache->removeKey(d->path, key);
668 return QFile::remove(realKey);
669}
670
671// QString Maildir::changeEntryFlags(const QString& key, const Akonadi::Item::Flags& flags)
672// {
673// QString realKey(d->findRealKey(key));
674// if (realKey.isEmpty()) {
675// qWarning() << "Maildir::changeEntryFlags unable to find: " << key;
676// d->lastError = i18n("Cannot locate mail file %1." , key);
677// return QString();
678// }
679//
680// const QRegExp rx = *(statusSeparatorRx());
681// QString finalKey = key.left(key.indexOf(rx));
682//
683// QStringList mailDirFlags;
684// Q_FOREACH (const Akonadi::Item::Flag &flag, flags) {
685// if (flag == Akonadi::MessageFlags::Forwarded)
686// mailDirFlags << QLatin1String("P");
687// if (flag == Akonadi::MessageFlags::Replied)
688// mailDirFlags << QLatin1String("R");
689// if (flag == Akonadi::MessageFlags::Seen)
690// mailDirFlags << QLatin1String("S");
691// if (flag == Akonadi::MessageFlags::Deleted)
692// mailDirFlags << QLatin1String("T");
693// if (flag == Akonadi::MessageFlags::Flagged)
694// mailDirFlags << QLatin1String("F");
695// }
696// mailDirFlags.sort();
697// if (!mailDirFlags.isEmpty()) {
698// #ifdef Q_OS_WIN
699// finalKey.append(QLatin1String("!2,") + mailDirFlags.join(QString()));
700// #else
701// finalKey.append(QLatin1String(":2,") + mailDirFlags.join(QString()));
702// #endif
703// }
704//
705// QString newUniqueKey = finalKey; //key without path
706// finalKey.prepend(d->path + QString::fromLatin1("/cur/"));
707//
708// if (realKey == finalKey) {
709// // Somehow it already is named this way (e.g. migration bug -> wrong status in akonadi)
710// return newUniqueKey;
711// }
712//
713// QFile f(realKey);
714// if (QFile::exists(finalKey)) {
715// QFile destFile(finalKey);
716// QByteArray destContent;
717// if (destFile.open(QIODevice::ReadOnly)) {
718// destContent = destFile.readAll();
719// destFile.close();
720// }
721// QByteArray sourceContent;
722// if (f.open(QIODevice::ReadOnly)) {
723// sourceContent = f.readAll();
724// f.close();
725// }
726//
727// if (destContent != sourceContent) {
728// QString newFinalKey = QLatin1String("1-") + newUniqueKey;
729// int i = 1;
730// while (QFile::exists(d->path + QString::fromLatin1("/cur/") + newFinalKey)) {
731// i++;
732// newFinalKey = QString::number(i) + QLatin1Char('-') + newUniqueKey;
733// }
734// finalKey = d->path + QString::fromLatin1("/cur/") + newFinalKey;
735// } else {
736// QFile::remove(finalKey); //they are the same
737// }
738// }
739//
740// if (!f.rename(finalKey)) {
741// qWarning() << "Maildir: Failed to rename entry: " << f.fileName() << " to " << finalKey << "! Error: " << f.errorString();
742// d->lastError = i18n("Failed to update the file name %1 to %2 on the disk. The error was: %3." , f.fileName(), finalKey, f.errorString());
743// return QString();
744// }
745//
746// KeyCache *keyCache = KeyCache::self();
747// keyCache->removeKey(d->path, key);
748// keyCache->addCurKey(d->path, newUniqueKey);
749//
750// return newUniqueKey;
751// }
752//
753Maildir::Flags Maildir::readEntryFlags(const QString& key) const
754{
755 Flags flags;
756 const QRegExp rx = *(statusSeparatorRx());
757 const int index = key.indexOf(rx);
758 if (index != -1) {
759 const QString mailDirFlags = key.mid(index + 3); // after "(:|!)2,"
760 const int flagSize(mailDirFlags.size());
761 for (int i = 0; i < flagSize; ++i) {
762 if (mailDirFlags[i] == QLatin1Char('P'))
763 flags |= Forwarded;
764 else if (mailDirFlags[i] == QLatin1Char('R'))
765 flags |= Replied;
766 else if (mailDirFlags[i] == QLatin1Char('S'))
767 flags |= Seen;
768 else if (mailDirFlags[i] == QLatin1Char('F'))
769 flags |= Flagged;
770 }
771 }
772
773 return flags;
774}
775
776bool Maildir::moveTo(const Maildir &newParent)
777{
778 if (d->isRoot)
779 return false; // not supported
780
781 QDir newDir(newParent.path());
782 if (!newParent.d->isRoot) {
783 newDir.cdUp();
784 if (!newDir.exists(newParent.d->subDirPath()))
785 newDir.mkdir(newParent.d->subDirPath());
786 newDir.cd(newParent.d->subDirPath());
787 }
788
789 QDir currentDir(d->path);
790 currentDir.cdUp();
791
792 if (newDir == currentDir)
793 return true;
794
795 return d->moveAndRename(newDir, name());
796}
797
798bool Maildir::rename(const QString &newName)
799{
800 if (name() == newName)
801 return true;
802 if (d->isRoot)
803 return false; // not (yet) supported
804
805 QDir dir(d->path);
806 dir.cdUp();
807
808 return d->moveAndRename(dir, newName);
809}
810
811QString Maildir::moveEntryTo(const QString &key, const Maildir &destination)
812{
813 const QString realKey(d->findRealKey(key));
814 if (realKey.isEmpty()) {
815 qWarning() << "Unable to find: " << key;
816 // d->lastError = i18n("Cannot locate mail file %1." , key);
817 return QString();
818 }
819 QFile f(realKey);
820 // ### is this safe regarding the maildir locking scheme?
821 const QString targetKey = destination.path() + QDir::separator() + QLatin1String("new") + QDir::separator() + key;
822 if (!f.rename(targetKey)) {
823 qDebug() << "Failed to rename" << realKey << "to" << targetKey << "! Error: " << f.errorString();;
824 d->lastError = f.errorString();
825 return QString();
826 }
827
828 KeyCache* keyCache = KeyCache::self();
829
830 keyCache->addNewKey(destination.path(), key);
831 keyCache->removeKey(d->path, key);
832
833 return key;
834}
835
836QString Maildir::subDirPathForFolderPath(const QString &folderPath)
837{
838 QDir dir(folderPath);
839 const QString dirName = dir.dirName();
840 dir.cdUp();
841 return QFileInfo(dir, Private::subDirNameForFolderName(dirName)).filePath();
842}
843
844QString Maildir::subDirNameForFolderName(const QString &folderName)
845{
846 return Private::subDirNameForFolderName(folderName);
847}
848
849void Maildir::removeCachedKeys(const QStringList & keys)
850{
851 KeyCache *keyCache = KeyCache::self();
852 Q_FOREACH (const QString &key, keys) {
853 keyCache->removeKey(d->path, key);
854 }
855}
856
857void Maildir::refreshKeyCache()
858{
859 KeyCache::self()->refreshKeys(d->path);
860}
861
862QString Maildir::lastError() const
863{
864 return d->lastError;
865}