• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

KIMAP Library

imapstreamparser.cpp
00001 /*
00002     Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
00003     Copyright (c) 2009 Andras Mantia <amantia@kde.org>
00004 
00005     Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
00006     Author: Kevin Ottens <kevin@kdab.com>
00007 
00008     This library is free software; you can redistribute it and/or modify it
00009     under the terms of the GNU Library General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or (at your
00011     option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful, but WITHOUT
00014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00016     License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to the
00020     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00021     02110-1301, USA.
00022 */
00023 
00024 #include "imapstreamparser.h"
00025 
00026 #include <ctype.h>
00027 #include <QIODevice>
00028 
00029 using namespace KIMAP;
00030 
00031 ImapStreamParser::ImapStreamParser( QIODevice *socket, bool serverModeEnabled )
00032 {
00033   m_socket = socket;
00034   m_isServerModeEnabled = serverModeEnabled;
00035   m_position = 0;
00036   m_literalSize = 0;
00037 }
00038 
00039 ImapStreamParser::~ImapStreamParser()
00040 {
00041 }
00042 
00043 QString ImapStreamParser::readUtf8String()
00044 {
00045   QByteArray tmp;
00046   tmp = readString();
00047   QString result = QString::fromUtf8( tmp );
00048   return result;
00049 }
00050 
00051 
00052 QByteArray ImapStreamParser::readString()
00053 {
00054   QByteArray result;
00055   if ( !waitForMoreData( m_data.length() == 0 ) )
00056     throw ImapParserException("Unable to read more data");
00057   stripLeadingSpaces();
00058   if ( !waitForMoreData( m_position >= m_data.length() ) )
00059     throw ImapParserException("Unable to read more data");
00060 
00061   // literal string
00062   // TODO: error handling
00063   if ( hasLiteral() ) {
00064     while (!atLiteralEnd()) {
00065       result += readLiteralPart();
00066     }
00067     return result;
00068   }
00069 
00070   // quoted string
00071   return parseQuotedString();
00072 }
00073 
00074 bool ImapStreamParser::hasString()
00075 {
00076   if ( !waitForMoreData( m_position >= m_data.length() ) )
00077     throw ImapParserException("Unable to read more data");
00078   int savedPos = m_position;
00079   stripLeadingSpaces();
00080   int pos = m_position;
00081   m_position = savedPos;
00082   if ( m_data.at(pos) == '{' )
00083     return true; //literal string
00084   if (m_data.at(pos) == '"' )
00085     return true; //quoted string
00086   if ( m_data.at(pos) != ' ' &&
00087        m_data.at(pos) != '(' &&
00088        m_data.at(pos) != ')' &&
00089        m_data.at(pos) != '[' &&
00090        m_data.at(pos) != ']' &&
00091        m_data.at(pos) != '\n' &&
00092        m_data.at(pos) != '\r' )
00093     return true;  //unquoted string
00094 
00095   return false; //something else, not a string
00096 }
00097 
00098 bool ImapStreamParser::hasLiteral()
00099 {
00100   if ( !waitForMoreData( m_position >= m_data.length() ) )
00101     throw ImapParserException("Unable to read more data");
00102   int savedPos = m_position;
00103   stripLeadingSpaces();
00104   if ( m_data.at(m_position) == '{' )
00105   {
00106     int end = -1;
00107     do {
00108       end = m_data.indexOf( '}', m_position );
00109       if ( !waitForMoreData( end == -1 ) )
00110         throw ImapParserException("Unable to read more data");
00111     } while (end == -1);
00112     Q_ASSERT( end > m_position );
00113     m_literalSize = m_data.mid( m_position + 1, end - m_position - 1 ).toInt();
00114     // strip CRLF
00115     m_position = end + 1;
00116 
00117     if ( m_position < m_data.length() && m_data.at(m_position) == '\r' )
00118       ++m_position;
00119     if ( m_position < m_data.length() && m_data.at(m_position) == '\n' )
00120       ++m_position;
00121 
00122     //FIXME: Makes sense only on the server side?
00123     if (m_isServerModeEnabled && m_literalSize > 0)
00124       sendContinuationResponse( m_literalSize );
00125     return true;
00126   } else
00127   {
00128     m_position = savedPos;
00129     return false;
00130   }
00131 }
00132 
00133 bool ImapStreamParser::atLiteralEnd() const
00134 {
00135   return (m_literalSize == 0);
00136 }
00137 
00138 QByteArray ImapStreamParser::readLiteralPart()
00139 {
00140   static qint64 maxLiteralPartSize = 4096;
00141   int size = qMin(maxLiteralPartSize, m_literalSize);
00142 
00143   if ( !waitForMoreData( m_data.length() < m_position + size ) )
00144     throw ImapParserException("Unable to read more data");
00145 
00146   if ( m_data.length() < m_position + size ) { // Still not enough data
00147     // Take what's already there
00148     size = m_data.length() - m_position;
00149   }
00150 
00151   QByteArray result = m_data.mid(m_position, size);
00152   m_position += size;
00153   m_literalSize -= size;
00154   Q_ASSERT(m_literalSize >= 0);
00155   trimBuffer();
00156 
00157   return result;
00158 }
00159 
00160 bool ImapStreamParser::hasList()
00161 {
00162   if ( !waitForMoreData( m_position >= m_data.length() ) )
00163     throw ImapParserException("Unable to read more data");
00164   int savedPos = m_position;
00165   stripLeadingSpaces();
00166   int pos = m_position;
00167   m_position = savedPos;
00168   if ( m_data.at(pos) == '(' )
00169   {
00170     return true;
00171   }
00172 
00173   return false;
00174 }
00175 
00176 bool ImapStreamParser::atListEnd()
00177 {
00178   if ( !waitForMoreData( m_position >= m_data.length() ) )
00179     throw ImapParserException("Unable to read more data");
00180   int savedPos = m_position;
00181   stripLeadingSpaces();
00182   int pos = m_position;
00183   m_position = savedPos;
00184   if ( m_data.at(pos) == ')' )
00185   {
00186     m_position = pos + 1;
00187     return true;
00188   }
00189 
00190   return false;
00191 }
00192 
00193 QList<QByteArray> ImapStreamParser::readParenthesizedList()
00194 {
00195   QList<QByteArray> result;
00196   if (! waitForMoreData( m_data.length() <= m_position ) )
00197     throw ImapParserException("Unable to read more data");
00198 
00199   stripLeadingSpaces();
00200   if ( m_data.at(m_position) != '(' )
00201     return result; //no list found
00202 
00203   bool concatToLast = false;
00204   int count = 0;
00205   int sublistbegin = m_position;
00206   int i = m_position + 1;
00207   Q_FOREVER {
00208     if ( !waitForMoreData( m_data.length() <= i ) )
00209     {
00210       m_position = i;
00211       throw ImapParserException("Unable to read more data");
00212     }
00213     if ( m_data.at(i) == '(' ) {
00214       ++count;
00215       if ( count == 1 )
00216         sublistbegin = i;
00217       ++i;
00218       continue;
00219     }
00220     if ( m_data.at(i) == ')' ) {
00221       if ( count <= 0 ) {
00222         m_position = i + 1;
00223         return result;
00224       }
00225       if ( count == 1 )
00226         result.append( m_data.mid( sublistbegin, i - sublistbegin + 1 ) );
00227       --count;
00228       ++i;
00229       continue;
00230     }
00231     if ( m_data.at(i) == ' ' ) {
00232       ++i;
00233       continue;
00234     }
00235     if ( m_data.at(i) == '"' ) {
00236       if ( count > 0 ) {
00237         m_position = i;
00238         parseQuotedString();
00239         i = m_position;
00240         continue;
00241       }
00242     }
00243     if ( m_data.at(i) == '[' ) {
00244       concatToLast = true;
00245       result.last()+='[';
00246       ++i;
00247       continue;
00248     }
00249     if ( m_data.at(i) == ']' ) {
00250       concatToLast = false;
00251       result.last()+=']';
00252       ++i;
00253       continue;
00254     }
00255     if ( count == 0 ) {
00256       m_position = i;
00257       QByteArray ba;
00258       if (hasLiteral()) {
00259         while (!atLiteralEnd()) {
00260           ba+=readLiteralPart();
00261         }
00262       } else {
00263         ba = readString();
00264       }
00265 
00266       // We might sometime get some unwanted CRLF, but we're still not at the end
00267       // of the list, would make further string reads fail so eat the CRLFs.
00268       while ( ( m_position < m_data.size() ) && ( m_data.at(m_position) == '\r' || m_data.at(m_position) == '\n' ) ) {
00269         m_position++;
00270       }
00271 
00272       i = m_position - 1;
00273       if (concatToLast) {
00274         result.last()+=ba;
00275       } else {
00276         result.append( ba );
00277       }
00278     }
00279     ++i;
00280   }
00281 
00282   throw ImapParserException( "Something went very very wrong!" );
00283 }
00284 
00285 bool ImapStreamParser::hasResponseCode()
00286 {
00287   if ( !waitForMoreData( m_position >= m_data.length() ) )
00288     throw ImapParserException("Unable to read more data");
00289   int savedPos = m_position;
00290   stripLeadingSpaces();
00291   int pos = m_position;
00292   m_position = savedPos;
00293   if ( m_data.at(pos) == '[' )
00294   {
00295     m_position = pos + 1;
00296     return true;
00297   }
00298 
00299   return false;
00300 }
00301 
00302 bool ImapStreamParser::atResponseCodeEnd()
00303 {
00304   if ( !waitForMoreData( m_position >= m_data.length() ) )
00305     throw ImapParserException("Unable to read more data");
00306   int savedPos = m_position;
00307   stripLeadingSpaces();
00308   int pos = m_position;
00309   m_position = savedPos;
00310   if ( m_data.at(pos) == ']' )
00311   {
00312     m_position = pos + 1;
00313     return true;
00314   }
00315 
00316   return false;
00317 }
00318 
00319 QByteArray ImapStreamParser::parseQuotedString()
00320 {
00321   QByteArray result;
00322   if (! waitForMoreData( m_data.length() == 0 ) )
00323     throw ImapParserException("Unable to read more data");
00324   stripLeadingSpaces();
00325   int end = m_position;
00326   result.clear();
00327   if ( !waitForMoreData( m_position >= m_data.length() ) )
00328     throw ImapParserException("Unable to read more data");
00329   if ( !waitForMoreData( m_position >= m_data.length() ) )
00330     throw ImapParserException("Unable to read more data");
00331 
00332   bool foundSlash = false;
00333   // quoted string
00334   if ( m_data.at(m_position) == '"' ) {
00335     ++m_position;
00336     int i = m_position;
00337     Q_FOREVER {
00338       if ( !waitForMoreData( m_data.length() <= i ) )
00339       {
00340         m_position = i;
00341         throw ImapParserException("Unable to read more data");
00342       }
00343       if ( m_data.at(i) == '\\' ) {
00344         i += 2;
00345         foundSlash = true;
00346         continue;
00347       }
00348       if ( m_data.at(i) == '"' ) {
00349         result = m_data.mid( m_position, i - m_position );
00350         end = i + 1; // skip the '"'
00351         break;
00352       }
00353       ++i;
00354     }
00355   }
00356 
00357   // unquoted string
00358   else {
00359     bool reachedInputEnd = true;
00360     int i = m_position;
00361     Q_FOREVER {
00362       if ( !waitForMoreData( m_data.length() <= i ) )
00363       {
00364         m_position = i;
00365         throw ImapParserException("Unable to read more data");
00366       }
00367       if ( m_data.at(i) == ' ' || m_data.at(i) == '(' || m_data.at(i) == ')' || m_data.at(i) == '[' || m_data.at(i) == ']' || m_data.at(i) == '\n' || m_data.at(i) == '\r' || m_data.at(i) == '"') {
00368         end = i;
00369         reachedInputEnd = false;
00370         break;
00371       }
00372       if (m_data.at(i) == '\\')
00373         foundSlash = true;
00374       i++;
00375     }
00376     if ( reachedInputEnd ) //FIXME: how can it get here?
00377       end = m_data.length();
00378 
00379     result = m_data.mid( m_position, end - m_position );
00380   }
00381 
00382   // strip quotes
00383   if ( foundSlash ) {
00384     while ( result.contains( "\\\"" ) )
00385       result.replace( "\\\"", "\"" );
00386     while ( result.contains( "\\\\" ) )
00387       result.replace( "\\\\", "\\" );
00388   }
00389   m_position = end;
00390   return result;
00391 }
00392 
00393 qint64 ImapStreamParser::readNumber( bool * ok )
00394 {
00395   qint64  result;
00396   if ( ok )
00397     *ok = false;
00398   if (! waitForMoreData( m_data.length() == 0 ) )
00399     throw ImapParserException("Unable to read more data");
00400   stripLeadingSpaces();
00401   if ( !waitForMoreData( m_position >= m_data.length() ) )
00402     throw ImapParserException("Unable to read more data");
00403   if ( m_position >= m_data.length() )
00404     throw ImapParserException("Unable to read more data");
00405   int i = m_position;
00406   Q_FOREVER {
00407     if ( !waitForMoreData( m_data.length() <= i ) )
00408     {
00409       m_position = i;
00410       throw ImapParserException("Unable to read more data");
00411     }
00412     if ( !isdigit( m_data.at( i ) ) )
00413       break;
00414     ++i;
00415   }
00416   const QByteArray tmp = m_data.mid( m_position, i - m_position );
00417   result = tmp.toLongLong( ok );
00418   m_position = i;
00419   return result;
00420 }
00421 
00422 void ImapStreamParser::stripLeadingSpaces()
00423 {
00424   for ( int i = m_position; i < m_data.length(); ++i ) {
00425     if ( m_data.at(i) != ' ' )
00426     {
00427       m_position = i;
00428       return;
00429     }
00430   }
00431   m_position = m_data.length();
00432 }
00433 
00434 bool ImapStreamParser::waitForMoreData( bool wait )
00435 {
00436    if ( wait ) {
00437      if ( m_socket->bytesAvailable() > 0 ||
00438           m_socket->waitForReadyRead(30000) ) {
00439         m_data.append( m_socket->readAll() );
00440      } else
00441      {
00442        return false;
00443      }
00444    }
00445    return true;
00446 }
00447 
00448 void ImapStreamParser::setData( const QByteArray &data )
00449 {
00450   m_data = data;
00451 }
00452 
00453 QByteArray ImapStreamParser::readRemainingData()
00454 {
00455   return m_data.mid(m_position);
00456 }
00457 
00458 int ImapStreamParser::availableDataSize() const
00459 {
00460   return m_socket->bytesAvailable()+m_data.size()-m_position;
00461 }
00462 
00463 bool ImapStreamParser::atCommandEnd()
00464 {
00465   int savedPos = m_position;
00466   do {
00467     if ( !waitForMoreData( m_position >= m_data.length() ) )
00468       throw ImapParserException("Unable to read more data");
00469     stripLeadingSpaces();
00470   } while ( m_position >= m_data.size() );
00471 
00472   if ( m_data.at(m_position) == '\n' || m_data.at(m_position) == '\r') {
00473     if ( m_data.at(m_position) == '\r' )
00474       ++m_position;
00475     if ( m_position < m_data.length() && m_data.at(m_position) == '\n' )
00476       ++m_position;
00477 
00478     // We'd better empty m_data from time to time before it grows out of control
00479     trimBuffer();
00480 
00481     return true; //command end
00482   }
00483   m_position = savedPos;
00484   return false; //something else
00485 }
00486 
00487 QByteArray ImapStreamParser::readUntilCommandEnd()
00488 {
00489   QByteArray result;
00490   int i = m_position;
00491   int paranthesisBalance = 0;
00492   Q_FOREVER {
00493     if ( !waitForMoreData( m_data.length() <= i ) )
00494     {
00495       m_position = i;
00496       throw ImapParserException("Unable to read more data");
00497     }
00498     if ( m_data.at(i) == '{' )
00499     {
00500       m_position = i - 1;
00501       hasLiteral(); //init literal size
00502       result.append(m_data.mid(i-1, m_position - i +1));
00503       while (!atLiteralEnd())
00504       {
00505         result.append( readLiteralPart() );
00506       }
00507       i = m_position;
00508     }
00509     if ( m_data.at(i) == '(' )
00510       paranthesisBalance++;
00511     if ( m_data.at(i) == ')' )
00512       paranthesisBalance--;
00513     if ( ( i == m_data.length() && paranthesisBalance == 0 ) || m_data.at(i) == '\n'  || m_data.at(i) == '\r')
00514       break; //command end
00515     result.append( m_data.at(i) );
00516     ++i;
00517   }
00518   m_position = i;
00519   atCommandEnd();
00520   return result;
00521 }
00522 
00523 void ImapStreamParser::sendContinuationResponse( qint64 size )
00524 {
00525   QByteArray block = "+ Ready for literal data (expecting "
00526                    + QByteArray::number( size ) + " bytes)\r\n";
00527   m_socket->write(block);
00528   m_socket->waitForBytesWritten(30000);
00529 }
00530 
00531 void ImapStreamParser::trimBuffer()
00532 {
00533   if ( m_position < 4096 ) // right() is expensive, so don't do it for every line
00534     return;
00535   m_data = m_data.right(m_data.size()-m_position);
00536   m_position = 0;
00537 }

KIMAP Library

Skip menu "KIMAP Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • 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
Generated for KDE-PIM Libraries by doxygen 1.7.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal