• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.9.3 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 == "MESSAGES" ) {
176  CollectionStatistics s = collection.statistics();
177  s.setCount( value.toLongLong() );
178  collection.setStatistics( s );
179  } else if ( key == "UNSEEN" ) {
180  CollectionStatistics s = collection.statistics();
181  s.setUnreadCount( value.toLongLong() );
182  collection.setStatistics( s );
183  } else if ( key == "SIZE" ) {
184  CollectionStatistics s = collection.statistics();
185  s.setSize( value.toLongLong() );
186  collection.setStatistics( s );
187  } else if ( key == "CACHEPOLICY" ) {
188  CachePolicy policy;
189  ProtocolHelper::parseCachePolicy( value, policy );
190  collection.setCachePolicy( policy );
191  } else if ( key == "ANCESTORS" ) {
192  parseAncestors( value, &collection );
193  } else {
194  Attribute* attr = AttributeFactory::createAttribute( key );
195  Q_ASSERT( attr );
196  attr->deserialize( value );
197  collection.addAttribute( attr );
198  }
199  }
200 
201  return pos;
202 }
203 
204 QByteArray ProtocolHelper::attributesToByteArray(const Entity & entity, bool ns )
205 {
206  QList<QByteArray> l;
207  foreach ( const Attribute *attr, entity.attributes() ) {
208  l << encodePartIdentifier( ns ? PartAttribute : PartGlobal, attr->type() );
209  l << ImapParser::quote( attr->serialized() );
210  }
211  return ImapParser::join( l, " " );
212 }
213 
214 QByteArray ProtocolHelper::encodePartIdentifier(PartNamespace ns, const QByteArray & label, int version )
215 {
216  const QByteArray versionString( version != 0 ? '[' + QByteArray::number( version ) + ']' : "" );
217  switch ( ns ) {
218  case PartGlobal:
219  return label + versionString;
220  case PartPayload:
221  return "PLD:" + label + versionString;
222  case PartAttribute:
223  return "ATR:" + label + versionString;
224  default:
225  Q_ASSERT( false );
226  }
227  return QByteArray();
228 }
229 
230 QByteArray ProtocolHelper::decodePartIdentifier( const QByteArray &data, PartNamespace & ns )
231 {
232  if ( data.startsWith( "PLD:" ) ) { //krazy:exclude=strings
233  ns = PartPayload;
234  return data.mid( 4 );
235  } else if ( data.startsWith( "ATR:" ) ) { //krazy:exclude=strings
236  ns = PartAttribute;
237  return data.mid( 4 );
238  } else {
239  ns = PartGlobal;
240  return data;
241  }
242 }
243 
244 QByteArray ProtocolHelper::hierarchicalRidToByteArray( const Collection &col )
245 {
246  if ( col == Collection::root() )
247  return QByteArray("(0 \"\")");
248  if ( col.remoteId().isEmpty() )
249  return QByteArray();
250  const QByteArray parentHrid = hierarchicalRidToByteArray( col.parentCollection() );
251  return '(' + QByteArray::number( col.id() ) + ' ' + ImapParser::quote( col.remoteId().toUtf8() ) + ") " + parentHrid;
252 }
253 
254 QByteArray ProtocolHelper::hierarchicalRidToByteArray( const Item &item )
255 {
256  const QByteArray parentHrid = hierarchicalRidToByteArray( item.parentCollection() );
257  return '(' + QByteArray::number( item.id() ) + ' ' + ImapParser::quote( item.remoteId().toUtf8() ) + ") " + parentHrid;
258 }
259 
260 QByteArray ProtocolHelper::itemFetchScopeToByteArray( const ItemFetchScope &fetchScope )
261 {
262  QByteArray command;
263 
264  if ( fetchScope.fullPayload() )
265  command += " " AKONADI_PARAM_FULLPAYLOAD;
266  if ( fetchScope.allAttributes() )
267  command += " " AKONADI_PARAM_ALLATTRIBUTES;
268  if ( fetchScope.cacheOnly() )
269  command += " " AKONADI_PARAM_CACHEONLY;
270  if ( fetchScope.ancestorRetrieval() != ItemFetchScope::None ) {
271  switch ( fetchScope.ancestorRetrieval() ) {
272  case ItemFetchScope::Parent:
273  command += " ANCESTORS 1";
274  break;
275  case ItemFetchScope::All:
276  command += " ANCESTORS INF";
277  break;
278  default:
279  Q_ASSERT( false );
280  }
281  }
282 
283  //TODO: detect somehow if server supports external payload attribute
284  command += " " AKONADI_PARAM_EXTERNALPAYLOAD;
285 
286  command += " (UID REMOTEID REMOTEREVISION COLLECTIONID FLAGS SIZE";
287  if ( fetchScope.fetchModificationTime() )
288  command += " DATETIME";
289  foreach ( const QByteArray &part, fetchScope.payloadParts() )
290  command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part );
291  foreach ( const QByteArray &part, fetchScope.attributes() )
292  command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part );
293  command += ")\n";
294 
295  return command;
296 }
297 
298 void ProtocolHelper::parseItemFetchResult( const QList<QByteArray> &lineTokens, Item &item, ProtocolHelperValuePool *valuePool )
299 {
300  // create a new item object
301  Item::Id uid = -1;
302  int rev = -1;
303  QString rid;
304  QString remoteRevision;
305  QString mimeType;
306  Entity::Id cid = -1;
307 
308  for ( int i = 0; i < lineTokens.count() - 1; i += 2 ) {
309  const QByteArray key = lineTokens.value( i );
310  const QByteArray value = lineTokens.value( i + 1 );
311 
312  if ( key == "UID" )
313  uid = value.toLongLong();
314  else if ( key == "REV" )
315  rev = value.toInt();
316  else if ( key == "REMOTEID" ) {
317  if ( !value.isEmpty() )
318  rid = QString::fromUtf8( value );
319  else
320  rid.clear();
321  } else if ( key == "REMOTEREVISION" ) {
322  remoteRevision = QString::fromUtf8( value );
323  } else if ( key == "COLLECTIONID" ) {
324  cid = value.toInt();
325  } else if ( key == "MIMETYPE" ) {
326  if ( valuePool )
327  mimeType = valuePool->mimeTypePool.sharedValue( QString::fromLatin1( value ) );
328  else
329  mimeType = QString::fromLatin1( value );
330  }
331  }
332 
333  if ( uid < 0 || rev < 0 || mimeType.isEmpty() ) {
334  kWarning() << "Broken fetch response: UID, RID, REV or MIMETYPE missing!";
335  return;
336  }
337 
338  item = Item( uid );
339  item.setRemoteId( rid );
340  item.setRevision( rev );
341  item.setRemoteRevision( remoteRevision );
342  item.setMimeType( mimeType );
343  item.setStorageCollectionId( cid );
344  if ( !item.isValid() )
345  return;
346 
347  // parse fetch response fields
348  for ( int i = 0; i < lineTokens.count() - 1; i += 2 ) {
349  const QByteArray key = lineTokens.value( i );
350  // skip stuff we dealt with already
351  if ( key == "UID" || key == "REV" || key == "REMOTEID" ||
352  key == "MIMETYPE" || key == "COLLECTIONID" || key == "REMOTEREVISION" )
353  continue;
354  // flags
355  if ( key == "FLAGS" ) {
356  QList<QByteArray> flags;
357  ImapParser::parseParenthesizedList( lineTokens[i + 1], flags );
358  if ( !flags.isEmpty() ) {
359  Item::Flags convertedFlags;
360  convertedFlags.reserve( flags.size() );
361  foreach ( const QByteArray &flag, flags ) {
362  if ( valuePool )
363  convertedFlags.insert( valuePool->flagPool.sharedValue( flag ) );
364  else
365  convertedFlags.insert( flag );
366  }
367  item.setFlags( convertedFlags );
368  }
369  } else if ( key == "SIZE" ) {
370  const quint64 size = lineTokens[i + 1].toLongLong();
371  item.setSize( size );
372  } else if ( key == "DATETIME" ) {
373  QDateTime datetime;
374  ImapParser::parseDateTime( lineTokens[i + 1], datetime );
375  item.setModificationTime( datetime );
376  } else if ( key == "ANCESTORS" ) {
377  ProtocolHelper::parseAncestorsCached( lineTokens[i + 1], &item, cid, valuePool );
378  } else {
379  int version = 0;
380  QByteArray plainKey( key );
381  ProtocolHelper::PartNamespace ns;
382 
383  ImapParser::splitVersionedKey( key, plainKey, version );
384  plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns );
385 
386  switch ( ns ) {
387  case ProtocolHelper::PartPayload:
388  {
389  bool isExternal = false;
390  const QByteArray fileKey = lineTokens.value( i + 1 );
391  if ( fileKey == "[FILE]" ) {
392  isExternal = true;
393  i++;
394  //kDebug() << "Payload is external: " << isExternal << " filename: " << lineTokens.value( i + 1 );
395  }
396  ItemSerializer::deserialize( item, plainKey, lineTokens.value( i + 1 ), version, isExternal );
397  break;
398  }
399  case ProtocolHelper::PartAttribute:
400  {
401  Attribute* attr = AttributeFactory::createAttribute( plainKey );
402  Q_ASSERT( attr );
403  if ( lineTokens.value( i + 1 ) == "[FILE]" ) {
404  ++i;
405  QFile file( QString::fromUtf8( lineTokens.value( i + 1 ) ) );
406  if ( file.open( QFile::ReadOnly ) )
407  attr->deserialize( file.readAll() );
408  else {
409  kWarning() << "Failed to open attribute file: " << lineTokens.value( i + 1 );
410  delete attr;
411  attr = 0;
412  }
413  } else {
414  attr->deserialize( lineTokens.value( i + 1 ) );
415  }
416  if ( attr )
417  item.addAttribute( attr );
418  break;
419  }
420  case ProtocolHelper::PartGlobal:
421  default:
422  kWarning() << "Unknown item part type:" << key;
423  }
424  }
425  }
426 
427  item.d_ptr->resetChangeLog();
428 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Nov 26 2012 16:48:20 by doxygen 1.8.1.2 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.9.3 API Reference

Skip menu "kdepimlibs-4.9.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • 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