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

Skip menu "kdepimlibs-4.11.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