• Skip to content
  • Skip to link menu
KDE 4.5 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     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 )
00032 {
00033   m_socket = socket;
00034   m_position = 0;
00035   m_literalSize = 0;
00036   m_continuationSize = 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[pos] == '{' )
00083     return true; //literal string
00084   if (m_data[pos] == '"' )
00085     return true; //quoted string
00086   if ( m_data[pos] != ' ' &&
00087        m_data[pos] != '(' &&
00088        m_data[pos] != ')' &&
00089        m_data[pos] != '[' &&
00090        m_data[pos] != ']' &&
00091        m_data[pos] != '\n' &&
00092        m_data[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[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[m_position] == '\r' )
00118       ++m_position;
00119     if ( m_position < m_data.length() && m_data[m_position] == '\n' )
00120       ++m_position;
00121 
00122     //FIXME: Makes sense only on the server side?
00123     //m_continuationSize = qMin(m_position + m_literalSize - m_data.length(), (qint64)4096);
00124     //if (m_continuationSize > 0)
00125     //  sendContinuationResponse();
00126     return true;
00127   } else
00128   {
00129     m_position = savedPos;
00130     return false;
00131   }
00132 }
00133 
00134 bool ImapStreamParser::atLiteralEnd() const
00135 {
00136   return (m_literalSize == 0);
00137 }
00138 
00139 QByteArray ImapStreamParser::readLiteralPart()
00140 {
00141   static qint64 maxLiteralPartSize = 4096;
00142   int size = qMin(maxLiteralPartSize, m_literalSize);
00143 
00144   if ( !waitForMoreData( m_data.length() < m_position + size ) )
00145     throw ImapParserException("Unable to read more data");
00146 
00147   if ( m_data.length() < m_position + size ) { // Still not enough data
00148     // Take what's already there
00149     size = m_data.length() - m_position;
00150   }
00151 
00152   QByteArray result = m_data.mid(m_position, size);
00153   m_position += size;
00154   m_literalSize -= size;
00155   Q_ASSERT(m_literalSize >= 0);
00156   m_data = m_data.right( m_data.size() - m_position );
00157   m_position = 0;
00158 
00159   return result;
00160 }
00161 
00162 bool ImapStreamParser::hasList()
00163 {
00164   if ( !waitForMoreData( m_position >= m_data.length() ) )
00165     throw ImapParserException("Unable to read more data");
00166   int savedPos = m_position;
00167   stripLeadingSpaces();
00168   int pos = m_position;
00169   m_position = savedPos;
00170   if ( m_data[pos] == '(' )
00171   {
00172     return true;
00173   }
00174 
00175   return false;
00176 }
00177 
00178 bool ImapStreamParser::atListEnd()
00179 {
00180   if ( !waitForMoreData( m_position >= m_data.length() ) )
00181     throw ImapParserException("Unable to read more data");
00182   int savedPos = m_position;
00183   stripLeadingSpaces();
00184   int pos = m_position;
00185   m_position = savedPos;
00186   if ( m_data[pos] == ')' )
00187   {
00188     m_position = pos + 1;
00189     return true;
00190   }
00191 
00192   return false;
00193 }
00194 
00195 QList<QByteArray> ImapStreamParser::readParenthesizedList()
00196 {
00197   QList<QByteArray> result;
00198   if (! waitForMoreData( m_data.length() <= m_position ) )
00199     throw ImapParserException("Unable to read more data");
00200 
00201   stripLeadingSpaces();
00202   if ( m_data[m_position] != '(' )
00203     return result; //no list found
00204 
00205   bool concatToLast = false;
00206   int count = 0;
00207   int sublistbegin = m_position;
00208   int i = m_position + 1;
00209   Q_FOREVER {
00210     if ( !waitForMoreData( m_data.length() <= i ) )
00211     {
00212       m_position = i;
00213       throw ImapParserException("Unable to read more data");
00214     }
00215     if ( m_data[i] == '(' ) {
00216       ++count;
00217       if ( count == 1 )
00218         sublistbegin = i;
00219       ++i;
00220       continue;
00221     }
00222     if ( m_data[i] == ')' ) {
00223       if ( count <= 0 ) {
00224         m_position = i + 1;
00225         return result;
00226       }
00227       if ( count == 1 )
00228         result.append( m_data.mid( sublistbegin, i - sublistbegin + 1 ) );
00229       --count;
00230       ++i;
00231       continue;
00232     }
00233     if ( m_data[i] == ' ' ) {
00234       ++i;
00235       continue;
00236     }
00237     if ( m_data[i] == '[' ) {
00238       concatToLast = true;
00239       result.last()+='[';
00240       ++i;
00241       continue;
00242     }
00243     if ( m_data[i] == ']' ) {
00244       concatToLast = false;
00245       result.last()+=']';
00246       ++i;
00247       continue;
00248     }
00249     if ( count == 0 ) {
00250       m_position = i;
00251       QByteArray ba;
00252       if (hasLiteral()) {
00253         while (!atLiteralEnd()) {
00254           ba+=readLiteralPart();
00255         }
00256       } else {
00257         ba = readString();
00258       }
00259 
00260       // We might sometime get some unwanted CRLF, but we're still not at the end
00261       // of the list, would make further string reads fail so eat the CRLFs.
00262       while ( ( m_position < m_data.size() ) && ( m_data[m_position]=='\r' || m_data[m_position]=='\n' ) ) {
00263         m_position++;
00264       }
00265 
00266       i = m_position - 1;
00267       if (concatToLast) {
00268         result.last()+=ba;
00269       } else {
00270         result.append( ba );
00271       }
00272     }
00273     ++i;
00274   }
00275 
00276   throw ImapParserException( "Something went very very wrong!" );
00277 }
00278 
00279 bool ImapStreamParser::hasResponseCode()
00280 {
00281   if ( !waitForMoreData( m_position >= m_data.length() ) )
00282     throw ImapParserException("Unable to read more data");
00283   int savedPos = m_position;
00284   stripLeadingSpaces();
00285   int pos = m_position;
00286   m_position = savedPos;
00287   if ( m_data[pos] == '[' )
00288   {
00289     m_position = pos + 1;
00290     return true;
00291   }
00292 
00293   return false;
00294 }
00295 
00296 bool ImapStreamParser::atResponseCodeEnd()
00297 {
00298   if ( !waitForMoreData( m_position >= m_data.length() ) )
00299     throw ImapParserException("Unable to read more data");
00300   int savedPos = m_position;
00301   stripLeadingSpaces();
00302   int pos = m_position;
00303   m_position = savedPos;
00304   if ( m_data[pos] == ']' )
00305   {
00306     m_position = pos + 1;
00307     return true;
00308   }
00309 
00310   return false;
00311 }
00312 
00313 QByteArray ImapStreamParser::parseQuotedString()
00314 {
00315   QByteArray result;
00316   if (! waitForMoreData( m_data.length() == 0 ) )
00317     throw ImapParserException("Unable to read more data");
00318   stripLeadingSpaces();
00319   int end = m_position;
00320   result.clear();
00321   if ( !waitForMoreData( m_position >= m_data.length() ) )
00322     throw ImapParserException("Unable to read more data");
00323   if ( !waitForMoreData( m_position >= m_data.length() ) )
00324     throw ImapParserException("Unable to read more data");
00325 
00326   bool foundSlash = false;
00327   // quoted string
00328   if ( m_data[m_position] == '"' ) {
00329     ++m_position;
00330     int i = m_position;
00331     Q_FOREVER {
00332       if ( !waitForMoreData( m_data.length() <= i ) )
00333       {
00334         m_position = i;
00335         throw ImapParserException("Unable to read more data");
00336       }
00337       if ( m_data[i] == '\\' ) {
00338         i += 2;
00339         foundSlash = true;
00340         continue;
00341       }
00342       if ( m_data[i] == '"' ) {
00343         result = m_data.mid( m_position, i - m_position );
00344         end = i + 1; // skip the '"'
00345         break;
00346       }
00347       ++i;
00348     }
00349   }
00350 
00351   // unquoted string
00352   else {
00353     bool reachedInputEnd = true;
00354     int i = m_position;
00355     Q_FOREVER {
00356       if ( !waitForMoreData( m_data.length() <= i ) )
00357       {
00358         m_position = i;
00359         throw ImapParserException("Unable to read more data");
00360       }
00361       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] == '"') {
00362         end = i;
00363         reachedInputEnd = false;
00364         break;
00365       }
00366       if (m_data[i] == '\\')
00367         foundSlash = true;
00368       i++;
00369     }
00370     if ( reachedInputEnd ) //FIXME: how can it get here?
00371       end = m_data.length();
00372 
00373     result = m_data.mid( m_position, end - m_position );
00374   }
00375 
00376   // strip quotes
00377   if ( foundSlash ) {
00378     while ( result.contains( "\\\"" ) )
00379       result.replace( "\\\"", "\"" );
00380     while ( result.contains( "\\\\" ) )
00381       result.replace( "\\\\", "\\" );
00382   }
00383   m_position = end;
00384   return result;
00385 }
00386 
00387 qint64 ImapStreamParser::readNumber( bool * ok )
00388 {
00389   qint64  result;
00390   if ( ok )
00391     *ok = false;
00392   if (! waitForMoreData( m_data.length() == 0 ) )
00393     throw ImapParserException("Unable to read more data");
00394   stripLeadingSpaces();
00395   if ( !waitForMoreData( m_position >= m_data.length() ) )
00396     throw ImapParserException("Unable to read more data");
00397   if ( m_position >= m_data.length() )
00398     throw ImapParserException("Unable to read more data");
00399   int i = m_position;
00400   Q_FOREVER {
00401     if ( !waitForMoreData( m_data.length() <= i ) )
00402     {
00403       m_position = i;
00404       throw ImapParserException("Unable to read more data");
00405     }
00406     if ( !isdigit( m_data.at( i ) ) )
00407       break;
00408     ++i;
00409   }
00410   const QByteArray tmp = m_data.mid( m_position, i - m_position );
00411   result = tmp.toLongLong( ok );
00412   m_position = i;
00413   return result;
00414 }
00415 
00416 void ImapStreamParser::stripLeadingSpaces()
00417 {
00418   for ( int i = m_position; i < m_data.length(); ++i ) {
00419     if ( m_data[i] != ' ' )
00420     {
00421       m_position = i;
00422       return;
00423     }
00424   }
00425   m_position = m_data.length();
00426 }
00427 
00428 bool ImapStreamParser::waitForMoreData( bool wait )
00429 {
00430    if ( wait ) {
00431      if ( m_socket->bytesAvailable() > 0 ||
00432           m_socket->waitForReadyRead(30000) ) {
00433         m_data.append( m_socket->readAll() );
00434      } else
00435      {
00436        return false;
00437      }
00438    }
00439    return true;
00440 }
00441 
00442 void ImapStreamParser::setData( const QByteArray &data )
00443 {
00444   m_data = data;
00445 }
00446 
00447 QByteArray ImapStreamParser::readRemainingData()
00448 {
00449   return m_data.mid(m_position);
00450 }
00451 
00452 int ImapStreamParser::availableDataSize() const
00453 {
00454   return m_socket->bytesAvailable()+m_data.size()-m_position;
00455 }
00456 
00457 bool ImapStreamParser::atCommandEnd()
00458 {
00459   if ( !waitForMoreData( m_position >= m_data.length() ) )
00460     throw ImapParserException("Unable to read more data");
00461   int savedPos = m_position;
00462   stripLeadingSpaces();
00463   if ( m_data[m_position] == '\n' || m_data[m_position] == '\r') {
00464     if ( m_position < m_data.length() && m_data[m_position] == '\r' )
00465       ++m_position;
00466     if ( m_position < m_data.length() && m_data[m_position] == '\n' )
00467       ++m_position;
00468 
00469     // We'd better empty m_data from time to time before it grows out of control
00470     m_data = m_data.right(m_data.size()-m_position);
00471     m_position = 0;
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[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[i] == '(' )
00502       paranthesisBalance++;
00503     if ( m_data[i] == ')' )
00504       paranthesisBalance--;
00505     if ( ( i == m_data.length() && paranthesisBalance == 0 ) || m_data[i] == '\n'  || m_data[i] == '\r')
00506       break; //command end
00507     result.append( m_data[i]);
00508     ++i;
00509   }
00510   m_position = i;
00511   atCommandEnd();
00512   return result;
00513 }
00514 
00515 void ImapStreamParser::sendContinuationResponse()
00516 {
00517   QByteArray block = "+ Ready for literal data (expecting "
00518                    + QByteArray::number(  m_continuationSize ) + " bytes)\r\n";
00519   m_socket->write(block);
00520   m_socket->waitForBytesWritten(30000);
00521 }

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