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

akonadi

protocolhelper.cpp
00001 /*
00002     Copyright (c) 2008 Volker Krause <vkrause@kde.org>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
00018 */
00019 
00020 #include "protocolhelper_p.h"
00021 
00022 #include "attributefactory.h"
00023 #include "collectionstatistics.h"
00024 #include "entity_p.h"
00025 #include "exception.h"
00026 #include "itemserializer_p.h"
00027 #include "itemserializerplugin.h"
00028 
00029 #include <QtCore/QDateTime>
00030 #include <QtCore/QFile>
00031 #include <QtCore/QVarLengthArray>
00032 
00033 #include <kdebug.h>
00034 #include <klocale.h>
00035 
00036 using namespace Akonadi;
00037 
00038 int ProtocolHelper::parseCachePolicy(const QByteArray & data, CachePolicy & policy, int start)
00039 {
00040   QVarLengthArray<QByteArray,16> params;
00041   int end = Akonadi::ImapParser::parseParenthesizedList( data, params, start );
00042   for ( int i = 0; i < params.count() - 1; i += 2 ) {
00043     const QByteArray key = params[i];
00044     const QByteArray value = params[i + 1];
00045 
00046     if ( key == "INHERIT" )
00047       policy.setInheritFromParent( value == "true" );
00048     else if ( key == "INTERVAL" )
00049       policy.setIntervalCheckTime( value.toInt() );
00050     else if ( key == "CACHETIMEOUT" )
00051       policy.setCacheTimeout( value.toInt() );
00052     else if ( key == "SYNCONDEMAND" )
00053       policy.setSyncOnDemand( value == "true" );
00054     else if ( key == "LOCALPARTS" ) {
00055       QVarLengthArray<QByteArray,16> tmp;
00056       QStringList parts;
00057       Akonadi::ImapParser::parseParenthesizedList( value, tmp );
00058       for ( int j=0; j<tmp.size(); j++ )
00059         parts << QString::fromLatin1( tmp[j] );
00060       policy.setLocalParts( parts );
00061     }
00062   }
00063   return end;
00064 }
00065 
00066 QByteArray ProtocolHelper::cachePolicyToByteArray(const CachePolicy & policy)
00067 {
00068   QByteArray rv = "CACHEPOLICY (";
00069   if ( policy.inheritFromParent() ) {
00070     rv += "INHERIT true";
00071   } else {
00072     rv += "INHERIT false";
00073     rv += " INTERVAL " + QByteArray::number( policy.intervalCheckTime() );
00074     rv += " CACHETIMEOUT " + QByteArray::number( policy.cacheTimeout() );
00075     rv += " SYNCONDEMAND " + ( policy.syncOnDemand() ? QByteArray("true") : QByteArray("false") );
00076     rv += " LOCALPARTS (" + policy.localParts().join( QLatin1String(" ") ).toLatin1() + ')';
00077   }
00078   rv += ')';
00079   return rv;
00080 }
00081 
00082 void ProtocolHelper::parseAncestorsCached( const QByteArray &data, Entity *entity, Collection::Id parentCollection,
00083                                            ProtocolHelperValuePool *pool, int start )
00084 {
00085   if ( !pool || parentCollection == -1 ) {
00086     // if no pool or parent collection id is provided we can't cache anything, so continue as usual
00087     parseAncestors( data, entity, start );
00088     return;
00089   }
00090 
00091   if ( pool->ancestorCollections.contains( parentCollection ) ) {
00092     // ancestor chain is cached already, so use the cached value
00093     entity->setParentCollection( pool->ancestorCollections.value( parentCollection ) );
00094   } else {
00095     // not cached yet, parse the chain
00096     parseAncestors( data, entity, start );
00097     pool->ancestorCollections.insert( parentCollection, entity->parentCollection() );
00098   }
00099 }
00100 
00101 void ProtocolHelper::parseAncestors( const QByteArray &data, Entity *entity, int start )
00102 {
00103   Q_UNUSED( start );
00104 
00105   static const Collection::Id rootCollectionId = Collection::root().id();
00106   QVarLengthArray<QByteArray, 16> ancestors;
00107   QVarLengthArray<QByteArray, 16> parentIds;
00108 
00109   ImapParser::parseParenthesizedList( data, ancestors );
00110   Entity* current = entity;
00111   for ( int i = 0; i < ancestors.count(); ++i ) {
00112     parentIds.clear();
00113     ImapParser::parseParenthesizedList( ancestors[ i ], parentIds );
00114     if ( parentIds.size() != 2 )
00115       break;
00116 
00117     const Collection::Id uid = parentIds[ 0 ].toLongLong();
00118     if ( uid == rootCollectionId ) {
00119       current->setParentCollection( Collection::root() );
00120       break;
00121     }
00122 
00123     current->parentCollection().setId( uid );
00124     current->parentCollection().setRemoteId( QString::fromUtf8( parentIds[ 1 ] ) );
00125     current = &current->parentCollection();
00126   }
00127 }
00128 
00129 int ProtocolHelper::parseCollection(const QByteArray & data, Collection & collection, int start)
00130 {
00131   int pos = start;
00132 
00133   // collection and parent id
00134   Collection::Id colId = -1;
00135   bool ok = false;
00136   pos = ImapParser::parseNumber( data, colId, &ok, pos );
00137   if ( !ok || colId <= 0 ) {
00138     kDebug() << "Could not parse collection id from response:" << data;
00139     return start;
00140   }
00141 
00142   Collection::Id parentId = -1;
00143   pos = ImapParser::parseNumber( data, parentId, &ok, pos );
00144   if ( !ok || parentId < 0 ) {
00145     kDebug() << "Could not parse parent id from response:" << data;
00146     return start;
00147   }
00148 
00149   collection = Collection( colId );
00150   collection.setParentCollection( Collection( parentId ) );
00151 
00152   // attributes
00153   QVarLengthArray<QByteArray,16> attributes;
00154   pos = ImapParser::parseParenthesizedList( data, attributes, pos );
00155 
00156   for ( int i = 0; i < attributes.count() - 1; i += 2 ) {
00157     const QByteArray key = attributes[i];
00158     const QByteArray value = attributes[i + 1];
00159 
00160     if ( key == "NAME" ) {
00161       collection.setName( QString::fromUtf8( value ) );
00162     } else if ( key == "REMOTEID" ) {
00163       collection.setRemoteId( QString::fromUtf8( value ) );
00164     } else if ( key == "REMOTEREVISION" ) {
00165       collection.setRemoteRevision( QString::fromUtf8( value ) );
00166     } else if ( key == "RESOURCE" ) {
00167       collection.setResource( QString::fromUtf8( value ) );
00168     } else if ( key == "MIMETYPE" ) {
00169       QVarLengthArray<QByteArray,16> ct;
00170       ImapParser::parseParenthesizedList( value, ct );
00171       QStringList ct2;
00172       for ( int j = 0; j < ct.size(); j++ )
00173         ct2 << QString::fromLatin1( ct[j] );
00174       collection.setContentMimeTypes( ct2 );
00175     } else if ( key == "MESSAGES" ) {
00176       CollectionStatistics s = collection.statistics();
00177       s.setCount( value.toLongLong() );
00178       collection.setStatistics( s );
00179     } else if ( key == "UNSEEN" ) {
00180       CollectionStatistics s = collection.statistics();
00181       s.setUnreadCount( value.toLongLong() );
00182       collection.setStatistics( s );
00183     } else if ( key == "SIZE" ) {
00184       CollectionStatistics s = collection.statistics();
00185       s.setSize( value.toLongLong() );
00186       collection.setStatistics( s );
00187     } else if ( key == "CACHEPOLICY" ) {
00188       CachePolicy policy;
00189       ProtocolHelper::parseCachePolicy( value, policy );
00190       collection.setCachePolicy( policy );
00191     } else if ( key == "ANCESTORS" ) {
00192       parseAncestors( value, &collection );
00193     } else {
00194       Attribute* attr = AttributeFactory::createAttribute( key );
00195       Q_ASSERT( attr );
00196       attr->deserialize( value );
00197       collection.addAttribute( attr );
00198     }
00199   }
00200 
00201   return pos;
00202 }
00203 
00204 QByteArray ProtocolHelper::attributesToByteArray(const Entity & entity, bool ns )
00205 {
00206   QList<QByteArray> l;
00207   foreach ( const Attribute *attr, entity.attributes() ) {
00208     l << encodePartIdentifier( ns ? PartAttribute : PartGlobal, attr->type() );
00209     l << ImapParser::quote( attr->serialized() );
00210   }
00211   return ImapParser::join( l, " " );
00212 }
00213 
00214 QByteArray ProtocolHelper::encodePartIdentifier(PartNamespace ns, const QByteArray & label, int version )
00215 {
00216   const QByteArray versionString( version != 0 ? '[' + QByteArray::number( version ) + ']' : "" );
00217   switch ( ns ) {
00218     case PartGlobal:
00219       return label + versionString;
00220     case PartPayload:
00221       return "PLD:" + label + versionString;
00222     case PartAttribute:
00223       return "ATR:" + label + versionString;
00224     default:
00225       Q_ASSERT( false );
00226   }
00227   return QByteArray();
00228 }
00229 
00230 QByteArray ProtocolHelper::decodePartIdentifier( const QByteArray &data, PartNamespace & ns )
00231 {
00232   if ( data.startsWith( "PLD:" ) ) { //krazy:exclude=strings
00233     ns = PartPayload;
00234     return data.mid( 4 );
00235   } else if ( data.startsWith( "ATR:" ) ) { //krazy:exclude=strings
00236     ns = PartAttribute;
00237     return data.mid( 4 );
00238   } else {
00239     ns = PartGlobal;
00240     return data;
00241   }
00242 }
00243 
00244 QByteArray ProtocolHelper::hierarchicalRidToByteArray( const Collection &col )
00245 {
00246   if ( col == Collection::root() )
00247     return QByteArray("(0 \"\")");
00248   if ( col.remoteId().isEmpty() )
00249     return QByteArray();
00250   const QByteArray parentHrid = hierarchicalRidToByteArray( col.parentCollection() );
00251   return '(' + QByteArray::number( col.id() ) + ' ' + ImapParser::quote( col.remoteId().toUtf8() ) + ") " + parentHrid;
00252 }
00253 
00254 QByteArray ProtocolHelper::hierarchicalRidToByteArray( const Item &item )
00255 {
00256   const QByteArray parentHrid = hierarchicalRidToByteArray( item.parentCollection() );
00257   return '(' + QByteArray::number( item.id() ) + ' ' + ImapParser::quote( item.remoteId().toUtf8() ) + ") " + parentHrid;
00258 }  
00259 
00260 QByteArray ProtocolHelper::itemFetchScopeToByteArray( const ItemFetchScope &fetchScope )
00261 {
00262   QByteArray command;
00263 
00264   if ( fetchScope.fullPayload() )
00265     command += " " AKONADI_PARAM_FULLPAYLOAD;
00266   if ( fetchScope.allAttributes() )
00267     command += " " AKONADI_PARAM_ALLATTRIBUTES;
00268   if ( fetchScope.cacheOnly() )
00269     command += " " AKONADI_PARAM_CACHEONLY;
00270   if ( fetchScope.ancestorRetrieval() != ItemFetchScope::None ) {
00271     switch ( fetchScope.ancestorRetrieval() ) {
00272       case ItemFetchScope::Parent:
00273         command += " ANCESTORS 1";
00274         break;
00275       case ItemFetchScope::All:
00276         command += " ANCESTORS INF";
00277         break;
00278       default:
00279         Q_ASSERT( false );
00280     }
00281   }
00282 
00283   //TODO: detect somehow if server supports external payload attribute
00284   command += " " AKONADI_PARAM_EXTERNALPAYLOAD;
00285 
00286   command += " (UID REMOTEID REMOTEREVISION COLLECTIONID FLAGS SIZE";
00287   if ( fetchScope.fetchModificationTime() )
00288     command += " DATETIME";
00289   foreach ( const QByteArray &part, fetchScope.payloadParts() )
00290     command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, part );
00291   foreach ( const QByteArray &part, fetchScope.attributes() )
00292     command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, part );
00293   command += ")\n";
00294 
00295   return command;
00296 }
00297 
00298 void ProtocolHelper::parseItemFetchResult( const QList<QByteArray> &lineTokens, Item &item, ProtocolHelperValuePool *valuePool )
00299 {
00300   // create a new item object
00301   Item::Id uid = -1;
00302   int rev = -1;
00303   QString rid;
00304   QString remoteRevision;
00305   QString mimeType;
00306   Entity::Id cid = -1;
00307 
00308   for ( int i = 0; i < lineTokens.count() - 1; i += 2 ) {
00309     const QByteArray key = lineTokens.value( i );
00310     const QByteArray value = lineTokens.value( i + 1 );
00311 
00312     if ( key == "UID" )
00313       uid = value.toLongLong();
00314     else if ( key == "REV" )
00315       rev = value.toInt();
00316     else if ( key == "REMOTEID" ) {
00317       if ( !value.isEmpty() )
00318         rid = QString::fromUtf8( value );
00319       else
00320         rid.clear();
00321     } else if ( key == "REMOTEREVISION" ) {
00322       remoteRevision = QString::fromUtf8( value );
00323     } else if ( key == "COLLECTIONID" ) {
00324       cid = value.toInt();
00325     } else if ( key == "MIMETYPE" ) {
00326       if ( valuePool )
00327         mimeType = valuePool->mimeTypePool.sharedValue( QString::fromLatin1( value ) );
00328       else
00329         mimeType = QString::fromLatin1( value );
00330     }
00331   }
00332 
00333   if ( uid < 0 || rev < 0 || mimeType.isEmpty() ) {
00334     kWarning() << "Broken fetch response: UID, RID, REV or MIMETYPE missing!";
00335     return;
00336   }
00337 
00338   item = Item( uid );
00339   item.setRemoteId( rid );
00340   item.setRevision( rev );
00341   item.setRemoteRevision( remoteRevision );
00342   item.setMimeType( mimeType );
00343   item.setStorageCollectionId( cid );
00344   if ( !item.isValid() )
00345     return;
00346 
00347   // parse fetch response fields
00348   for ( int i = 0; i < lineTokens.count() - 1; i += 2 ) {
00349     const QByteArray key = lineTokens.value( i );
00350     // skip stuff we dealt with already
00351     if ( key == "UID" || key == "REV" || key == "REMOTEID" ||
00352          key == "MIMETYPE"  || key == "COLLECTIONID" || key == "REMOTEREVISION" )
00353       continue;
00354     // flags
00355     if ( key == "FLAGS" ) {
00356       QList<QByteArray> flags;
00357       ImapParser::parseParenthesizedList( lineTokens[i + 1], flags );
00358       if ( !flags.isEmpty() ) {
00359         Item::Flags convertedFlags;
00360         convertedFlags.reserve( flags.size() );
00361         foreach ( const QByteArray &flag, flags ) {
00362           if ( valuePool )
00363             convertedFlags.insert( valuePool->flagPool.sharedValue( flag ) );
00364           else
00365             convertedFlags.insert( flag );
00366         }
00367         item.setFlags( convertedFlags );
00368       }
00369     } else if ( key == "SIZE" ) {
00370       const quint64 size = lineTokens[i + 1].toLongLong();
00371       item.setSize( size );
00372     } else if ( key == "DATETIME" ) {
00373       QDateTime datetime;
00374       ImapParser::parseDateTime( lineTokens[i + 1], datetime );
00375       item.setModificationTime( datetime );
00376     } else if ( key == "ANCESTORS" ) {
00377       ProtocolHelper::parseAncestorsCached( lineTokens[i + 1], &item, cid, valuePool );
00378     } else {
00379       int version = 0;
00380       QByteArray plainKey( key );
00381       ProtocolHelper::PartNamespace ns;
00382 
00383       ImapParser::splitVersionedKey( key, plainKey, version );
00384       plainKey = ProtocolHelper::decodePartIdentifier( plainKey, ns );
00385 
00386       switch ( ns ) {
00387         case ProtocolHelper::PartPayload:
00388         {
00389           bool isExternal = false;
00390           const QByteArray fileKey = lineTokens.value( i + 1 );
00391           if ( fileKey == "[FILE]" ) {
00392             isExternal = true;
00393             i++;
00394             //kDebug() << "Payload is external: " << isExternal << " filename: " << lineTokens.value( i + 1 );
00395           }
00396           ItemSerializer::deserialize( item, plainKey, lineTokens.value( i + 1 ), version, isExternal );
00397           break;
00398         }
00399         case ProtocolHelper::PartAttribute:
00400         {
00401           Attribute* attr = AttributeFactory::createAttribute( plainKey );
00402           Q_ASSERT( attr );
00403           if ( lineTokens.value( i + 1 ) == "[FILE]" ) {
00404             ++i;
00405             QFile file( QString::fromUtf8( lineTokens.value( i + 1 ) ) );
00406             if ( file.open( QFile::ReadOnly ) )
00407               attr->deserialize( file.readAll() );
00408             else {
00409               kWarning() << "Failed to open attribute file: " << lineTokens.value( i + 1 );
00410               delete attr;
00411               attr = 0;
00412             }
00413           } else {
00414             attr->deserialize( lineTokens.value( i + 1 ) );
00415           }
00416           if ( attr )
00417             item.addAttribute( attr );
00418           break;
00419         }
00420         case ProtocolHelper::PartGlobal:
00421         default:
00422           kWarning() << "Unknown item part type:" << key;
00423       }
00424     }
00425   }
00426 
00427   item.d_ptr->resetChangeLog();
00428 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue May 8 2012 00:00:45 by doxygen 1.8.0 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.8.3 API Reference

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