22#include "itemcreatejob.h"
24#include "collection.h"
25#include "imapparser_p.h"
28#include "itemserializer_p.h"
30#include "protocolhelper_p.h"
31#include "gid/gidextractor_p.h"
33#include <QtCore/QDateTime>
34#include <QtCore/QFile>
40class Akonadi::ItemCreateJobPrivate :
public JobPrivate
46 , mItemReceived(false)
50 QByteArray nextPartHeader();
54 QSet<QByteArray> mParts;
57 QByteArray mPendingData;
58 ItemCreateJob::MergeOptions mMergeOptions;
62QByteArray ItemCreateJobPrivate::nextPartHeader()
65 if (!mParts.isEmpty()) {
66 QSetIterator<QByteArray> it(mParts);
67 const QByteArray label = it.next();
74 if (mPendingData.size() > 0) {
75 command +=
" {" + QByteArray::number(mPendingData.size()) +
"}\n";
77 if (mPendingData.isNull()) {
82 command += nextPartHeader();
91 :
Job(new ItemCreateJobPrivate(this), parent)
95 Q_ASSERT(!
item.mimeType().isEmpty());
97 d->mParts = d->mItem.loadedPayloadParts();
98 d->mCollection = collection;
111 QList<QByteArray> flags;
112 flags.append(
"\\MimeType[" + d->mItem.mimeType().toLatin1() +
']');
115 flags.append(ImapParser::quote(
"\\Gid[" + gid.toUtf8() +
']'));
117 if (!d->mItem.remoteId().isEmpty()) {
118 flags.append(ImapParser::quote(
"\\RemoteId[" + d->mItem.remoteId().toUtf8() +
']'));
120 if (!d->mItem.remoteRevision().isEmpty()) {
121 flags.append(ImapParser::quote(
"\\RemoteRevision[" + d->mItem.remoteRevision().toUtf8() +
']'));
123 const bool mergeByGid = (d->mMergeOptions &
GID) && !d->mItem.gid().isEmpty();
124 const bool mergeByRid = (d->mMergeOptions &
RID) && !d->mItem.remoteId().isEmpty();
125 const bool mergeSilent = (d->mMergeOptions &
Silent);
126 const bool merge = mergeByGid || mergeByRid;
127 if (d->mItem.d_func()->mFlagsOverwritten || !merge) {
128 flags += d->mItem.flags().toList();
130 Q_FOREACH(
const QByteArray &flag, d->mItem.d_func()->mAddedFlags.toList()) {
133 Q_FOREACH(
const QByteArray &flag, d->mItem.d_func()->mDeletedFlags.toList()) {
137 if (d->mItem.d_func()->mTagsOverwritten || !merge) {
138 Q_FOREACH(
const Akonadi::Tag &tag, d->mItem.d_func()->mAddedTags) {
139 if (tag.gid().isEmpty() && !tag.remoteId().isEmpty()) {
140 flags +=
"\\RTag[" + tag.remoteId() +
']';
141 }
else if (!tag.gid().isEmpty()) {
142 flags +=
"\\Tag[" + tag.gid() +
']';
146 Q_FOREACH(
const Akonadi::Tag &tag, d->mItem.d_func()->mAddedTags) {
147 if (tag.gid().isEmpty() && !tag.remoteId().isEmpty()) {
148 flags +=
"+\\RTag[" + tag.remoteId() +
']';
149 }
else if (!tag.gid().isEmpty()) {
150 flags +=
"+\\Tag[" + tag.gid() +
']';
153 Q_FOREACH(
const Akonadi::Tag &tag, d->mItem.d_func()->mDeletedTags) {
154 if (tag.gid().isEmpty() && !tag.remoteId().isEmpty()) {
155 flags +=
"-\\RTag[" + tag.remoteId() +
']';
156 }
else if (!tag.gid().isEmpty()) {
157 flags +=
"-\\Tag[" + tag.gid() +
']';
162 QByteArray command = d->newTag();
164 QList<QByteArray> mergeArgs;
169 mergeArgs <<
"REMOTEID";
172 mergeArgs <<
"SILENT";
174 command +=
" MERGE (" + ImapParser::join(mergeArgs,
" ") +
") ";
176 command +=
" X-AKAPPEND ";
179 command += QByteArray::number(d->mCollection.id())
180 +
' ' + QByteArray::number(d->mItem.size())
181 +
" (" + ImapParser::join(flags,
" ") +
")"
184 if (!attrs.isEmpty()) {
188 command += d->nextPartHeader();
190 d->writeData(command);
198 if (data.startsWith(
"STREAM")) {
200 if (!ProtocolHelper::streamPayloadToFile(data, d->mPendingData, error)) {
201 d->writeData(
"* NO " + error);
205 d->writeData(d->mPendingData);
207 d->writeData(d->nextPartHeader());
211 int begin = data.indexOf(
"FETCH");
213 QList<QByteArray> fetchResponse;
214 ImapParser::parseParenthesizedList(data, fetchResponse, begin + 6);
218 if (!
item.isValid()) {
222 d->mItemReceived =
true;
227 if (tag == d->tag()) {
228 int uidNextPos = data.indexOf(
"UIDNEXT");
229 if (uidNextPos != -1) {
231 ImapParser::parseNumber(data, d->mUid, &ok, uidNextPos + 7);
233 kDebug() <<
"Invalid UIDNEXT response to APPEND command: "
237 int dateTimePos = data.indexOf(
"DATETIME");
238 if (dateTimePos != -1) {
239 int resultPos = ImapParser::parseDateTime(data, d->mDatetime, dateTimePos + 8);
240 if (resultPos == (dateTimePos + 8)) {
241 kDebug() <<
"Invalid DATETIME response to APPEND command: "
252 d->mMergeOptions = options;
259 if (d->mItemReceived) {
270 item.setModificationTime(d->mDatetime);
271 item.setParentCollection(d->mCollection);
272 item.setStorageCollectionId(d->mCollection.id());
Represents a collection of PIM items.
Job that creates a new item in the Akonadi storage.
~ItemCreateJob()
Destroys the item create job.
Item item() const
Returns the created item with the new unique id, or an invalid item if the job failed.
@ Silent
Only return the id of the merged/created item.
void setMerge(MergeOptions options)
Merge this item into an existing one if available.
virtual void doHandleResponse(const QByteArray &tag, const QByteArray &data)
This method should be reimplemented in the concrete jobs in case you want to handle incoming data.
ItemCreateJob(const Item &item, const Collection &collection, QObject *parent=0)
Creates a new item create job.
virtual void doStart()
This method must be reimplemented in the concrete jobs.
static void serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
throws ItemSerializerException on failure
Base class for all actions in the Akonadi storage.
static QByteArray attributesToByteArray(const Entity &entity, bool ns=false)
Convert attributes to their protocol representation.
static void parseItemFetchResult(const QList< QByteArray > &lineTokens, Item &item, ProtocolHelperValuePool *valuePool=0)
Parses a single line from an item fetch job result into an Item object.
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
FreeBusyManager::Singleton.