1 | /* |
2 | Copyright (c) 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 "notificationmessage_p.h" |
21 | #include "imapparser_p.h" |
22 | |
23 | #include <QtCore/QDebug> |
24 | #include <QtCore/QHash> |
25 | #include <QtDBus/QDBusMetaType> |
26 | |
27 | using namespace Akonadi; |
28 | |
29 | class NotificationMessage::Private : public QSharedData |
30 | { |
31 | public: |
32 | Private() |
33 | : QSharedData() |
34 | , type(NotificationMessage::InvalidType) |
35 | , operation(NotificationMessage::InvalidOp) |
36 | , uid(-1) |
37 | , parentCollection(-1) |
38 | , parentDestCollection(-1) |
39 | { |
40 | } |
41 | |
42 | Private(const Private &other) |
43 | : QSharedData(other) |
44 | { |
45 | sessionId = other.sessionId; |
46 | type = other.type; |
47 | operation = other.operation; |
48 | uid = other.uid; |
49 | remoteId = other.remoteId; |
50 | resource = other.resource; |
51 | destResource = other.destResource; |
52 | parentCollection = other.parentCollection; |
53 | parentDestCollection = other.parentDestCollection; |
54 | mimeType = other.mimeType; |
55 | parts = other.parts; |
56 | } |
57 | |
58 | bool compareWithoutOpAndParts(const Private &other) const |
59 | { |
60 | return uid == other.uid |
61 | && type == other.type |
62 | && sessionId == other.sessionId |
63 | && remoteId == other.remoteId |
64 | && resource == other.resource |
65 | && destResource == other.destResource |
66 | && parentCollection == other.parentCollection |
67 | && parentDestCollection == other.parentDestCollection |
68 | && mimeType == other.mimeType; |
69 | } |
70 | |
71 | bool operator==(const Private &other) const |
72 | { |
73 | return operation == other.operation && parts == other.parts && compareWithoutOpAndParts(other); |
74 | } |
75 | |
76 | QByteArray sessionId; |
77 | NotificationMessage::Type type; |
78 | NotificationMessage::Operation operation; |
79 | Id uid; |
80 | QString remoteId; |
81 | QByteArray resource; |
82 | QByteArray destResource; |
83 | Id parentCollection; |
84 | Id parentDestCollection; |
85 | QString mimeType; |
86 | QSet<QByteArray> parts; |
87 | }; |
88 | |
89 | NotificationMessage::NotificationMessage() |
90 | : d(new Private) |
91 | { |
92 | } |
93 | |
94 | NotificationMessage::NotificationMessage(const NotificationMessage &other) |
95 | : d(other.d) |
96 | { |
97 | } |
98 | |
99 | NotificationMessage::~NotificationMessage() |
100 | { |
101 | } |
102 | |
103 | NotificationMessage &NotificationMessage::operator=(const NotificationMessage &other) |
104 | { |
105 | if (this != &other) { |
106 | d = other.d; |
107 | } |
108 | |
109 | return *this; |
110 | } |
111 | |
112 | bool NotificationMessage::operator==(const NotificationMessage &other) const |
113 | { |
114 | return d == other.d; |
115 | } |
116 | |
117 | void NotificationMessage::registerDBusTypes() |
118 | { |
119 | qDBusRegisterMetaType<Akonadi::NotificationMessage>(); |
120 | qDBusRegisterMetaType<Akonadi::NotificationMessage::List>(); |
121 | } |
122 | |
123 | QByteArray NotificationMessage::sessionId() const |
124 | { |
125 | return d->sessionId; |
126 | } |
127 | |
128 | void NotificationMessage::setSessionId(const QByteArray &sessionId) |
129 | { |
130 | d->sessionId = sessionId; |
131 | } |
132 | |
133 | NotificationMessage::Type NotificationMessage::type() const |
134 | { |
135 | return d->type; |
136 | } |
137 | |
138 | void NotificationMessage::setType(Type type) |
139 | { |
140 | d->type = type; |
141 | } |
142 | |
143 | NotificationMessage::Operation NotificationMessage::operation() const |
144 | { |
145 | return d->operation; |
146 | } |
147 | |
148 | void NotificationMessage::setOperation(Operation operation) |
149 | { |
150 | d->operation = operation; |
151 | } |
152 | |
153 | NotificationMessage::Id NotificationMessage::uid() const |
154 | { |
155 | return d->uid; |
156 | } |
157 | |
158 | void NotificationMessage::setUid(Id uid) |
159 | { |
160 | d->uid = uid; |
161 | } |
162 | |
163 | QString NotificationMessage::remoteId() const |
164 | { |
165 | return d->remoteId; |
166 | } |
167 | |
168 | void NotificationMessage::setRemoteId(const QString &remoteId) |
169 | { |
170 | d->remoteId = remoteId; |
171 | } |
172 | |
173 | QByteArray NotificationMessage::resource() const |
174 | { |
175 | return d->resource; |
176 | } |
177 | |
178 | void NotificationMessage::setResource(const QByteArray &resource) |
179 | { |
180 | d->resource = resource; |
181 | } |
182 | |
183 | NotificationMessage::Id NotificationMessage::parentCollection() const |
184 | { |
185 | return d->parentCollection; |
186 | } |
187 | |
188 | NotificationMessage::Id NotificationMessage::parentDestCollection() const |
189 | { |
190 | return d->parentDestCollection; |
191 | } |
192 | |
193 | void NotificationMessage::setParentCollection(Id parent) |
194 | { |
195 | d->parentCollection = parent; |
196 | } |
197 | |
198 | void NotificationMessage::setParentDestCollection(Id parent) |
199 | { |
200 | d->parentDestCollection = parent; |
201 | } |
202 | |
203 | void NotificationMessage::setDestinationResource(const QByteArray &destResource) |
204 | { |
205 | d->destResource = destResource; |
206 | } |
207 | |
208 | QByteArray NotificationMessage::destinationResource() const |
209 | { |
210 | return d->destResource; |
211 | } |
212 | |
213 | QString NotificationMessage::mimeType() const |
214 | { |
215 | return d->mimeType; |
216 | } |
217 | |
218 | void NotificationMessage::setMimeType(const QString &mimeType) |
219 | { |
220 | d->mimeType = mimeType; |
221 | } |
222 | |
223 | QSet<QByteArray> NotificationMessage::itemParts() const |
224 | { |
225 | return d->parts; |
226 | } |
227 | |
228 | void NotificationMessage::setItemParts(const QSet<QByteArray> &parts) |
229 | { |
230 | d->parts = parts; |
231 | } |
232 | |
233 | QString NotificationMessage::toString() const |
234 | { |
235 | QString rv; |
236 | // some tests before making the string |
237 | if (type() == InvalidType) { |
238 | return QLatin1String("Error: Type is not set" ); |
239 | } |
240 | if (uid() == -1) { |
241 | return QLatin1String("Error: uid is not set" ); |
242 | } |
243 | if (remoteId().isEmpty()) { |
244 | return QLatin1String("Error: remoteId is empty" ); |
245 | } |
246 | if (operation() == InvalidOp) { |
247 | return QLatin1String("Error: operation is not set" ); |
248 | } |
249 | |
250 | switch (type()) { |
251 | case Item: |
252 | rv += QLatin1String("Item " ); |
253 | break; |
254 | case Collection: |
255 | rv += QLatin1String("Collection " ); |
256 | break; |
257 | case InvalidType: |
258 | // already done above |
259 | break; |
260 | } |
261 | |
262 | rv += QString::fromLatin1("(%1, %2) " ).arg(uid()).arg(remoteId()); |
263 | |
264 | if (parentCollection() >= 0) { |
265 | if (parentDestCollection() >= 0) { |
266 | rv += QString::fromLatin1("from " ); |
267 | } else { |
268 | rv += QString::fromLatin1("in " ); |
269 | } |
270 | rv += QString::fromLatin1("collection %1 " ).arg(parentCollection()); |
271 | } else { |
272 | rv += QLatin1String("unspecified parent collection " ); |
273 | } |
274 | |
275 | rv += QString::fromLatin1("mimetype %1 " ).arg(mimeType().isEmpty() ? QLatin1String("unknown" ) : mimeType()); |
276 | |
277 | switch (operation()) { |
278 | case Add: |
279 | rv += QLatin1String("added" ); |
280 | break; |
281 | case Modify: |
282 | rv += QLatin1String("modified parts (" ); |
283 | rv += QString::fromLatin1(ImapParser::join(itemParts().toList(), ", " )); |
284 | rv += QLatin1String(")" ); |
285 | break; |
286 | case Move: |
287 | rv += QLatin1String("moved" ); |
288 | break; |
289 | case Remove: |
290 | rv += QLatin1String("removed" ); |
291 | break; |
292 | case Link: |
293 | rv += QLatin1String("linked" ); |
294 | break; |
295 | case Unlink: |
296 | rv += QLatin1String("unlinked" ); |
297 | break; |
298 | case Subscribe: |
299 | rv += QLatin1String("subscribed" ); |
300 | break; |
301 | case Unsubscribe: |
302 | rv += QLatin1String("unsubscribed" ); |
303 | break; |
304 | case InvalidOp: |
305 | // already done above |
306 | break; |
307 | } |
308 | |
309 | if (parentDestCollection() >= 0) { |
310 | rv += QString::fromLatin1(" to collection %1" ).arg(parentDestCollection()); |
311 | } |
312 | |
313 | return rv; |
314 | } |
315 | |
316 | void NotificationMessage::appendAndCompress(NotificationMessage::List &list, const NotificationMessage &msg) |
317 | { |
318 | bool appended; |
319 | appendAndCompress(list, msg, &appended); |
320 | } |
321 | |
322 | void NotificationMessage::appendAndCompress(NotificationMessage::List &list, const NotificationMessage &msg, bool *appended) |
323 | { |
324 | // fast-path for stuff that is not considered during O(n) compression below |
325 | if (msg.operation() != Add && msg.operation() != Link && msg.operation() != Unlink && msg.operation() != Subscribe && msg.operation() != Unsubscribe && msg.operation() != Move) { |
326 | NotificationMessage::List::Iterator end = list.end(); |
327 | for (NotificationMessage::List::Iterator it = list.begin(); it != end;) { |
328 | if (msg.d.constData()->compareWithoutOpAndParts(*((*it).d.constData()))) { |
329 | // same operation: merge changed parts and drop the new one |
330 | if (msg.operation() == (*it).operation()) { |
331 | (*it).setItemParts((*it).itemParts() + msg.itemParts()); |
332 | *appended = false; |
333 | return; |
334 | } else if (msg.operation() == Modify) { |
335 | // new one is a modification, the existing one not, so drop the new one |
336 | *appended = false; |
337 | return; |
338 | } else if (msg.operation() == Remove && (*it).operation() == Modify) { |
339 | // new on is a deletion, erase the existing modification ones (and keep going, in case there are more) |
340 | it = list.erase(it); |
341 | end = list.end(); |
342 | } else { |
343 | // keep looking |
344 | ++it; |
345 | } |
346 | } else { |
347 | ++it; |
348 | } |
349 | } |
350 | } |
351 | *appended = true; |
352 | list.append(msg); |
353 | } |
354 | |
355 | QDBusArgument &operator<<(QDBusArgument &arg, const NotificationMessage &msg) |
356 | { |
357 | arg.beginStructure(); |
358 | arg << msg.sessionId(); |
359 | arg << msg.type(); |
360 | arg << msg.operation(); |
361 | arg << msg.uid(); |
362 | arg << msg.remoteId(); |
363 | arg << msg.resource(); |
364 | arg << msg.parentCollection(); |
365 | arg << msg.parentDestCollection(); |
366 | arg << msg.mimeType(); |
367 | |
368 | QStringList itemParts; |
369 | if (msg.operation() == NotificationMessage::Move) { |
370 | // encode destination resource in parts, as a backward compat hack |
371 | itemParts.push_back(QString::fromLatin1(msg.destinationResource())); |
372 | } else { |
373 | Q_FOREACH (const QByteArray &itemPart, msg.itemParts()) { |
374 | itemParts.append(QString::fromLatin1(itemPart)); |
375 | } |
376 | } |
377 | |
378 | arg << itemParts; |
379 | arg.endStructure(); |
380 | return arg; |
381 | } |
382 | |
383 | const QDBusArgument &operator>>(const QDBusArgument &arg, NotificationMessage &msg) |
384 | { |
385 | arg.beginStructure(); |
386 | QByteArray b; |
387 | arg >> b; |
388 | msg.setSessionId(b); |
389 | int i; |
390 | arg >> i; |
391 | msg.setType(static_cast<NotificationMessage::Type>(i)); |
392 | arg >> i; |
393 | msg.setOperation(static_cast<NotificationMessage::Operation>(i)); |
394 | NotificationMessage::Id id; |
395 | arg >> id; |
396 | msg.setUid(id); |
397 | QString s; |
398 | arg >> s; |
399 | msg.setRemoteId(s); |
400 | arg >> b; |
401 | msg.setResource(b); |
402 | arg >> id; |
403 | msg.setParentCollection(id); |
404 | arg >> id; |
405 | msg.setParentDestCollection(id); |
406 | arg >> s; |
407 | msg.setMimeType(s); |
408 | QStringList l; |
409 | arg >> l; |
410 | |
411 | QSet<QByteArray> itemParts; |
412 | if (msg.operation() == NotificationMessage::Move && l.size() >= 1) { |
413 | // decode destination resource, which is stored in parts as a backward compat hack |
414 | msg.setDestinationResource(l.first().toLatin1()); |
415 | } else { |
416 | Q_FOREACH (const QString &itemPart, l) { |
417 | itemParts.insert(itemPart.toLatin1()); |
418 | } |
419 | } |
420 | |
421 | msg.setItemParts(itemParts); |
422 | arg.endStructure(); |
423 | return arg; |
424 | } |
425 | |
426 | uint qHash(const Akonadi::NotificationMessage &msg) |
427 | { |
428 | return qHash(msg.uid() + (msg.type() << 31) + (msg.operation() << 28)); |
429 | } |
430 | |