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

akonadi

  • sources
  • kde-4.12
  • kdepimlibs
  • 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 {
44 }
45 
46 void ItemModifyJobPrivate::setClean()
47 {
48  mOperations.insert( Dirty );
49 }
50 
51 QByteArray ItemModifyJobPrivate::nextPartHeader()
52 {
53  QByteArray command;
54  if ( !mParts.isEmpty() ) {
55  QSetIterator<QByteArray> it( mParts );
56  const QByteArray label = it.next();
57  mParts.remove( label );
58 
59  mPendingData.clear();
60  int version = 0;
61  ItemSerializer::serialize( mItems.first(), label, mPendingData, version );
62  command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, label, version );
63  if ( mPendingData.size() > 0 ) {
64  command += " {" + QByteArray::number( mPendingData.size() ) + "}\n";
65  } else {
66  if ( mPendingData.isNull() )
67  command += " NIL";
68  else
69  command += " \"\"";
70  command += nextPartHeader();
71  }
72  } else {
73  command += ")\n";
74  }
75  return command;
76 }
77 
78 void ItemModifyJobPrivate::conflictResolved()
79 {
80  Q_Q( ItemModifyJob );
81 
82  q->setError( KJob::NoError );
83  q->setErrorText( QString() );
84  q->emitResult();
85 }
86 
87 void ItemModifyJobPrivate::conflictResolveError( const QString &message )
88 {
89  Q_Q( ItemModifyJob );
90 
91  q->setErrorText( q->errorText() + message );
92  q->emitResult();
93 }
94 
95 void ItemModifyJobPrivate::doUpdateItemRevision( Akonadi::Item::Id itemId, int oldRevision, int newRevision )
96 {
97  Item::List::iterator it = std::find_if( mItems.begin(), mItems.end(), boost::bind( &Item::id, _1 ) == itemId );
98  if ( it != mItems.end() && (*it).revision() == oldRevision )
99  (*it).setRevision( newRevision );
100 }
101 
102 QString ItemModifyJobPrivate::jobDebuggingString() const
103 {
104  try {
105  return QString::fromUtf8( fullCommand() );
106  } catch ( const Exception &e ) {
107  return QString::fromUtf8( e.what() );
108  }
109 }
110 
111 ItemModifyJob::ItemModifyJob( const Item &item, QObject * parent )
112  : Job( new ItemModifyJobPrivate( this ), parent )
113 {
114  Q_D( ItemModifyJob );
115 
116  d->mItems.append( item );
117  d->mParts = item.loadedPayloadParts();
118 
119  d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
120  d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
121 }
122 
123 ItemModifyJob::ItemModifyJob( const Akonadi::Item::List &items, QObject *parent)
124  : Job( new ItemModifyJobPrivate( this ), parent )
125 {
126  Q_ASSERT( !items.isEmpty() );
127  Q_D( ItemModifyJob );
128  d->mItems = items;
129 
130  // same as single item ctor
131  if ( d->mItems.size() == 1 ) {
132  d->mParts = items.first().loadedPayloadParts();
133  d->mOperations.insert( ItemModifyJobPrivate::RemoteId );
134  d->mOperations.insert( ItemModifyJobPrivate::RemoteRevision );
135  } else {
136  d->mIgnorePayload = true;
137  d->mRevCheck = false;
138  }
139 }
140 
141 ItemModifyJob::~ItemModifyJob()
142 {
143 }
144 
145 QByteArray ItemModifyJobPrivate::fullCommand() const
146 {
147  const Akonadi::Item item = mItems.first();
148  QList<QByteArray> changes;
149  foreach ( int op, mOperations ) {
150  switch ( op ) {
151  case ItemModifyJobPrivate::RemoteId:
152  if ( !item.remoteId().isNull() ) {
153  changes << "REMOTEID";
154  changes << ImapParser::quote( item.remoteId().toUtf8() );
155  }
156  break;
157  case ItemModifyJobPrivate::Gid: {
158  const QString gid = GidExtractor::getGid( item );
159  if ( !gid.isNull() ) {
160  changes << "GID";
161  changes << ImapParser::quote( gid.toUtf8() );
162  }
163  break;
164  }
165  case ItemModifyJobPrivate::RemoteRevision:
166  if ( !item.remoteRevision().isNull() ) {
167  changes << "REMOTEREVISION";
168  changes << ImapParser::quote( item.remoteRevision().toUtf8() );
169  }
170  break;
171  case ItemModifyJobPrivate::Dirty:
172  changes << "DIRTY";
173  changes << "false";
174  break;
175  }
176  }
177 
178  if ( item.d_func()->mClearPayload )
179  changes << "INVALIDATECACHE";
180 
181  if ( item.d_func()->mFlagsOverwritten ) {
182  changes << "FLAGS";
183  changes << '(' + ImapParser::join( item.flags(), " " ) + ')';
184  } else {
185  if ( !item.d_func()->mAddedFlags.isEmpty() ) {
186  changes << "+FLAGS";
187  changes << '(' + ImapParser::join( item.d_func()->mAddedFlags, " " ) + ')';
188  }
189  if ( !item.d_func()->mDeletedFlags.isEmpty() ) {
190  changes << "-FLAGS";
191  changes << '(' + ImapParser::join( item.d_func()->mDeletedFlags, " " ) + ')';
192  }
193  }
194 
195  if ( !item.d_func()->mDeletedAttributes.isEmpty() ) {
196  changes << "-PARTS";
197  QList<QByteArray> attrs;
198  foreach ( const QByteArray &attr, item.d_func()->mDeletedAttributes )
199  attrs << ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr );
200  changes << '(' + ImapParser::join( attrs, " " ) + ')';
201  }
202 
203  // nothing to do
204  if ( changes.isEmpty() && mParts.isEmpty() && item.attributes().isEmpty() ) {
205  return QByteArray();
206  }
207 
208  QByteArray command;
209  command += ProtocolHelper::entitySetToByteArray( mItems, "STORE" ); // can throw an exception
210  command += ' ';
211  if ( !mRevCheck || item.revision() < 0 ) {
212  command += "NOREV ";
213  } else {
214  command += "REV " + QByteArray::number( item.revision() ) + ' ';
215  }
216 
217  if ( item.d_func()->mSizeChanged )
218  command += "SIZE " + QByteArray::number( item.size() );
219 
220  command += " (" + ImapParser::join( changes, " " );
221  const QByteArray attrs = ProtocolHelper::attributesToByteArray( item, true );
222  if ( !attrs.isEmpty() )
223  command += ' ' + attrs;
224  return command;
225 }
226 
227 void ItemModifyJob::doStart()
228 {
229  Q_D( ItemModifyJob );
230 
231  QByteArray command;
232  try {
233  command = d->fullCommand();
234  } catch ( const Exception &e ) {
235  setError( Job::Unknown );
236  setErrorText( QString::fromUtf8( e.what() ) );
237  emitResult();
238  return;
239  }
240  if ( command.isEmpty() ) {
241  emitResult();
242  return;
243  }
244 
245  d->mTag = d->newTag();
246  command.prepend( d->mTag );
247 
248  command += d->nextPartHeader();
249 
250  d->writeData( command );
251  d->newTag(); // hack to circumvent automatic response handling
252 }
253 
254 void ItemModifyJob::doHandleResponse(const QByteArray &_tag, const QByteArray & data)
255 {
256  Q_D( ItemModifyJob );
257 
258  if ( _tag == "+" ) { // ready for literal data
259  d->writeData( d->mPendingData );
260  d->writeData( d->nextPartHeader() );
261  return;
262  }
263 
264  if ( _tag == d->mTag ) {
265  if ( data.startsWith( "OK" ) ) { //krazy:exclude=strings
266  QDateTime modificationDateTime;
267  int dateTimePos = data.indexOf( "DATETIME" );
268  if ( dateTimePos != -1 ) {
269  int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 );
270  if ( resultPos == (dateTimePos + 8) ) {
271  kDebug() << "Invalid DATETIME response to STORE command: " << _tag << data;
272  }
273  }
274 
275  Item &item = d->mItems.first();
276  item.setModificationTime( modificationDateTime );
277  item.d_ptr->resetChangeLog();
278  } else {
279  setError( Unknown );
280  setErrorText( QString::fromUtf8( data ) );
281 
282  if ( data.contains( "[LLCONFLICT]" ) ) {
283  if ( d->mAutomaticConflictHandlingEnabled ) {
284  ConflictHandler *handler = new ConflictHandler( ConflictHandler::LocalLocalConflict, this );
285  handler->setConflictingItems( d->mItems.first(), d->mItems.first() );
286  connect( handler, SIGNAL(conflictResolved()), SLOT(conflictResolved()) );
287  connect( handler, SIGNAL(error(QString)), SLOT(conflictResolveError(QString)) );
288 
289  QMetaObject::invokeMethod( handler, "start", Qt::QueuedConnection );
290  return;
291  }
292  }
293  }
294 
295  foreach ( const Item &item, d->mItems ) {
296  ChangeMediator::invalidateItem(item);
297  }
298 
299  emitResult();
300  return;
301  }
302 
303  if ( _tag == "*" ) {
304  Akonadi::Item::Id id;
305  ImapParser::parseNumber( data, id );
306  int pos = data.indexOf( '(' );
307  if ( pos <= 0 || id <= 0 ) {
308  kDebug() << "Ignoring strange response: " << _tag << data;
309  return;
310  }
311  Item::List::iterator it = std::find_if( d->mItems.begin(), d->mItems.end(), boost::bind( &Item::id, _1 ) == id );
312  if ( it == d->mItems.end() ) {
313  kDebug() << "Received STORE response for an item we did not modify: " << _tag << data;
314  return;
315  }
316  QList<QByteArray> attrs;
317  ImapParser::parseParenthesizedList( data, attrs, pos );
318  for ( int i = 0; i < attrs.size() - 1; i += 2 ) {
319  const QByteArray key = attrs.at( i );
320  if ( key == "REV" ) {
321  const int newRev = attrs.at( i + 1 ).toInt();
322  const int oldRev = (*it).revision();
323  if ( newRev < oldRev || newRev < 0 )
324  continue;
325  d->itemRevisionChanged( (*it).id(), oldRev, newRev );
326  (*it).setRevision( newRev );
327  }
328  }
329  return;
330  }
331 
332  kDebug() << "Unhandled response: " << _tag << data;
333 }
334 
335 void ItemModifyJob::setIgnorePayload( bool ignore )
336 {
337  Q_D( ItemModifyJob );
338 
339  if ( d->mIgnorePayload == ignore )
340  return;
341 
342  d->mIgnorePayload = ignore;
343  if ( d->mIgnorePayload )
344  d->mParts = QSet<QByteArray>();
345  else {
346  Q_ASSERT( !d->mItems.first().mimeType().isEmpty() );
347  d->mParts = d->mItems.first().loadedPayloadParts();
348  }
349 }
350 
351 bool ItemModifyJob::ignorePayload() const
352 {
353  Q_D( const ItemModifyJob );
354 
355  return d->mIgnorePayload;
356 }
357 
358 void ItemModifyJob::setUpdateGid( bool update )
359 {
360  Q_D( ItemModifyJob );
361  if ( update && !updateGid() ) {
362  d->mOperations.insert( ItemModifyJobPrivate::Gid );
363  } else {
364  d->mOperations.remove( ItemModifyJobPrivate::Gid );
365  }
366 }
367 
368 bool ItemModifyJob::updateGid() const
369 {
370  Q_D( const ItemModifyJob );
371  return d->mOperations.contains( ItemModifyJobPrivate::Gid );
372 }
373 
374 void ItemModifyJob::disableRevisionCheck()
375 {
376  Q_D( ItemModifyJob );
377 
378  d->mRevCheck = false;
379 }
380 
381 void ItemModifyJob::disableAutomaticConflictHandling()
382 {
383  Q_D( ItemModifyJob );
384 
385  d->mAutomaticConflictHandlingEnabled = false;
386 }
387 
388 Item ItemModifyJob::item() const
389 {
390  Q_D( const ItemModifyJob );
391  Q_ASSERT( d->mItems.size() == 1 );
392 
393  return d->mItems.first();
394 }
395 
396 Item::List ItemModifyJob::items() const
397 {
398  Q_D( const ItemModifyJob );
399  return d->mItems;
400 }
401 
402 #include "moc_itemmodifyjob.cpp"
Akonadi::ItemModifyJob::~ItemModifyJob
virtual ~ItemModifyJob()
Destroys the item modify job.
Definition: itemmodifyjob.cpp:141
Akonadi::ItemModifyJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition: itemmodifyjob.cpp:227
Akonadi::ItemModifyJob::disableRevisionCheck
void disableRevisionCheck()
Disables the check of the revision number.
Definition: itemmodifyjob.cpp:374
Akonadi::ItemModifyJob::updateGid
bool updateGid() const
Returns wheter the GID should be updated.
Definition: itemmodifyjob.cpp:368
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:358
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:122
Akonadi::ItemModifyJob::item
Item item() const
Returns the modified and stored item including the changed revision number.
Definition: itemmodifyjob.cpp:388
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:396
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:216
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:335
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:351
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:381
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:254
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:111
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 Tue Oct 14 2014 23:00:27 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



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