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

KDE's Doxygen guidelines are available online.

KIMAP Library

Skip menu "KIMAP Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • 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