1 | /* |
2 | Copyright (c) 2006 - 2007 Volker Krause <vkrause@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 "itemfetchjob.h" |
21 | |
22 | #include "attributefactory.h" |
23 | #include "collection.h" |
24 | #include "collectionselectjob_p.h" |
25 | #include "imapparser_p.h" |
26 | #include "itemfetchscope.h" |
27 | #include "job_p.h" |
28 | #include "protocol_p.h" |
29 | #include "protocolhelper_p.h" |
30 | #include "session_p.h" |
31 | |
32 | #include <kdebug.h> |
33 | #include <KLocalizedString> |
34 | |
35 | #include <QtCore/QStringList> |
36 | #include <QtCore/QTimer> |
37 | |
38 | using namespace Akonadi; |
39 | |
40 | class Akonadi::ItemFetchJobPrivate : public JobPrivate |
41 | { |
42 | public: |
43 | ItemFetchJobPrivate(ItemFetchJob *parent) |
44 | : JobPrivate(parent) |
45 | , mEmitTimer(0) |
46 | , mValuePool(0) |
47 | , mCount(0) |
48 | { |
49 | mCollection = Collection::root(); |
50 | mDeliveryOptions = ItemFetchJob::Default; |
51 | } |
52 | |
53 | ~ItemFetchJobPrivate() |
54 | { |
55 | delete mValuePool; |
56 | } |
57 | |
58 | void init() |
59 | { |
60 | Q_Q(ItemFetchJob); |
61 | mEmitTimer = new QTimer(q); |
62 | mEmitTimer->setSingleShot(true); |
63 | mEmitTimer->setInterval(100); |
64 | q->connect(mEmitTimer, SIGNAL(timeout()), q, SLOT(timeout())); |
65 | } |
66 | |
67 | void aboutToFinish() |
68 | { |
69 | timeout(); |
70 | } |
71 | |
72 | void timeout() |
73 | { |
74 | Q_Q(ItemFetchJob); |
75 | |
76 | mEmitTimer->stop(); // in case we are called by result() |
77 | if (!mPendingItems.isEmpty()) { |
78 | if (!q->error()) { |
79 | emit q->itemsReceived(mPendingItems); |
80 | } |
81 | mPendingItems.clear(); |
82 | } |
83 | } |
84 | |
85 | QString jobDebuggingString() const /*Q_DECL_OVERRIDE*/ |
86 | { |
87 | if (mRequestedItems.isEmpty()) { |
88 | QString str = QString::fromLatin1( "All items from collection %1" ).arg( mCollection.id() ); |
89 | if ( mFetchScope.fetchChangedSince().isValid() ) |
90 | str += QString::fromLatin1( " changed since %1" ).arg( mFetchScope.fetchChangedSince().toString() ); |
91 | return str; |
92 | |
93 | } else { |
94 | try { |
95 | return QString::fromLatin1(ProtocolHelper::entitySetToByteArray(mRequestedItems, AKONADI_CMD_ITEMFETCH)); |
96 | } catch (const Exception &e) { |
97 | return QString::fromUtf8(e.what()); |
98 | } |
99 | } |
100 | } |
101 | |
102 | Q_DECLARE_PUBLIC(ItemFetchJob) |
103 | |
104 | Collection mCollection; |
105 | Tag mTag; |
106 | Item::List mRequestedItems; |
107 | Item::List mResultItems; |
108 | ItemFetchScope mFetchScope; |
109 | Item::List mPendingItems; // items pending for emitting itemsReceived() |
110 | QTimer *mEmitTimer; |
111 | ProtocolHelperValuePool *mValuePool; |
112 | ItemFetchJob::DeliveryOptions mDeliveryOptions; |
113 | int mCount; |
114 | }; |
115 | |
116 | ItemFetchJob::ItemFetchJob(const Collection &collection, QObject *parent) |
117 | : Job(new ItemFetchJobPrivate(this), parent) |
118 | { |
119 | Q_D(ItemFetchJob); |
120 | |
121 | d->init(); |
122 | d->mCollection = collection; |
123 | d->mValuePool = new ProtocolHelperValuePool; // only worth it for lots of results |
124 | } |
125 | |
126 | ItemFetchJob::ItemFetchJob(const Item &item, QObject *parent) |
127 | : Job(new ItemFetchJobPrivate(this), parent) |
128 | { |
129 | Q_D(ItemFetchJob); |
130 | |
131 | d->init(); |
132 | d->mRequestedItems.append(item); |
133 | } |
134 | |
135 | ItemFetchJob::ItemFetchJob(const Akonadi::Item::List &items, QObject *parent) |
136 | : Job(new ItemFetchJobPrivate(this), parent) |
137 | { |
138 | Q_D(ItemFetchJob); |
139 | |
140 | d->init(); |
141 | d->mRequestedItems = items; |
142 | } |
143 | |
144 | ItemFetchJob::ItemFetchJob(const QList<Akonadi::Item::Id> &items, QObject *parent) |
145 | : Job(new ItemFetchJobPrivate(this), parent) |
146 | { |
147 | Q_D(ItemFetchJob); |
148 | |
149 | d->init(); |
150 | foreach (Item::Id id, items) { |
151 | d->mRequestedItems.append(Item(id)); |
152 | } |
153 | } |
154 | |
155 | ItemFetchJob::ItemFetchJob(const Tag &tag, QObject *parent) |
156 | : Job(new ItemFetchJobPrivate(this), parent) |
157 | { |
158 | Q_D(ItemFetchJob); |
159 | |
160 | d->init(); |
161 | d->mTag = tag; |
162 | d->mValuePool = new ProtocolHelperValuePool; |
163 | } |
164 | |
165 | ItemFetchJob::~ItemFetchJob() |
166 | { |
167 | } |
168 | |
169 | void ItemFetchJob::doStart() |
170 | { |
171 | Q_D(ItemFetchJob); |
172 | |
173 | QByteArray command = d->newTag(); |
174 | try { |
175 | command += ProtocolHelper::commandContextToByteArray(d->mCollection, d->mTag, d->mRequestedItems, AKONADI_CMD_ITEMFETCH); |
176 | } catch (const Akonadi::Exception &e) { |
177 | setError(Job::Unknown); |
178 | setErrorText(QString::fromUtf8(e.what())); |
179 | emitResult(); |
180 | return; |
181 | } |
182 | |
183 | // This is only required for 4.10 |
184 | if (d->protocolVersion() < 30) { |
185 | if (d->mFetchScope.ignoreRetrievalErrors()) { |
186 | kDebug() << "IGNOREERRORS is not available with this version of Akonadi server" ; |
187 | } |
188 | d->mFetchScope.setIgnoreRetrievalErrors(false); |
189 | } |
190 | |
191 | command += ProtocolHelper::itemFetchScopeToByteArray(d->mFetchScope); |
192 | d->writeData(command); |
193 | } |
194 | |
195 | void ItemFetchJob::doHandleResponse(const QByteArray &tag, const QByteArray &data) |
196 | { |
197 | Q_D(ItemFetchJob); |
198 | |
199 | if (tag == "*" ) { |
200 | int begin = data.indexOf("FETCH" ); |
201 | if (begin >= 0) { |
202 | |
203 | // split fetch response into key/value pairs |
204 | QList<QByteArray> fetchResponse; |
205 | ImapParser::parseParenthesizedList(data, fetchResponse, begin + 6); |
206 | |
207 | Item item; |
208 | ProtocolHelper::parseItemFetchResult(fetchResponse, item, d->mValuePool); |
209 | if (!item.isValid()) { |
210 | return; |
211 | } |
212 | |
213 | d->mCount++; |
214 | |
215 | if (d->mDeliveryOptions & ItemGetter) { |
216 | d->mResultItems.append(item); |
217 | } |
218 | |
219 | if (d->mDeliveryOptions & EmitItemsInBatches) { |
220 | d->mPendingItems.append(item); |
221 | if (!d->mEmitTimer->isActive()) { |
222 | d->mEmitTimer->start(); |
223 | } |
224 | } else if (d->mDeliveryOptions & EmitItemsIndividually) { |
225 | emit itemsReceived(Item::List() << item); |
226 | } |
227 | return; |
228 | } |
229 | } |
230 | kDebug() << "Unhandled response: " << tag << data; |
231 | } |
232 | |
233 | Item::List ItemFetchJob::items() const |
234 | { |
235 | Q_D(const ItemFetchJob); |
236 | |
237 | return d->mResultItems; |
238 | } |
239 | |
240 | void ItemFetchJob::clearItems() |
241 | { |
242 | Q_D(ItemFetchJob); |
243 | |
244 | d->mResultItems.clear(); |
245 | } |
246 | |
247 | void ItemFetchJob::setFetchScope(ItemFetchScope &fetchScope) |
248 | { |
249 | Q_D(ItemFetchJob); |
250 | |
251 | d->mFetchScope = fetchScope; |
252 | } |
253 | |
254 | void ItemFetchJob::setFetchScope(const ItemFetchScope &fetchScope) |
255 | { |
256 | Q_D(ItemFetchJob); |
257 | |
258 | d->mFetchScope = fetchScope; |
259 | } |
260 | |
261 | ItemFetchScope &ItemFetchJob::fetchScope() |
262 | { |
263 | Q_D(ItemFetchJob); |
264 | |
265 | return d->mFetchScope; |
266 | } |
267 | |
268 | void ItemFetchJob::setCollection(const Akonadi::Collection &collection) |
269 | { |
270 | Q_D(ItemFetchJob); |
271 | |
272 | d->mCollection = collection; |
273 | } |
274 | |
275 | void ItemFetchJob::setDeliveryOption(DeliveryOptions options) |
276 | { |
277 | Q_D(ItemFetchJob); |
278 | |
279 | d->mDeliveryOptions = options; |
280 | } |
281 | |
282 | ItemFetchJob::DeliveryOptions ItemFetchJob::deliveryOptions() const |
283 | { |
284 | Q_D(const ItemFetchJob); |
285 | |
286 | return d->mDeliveryOptions; |
287 | } |
288 | |
289 | int ItemFetchJob::count() const |
290 | { |
291 | Q_D(const ItemFetchJob); |
292 | |
293 | return d->mCount; |
294 | } |
295 | #include "moc_itemfetchjob.cpp" |
296 | |