Commit fa93d5df authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Introduce MoveJob which implements the MOVE extension (RFC6851)

The MoveJob API is the same as CopyJob API, since the RFC6851
is modelled closely from the COPY command, having the same
parameters and responses.

Unlike with the traditional COPY + STORE \Deleted + EXPUNGE
approach, the RFC6851 guarantees the entire operation to be
atomic and is faster and uses less bandwidth.

When UIDPLUS extension is supported, the resulting UIDs are
returned via COPYUID response (like with CopyJob).
parent 7516820e
......@@ -19,7 +19,7 @@ include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
include(ECMQtDeclareLoggingCategory)
set(PIM_VERSION "5.3.40")
set(PIM_VERSION "5.3.41")
set(KIMAP_LIB_VERSION ${PIM_VERSION})
set(KMIME_LIBS_VERSION "5.3.40")
......
......@@ -46,4 +46,5 @@ KIMAP_UNIT_TESTS(
setmetadatajobtest
appendjobtest
statusjobtest
movejobtest
)
/*
Copyright (C) 2016 Daniel Vrátil <dvratil@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <qtest.h>
#include "kimaptest/fakeserver.h"
#include "kimap/session.h"
#include "kimap/imapset.h"
#include "../src/movejob.h"
#include <QtTest>
class MoveJobTest: public QObject
{
Q_OBJECT
private Q_SLOTS:
void testMove_data()
{
QTest::addColumn<bool>("uidBased");
QTest::addColumn<qint64>("id");
QTest::addColumn<qint64>("resultUid");
QTest::addColumn<QString>("mailbox");
QTest::addColumn< QList<QByteArray> >("scenario");
QList<QByteArray> scenario;
scenario << FakeServer::preauth()
<< "C: A000001 MOVE 3 \"foo\""
<< "S: * OK [COPYUID 12345 3 7]"
<< "S: A000001 OK MOVE completed";
QTest::newRow("not uid based") << false << qint64(3) << qint64(7)
<< QStringLiteral("foo") << scenario;
scenario.clear();
scenario << FakeServer::preauth()
<< "C: A000001 UID MOVE 1024 \"bar\""
<< "S: * OK [COPYUID 12346 4 2048]"
<< "S: A000001 OK MOVE completed";
QTest::newRow("uid based") << true << qint64(1024) << qint64(2048)
<< QStringLiteral("bar") << scenario;
}
void testMove()
{
QFETCH(bool, uidBased);
QFETCH(qint64, id);
QFETCH(qint64, resultUid);
QFETCH(QString, mailbox);
QFETCH(QList<QByteArray>, scenario);
FakeServer fakeServer;
fakeServer.setScenario(scenario);
fakeServer.startAndWait();
KIMAP::Session session(QStringLiteral("127.0.0.1"), 5989);
auto job = new KIMAP::MoveJob(&session);
job->setMailBox(mailbox);
job->setUidBased(uidBased);
job->setSequenceSet(KIMAP::ImapSet(id));
bool result = job->exec();
QVERIFY(result);
QCOMPARE(job->resultingUids(), KIMAP::ImapSet(resultUid));
fakeServer.quit();
}
};
QTEST_GUILESS_MAIN(MoveJobTest)
#include "movejobtest.moc"
......@@ -30,6 +30,7 @@ set(kimap_SRCS
loginjob.cpp
logoutjob.cpp
metadatajobbase.cpp
movejob.cpp
myrightsjob.cpp
namespacejob.cpp
quotajobbase.cpp
......@@ -104,6 +105,7 @@ ecm_generate_headers(KIMAP_CamelCase_HEADERS
LoginJob
LogoutJob
MetaDataJobBase
MoveJob
MyRightsJob
QuotaJobBase
RenameJob
......
/*
Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "movejob.h"
#include "job_p.h"
#include "message_p.h"
#include "session_p.h"
#include "rfccodecs.h"
#include <KLocalizedString>
//TODO: when custom error codes are introduced, handle the NO [TRYCREATE] response
namespace KIMAP
{
class MoveJobPrivate : public JobPrivate
{
public:
MoveJobPrivate(Session *session, const QString &name)
: JobPrivate(session, name)
, uidBased(false)
{}
~MoveJobPrivate()
{}
QString mailBox;
ImapSet set;
bool uidBased;
ImapSet resultingUids;
};
}
using namespace KIMAP;
MoveJob::MoveJob(Session *session)
: Job(*new MoveJobPrivate(session, i18n("Move")))
{
Q_D(MoveJob);
d->uidBased = false;
}
MoveJob::~MoveJob()
{
}
void MoveJob::setMailBox(const QString &mailBox)
{
Q_D(MoveJob);
d->mailBox = mailBox;
}
QString MoveJob::mailBox() const
{
Q_D(const MoveJob);
return d->mailBox;
}
void MoveJob::setSequenceSet(const ImapSet &set)
{
Q_D(MoveJob);
d->set = set;
}
ImapSet MoveJob::sequenceSet() const
{
Q_D(const MoveJob);
return d->set;
}
void MoveJob::setUidBased(bool uidBased)
{
Q_D(MoveJob);
d->uidBased = uidBased;
}
bool MoveJob::isUidBased() const
{
Q_D(const MoveJob);
return d->uidBased;
}
ImapSet MoveJob::resultingUids() const
{
Q_D(const MoveJob);
return d->resultingUids;
}
void MoveJob::doStart()
{
Q_D(MoveJob);
QByteArray parameters = d->set.toImapSequenceSet() + ' ';
parameters += '\"' + KIMAP::encodeImapFolderName(d->mailBox.toUtf8()) + '\"';
QByteArray command = "MOVE";
if (d->uidBased) {
command = "UID " + command;
}
d->tags << d->sessionInternal()->sendCommand(command, parameters);
}
void MoveJob::handleResponse(const Message &response)
{
Q_D(MoveJob);
for (QList<Message::Part>::ConstIterator it = response.responseCode.begin();
it != response.responseCode.end(); ++it) {
if (it->toString() == "COPYUID") {
it = it + 3;
if (it < response.responseCode.end()) {
d->resultingUids = ImapSet::fromImapSequenceSet(it->toString());
}
break;
}
}
handleErrorReplies(response);
}
/*
Copyright (c) 2016 Daniel Vrátil <dvratil@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#ifndef KIMAP_MOVE_H_
#define KIMAP_MOVE_H_
#include "kimap_export.h"
#include "job.h"
#include "imapset.h"
namespace KIMAP {
class MoveJobPrivate;
/**
* Moves messages from current mailbox to another
*
* Note that move functionality is not specified in the base IMAP
* protocol and is defined as an extension in RFC6851. That means
* that the MoveJob can only be used when the server lists "MOVE"
* in response to CAPABILITY command.
*
* Unlike the traditional emulation of moving messages, i.e. COPY + STORE + EXPUNGE,
* MOVE guarantees the transaction to be atomic on the server.
*
* @since 5.4
*/
class KIMAP_EXPORT MoveJob : public Job
{
Q_OBJECT
Q_DECLARE_PRIVATE(MoveJob)
friend class SessionPrivate;
public:
explicit MoveJob(Session *session);
virtual ~MoveJob();
/**
* Set the destination mailbox
*
* If the mailbox does not exist, the server should not create
* it automatically and the job should fail. Note, however,
* that a conforming server may create the mailbox automatically.
*
* @param mailBox the (unquoted) name of the mailbox where the
* messages should be moved to
*/
void setMailBox(const QString &mailbox);
/**
* The destination mailbox
*/
QString mailBox() const;
/**
* Sets the messages to be moved,
*
* If sequence numbers are given, isUidBased() should be false. If UIDs
* are given, isUidBased() should be true.
*
* @param set the sequence numbers or UIDs of the messages to be moved
*/
void setSequenceSet(const ImapSet &set);
/**
* The messages that will be moved.
*
* isUidBased() can be used to check whether the ImapSet contains
* sequence numbers or UIDs.
*
* @return the sequence numbers or UIDs of the messages to be moved
*/
ImapSet sequenceSet() const;
/**
* Set how the sequence set should be interpreted.
*
* @param uidBased if @c true the argument to setSequenceSet will be
* interpreted as UIDs, if @c false it will be interpreted
* as sequence numbers
*/
void setUidBased(bool uidBased);
/**
* How to interpret the sequence set.
*
* @return if @c true the result of sequenceSet() should be
* interpreted as UIDs, if @c false it should be interpreted
* as sequence numbers
*/
bool isUidBased() const;
/**
* The UIDs of the moved messages in the destination mailbox.
*
* This will be an empty set if no messages have been moved yet
* or if the server does not support the UIDPLUS extension.
*/
ImapSet resultingUids() const;
protected:
void doStart() Q_DECL_OVERRIDE;
void handleResponse(const KIMAP::Message &response) Q_DECL_OVERRIDE;
};
}
#endif
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment