File indexing completed on 2024-11-24 04:44:31
0001 /* 0002 SPDX-FileCopyrightText: 2009 Thomas McGuire <mcguire@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "pop3test.h" 0008 0009 #include <Akonadi/AgentInstanceCreateJob> 0010 #include <Akonadi/AgentManager> 0011 #include <Akonadi/CollectionFetchJob> 0012 #include <Akonadi/Control> 0013 #include <Akonadi/ItemDeleteJob> 0014 #include <Akonadi/ItemFetchJob> 0015 #include <Akonadi/ItemFetchScope> 0016 #include <Akonadi/Monitor> 0017 #include <Akonadi/ServerManager> 0018 #include <KMime/Message> 0019 #include <QElapsedTimer> 0020 #include <akonadi/qtest_akonadi.h> 0021 0022 #include <QStandardPaths> 0023 0024 QTEST_AKONADIMAIN(Pop3Test) 0025 0026 using namespace Akonadi; 0027 0028 constexpr int serverSettleTimeout = 200; /* ms */ 0029 0030 void Pop3Test::initTestCase() 0031 { 0032 AkonadiTest::checkTestIsIsolated(); 0033 QVERIFY(Akonadi::Control::start()); 0034 0035 // switch all resources offline to reduce interference from them 0036 const auto instances{Akonadi::AgentManager::self()->instances()}; 0037 for (Akonadi::AgentInstance agent : instances) { 0038 agent.setIsOnline(false); 0039 } 0040 0041 /* 0042 qDebug() << "==========================================================="; 0043 qDebug() << "============ Stopping for debugging ======================="; 0044 qDebug() << "==========================================================="; 0045 kill( qApp->applicationPid(), SIGSTOP ); 0046 */ 0047 0048 // 0049 // Create the maildir and pop3 resources 0050 // 0051 AgentType maildirType = AgentManager::self()->type(QStringLiteral("akonadi_maildir_resource")); 0052 auto agentCreateJob = new AgentInstanceCreateJob(maildirType); 0053 const bool maildirCreateSuccess = agentCreateJob->exec(); 0054 if (!maildirCreateSuccess) { 0055 qWarning() << "Failed to create maildir resource:" << agentCreateJob->errorString(); 0056 } 0057 QVERIFY(maildirCreateSuccess); 0058 mMaildirIdentifier = agentCreateJob->instance().identifier(); 0059 0060 AgentType popType = AgentManager::self()->type(QStringLiteral("akonadi_pop3_resource")); 0061 agentCreateJob = new AgentInstanceCreateJob(popType); 0062 const bool pop3CreateSuccess = agentCreateJob->exec(); 0063 if (!pop3CreateSuccess) { 0064 qWarning() << "Failed to create pop3 resource:" << agentCreateJob->errorString(); 0065 } 0066 QVERIFY(pop3CreateSuccess); 0067 mPop3Identifier = agentCreateJob->instance().identifier(); 0068 0069 // 0070 // Configure the maildir resource 0071 // 0072 QString maildirRootPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QLatin1StringView("tester"); 0073 mMaildirPath = maildirRootPath + QLatin1StringView("/new"); 0074 QDir::current().mkpath(mMaildirPath); 0075 QDir::current().mkpath(maildirRootPath + QLatin1StringView("/tmp")); 0076 0077 QString service = QLatin1StringView("org.freedesktop.Akonadi.Resource.") + mMaildirIdentifier; 0078 if (Akonadi::ServerManager::hasInstanceIdentifier()) { 0079 service += QLatin1Char('.') + Akonadi::ServerManager::instanceIdentifier(); 0080 } 0081 0082 mMaildirSettingsInterface = new OrgKdeAkonadiMaildirSettingsInterface(service, QStringLiteral("/Settings"), QDBusConnection::sessionBus(), this); 0083 QDBusReply<void> setPathReply = mMaildirSettingsInterface->setPath(maildirRootPath); 0084 QVERIFY(setPathReply.isValid()); 0085 mMaildirSettingsInterface->save(); 0086 AgentManager::self()->instance(mMaildirIdentifier).reconfigure(); 0087 QDBusReply<QString> getPathReply = mMaildirSettingsInterface->path(); 0088 QCOMPARE(getPathReply.value(), maildirRootPath); 0089 AgentManager::self()->instance(mMaildirIdentifier).synchronize(); 0090 0091 // 0092 // Find the root maildir collection 0093 // 0094 bool found = false; 0095 QElapsedTimer time; 0096 time.start(); 0097 while (!found) { 0098 auto job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive); 0099 QVERIFY(job->exec()); 0100 const Collection::List collections = job->collections(); 0101 for (const Collection &col : collections) { 0102 if (col.resource() == AgentManager::self()->instance(mMaildirIdentifier).identifier() && col.remoteId() == maildirRootPath) { 0103 mMaildirCollection = col; 0104 found = true; 0105 break; 0106 } 0107 } 0108 0109 QVERIFY(time.elapsed() < 10 * 1000); // maildir should not need more than 10 secs to sync 0110 } 0111 0112 // 0113 // Start the fake POP3 server 0114 // 0115 mFakeServerThread = new FakeServerThread(this); 0116 mFakeServerThread->start(); 0117 QTest::qWait(100); 0118 QVERIFY(mFakeServerThread->server() != nullptr); 0119 0120 // 0121 // Configure the pop3 resource 0122 // 0123 mPOP3SettingsInterface = new OrgKdeAkonadiPOP3SettingsInterface(Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Resource, mPop3Identifier), 0124 QStringLiteral("/Settings"), 0125 QDBusConnection::sessionBus(), 0126 this); 0127 0128 QDBusReply<uint> reply0 = mPOP3SettingsInterface->port(); 0129 QVERIFY(reply0.isValid()); 0130 QCOMPARE(reply0.value(), 110u); 0131 0132 mPOP3SettingsInterface->setPort(5989).waitForFinished(); 0133 mPOP3SettingsInterface->save(); 0134 AgentManager::self()->instance(mPop3Identifier).reconfigure(); 0135 QDBusReply<uint> reply = mPOP3SettingsInterface->port(); 0136 QVERIFY(reply.isValid()); 0137 QCOMPARE(reply.value(), 5989u); 0138 0139 mPOP3SettingsInterface->setHost(QStringLiteral("localhost")).waitForFinished(); 0140 mPOP3SettingsInterface->save(); 0141 AgentManager::self()->instance(mPop3Identifier).reconfigure(); 0142 QDBusReply<QString> reply2 = mPOP3SettingsInterface->host(); 0143 QVERIFY(reply2.isValid()); 0144 QCOMPARE(reply2.value(), QLatin1StringView("localhost")); 0145 mPOP3SettingsInterface->setLogin(QStringLiteral("HansWurst")).waitForFinished(); 0146 mPOP3SettingsInterface->save(); 0147 AgentManager::self()->instance(mPop3Identifier).reconfigure(); 0148 QDBusReply<QString> reply3 = mPOP3SettingsInterface->login(); 0149 QVERIFY(reply3.isValid()); 0150 QCOMPARE(reply3.value(), QLatin1StringView("HansWurst")); 0151 0152 mPOP3SettingsInterface->setUnitTestPassword(QStringLiteral("Geheim")).waitForFinished(); 0153 mPOP3SettingsInterface->save(); 0154 AgentManager::self()->instance(mPop3Identifier).reconfigure(); 0155 QDBusReply<QString> reply4 = mPOP3SettingsInterface->unitTestPassword(); 0156 QVERIFY(reply4.isValid()); 0157 QCOMPARE(reply4.value(), QLatin1StringView("Geheim")); 0158 0159 mPOP3SettingsInterface->setTargetCollection(mMaildirCollection.id()).waitForFinished(); 0160 mPOP3SettingsInterface->save(); 0161 AgentManager::self()->instance(mPop3Identifier).reconfigure(); 0162 QDBusReply<qlonglong> reply5 = mPOP3SettingsInterface->targetCollection(); 0163 QVERIFY(reply5.isValid()); 0164 QCOMPARE(reply5.value(), mMaildirCollection.id()); 0165 } 0166 0167 void Pop3Test::cleanupTestCase() 0168 { 0169 // test might have failed before thread got created 0170 if (mFakeServerThread) { 0171 mFakeServerThread->quit(); 0172 if (!mFakeServerThread->wait(10000)) { 0173 qWarning() << "The fake server thread has not yet finished, what is wrong!?"; 0174 } 0175 } 0176 } 0177 0178 static const QByteArray simpleMail1 = 0179 "From: \"Bill Lumbergh\" <BillLumbergh@initech.com>\r\n" 0180 "To: \"Peter Gibbons\" <PeterGibbons@initech.com>\r\n" 0181 "Subject: TPS Reports - New Cover Sheets\r\n" 0182 "MIME-Version: 1.0\r\n" 0183 "Content-Type: text/plain\r\n" 0184 "Date: Mon, 23 Mar 2009 18:04:05 +0300\r\n" 0185 "\r\n" 0186 "Hi, Peter. What's happening? We need to talk about your TPS reports.\r\n"; 0187 0188 static const QByteArray simpleMail2 = 0189 "From: \"Amy McCorkell\" <yooper@mtao.net>\r\n" 0190 "To: gov.palin@yaho.com\r\n" 0191 "Subject: HI SARAH\r\n" 0192 "MIME-Version: 1.0\r\n" 0193 "Content-Type: text/plain\r\n" 0194 "Date: Mon, 23 Mar 2009 18:04:05 +0300\r\n" 0195 "\r\n" 0196 "Hey Sarah,\r\n" 0197 "bla bla bla bla bla\r\n"; 0198 0199 static const QByteArray simpleMail3 = 0200 "From: chunkylover53@aol.com\r\n" 0201 "To: tylerdurden@paperstreetsoapcompany.com\r\n" 0202 "Subject: ILOVEYOU\r\n" 0203 "MIME-Version: 1.0\r\n" 0204 "Content-Type: text/plain\r\n" 0205 "Date: Mon, 23 Mar 2009 18:04:05 +0300\r\n" 0206 "\r\n" 0207 "kindly check the attached LOVELETTER coming from me.\r\n"; 0208 0209 static const QByteArray simpleMail4 = 0210 "From: karl@aol.com\r\n" 0211 "To: lenny@aol.com\r\n" 0212 "Subject: Who took the donuts?\r\n" 0213 "\r\n" 0214 "Hi Lenny, do you know who took all the donuts?\r\n"; 0215 0216 static const QByteArray simpleMail5 = 0217 "From: foo@bar.com\r\n" 0218 "To: bar@foo.com\r\n" 0219 "Subject: Hello\r\n" 0220 "\r\n" 0221 "Hello World!!\r\n"; 0222 0223 void Pop3Test::cleanupMaildir(const Akonadi::Item::List &items) 0224 { 0225 // Delete all mails so the maildir is clean for the next test 0226 if (!items.isEmpty()) { 0227 auto job = new ItemDeleteJob(items); 0228 QVERIFY(job->exec()); 0229 } 0230 0231 QElapsedTimer time; 0232 time.start(); 0233 int lastCount = -1; 0234 for (;;) { 0235 QTest::qWait(500); 0236 QDir maildir(mMaildirPath); 0237 maildir.refresh(); 0238 const int curCount = maildir.entryList(QDir::Files | QDir::NoDotAndDotDot).count(); 0239 0240 // Restart the timer when a mail arrives, as it shows that the maildir resource is 0241 // still alive and kicking. 0242 if (curCount != lastCount) { 0243 time.restart(); 0244 lastCount = curCount; 0245 } 0246 0247 if (curCount == 0) { 0248 break; 0249 } 0250 0251 QVERIFY(time.elapsed() < 60000 || time.elapsed() > 80000000); 0252 } 0253 } 0254 0255 void Pop3Test::checkMailsInMaildir(const QList<QByteArray> &mails) 0256 { 0257 // Now, test that all mails actually ended up in the maildir. Since the maildir resource 0258 // might be slower, give it a timeout so it can write the files to disk 0259 QElapsedTimer time; 0260 time.start(); 0261 int lastCount = -1; 0262 for (;;) { 0263 QTest::qWait(500); 0264 QDir maildir(mMaildirPath); 0265 maildir.refresh(); 0266 const int curCount = maildir.entryList(QDir::Files | QDir::NoDotAndDotDot).count(); 0267 0268 if (curCount == mails.count()) { 0269 break; // all done 0270 } 0271 0272 // Restart the timer when a mail arrives, as it shows that the maildir resource is 0273 // still alive and kicking. 0274 if (curCount != lastCount) { 0275 time.start(); 0276 lastCount = curCount; 0277 } 0278 0279 QVERIFY(curCount <= mails.count()); 0280 QVERIFY(time.elapsed() < 60000 || time.elapsed() > 80000000); 0281 } 0282 0283 // TODO: check file contents as well or is this overkill? 0284 } 0285 0286 Akonadi::Item::List Pop3Test::checkMailsOnAkonadiServer(const QList<QByteArray> &mails) 0287 { 0288 // The fake server got disconnected, which means the pop3 resource has entered the QUIT 0289 // stage. That means all messages should be on the server now, so test that. 0290 auto job = new ItemFetchJob(mMaildirCollection); 0291 job->fetchScope().fetchFullPayload(); 0292 const bool ok = job->exec(); 0293 Q_ASSERT(ok); 0294 if (!ok) { 0295 return {}; 0296 } 0297 const Item::List items = job->items(); 0298 Q_ASSERT(mails.size() == items.size()); 0299 0300 QSet<QByteArray> ourMailBodies; 0301 QSet<QByteArray> itemMailBodies; 0302 0303 for (const Item &item : items) { 0304 auto itemMail = item.payload<KMime::Message::Ptr>(); 0305 QByteArray itemMailBody = itemMail->body(); 0306 0307 // For some reason, the body in the maildir has one additional newline. 0308 // Get rid of this so we can compare them. 0309 // FIXME: is this a bug? Find out where the newline comes from! 0310 itemMailBody.chop(1); 0311 itemMailBodies.insert(itemMailBody); 0312 } 0313 0314 for (const QByteArray &mail : mails) { 0315 KMime::Message::Ptr ourMail(new KMime::Message()); 0316 ourMail->setContent(KMime::CRLFtoLF(mail)); 0317 ourMail->parse(); 0318 QByteArray ourMailBody = ourMail->body(); 0319 ourMailBodies.insert(ourMailBody); 0320 } 0321 0322 Q_ASSERT(ourMailBodies == itemMailBodies); 0323 return items; 0324 } 0325 0326 void Pop3Test::syncAndWaitForFinish() 0327 { 0328 AgentManager::self()->instance(mPop3Identifier).synchronize(); 0329 0330 // The pop3 resource, ioslave and the fakeserver are all in different processes or threads. 0331 // We simply wait until the FakeServer got disconnected or until a timeout. 0332 // Since POP3 fetching can take longer, we reset the timeout timer when the FakeServer 0333 // does some processing. 0334 QElapsedTimer time; 0335 time.start(); 0336 int lastProgress = -1; 0337 for (;;) { 0338 qApp->processEvents(); 0339 0340 // Finish correctly when the connection got closed 0341 if (mFakeServerThread->server()->gotDisconnected()) { 0342 break; 0343 } 0344 0345 // Reset the timeout when the server is working 0346 const int newProgress = mFakeServerThread->server()->progress(); 0347 if (newProgress != lastProgress) { 0348 time.restart(); 0349 lastProgress = newProgress; 0350 } 0351 0352 // Assert when nothing happens for a certain timeout, that indicates something went 0353 // wrong and is stuck somewhere 0354 if (time.elapsed() >= 60000) { 0355 Q_ASSERT_X(false, "poptest", "FakeServer timed out."); 0356 break; 0357 } 0358 } 0359 0360 // Once the messages are processed give the Akonadi server and the maildir resource some time to 0361 // process the item operations. Do this by running a monitor together with a timer. Each captured 0362 // item operation bumps the timer to wait longer. After 200ms of inactivity the state is considered 0363 // stable and the test case can proceed. 0364 Akonadi::Monitor mon(this); 0365 mon.setResourceMonitored(mPop3Identifier.toLatin1()); 0366 mon.setResourceMonitored(mMaildirIdentifier.toLatin1()); 0367 QEventLoop settleLoop; 0368 QTimer settleTimer; 0369 settleTimer.setSingleShot(true); 0370 connect(&mon, &Akonadi::Monitor::itemAdded, this, [&](const Akonadi::Item &, const Akonadi::Collection &) { 0371 settleTimer.start(serverSettleTimeout); 0372 }); 0373 connect(&mon, &Akonadi::Monitor::itemChanged, this, [&](const Akonadi::Item &, const QSet<QByteArray> &) { 0374 settleTimer.start(serverSettleTimeout); 0375 }); 0376 connect(&mon, &Akonadi::Monitor::itemRemoved, this, [&](const Akonadi::Item &) { 0377 settleTimer.start(serverSettleTimeout); 0378 }); 0379 0380 settleTimer.start(serverSettleTimeout); 0381 connect(&settleTimer, &QTimer::timeout, this, [&]() { 0382 settleLoop.exit(0); 0383 }); 0384 settleLoop.exec(); 0385 } 0386 0387 QString Pop3Test::loginSequence() const 0388 { 0389 return QStringLiteral( 0390 "C: USER HansWurst\r\n" 0391 "S: +OK May I have your password, please?\r\n" 0392 "C: PASS Geheim\r\n" 0393 "S: +OK Mailbox locked and ready\r\n"); 0394 } 0395 0396 QString Pop3Test::retrieveSequence(const QList<QByteArray> &mails, const QList<int> &exceptions) const 0397 { 0398 QString result; 0399 for (int i = 1; i <= mails.size(); i++) { 0400 if (!exceptions.contains(i)) { 0401 result += QLatin1StringView( 0402 "C: RETR %RETR%\r\n" 0403 "S: +OK Here is your spam\r\n" 0404 "%MAIL%\r\n" 0405 ".\r\n"); 0406 } 0407 } 0408 return result; 0409 } 0410 0411 QString Pop3Test::deleteSequence(int numToDelete) const 0412 { 0413 QString result; 0414 for (int i = 0; i < numToDelete; i++) { 0415 result += QLatin1StringView( 0416 "C: DELE %DELE%\r\n" 0417 "S: +OK message sent to /dev/null\r\n"); 0418 } 0419 return result; 0420 } 0421 0422 QString Pop3Test::quitSequence() const 0423 { 0424 return QStringLiteral( 0425 "C: QUIT\r\n" 0426 "S: +OK Have a nice day.\r\n"); 0427 } 0428 0429 QString Pop3Test::listSequence(const QList<QByteArray> &mails) const 0430 { 0431 QString result = QStringLiteral( 0432 "C: LIST\r\n" 0433 "S: +OK You got new spam\r\n"); 0434 for (int i = 1; i <= mails.size(); i++) { 0435 result += QStringLiteral("%1 %MAILSIZE%\r\n").arg(i); 0436 } 0437 result += QLatin1StringView(".\r\n"); 0438 return result; 0439 } 0440 0441 QString Pop3Test::uidSequence(const QStringList &uids) const 0442 { 0443 QString result = QStringLiteral( 0444 "C: UIDL\r\n" 0445 "S: +OK\r\n"); 0446 for (int i = 1; i <= uids.size(); i++) { 0447 result += QStringLiteral("%1 %2\r\n").arg(i).arg(uids[i - 1]); 0448 } 0449 result += QLatin1StringView(".\r\n"); 0450 return result; 0451 } 0452 0453 static bool sortedEqual(const QStringList &list1, const QStringList &list2) 0454 { 0455 QStringList sorted1 = list1; 0456 sorted1.sort(); 0457 QStringList sorted2 = list2; 0458 sorted2.sort(); 0459 0460 return std::equal(sorted1.begin(), sorted1.end(), sorted2.begin()); 0461 } 0462 0463 void Pop3Test::lowerTimeOfSeenMail(const QString &uidOfMail, int secondsToLower) 0464 { 0465 const int index = mPOP3SettingsInterface->seenUidList().value().indexOf(uidOfMail); 0466 QList<int> seenTimeList = mPOP3SettingsInterface->seenUidTimeList().value(); 0467 int msgTime = seenTimeList.at(index); 0468 msgTime -= secondsToLower; 0469 seenTimeList.replace(index, msgTime); 0470 mPOP3SettingsInterface->setSeenUidTimeList(seenTimeList).waitForFinished(); 0471 } 0472 0473 void Pop3Test::testSimpleDownload() 0474 { 0475 const QList<QByteArray> mails = {simpleMail1, simpleMail2, simpleMail3}; 0476 const QStringList uids = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0477 mFakeServerThread->server()->setAllowedDeletions(QStringLiteral("1,2,3")); 0478 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("1,2,3")); 0479 mFakeServerThread->server()->setMails(mails); 0480 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) 0481 + deleteSequence(mails.size()) + quitSequence()); 0482 0483 syncAndWaitForFinish(); 0484 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0485 checkMailsInMaildir(mails); 0486 cleanupMaildir(items); 0487 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0488 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0489 } 0490 0491 void Pop3Test::testBigFetch() 0492 { 0493 QList<QByteArray> mails; 0494 QStringList uids; 0495 QString allowedRetrs; 0496 mails.reserve(1000); 0497 uids.reserve(1000); 0498 for (int i = 0; i < 1000; i++) { 0499 QByteArray newMail = simpleMail1; 0500 newMail.append(QString::number(i + 1).toLatin1()); 0501 mails << newMail; 0502 uids << QStringLiteral("UID%1").arg(i + 1); 0503 allowedRetrs += QString::number(i + 1) + QLatin1Char(','); 0504 } 0505 allowedRetrs.chop(1); 0506 0507 mFakeServerThread->server()->setMails(mails); 0508 mFakeServerThread->server()->setAllowedRetrieves(allowedRetrs); 0509 mFakeServerThread->server()->setAllowedDeletions(allowedRetrs); 0510 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) 0511 + deleteSequence(mails.size()) + quitSequence()); 0512 0513 syncAndWaitForFinish(); 0514 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0515 checkMailsInMaildir(mails); 0516 cleanupMaildir(items); 0517 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0518 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0519 } 0520 0521 void Pop3Test::testSeenUIDCleanup() 0522 { 0523 // 0524 // First, fetch 3 normal mails, but leave them on the server. 0525 // 0526 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0527 const QList<QByteArray> mails = {simpleMail1, simpleMail2, simpleMail3}; 0528 const QStringList uids = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0529 mFakeServerThread->server()->setAllowedDeletions(QString()); 0530 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("1,2,3")); 0531 mFakeServerThread->server()->setMails(mails); 0532 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) + quitSequence()); 0533 0534 syncAndWaitForFinish(); 0535 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0536 checkMailsInMaildir(mails); 0537 cleanupMaildir(items); 0538 0539 QVERIFY(sortedEqual(uids, mPOP3SettingsInterface->seenUidList().value())); 0540 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0541 0542 // 0543 // Now, pretend that the messages were removed from the server in the meantime 0544 // by having no mails on the fake server. 0545 // 0546 mFakeServerThread->server()->setMails(QList<QByteArray>()); 0547 mFakeServerThread->server()->setAllowedRetrieves(QString()); 0548 mFakeServerThread->server()->setAllowedDeletions(QString()); 0549 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(QList<QByteArray>()) + uidSequence(QStringList()) + quitSequence()); 0550 syncAndWaitForFinish(); 0551 items = checkMailsOnAkonadiServer(QList<QByteArray>()); 0552 checkMailsInMaildir(QList<QByteArray>()); 0553 cleanupMaildir(items); 0554 0555 QVERIFY(mPOP3SettingsInterface->seenUidList().value().isEmpty()); 0556 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0557 0558 mPOP3SettingsInterface->setLeaveOnServer(false).waitForFinished(); 0559 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0560 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0561 } 0562 0563 void Pop3Test::testSimpleLeaveOnServer() 0564 { 0565 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0566 0567 const QList<QByteArray> mails = {simpleMail1, simpleMail2, simpleMail3}; 0568 const QStringList uids = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0569 mFakeServerThread->server()->setMails(mails); 0570 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("1,2,3")); 0571 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) + quitSequence()); 0572 0573 syncAndWaitForFinish(); 0574 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0575 checkMailsInMaildir(mails); 0576 0577 // The resource should have saved the UIDs of the seen messages 0578 QVERIFY(sortedEqual(uids, mPOP3SettingsInterface->seenUidList().value())); 0579 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0580 const auto seenUidTimeListValue{mPOP3SettingsInterface->seenUidTimeList().value()}; 0581 for (int seenTime : seenUidTimeListValue) { 0582 // Those message were just downloaded from the fake server, so they are at maximum 0583 // 10 minutes old (for slooooow running tests) 0584 QVERIFY(seenTime >= time(nullptr) - 10 * 60); 0585 } 0586 0587 // 0588 // OK, next mail check: We have to check that the old seen messages are not downloaded again, 0589 // only new mails. 0590 // 0591 QList<QByteArray> newMails(mails); 0592 newMails << simpleMail4; 0593 QStringList newUids(uids); 0594 newUids << QStringLiteral("newUID"); 0595 const QList<int> idsToNotDownload = {1, 2, 3}; 0596 mFakeServerThread->server()->setMails(newMails); 0597 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("4")); 0598 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(newMails) + uidSequence(newUids) 0599 + retrieveSequence(newMails, idsToNotDownload) + quitSequence(), 0600 idsToNotDownload); 0601 0602 syncAndWaitForFinish(); 0603 items = checkMailsOnAkonadiServer(newMails); 0604 checkMailsInMaildir(newMails); 0605 QVERIFY(sortedEqual(newUids, mPOP3SettingsInterface->seenUidList().value())); 0606 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0607 0608 // 0609 // Ok, next test: When turning off leaving on the server, all mails should be deleted, but 0610 // none downloaded. 0611 // 0612 mPOP3SettingsInterface->setLeaveOnServer(false).waitForFinished(); 0613 0614 mFakeServerThread->server()->setAllowedDeletions(QStringLiteral("1,2,3,4")); 0615 mFakeServerThread->server()->setAllowedRetrieves(QString()); 0616 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(newMails) + uidSequence(newUids) + deleteSequence(newMails.size()) 0617 + quitSequence()); 0618 0619 syncAndWaitForFinish(); 0620 items = checkMailsOnAkonadiServer(newMails); 0621 checkMailsInMaildir(newMails); 0622 cleanupMaildir(items); 0623 QVERIFY(mPOP3SettingsInterface->seenUidList().value().isEmpty()); 0624 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0625 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0626 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0627 } 0628 0629 void Pop3Test::testTimeBasedLeaveRule() 0630 { 0631 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0632 mPOP3SettingsInterface->setLeaveOnServerDays(2).waitForFinished(); 0633 0634 // 0635 // First download 3 mails and leave them on the server 0636 // 0637 const QList<QByteArray> mails = {simpleMail1, simpleMail2, simpleMail3}; 0638 QStringList uids = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0639 mFakeServerThread->server()->setMails(mails); 0640 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("1,2,3")); 0641 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) + quitSequence()); 0642 0643 syncAndWaitForFinish(); 0644 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0645 checkMailsInMaildir(mails); 0646 0647 QVERIFY(sortedEqual(uids, mPOP3SettingsInterface->seenUidList().value())); 0648 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0649 0650 // 0651 // Now, modify the seenUidTimeList on the server for UID2 to pretend it 0652 // was downloaded 3 days ago, which means it should be deleted. 0653 // 0654 lowerTimeOfSeenMail(QStringLiteral("UID2"), 60 * 60 * 24 * 3); 0655 0656 const QList<int> idsToNotDownload = {1, 2, 3}; 0657 mFakeServerThread->server()->setAllowedDeletions(QStringLiteral("2")); 0658 mFakeServerThread->server()->setAllowedRetrieves(QString()); 0659 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + deleteSequence(1) + quitSequence(), 0660 idsToNotDownload); 0661 syncAndWaitForFinish(); 0662 items = checkMailsOnAkonadiServer(mails); 0663 checkMailsInMaildir(mails); 0664 cleanupMaildir(items); 0665 0666 uids.removeAll(QStringLiteral("UID2")); 0667 QVERIFY(sortedEqual(uids, mPOP3SettingsInterface->seenUidList().value())); 0668 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0669 const auto seenUidTimeListValue{mPOP3SettingsInterface->seenUidTimeList().value()}; 0670 for (int seenTime : seenUidTimeListValue) { 0671 QVERIFY(seenTime >= time(nullptr) - 10 * 60); 0672 } 0673 0674 mPOP3SettingsInterface->setLeaveOnServer(false).waitForFinished(); 0675 mPOP3SettingsInterface->setLeaveOnServerDays(0).waitForFinished(); 0676 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0677 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0678 } 0679 0680 void Pop3Test::testCountBasedLeaveRule() 0681 { 0682 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0683 mPOP3SettingsInterface->setLeaveOnServerCount(3).waitForFinished(); 0684 0685 // 0686 // First download 3 mails and leave them on the server 0687 // 0688 const QList<QByteArray> mails = {simpleMail1, simpleMail2, simpleMail3}; 0689 const QStringList uids = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0690 mFakeServerThread->server()->setMails(mails); 0691 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("1,2,3")); 0692 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) + quitSequence()); 0693 0694 syncAndWaitForFinish(); 0695 checkMailsOnAkonadiServer(mails); 0696 checkMailsInMaildir(mails); 0697 0698 // Make the 3 just downloaded mails appear older than they are 0699 lowerTimeOfSeenMail(QStringLiteral("UID1"), 60 * 60 * 24 * 2); 0700 lowerTimeOfSeenMail(QStringLiteral("UID2"), 60 * 60 * 24 * 1); 0701 lowerTimeOfSeenMail(QStringLiteral("UID3"), 60 * 60 * 24 * 3); 0702 0703 // 0704 // Now, download 2 more mails. Since only 3 mails are allowed to be left 0705 // on the server, the oldest ones, UID1 and UID3, should be deleted 0706 // 0707 const QList<QByteArray> moreMails = {simpleMail4, simpleMail5}; 0708 const QStringList moreUids = {QStringLiteral("UID4"), QStringLiteral("UID5")}; 0709 mFakeServerThread->server()->setMails(mails + moreMails); 0710 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("4,5")); 0711 mFakeServerThread->server()->setAllowedDeletions(QStringLiteral("1,3")); 0712 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails + moreMails) + uidSequence(uids + moreUids) 0713 + retrieveSequence(moreMails) + deleteSequence(2) + quitSequence(), 0714 {1, 2, 3}); 0715 0716 syncAndWaitForFinish(); 0717 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails + moreMails); 0718 checkMailsInMaildir(mails + moreMails); 0719 cleanupMaildir(items); 0720 0721 const QStringList uidsLeft = {QStringLiteral("UID2"), QStringLiteral("UID4"), QStringLiteral("UID5")}; 0722 QVERIFY(sortedEqual(uidsLeft, mPOP3SettingsInterface->seenUidList().value())); 0723 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0724 0725 mPOP3SettingsInterface->setLeaveOnServer(false).waitForFinished(); 0726 mPOP3SettingsInterface->setLeaveOnServerCount(0).waitForFinished(); 0727 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0728 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0729 } 0730 0731 void Pop3Test::testSizeBasedLeaveRule() 0732 { 0733 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0734 mPOP3SettingsInterface->setLeaveOnServerSize(10).waitForFinished(); // 10 MB 0735 0736 // 0737 // First download 3 mails and leave them on the server. 0738 // 0739 const QList<QByteArray> mails = {simpleMail1, simpleMail2, simpleMail3}; 0740 const QStringList uids = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0741 mFakeServerThread->server()->setMails(mails); 0742 mFakeServerThread->server()->setAllowedRetrieves(QStringLiteral("1,2,3")); 0743 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) + quitSequence()); 0744 0745 syncAndWaitForFinish(); 0746 checkMailsOnAkonadiServer(mails); 0747 checkMailsInMaildir(mails); 0748 0749 // Make the 3 just downloaded mails appear older than they are 0750 lowerTimeOfSeenMail(QStringLiteral("UID1"), 60 * 60 * 24 * 2); 0751 lowerTimeOfSeenMail(QStringLiteral("UID2"), 60 * 60 * 24 * 1); 0752 lowerTimeOfSeenMail(QStringLiteral("UID3"), 60 * 60 * 24 * 3); 0753 0754 // Now, do another mail check, but with no new mails on the server. 0755 // Instead we let the server pretend that the mails have a fake size, 0756 // each 7 MB. That means the two oldest get deleted, because the total 0757 // mail size is over 10 MB with them. 0758 mFakeServerThread->server()->setMails(mails); 0759 mFakeServerThread->server()->setAllowedRetrieves(QString()); 0760 mFakeServerThread->server()->setAllowedDeletions(QStringLiteral("1,3")); 0761 mFakeServerThread->server()->setNextConversation(loginSequence() 0762 + QLatin1StringView("C: LIST\r\n" 0763 "S: +OK You got new spam\r\n" 0764 "1 7340032\r\n" 0765 "2 7340032\r\n" 0766 "3 7340032\r\n" 0767 ".\r\n") 0768 + uidSequence(uids) + deleteSequence(2) + quitSequence()); 0769 0770 syncAndWaitForFinish(); 0771 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0772 checkMailsInMaildir(mails); 0773 cleanupMaildir(items); 0774 0775 const QStringList uidsLeft = {QStringLiteral("UID2")}; 0776 QVERIFY(sortedEqual(uidsLeft, mPOP3SettingsInterface->seenUidList().value())); 0777 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0778 0779 mPOP3SettingsInterface->setLeaveOnServer(false).waitForFinished(); 0780 mPOP3SettingsInterface->setLeaveOnServerCount(0).waitForFinished(); 0781 mPOP3SettingsInterface->setLeaveOnServerSize(0).waitForFinished(); 0782 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0783 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0784 } 0785 0786 void Pop3Test::testMixedLeaveRules() 0787 { 0788 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0789 // 0790 // Generate 10 mails 0791 // 0792 QList<QByteArray> mails; 0793 mails.reserve(10); 0794 QStringList uids; 0795 uids.reserve(10); 0796 QString allowedRetrs; 0797 for (int i = 0; i < 10; i++) { 0798 QByteArray newMail = simpleMail1; 0799 newMail.append(QString::number(i + 1).toLatin1()); 0800 mails << newMail; 0801 uids << QStringLiteral("UID%1").arg(i + 1); 0802 allowedRetrs += QString::number(i + 1) + QLatin1Char(','); 0803 } 0804 allowedRetrs.chop(1); 0805 0806 // 0807 // Now, download these 10 mails 0808 // 0809 mFakeServerThread->server()->setMails(mails); 0810 mFakeServerThread->server()->setAllowedRetrieves(allowedRetrs); 0811 mFakeServerThread->server()->setNextConversation(loginSequence() + listSequence(mails) + uidSequence(uids) + retrieveSequence(mails) + quitSequence()); 0812 0813 syncAndWaitForFinish(); 0814 checkMailsOnAkonadiServer(mails); 0815 checkMailsInMaildir(mails); 0816 0817 // Fake the time of the messages, UID1 is one day old, UID2 is two days old, etc 0818 for (int i = 1; i <= 10; i++) { 0819 lowerTimeOfSeenMail(QStringLiteral("UID%1").arg(i), 60 * 60 * 24 * i); 0820 } 0821 0822 mPOP3SettingsInterface->setLeaveOnServer(true).waitForFinished(); 0823 mPOP3SettingsInterface->setLeaveOnServerSize(25).waitForFinished(); // UID 4, 5 oldest here 0824 mPOP3SettingsInterface->setLeaveOnServerCount(5).waitForFinished(); // UID 6, 7 oldest here 0825 mPOP3SettingsInterface->setLeaveOnServerDays(7).waitForFinished(); // UID 8, 9 and 10 too old 0826 0827 // Ok, now we do another mail check that only deletes stuff from the server. 0828 // Above are the UIDs that should be deleted. 0829 mFakeServerThread->server()->setMails(mails); 0830 mFakeServerThread->server()->setAllowedRetrieves(QString()); 0831 mFakeServerThread->server()->setAllowedDeletions(QStringLiteral("4,5,6,7,8,9,10")); 0832 mFakeServerThread->server()->setNextConversation(loginSequence() 0833 + QLatin1StringView("C: LIST\r\n" 0834 "S: +OK You got new spam\r\n" 0835 "1 7340032\r\n" 0836 "2 7340032\r\n" 0837 "3 7340032\r\n" 0838 "4 7340032\r\n" 0839 "5 7340032\r\n" 0840 "6 7340032\r\n" 0841 "7 7340032\r\n" 0842 "8 7340032\r\n" 0843 "9 7340032\r\n" 0844 "10 7340032\r\n" 0845 ".\r\n") 0846 + uidSequence(uids) + deleteSequence(7) + quitSequence()); 0847 0848 syncAndWaitForFinish(); 0849 Akonadi::Item::List items = checkMailsOnAkonadiServer(mails); 0850 checkMailsInMaildir(mails); 0851 cleanupMaildir(items); 0852 0853 const QStringList uidsLeft = {QStringLiteral("UID1"), QStringLiteral("UID2"), QStringLiteral("UID3")}; 0854 QVERIFY(sortedEqual(uidsLeft, mPOP3SettingsInterface->seenUidList().value())); 0855 QVERIFY(mPOP3SettingsInterface->seenUidTimeList().value().size() == mPOP3SettingsInterface->seenUidList().value().size()); 0856 0857 mPOP3SettingsInterface->setLeaveOnServer(false).waitForFinished(); 0858 mPOP3SettingsInterface->setLeaveOnServerCount(0).waitForFinished(); 0859 mPOP3SettingsInterface->setLeaveOnServerSize(0).waitForFinished(); 0860 mPOP3SettingsInterface->setSeenUidTimeList(QList<int>()).waitForFinished(); 0861 mPOP3SettingsInterface->setSeenUidList(QStringList()).waitForFinished(); 0862 } 0863 0864 #include "moc_pop3test.cpp"