1/*
2 Copyright (c) 2014 Christian Mollekopf <mollekopf@kolabsys.com>
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 "tagfetchjob.h"
21#include "job_p.h"
22#include "tag.h"
23#include "protocolhelper_p.h"
24#include "tagfetchscope.h"
25#include <QTimer>
26#include <QFile>
27#include <akonadi/attributefactory.h>
28
29using namespace Akonadi;
30
31class Akonadi::TagFetchJobPrivate : public JobPrivate
32{
33public:
34 TagFetchJobPrivate(TagFetchJob *parent)
35 : JobPrivate(parent)
36 , mEmitTimer(0)
37 {
38 }
39
40 void init()
41 {
42 Q_Q(TagFetchJob);
43 mEmitTimer = new QTimer(q);
44 mEmitTimer->setSingleShot(true);
45 mEmitTimer->setInterval(100);
46 q->connect(mEmitTimer, SIGNAL(timeout()), q, SLOT(timeout()));
47 }
48
49 void aboutToFinish()
50 {
51 timeout();
52 }
53
54 void timeout()
55 {
56 Q_Q(TagFetchJob);
57 mEmitTimer->stop(); // in case we are called by result()
58 if (!mPendingTags.isEmpty()) {
59 if (!q->error()) {
60 emit q->tagsReceived(mPendingTags);
61 }
62 mPendingTags.clear();
63 }
64 }
65
66 Q_DECLARE_PUBLIC(TagFetchJob)
67
68 Tag::List mRequestedTags;
69 Tag::List mResultTags;
70 Tag::List mPendingTags; // items pending for emitting itemsReceived()
71 QTimer *mEmitTimer;
72 TagFetchScope mFetchScope;
73};
74
75TagFetchJob::TagFetchJob(QObject *parent)
76 : Job(new TagFetchJobPrivate(this), parent)
77{
78 Q_D(TagFetchJob);
79 d->init();
80}
81
82TagFetchJob::TagFetchJob(const Tag &tag, QObject *parent)
83 : Job(new TagFetchJobPrivate(this), parent)
84{
85 Q_D(TagFetchJob);
86 d->init();
87 d->mRequestedTags << tag;
88}
89
90TagFetchJob::TagFetchJob(const Tag::List &tags, QObject *parent)
91 : Job(new TagFetchJobPrivate(this), parent)
92{
93 Q_D(TagFetchJob);
94 d->init();
95 d->mRequestedTags = tags;
96}
97
98TagFetchJob::TagFetchJob(const QList<Tag::Id> &ids, QObject *parent)
99 : Job(new TagFetchJobPrivate(this), parent)
100{
101 Q_D(TagFetchJob);
102 d->init();
103 Q_FOREACH (Tag::Id id, ids) {
104 d->mRequestedTags << Tag(id);
105 }
106}
107
108void TagFetchJob::setFetchScope(const TagFetchScope &fetchScope)
109{
110 Q_D(TagFetchJob);
111 d->mFetchScope = fetchScope;
112}
113
114TagFetchScope &TagFetchJob::fetchScope()
115{
116 Q_D(TagFetchJob);
117 return d->mFetchScope;
118}
119
120void TagFetchJob::doStart()
121{
122 Q_D(TagFetchJob);
123
124 QByteArray command = d->newTag();
125 if (d->mRequestedTags.isEmpty()) {
126 command += " UID TAGFETCH 1:*";
127 } else {
128 try {
129 command += ProtocolHelper::tagSetToByteArray(d->mRequestedTags, "TAGFETCH");
130 } catch (const Exception &e) {
131 setError(Job::Unknown);
132 setErrorText(QString::fromUtf8(e.what()));
133 emitResult();
134 return;
135 }
136 }
137 command += " (UID";
138 Q_FOREACH (const QByteArray &part, d->mFetchScope.attributes()) {
139 command += ' ' + ProtocolHelper::encodePartIdentifier(ProtocolHelper::PartAttribute, part);
140 }
141 command += ")\n";
142
143 d->writeData(command);
144}
145
146void TagFetchJob::doHandleResponse(const QByteArray &tag, const QByteArray &data)
147{
148 Q_D(TagFetchJob);
149
150 if (tag == "*") {
151 int begin = data.indexOf("TAGFETCH");
152 if (begin >= 0) {
153 // split fetch response into key/value pairs
154 QList<QByteArray> fetchResponse;
155 ImapParser::parseParenthesizedList(data, fetchResponse, begin + 8);
156
157 Tag tag;
158 ProtocolHelper::parseTagFetchResult(fetchResponse, tag);
159
160 if (tag.isValid()) {
161 d->mResultTags.append(tag);
162 d->mPendingTags.append(tag);
163 if (!d->mEmitTimer->isActive()) {
164 d->mEmitTimer->start();
165 }
166 }
167 return;
168 }
169 }
170 kDebug() << "Unhandled response: " << tag << data;
171}
172
173Tag::List TagFetchJob::tags() const
174{
175 Q_D(const TagFetchJob);
176 return d->mResultTags;
177}
178
179#include "moc_tagfetchjob.cpp"
180