• 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       concatToLast = true;
00237       result.last()+='[';
00238       ++i;
00239       continue;
00240     }
00241     if ( m_data.at(i) == ']' ) {
00242       concatToLast = false;
00243       result.last()+=']';
00244       ++i;
00245       continue;
00246     }
00247     if ( count == 0 ) {
00248       m_position = i;
00249       QByteArray ba;
00250       if (hasLiteral()) {
00251         while (!atLiteralEnd()) {
00252           ba+=readLiteralPart();
00253         }
00254       } else {
00255         ba = readString();
00256       }
00257 
00258       // We might sometime get some unwanted CRLF, but we're still not at the end
00259       // of the list, would make further string reads fail so eat the CRLFs.
00260       while ( ( m_position < m_data.size() ) && ( m_data.at(m_position) == '\r' || m_data.at(m_position) == '\n' ) ) {
00261         m_position++;
00262       }
00263 
00264       i = m_position - 1;
00265       if (concatToLast) {
00266         result.last()+=ba;
00267       } else {
00268         result.append( ba );
00269       }
00270     }
00271     ++i;
00272   }
00273 
00274   throw ImapParserException( "Something went very very wrong!" );
00275 }
00276 
00277 bool ImapStreamParser::hasResponseCode()
00278 {
00279   if ( !waitForMoreData( m_position >= m_data.length() ) )
00280     throw ImapParserException("Unable to read more data");
00281   int savedPos = m_position;
00282   stripLeadingSpaces();
00283   int pos = m_position;
00284   m_position = savedPos;
00285   if ( m_data.at(pos) == '[' )
00286   {
00287     m_position = pos + 1;
00288     return true;
00289   }
00290 
00291   return false;
00292 }
00293 
00294 bool ImapStreamParser::atResponseCodeEnd()
00295 {
00296   if ( !waitForMoreData( m_position >= m_data.length() ) )
00297     throw ImapParserException("Unable to read more data");
00298   int savedPos = m_position;
00299   stripLeadingSpaces();
00300   int pos = m_position;
00301   m_position = savedPos;
00302   if ( m_data.at(pos) == ']' )
00303   {
00304     m_position = pos + 1;
00305     return true;
00306   }
00307 
00308   return false;
00309 }
00310 
00311 QByteArray ImapStreamParser::parseQuotedString()
00312 {
00313   QByteArray result;
00314   if (! waitForMoreData( m_data.length() == 0 ) )
00315     throw ImapParserException("Unable to read more data");
00316   stripLeadingSpaces();
00317   int end = m_position;
00318   result.clear();
00319   if ( !waitForMoreData( m_position >= m_data.length() ) )
00320     throw ImapParserException("Unable to read more data");
00321   if ( !waitForMoreData( m_position >= m_data.length() ) )
00322     throw ImapParserException("Unable to read more data");
00323 
00324   bool foundSlash = false;
00325   // quoted string
00326   if ( m_data.at(m_position) == '"' ) {
00327     ++m_position;
00328     int i = m_position;
00329     Q_FOREVER {
00330       if ( !waitForMoreData( m_data.length() <= i ) )
00331       {
00332         m_position = i;
00333         throw ImapParserException("Unable to read more data");
00334       }
00335       if ( m_data.at(i) == '\\' ) {
00336         i += 2;
00337         foundSlash = true;
00338         continue;
00339       }
00340       if ( m_data.at(i) == '"' ) {
00341         result = m_data.mid( m_position, i - m_position );
00342         end = i + 1; // skip the '"'
00343         break;
00344       }
00345       ++i;
00346     }
00347   }
00348 
00349   // unquoted string
00350   else {
00351     bool reachedInputEnd = true;
00352     int i = m_position;
00353     Q_FOREVER {
00354       if ( !waitForMoreData( m_data.length() <= i ) )
00355       {
00356         m_position = i;
00357         throw ImapParserException("Unable to read more data");
00358       }
00359       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) == '"') {
00360         end = i;
00361         reachedInputEnd = false;
00362         break;
00363       }
00364       if (m_data.at(i) == '\\')
00365         foundSlash = true;
00366       i++;
00367     }
00368     if ( reachedInputEnd ) //FIXME: how can it get here?
00369       end = m_data.length();
00370 
00371     result = m_data.mid( m_position, end - m_position );
00372   }
00373 
00374   // strip quotes
00375   if ( foundSlash ) {
00376     while ( result.contains( "\\\"" ) )
00377       result.replace( "\\\"", "\"" );
00378     while ( result.contains( "\\\\" ) )
00379       result.replace( "\\\\", "\\" );
00380   }
00381   m_position = end;
00382   return result;
00383 }
00384 
00385 qint64 ImapStreamParser::readNumber( bool * ok )
00386 {
00387   qint64  result;
00388   if ( ok )
00389     *ok = false;
00390   if (! waitForMoreData( m_data.length() == 0 ) )
00391     throw ImapParserException("Unable to read more data");
00392   stripLeadingSpaces();
00393   if ( !waitForMoreData( m_position >= m_data.length() ) )
00394     throw ImapParserException("Unable to read more data");
00395   if ( m_position >= m_data.length() )
00396     throw ImapParserException("Unable to read more data");
00397   int i = m_position;
00398   Q_FOREVER {
00399     if ( !waitForMoreData( m_data.length() <= i ) )
00400     {
00401       m_position = i;
00402       throw ImapParserException("Unable to read more data");
00403     }
00404     if ( !isdigit( m_data.at( i ) ) )
00405       break;
00406     ++i;
00407   }
00408   const QByteArray tmp = m_data.mid( m_position, i - m_position );
00409   result = tmp.toLongLong( ok );
00410   m_position = i;
00411   return result;
00412 }
00413 
00414 void ImapStreamParser::stripLeadingSpaces()
00415 {
00416   for ( int i = m_position; i < m_data.length(); ++i ) {
00417     if ( m_data.at(i) != ' ' )
00418     {
00419       m_position = i;
00420       return;
00421     }
00422   }
00423   m_position = m_data.length();
00424 }
00425 
00426 bool ImapStreamParser::waitForMoreData( bool wait )
00427 {
00428    if ( wait ) {
00429      if ( m_socket->bytesAvailable() > 0 ||
00430           m_socket->waitForReadyRead(30000) ) {
00431         m_data.append( m_socket->readAll() );
00432      } else
00433      {
00434        return false;
00435      }
00436    }
00437    return true;
00438 }
00439 
00440 void ImapStreamParser::setData( const QByteArray &data )
00441 {
00442   m_data = data;
00443 }
00444 
00445 QByteArray ImapStreamParser::readRemainingData()
00446 {
00447   return m_data.mid(m_position);
00448 }
00449 
00450 int ImapStreamParser::availableDataSize() const
00451 {
00452   return m_socket->bytesAvailable()+m_data.size()-m_position;
00453 }
00454 
00455 bool ImapStreamParser::atCommandEnd()
00456 {
00457   int savedPos = m_position;
00458   do {
00459     if ( !waitForMoreData( m_position >= m_data.length() ) )
00460       throw ImapParserException("Unable to read more data");
00461     stripLeadingSpaces();
00462   } while ( m_position >= m_data.size() );
00463 
00464   if ( m_data.at(m_position) == '\n' || m_data.at(m_position) == '\r') {
00465     if ( m_data.at(m_position) == '\r' )
00466       ++m_position;
00467     if ( m_position < m_data.length() && m_data.at(m_position) == '\n' )
00468       ++m_position;
00469 
00470     // We'd better empty m_data from time to time before it grows out of control
00471     trimBuffer();
00472 
00473     return true; //command end
00474   }
00475   m_position = savedPos;
00476   return false; //something else
00477 }
00478 
00479 QByteArray ImapStreamParser::readUntilCommandEnd()
00480 {
00481   QByteArray result;
00482   int i = m_position;
00483   int paranthesisBalance = 0;
00484   Q_FOREVER {
00485     if ( !waitForMoreData( m_data.length() <= i ) )
00486     {
00487       m_position = i;
00488       throw ImapParserException("Unable to read more data");
00489     }
00490     if ( m_data.at(i) == '{' )
00491     {
00492       m_position = i - 1;
00493       hasLiteral(); //init literal size
00494       result.append(m_data.mid(i-1, m_position - i +1));
00495       while (!atLiteralEnd())
00496       {
00497         result.append( readLiteralPart() );
00498       }
00499       i = m_position;
00500     }
00501     if ( m_data.at(i) == '(' )
00502       paranthesisBalance++;
00503     if ( m_data.at(i) == ')' )
00504       paranthesisBalance--;
00505     if ( ( i == m_data.length() && paranthesisBalance == 0 ) || m_data.at(i) == '\n'  || m_data.at(i) == '\r')
00506       break; //command end
00507     result.append( m_data.at(i) );
00508     ++i;
00509   }
00510   m_position = i;
00511   atCommandEnd();
00512   return result;
00513 }
00514 
00515 void ImapStreamParser::sendContinuationResponse( qint64 size )
00516 {
00517   QByteArray block = "+ Ready for literal data (expecting "
00518                    + QByteArray::number( size ) + " bytes)\r\n";
00519   m_socket->write(block);
00520   m_socket->waitForBytesWritten(30000);
00521 }
00522 
00523 void ImapStreamParser::trimBuffer()
00524 {
00525   if ( m_position < 4096 ) // right() is expensive, so don't do it for every line
00526     return;
00527   m_data = m_data.right(m_data.size()-m_position);
00528   m_position = 0;
00529 }

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.3
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