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

akonadi

  • akonadi
protocolhelper.cpp
1 /*
2  Copyright (c) 2008 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 "protocolhelper_p.h"
21 
22 #include "attributefactory.h"
23 #include "collectionstatistics.h"
24 #include "entity_p.h"
25 #include "exception.h"
26 #include "itemserializer_p.h"
27 #include "itemserializerplugin.h"
28 
29 #include <QtCore/QDateTime>
30 #include <QtCore/QFile>
31 #include <QtCore/QVarLengthArray>
32 
33 #include <kdebug.h>
34 #include <klocale.h>
35 
36 using namespace Akonadi;
37 
38 int ProtocolHelper::parseCachePolicy(const QByteArray & data, CachePolicy & policy, int start)
39 {
40  QVarLengthArray<QByteArray,16> params;
41  int end = Akonadi::ImapParser::parseParenthesizedList( data, params, start );
42  for ( int i = 0; i < params.count() - 1; i += 2 ) {
43  const QByteArray key = params[i];
44  const QByteArray value = params[i + 1];
45 
46  if ( key == "INHERIT" )
47  policy.setInheritFromParent( value == "true" );
48  else if ( key == "INTERVAL" )
49  policy.setIntervalCheckTime( value.toInt() );
50  else if ( key == "CACHETIMEOUT" )
51  policy.setCacheTimeout( value.toInt() );
52  else if ( key == "SYNCONDEMAND" )
53  policy.setSyncOnDemand( value == "true" );
54  else if ( key == "LOCALPARTS" ) {
55  QVarLengthArray<QByteArray,16> tmp;
56  QStringList parts;
57  Akonadi::ImapParser::parseParenthesizedList( value, tmp );
58  for ( int j=0; j<tmp.size(); j++ )
59  parts << QString::fromLatin1( tmp[j] );
60  policy.setLocalParts( parts );
61  }
62  }
63  return end;
64 }
65 
66 QByteArray ProtocolHelper::cachePolicyToByteArray(const CachePolicy & policy)
67 {
68  QByteArray rv = "CACHEPOLICY (";
69  if ( policy.inheritFromParent() ) {
70  rv += "INHERIT true";
71  } else {
72  rv += "INHERIT false";
73  rv += " INTERVAL " + QByteArray::number( policy.intervalCheckTime() );
74  rv += " CACHETIMEOUT " + QByteArray::number( policy.cacheTimeout() );
75  rv += " SYNCONDEMAND " + ( policy.syncOnDemand() ? QByteArray("true") : QByteArray("false") );
76  rv += " LOCALPARTS (" + policy.localParts().join( QLatin1String(" ") ).toLatin1() + ')';
77  }
78  rv += ')';
79  return rv;
80 }
81 
82 void ProtocolHelper::parseAncestorsCached( const QByteArray &data, Entity *entity, Collection::Id parentCollection,
83  ProtocolHelperValuePool *pool, int start )
84 {
85  if ( !pool || parentCollection == -1 ) {
86  // if no pool or parent collection id is provided we can't cache anything, so continue as usual
87  parseAncestors( data, entity, start );
88  return;
89  }
90 
91  if ( pool->ancestorCollections.contains( parentCollection ) ) {
92  // ancestor chain is cached already, so use the cached value
93  entity->setParentCollection( pool->ancestorCollections.value( parentCollection ) );
94  } else {
95  // not cached yet, parse the chain
96  parseAncestors( data, entity, start );
97  pool->ancestorCollections.insert( parentCollection, entity->parentCollection() );
98  }
99 }
100 
101 void ProtocolHelper::parseAncestors( const QByteArray &data, Entity *entity, int start )
102 {
103  Q_UNUSED( start );
104 
105  static const Collection::Id rootCollectionId = Collection::root().id();
106  QVarLengthArray<QByteArray, 16> ancestors;
107  QVarLengthArray<QByteArray, 16> parentIds;
108 
109  ImapParser::parseParenthesizedList( data, ancestors );
110  Entity* current = entity;
111  for ( int i = 0; i < ancestors.count(); ++i ) {
112  parentIds.clear();
113  ImapParser::parseParenthesizedList( ancestors[ i ], parentIds );
114  if ( parentIds.size() != 2 )
115  break;
116 
117  const Collection::Id uid = parentIds[ 0 ].toLongLong();
118  if ( uid == rootCollectionId ) {
119  current->setParentCollection( Collection::root() );
120  break;
121  }
122 
123  current->parentCollection().setId( uid );
124  current->parentCollection().setRemoteId( QString::fromUtf8( parentIds[ 1 ] ) );
125  current = &current->parentCollection();
126  }
127 }
128 
129 int ProtocolHelper::parseCollection(const QByteArray & data, Collection & collection, int start)
130 {
131  int pos = start;
132 
133  // collection and parent id
134  Collection::Id colId = -1;
135  bool ok = false;
136  pos = ImapParser::parseNumber( data, colId, &ok, pos );
137  if ( !ok || colId <= 0 ) {
138  kDebug() << "Could not parse collection id from response:" << data;
139  return start;
140  }
141 
142  Collection::Id parentId = -1;
143  pos = ImapParser::parseNumber( data, parentId, &ok, pos );
144  if ( !ok || parentId < 0 ) {
145  kDebug() << "Could not parse parent id from response:" << data;
146  return start;
147  }
148 
149  collection = Collection( colId );
150  collection.setParentCollection( Collection( parentId ) );
151 
152  // attributes
153  QVarLengthArray<QByteArray,16> attributes;
154  pos = ImapParser::parseParenthesizedList( data, attributes, pos );
155 
156  for ( int i = 0; i < attributes.count() - 1; i += 2 ) {
157  const QByteArray key = attributes[i];
158  const QByteArray value = attributes[i + 1];
159 
160  if ( key == "NAME" ) {
161  collection.setName( QString::fromUtf8( value ) );
162  } else if ( key == "REMOTEID" ) {
163  collection.setRemoteId( QString::fromUtf8( value ) );
164  } else if ( key == "REMOTEREVISION" ) {
165  collection.setRemoteRevision( QString::fromUtf8( value ) );
166  } else if ( key == "RESOURCE" ) {
167  collection.setResource( QString::fromUtf8( value ) );
168  } else if ( key == "MIMETYPE" ) {
169  QVarLengthArray<QByteArray,16> ct;
170  ImapParser::parseParenthesizedList( value, ct );
171  QStringList ct2;
172  for ( int j = 0; j < ct.size(); j++ )
173  ct2 << QString::fromLatin1( ct[j] );
174  collection.setContentMimeTypes( ct2 );
175  } else if ( key == "VIRTUAL" ) {
176  collection.setVirtual( value.toUInt() != 0 );
177  } else if ( key == "MESSAGES" ) {
178  CollectionStatistics s = collection.statistics();
179  s.setCount( value.toLongLong() );
180  collection.setStatistics( s );
181  } else if ( key == "UNSEEN" ) {
182  CollectionStatistics s = collection.statistics();
183  s.setUnreadCount( value.toLongLong() );
184  collection.setStatistics( s );
185  } else if ( key == "SIZE" ) {
186  CollectionStatistics s = collection.statistics();
187  s.setSize( value.toLongLong() );
188  collection.setStatistics( s );
189  } else if ( key == "CACHEPOLICY" ) {
190  CachePolicy policy;
191  ProtocolHelper::parseCachePolicy( value, policy );
192  collection.setCachePolicy( policy );
193  } else if ( key == "ANCESTORS" ) {
194  parseAncestors( value, &collection );
195  } else {
196  Attribute* attr = AttributeFactory::createAttribute( key );
197  Q_ASSERT( attr );
198  attr->deserialize( value );
199  collection.addAttribute( attr );
200  }
201  }
202 
203  return pos;
204 }
205 
206 QByteArray ProtocolHelper::attributesToByteArray(const Entity & entity, bool ns )
207 {
208  QList<QByteArray> l;
209  foreach ( const Attribute *attr, entity.attributes() ) {
210  l << encodePartIdentifier( ns ? PartAttribute : PartGlobal, attr->type() );
211  l << ImapParser::quote( attr->serialized() );
212  }
213  return ImapParser::join( l, " " );
214 }
215 
216 QByteArray ProtocolHelper::encodePartIdentifier(PartNamespace ns, const QByteArray & label, int version )
217 {
218  const QByteArray versionString( version != 0 ? '[' + QByteArray::number( version ) + ']' : "" );
219  switch ( ns ) {
220  case PartGlobal:
221  return label + versionString;
222  case PartPayload:
223  return "PLD:" + label + versionString;
224  case PartAttribute:
225  return "ATR:" + label + versionString;
226  default:
227  Q_ASSERT( false );
228  }
229  return QByteArray();
230 }
231 
232 QByteArray ProtocolHelper::decodePartIdentifier( const QByteArray &data, PartNamespace & ns )
233 {
234  if ( data.startsWith( "PLD:" ) ) { //krazy:exclude=strings
235  ns = PartPayload;
236  return data.mid( 4 );
237  } else if ( data.startsWith( "ATR:" ) ) { //krazy:exclude=strings
238  ns = PartAttribute;
239  return data.mid( 4 );
240  } else {
241  ns = PartGlobal;
242  return data;
243  }
244 }
245 
246 QByteArray ProtocolHelper::hierarchicalRidToByteArray( const Collection &col )
247 {
248  if ( col == Collection::root() )
249  return QByteArray("(0 \"\")");
250  if ( col.remoteId().isEmpty() )
251  return QByteArray();
252  const QByteArray parentHrid = hierarchicalRidToByteArray( col.parentCollection() );
253  return '(' + QByteArray::number( col.id() ) + ' ' + ImapParser::quote( col.remoteId().toUtf8() ) + ") " + parentHrid;
254 }
255 
256 QByteArray ProtocolHelper::hierarchicalRidToByteArray( const Item &item )
257 {
258  const QByteArray parentHrid = hierarchicalRidToByteArray( item.parentCollection() );
259  return '(' + QByteArray::number( item.id() ) + ' ' + ImapParser::quote( item.remoteId().toUtf8() ) + ") " + parentHrid;
260 }
261 
262 QByteArray ProtocolHelper::itemFetchScopeToByteArray( const ItemFetchScope &fetchScope )
263 {
264  QByteArray command;
265 
266  if ( fetchScope.fullPayload() )
267  command += " " AKONADI_PARAM_FULLPAYLOAD;
268  if ( fetchScope.allAttributes() )
269  command += " " AKONADI_PARAM_ALLATTRIBUTES;
270  if ( fetchScope.cacheOnly() )
271  command += " " AKONADI_PARAM_CACHEONLY;
272  if ( fetchScope.ignoreRetrievalErrors() )
273  command += " " "IGNOREERRORS";
274  if ( fetchScope.ancestorRetrieval() != ItemFetchScope::None ) {
275  switch ( fetchScope.ancestorRetrieval() ) {
276  case ItemFetchScope::Parent:
277  command += " ANCESTORS 1";
278  break;
279  case ItemFetchScope::All:
280  command += " ANCESTORS INF";
281  break;
282  default:
283  Q_ASSERT( false );
284  }
285  }
286 
287  //TODO: detect somehow if server supports external payload attribute
288  command += " " AKONADI_PARAM_EXTERNALPAYLOAD;
289 
290  command += " (UID REMOTEID REMOTEREVISION COLLECTIONID FLAGS SIZE";
291  if ( fetchScope.fetchModificationTime() )
292  command += " DATETIME";
293  foreach ( const QByteArray &part, fetchScope.payloadParts() )
294  command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part );
295  foreach ( const QByteArray &part, fetchScope.attributes() )
296  command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part );
297  command += ")\n";
298 
299  return command;
300 }
301 
302 void ProtocolHelper::parseItemFetchResult( const QList<QByteArray> &lineTokens, Item &item, ProtocolHelperValuePool *valuePool )
303 {
304  // create a new item object
305  Item::Id uid = -1;
306  int rev = -1;
307  QString rid;
308  QString remoteRevision;
309  QString mimeType;
310  Entity::Id cid = -1;
311 
312  for ( int i = 0; i < lineTokens.count() - 1; i += 2 ) {
313  const QByteArray key = lineTokens.value( i );
314  const QByteArray value = lineTokens.value( i + 1 );
315 
316  if ( key == "UID" )
317  uid = value.toLongLong();
318  else if ( key == "REV" )
319  rev = value.toInt();
320  else if ( key == "REMOTEID" ) {
321  if ( !value.isEmpty() )
322  rid = QString::fromUtf8( value );
323  else
324  rid.clear();
325  } else if ( key == "REMOTEREVISION" ) {
326  remoteRevision = QString::fromUtf8( value );
327  } else if ( key == "COLLECTIONID" ) {
328  cid = value.toInt();
329  } else if ( key == "MIMETYPE" ) {
330  if ( valuePool )
331  mimeType = valuePool->mimeTypePool.sharedValue( QString::fromLatin1( value ) );
332  else
333  mimeType = QString::fromLatin1( value );
334  }
335  }
336 
337  if ( uid < 0 || rev < 0 || mimeType.isEmpty() ) {
338  kWarning() << "Broken fetch response: UID, RID, REV or MIMETYPE missing!";
339  return;
340  }
341 
342  item = Item( uid );
343  item.setRemoteId( rid );
344  item.setRevision( rev );
345  item.setRemoteRevision( remoteRevision );
346  item.setMimeType( mimeType );
347  item.setStorageCollectionId( cid );
348  if ( !item.isValid() )
349  return;
350 
351  // parse fetch response fields
352  for ( int i = 0; i < lineTokens.count() - 1; i += 2 ) {
353  const QByteArray key = lineTokens.value( i );
354  // skip stuff we dealt with already
355  if ( key == "UID" || key == "REV" || key == "REMOTEID" ||
356  key == "MIMETYPE" || key == "COLLECTIONID" || key == "REMOTEREVISION" )
357  continue;
358  // flags
359  if ( key == "FLAGS" ) {
360  QList<QByteArray> flags;
361  ImapParser::parseParenthesizedList( lineTokens[i + 1], flags );
362  if ( !flags.isEmpty() ) {
363  Item::Flags convertedFlags;
364  convertedFlags.reserve( flags.size() );
365  foreach ( const QByteArray &flag, flags ) {
366  if ( valuePool )
367  convertedFlags.insert( valuePool->flagPool.sharedValue( flag ) );
368  else
369  convertedFlags.insert( flag );
370  }
371  item.setFlags( convertedFlags );
372  }
373  } else if ( key == "SIZE" ) {
374  const quint64 size = lineTokens[i + 1].toLongLong();
375  item.setSize( size );
376  } else if ( key == "DATETIME" ) {
377  QDateTime datetime;
378  ImapParser::parseDateTime( lineTokens[i + 1], datetime );
379  item.setModificationTime( datetime );
380  } else if ( key == "ANCESTORS" ) {
381  ProtocolHelper::parseAncestorsCached( lineTokens[i + 1], &item, cid, valuePool );
382  } else {
383  int version = 0;
384  QByteArray plainKey( key );
385  ProtocolHelper::PartNamespace ns;
386 
387  ImapParser::splitVersionedKey( key, plainKey, version );
388  plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns );
389 
390  switch ( ns ) {
391  case ProtocolHelper::PartPayload:
392  {
393  bool isExternal = false;
394  const QByteArray fileKey = lineTokens.value( i + 1 );
395  if ( fileKey == "[FILE]" ) {
396  isExternal = true;
397  i++;
398  //kDebug() << "Payload is external: " << isExternal << " filename: " << lineTokens.value( i + 1 );
399  }
400  ItemSerializer::deserialize( item, plainKey, lineTokens.value( i + 1 ), version, isExternal );
401  break;
402  }
403  case ProtocolHelper::PartAttribute:
404  {
405  Attribute* attr = AttributeFactory::createAttribute( plainKey );
406  Q_ASSERT( attr );
407  if ( lineTokens.value( i + 1 ) == "[FILE]" ) {
408  ++i;
409  QFile file( QString::fromUtf8( lineTokens.value( i + 1 ) ) );
410  if ( file.open( QFile::ReadOnly ) )
411  attr->deserialize( file.readAll() );
412  else {
413  kWarning() << "Failed to open attribute file: " << lineTokens.value( i + 1 );
414  delete attr;
415  attr = 0;
416  }
417  } else {
418  attr->deserialize( lineTokens.value( i + 1 ) );
419  }
420  if ( attr )
421  item.addAttribute( attr );
422  break;
423  }
424  case ProtocolHelper::PartGlobal:
425  default:
426  kWarning() << "Unknown item part type:" << key;
427  }
428  }
429  }
430 
431  item.d_ptr->resetChangeLog();
432 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:27:40 by doxygen 1.8.3.1 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.10.5 API Reference

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