• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.13.3 API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • akonadi
itemmodifyjob.cpp
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 "itemmodifyjob.h"
21 #include "itemmodifyjob_p.h"
22 
23 #include "changemediator_p.h"
24 #include "collection.h"
25 #include "conflicthandling/conflicthandler_p.h"
26 #include "entity_p.h"
27 #include "imapparser_p.h"
28 #include "item_p.h"
29 #include "itemserializer_p.h"
30 #include "job_p.h"
31 #include "protocolhelper_p.h"
32 #include "gid/gidextractor_p.h"
33 
34 #include <kdebug.h>
35 
36 using namespace Akonadi;
37 
38 ItemModifyJobPrivate::ItemModifyJobPrivate( ItemModifyJob *parent )
39  : JobPrivate( parent ),
40  mRevCheck( true ),
41  mIgnorePayload( false ),
42  mAutomaticConflictHandlingEnabled( true ),
43  mSilent( false )
44 {
45 }
46 
47 void ItemModifyJobPrivate::setClean()
48 {
49  mOperations.insert( Dirty );
50 }
51 
52 QByteArray ItemModifyJobPrivate::nextPartHeader()
53 {
54  QByteArray command;
55  if ( !mParts.isEmpty() ) {
56  QSetIterator<QByteArray> it( mParts );
57  const QByteArray label = it.next();
58  mParts.remove( label );
59 
60  mPendingData.clear();
61  int version = 0;
62  ItemSerializer::serialize( mItems.first(), label, mPendingData, version );
63  command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, label, version );
64  if ( mPendingData.size() > 0 ) {
65  command += " {" + QByteArray::number( mPendingData.size() ) + "}\n";
66  } else {
67  if ( mPendingData.isNull() )
68  command += " NIL";
69  else
70  command += " \"\"";
71  command += nextPartHeader();
72  }
73  } else {
74  command += ")\n";
75  }
76  return command;
77 }
78 
79 void ItemModifyJobPrivate::conflictResolved()
80 {
81  Q_Q( ItemModifyJob );
82 
83  q->setError( KJob::NoError );
84  q->setErrorText( QString() );
85  q->emitResult();
86 }
87 
88 void ItemModifyJobPrivate::conflictResolveError( const QString &message )
89 {
90  Q_Q( ItemModifyJob );
91 
92  q->setErrorText( q->errorText() + message );
93  q->emitResult();
94 }
95 
96 void ItemModifyJobPrivate::doUpdateItemRevision( Akonadi::Item::Id itemId, int oldRevision, int newRevision )
97 {
98  Item::List::iterator it = std::find_if( mItems.begin(), mItems.end(), boost::bind( &Item::id, _1 ) == itemId );
99  if ( it != mItems.end() && (*it).revision() == oldRevision )
100  (*it).setRevision( newRevision );
101 }
102 
103 QString ItemModifyJobPrivate::jobDebuggingString() const
104 {
105  try {
106  return QString::fromUtf8( fullCommand() );
107  } catch ( const Exception &e ) {
108  return QString::fromUtf8( e.what() );
109  }
110 }
111 
112 void ItemModifyJobPrivate::setSilent( bool silent )
113 {
114  mSilent = silent;
115 }
116 
117 ItemModifyJob::ItemModifyJob( const Item &item, QObject * parent )
118  : Job( new ItemModifyJobPrivate( this ), parent )
119 {
120  Q_D( ItemModifyJob );
121 
122  d->mItems.append( item );
123  d->mParts = item.loadedPayloadParts();
124 
125  d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
126  d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
127 }
128 
129 ItemModifyJob::ItemModifyJob( const Akonadi::Item::List &items, QObject *parent)
130  : Job( new ItemModifyJobPrivate( this ), parent )
131 {
132  Q_ASSERT( !items.isEmpty() );
133  Q_D( ItemModifyJob );
134  d->mItems = items;
135 
136  // same as single item ctor
137  if ( d->mItems.size() == 1 ) {
138  d->mParts = items.first().loadedPayloadParts();
139  d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
140  d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
141  } else {
142  d->mIgnorePayload = true;
143  d->mRevCheck = false;
144  }
145 }
146 
147 ItemModifyJob::~ItemModifyJob()
148 {
149 }
150 
151 QByteArray ItemModifyJobPrivate::fullCommand() const
152 {
153  const Akonadi::Item item = mItems.first();
154  QList<QByteArray> changes;
155  foreach ( int op, mOperations ) {
156  switch ( op ) {
157  case ItemModifyJobPrivate::RemoteId:
158  if ( !item.remoteId().isNull() ) {
159  changes << "REMOTEID";
160  changes << ImapParser::quote( item.remoteId().toUtf8() );
161  }
162  break;
163  case ItemModifyJobPrivate::Gid: {
164  const QString gid = GidExtractor::getGid( item );
165  if ( !gid.isNull() ) {
166  changes << "GID";
167  changes << ImapParser::quote( gid.toUtf8() );
168  }
169  break;
170  }
171  case ItemModifyJobPrivate::RemoteRevision:
172  if ( !item.remoteRevision().isNull() ) {
173  changes << "REMOTEREVISION";
174  changes << ImapParser::quote( item.remoteRevision().toUtf8() );
175  }
176  break;
177  case ItemModifyJobPrivate::Dirty:
178  changes << "DIRTY";
179  changes << "false";
180  break;
181  }
182  }
183 
184  if ( item.d_func()->mClearPayload )
185  changes << "INVALIDATECACHE";
186 
187  if ( mSilent ) {
188  changes << "SILENT";
189  }
190 
191  if ( item.d_func()->mFlagsOverwritten ) {
192  changes << "FLAGS";
193  changes << '(' + ImapParser::join( item.flags(), " " ) + ')';
194  } else {
195  if ( !item.d_func()->mAddedFlags.isEmpty() ) {
196  changes << "+FLAGS";
197  changes << '(' + ImapParser::join( item.d_func()->mAddedFlags, " " ) + ')';
198  }
199  if ( !item.d_func()->mDeletedFlags.isEmpty() ) {
200  changes << "-FLAGS";
201  changes << '(' + ImapParser::join( item.d_func()->mDeletedFlags, " " ) + ')';
202  }
203  }
204 
205  if ( item.d_func()->mTagsOverwritten ) {
206  changes << "TAGS";
207  changes << ' ' + ProtocolHelper::tagSetToImapSequenceSet( item.tags() );
208  } else {
209  if ( !item.d_func()->mAddedTags.isEmpty() ) {
210  changes << "+TAGS";
211  changes << ' ' + ProtocolHelper::tagSetToImapSequenceSet( item.d_func()->mAddedTags );
212  }
213  if ( !item.d_func()->mDeletedTags.isEmpty() ) {
214  changes << "-TAGS";
215  changes << ' ' + ProtocolHelper::tagSetToImapSequenceSet( item.d_func()->mDeletedTags );
216  }
217  }
218 
219  if ( !item.d_func()->mDeletedAttributes.isEmpty() ) {
220  changes << "-PARTS";
221  QList<QByteArray> attrs;
222  foreach ( const QByteArray &attr, item.d_func()->mDeletedAttributes )
223  attrs << ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr );
224  changes << '(' + ImapParser::join( attrs, " " ) + ')';
225  }
226 
227  // nothing to do
228  if ( changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty() ) {
229  return QByteArray();
230  }
231 
232  QByteArray command;
233  command += ProtocolHelper::entitySetToByteArray( mItems, "STORE" ); // can throw an exception
234  command += ' ';
235  if ( !mRevCheck || item.revision() < 0 ) {
236  command += "NOREV ";
237  } else {
238  command += "REV " + QByteArray::number( item.revision() ) + ' ';
239  }
240 
241  if ( item.d_func()->mSizeChanged )
242  command += "SIZE " + QByteArray::number( item.size() );
243 
244  command += " (" + ImapParser::join( changes, " " );
245  const QByteArray attrs = ProtocolHelper::attributesToByteArray( item, true );
246  if ( !attrs.isEmpty() )
247  command += ' ' + attrs;
248  return command;
249 }
250 
251 void ItemModifyJob::doStart()
252 {
253  Q_D( ItemModifyJob );
254 
255  QByteArray command;
256  try {
257  command = d->fullCommand();
258  } catch ( const Exception &e ) {
259  setError( Job::Unknown );
260  setErrorText( QString::fromUtf8( e.what() ) );
261  emitResult();
262  return;
263  }
264  if ( command.isEmpty() ) {
265  emitResult();
266  return;
267  }
268 
269  d->mTag = d->newTag();
270  command.prepend( d->mTag );
271 
272  command += d->nextPartHeader();
273 
274  d->writeData( command );
275  d->newTag(); // hack to circumvent automatic response handling
276 }
277 
278 void ItemModifyJob::doHandleResponse(const QByteArray &_tag, const QByteArray & data)
279 {
280  Q_D( ItemModifyJob );
281 
282  if ( _tag == "+" ) { // ready for literal data
283  d->writeData( d->mPendingData );
284  d->writeData( d->nextPartHeader() );
285  return;
286  }
287 
288  if ( _tag == d->mTag ) {
289  if ( data.startsWith( "OK" ) ) { //krazy:exclude=strings
290  QDateTime modificationDateTime;
291  int dateTimePos = data.indexOf( "DATETIME" );
292  if ( dateTimePos != -1 ) {
293  int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 );
294  if ( resultPos == (dateTimePos + 8) ) {
295  kDebug() << "Invalid DATETIME response to STORE command: " << _tag << data;
296  }
297  }
298 
299  Item &item = d->mItems.first();
300  item.setModificationTime( modificationDateTime );
301  item.d_ptr->resetChangeLog();
302  } else {
303  setError( Unknown );
304  setErrorText( QString::fromUtf8( data ) );
305 
306  if ( data.contains( "[LLCONFLICT]" ) ) {
307  if ( d->mAutomaticConflictHandlingEnabled ) {
308  ConflictHandler *handler = new ConflictHandler( ConflictHandler::LocalLocalConflict, this );
309  handler->setConflictingItems( d->mItems.first(), d->mItems.first() );
310  connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) );
311  connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) );
312 
313  QMetaObject::invokeMethod( handler, "start", Qt::QueuedConnection );
314  return;
315  }
316  }
317  }
318 
319  foreach ( const Item &item, d->mItems ) {
320  ChangeMediator::invalidateItem(item);
321  }
322 
323  emitResult();
324  return;
325  }
326 
327  if ( _tag == "*" ) {
328  Akonadi::Item::Id id;
329  ImapParser::parseNumber( data, id );
330  int pos = data.indexOf( '(' );
331  if ( pos <= 0 || id <= 0 ) {
332  kDebug() << "Ignoring strange response: " << _tag << data;
333  return;
334  }
335  Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id );
336  if ( it == d->mItems.end() ) {
337  kDebug() << "Received STORE response for an item we did not modify: " << _tag << data;
338  return;
339  }
340  QList<QByteArray> attrs;
341  ImapParser::parseParenthesizedList( data, attrs, pos );
342  for ( int i = 0; i < attrs.size() - 1; i += 2 ) {
343  const QByteArray key = attrs.at( i );
344  if ( key == "REV" ) {
345  const int newRev = attrs.at( i + 1 ).toInt();
346  const int oldRev = (*it).revision();
347  if ( newRev < oldRev || newRev < 0 )
348  continue;
349  d->itemRevisionChanged( (*it).id(), oldRev, newRev );
350  (*it).setRevision( newRev );
351  }
352  }
353  return;
354  }
355 
356  kDebug() << "Unhandled response: " << _tag << data;
357 }
358 
359 void ItemModifyJob::setIgnorePayload( bool ignore )
360 {
361  Q_D( ItemModifyJob );
362 
363  if ( d->mIgnorePayload == ignore )
364  return;
365 
366  d->mIgnorePayload = ignore;
367  if ( d->mIgnorePayload )
368  d->mParts = QSet<QByteArray>();
369  else {
370  Q_ASSERT( !d->mItems.first().mimeType().isEmpty() );
371  d->mParts = d->mItems.first().loadedPayloadParts();
372  }
373 }
374 
375 bool ItemModifyJob::ignorePayload() const
376 {
377  Q_D( const ItemModifyJob );
378 
379  return d->mIgnorePayload;
380 }
381 
382 void ItemModifyJob::setUpdateGid( bool update )
383 {
384  Q_D( ItemModifyJob );
385  if ( update && !updateGid() ) {
386  d->mOperations.insert( ItemModifyJobPrivate::Gid );
387  } else {
388  d->mOperations.remove( ItemModifyJobPrivate::Gid );
389  }
390 }
391 
392 bool ItemModifyJob::updateGid() const
393 {
394  Q_D( const ItemModifyJob );
395  return d->mOperations.contains( ItemModifyJobPrivate::Gid );
396 }
397 
398 void ItemModifyJob::disableRevisionCheck()
399 {
400  Q_D( ItemModifyJob );
401 
402  d->mRevCheck = false;
403 }
404 
405 void ItemModifyJob::disableAutomaticConflictHandling()
406 {
407  Q_D( ItemModifyJob );
408 
409  d->mAutomaticConflictHandlingEnabled = false;
410 }
411 
412 Item ItemModifyJob::item() const
413 {
414  Q_D( const ItemModifyJob );
415  Q_ASSERT( d->mItems.size() == 1 );
416 
417  return d->mItems.first();
418 }
419 
420 Item::List ItemModifyJob::items() const
421 {
422  Q_D( const ItemModifyJob );
423  return d->mItems;
424 }
425 
426 #include "moc_itemmodifyjob.cpp"
Akonadi::ItemModifyJob::~ItemModifyJob
virtual ~ItemModifyJob()
Destroys the item modify job.
Definition: itemmodifyjob.cpp:147
Akonadi::ItemModifyJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition: itemmodifyjob.cpp:251
Akonadi::ItemModifyJob::disableRevisionCheck
void disableRevisionCheck()
Disables the check of the revision number.
Definition: itemmodifyjob.cpp:398
Akonadi::ItemModifyJob::updateGid
bool updateGid() const
Returns wheter the GID should be updated.
Definition: itemmodifyjob.cpp:392
Akonadi::ItemModifyJob::setUpdateGid
void setUpdateGid(bool update)
Sets whether the GID shall be updated either from the gid parameter or by extracting it from the payl...
Definition: itemmodifyjob.cpp:382
Akonadi::Job::Unknown
Unknown error.
Definition: job.h:109
Akonadi::ProtocolHelper::entitySetToByteArray
static QByteArray entitySetToByteArray(const QList< T > &_objects, const QByteArray &command)
Converts the given set of items into a protocol representation.
Definition: protocolhelper_p.h:125
Akonadi::ItemModifyJob::item
Item item() const
Returns the modified and stored item including the changed revision number.
Definition: itemmodifyjob.cpp:412
Akonadi::ProtocolHelper::attributesToByteArray
static QByteArray attributesToByteArray(const Entity &entity, bool ns=false)
Convert attributes to their protocol representation.
Definition: protocolhelper.cpp:206
Akonadi::ItemModifyJob::items
Item::List items() const
Returns the modified and stored items including the changed revision number.
Definition: itemmodifyjob.cpp:420
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition: job.h:86
Akonadi::ConflictHandler::setConflictingItems
void setConflictingItems(const Akonadi::Item &changedItem, const Akonadi::Item &conflictingItem)
Sets the items that causes the conflict.
Definition: conflicthandler.cpp:41
Akonadi::ConflictHandler::LocalLocalConflict
Changes of two Akonadi client applications conflict.
Definition: conflicthandler_p.h:49
Akonadi::ProtocolHelper::encodePartIdentifier
static QByteArray encodePartIdentifier(PartNamespace ns, const QByteArray &label, int version=0)
Encodes part label and namespace.
Definition: protocolhelper.cpp:226
Akonadi::GidExtractor::getGid
static QString getGid(const Item &item)
Extracts the gid from item.
Definition: gidextractor.cpp:39
Akonadi::ItemModifyJob::setIgnorePayload
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
Definition: itemmodifyjob.cpp:359
Akonadi::ItemModifyJob::ignorePayload
bool ignorePayload() const
Returns whether the payload of the modified item shall be omitted from transmission to the Akonadi st...
Definition: itemmodifyjob.cpp:375
Akonadi::Exception
Base class for exceptions used by the Akonadi library.
Definition: exception.h:35
Akonadi::ItemModifyJob
Job that modifies an existing item in the Akonadi storage.
Definition: itemmodifyjob.h:97
Akonadi::JobPrivate
Definition: job_p.h:31
Akonadi::ItemModifyJob::disableAutomaticConflictHandling
void disableAutomaticConflictHandling()
Disables the automatic handling of conflicts.
Definition: itemmodifyjob.cpp:405
Akonadi::ItemModifyJob::doHandleResponse
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...
Definition: itemmodifyjob.cpp:278
Akonadi::ConflictHandler
A class to handle conflicts in Akonadi.
Definition: conflicthandler_p.h:39
Akonadi::ItemModifyJob::ItemModifyJob
ItemModifyJob(const Item &item, QObject *parent=0)
Creates a new item modify job.
Definition: itemmodifyjob.cpp:117
Akonadi::ItemSerializer::serialize
static void serialize(const Item &item, const QByteArray &label, QByteArray &data, int &version)
throws ItemSerializerException on failure
Definition: itemserializer.cpp:127
Akonadi::ItemModifyJobPrivate
Definition: itemmodifyjob_p.h:30
Akonadi::Exception::what
const char * what() const
Returns the error message associated with this exception.
Definition: exception.cpp:92
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Mon Jul 21 2014 08:03:53 by doxygen 1.8.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs-4.13.3 API Reference

Skip menu "kdepimlibs-4.13.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal