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

KIMAP Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
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