• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

KIMAP Library

  • sources
  • kde-4.12
  • kdepimlibs
  • kimap
imapstreamparser.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
3  Copyright (c) 2009 Andras Mantia <amantia@kde.org>
4 
5  Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6  Author: Kevin Ottens <kevin@kdab.com>
7 
8  This library is free software; you can redistribute it and/or modify it
9  under the terms of the GNU Library General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or (at your
11  option) any later version.
12 
13  This library is distributed in the hope that it will be useful, but WITHOUT
14  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
16  License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to the
20  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  02110-1301, USA.
22 */
23 
24 #include "imapstreamparser.h"
25 
26 #include <ctype.h>
27 #include <QIODevice>
28 
29 using namespace KIMAP;
30 
31 ImapStreamParser::ImapStreamParser( QIODevice *socket, bool serverModeEnabled )
32 {
33  m_socket = socket;
34  m_isServerModeEnabled = serverModeEnabled;
35  m_position = 0;
36  m_literalSize = 0;
37 }
38 
39 ImapStreamParser::~ImapStreamParser()
40 {
41 }
42 
43 QString ImapStreamParser::readUtf8String()
44 {
45  QByteArray tmp;
46  tmp = readString();
47  QString result = QString::fromUtf8( tmp );
48  return result;
49 }
50 
51 QByteArray ImapStreamParser::readString()
52 {
53  QByteArray result;
54  if ( !waitForMoreData( m_data.length() == 0 ) ) {
55  throw ImapParserException( "Unable to read more data" );
56  }
57  stripLeadingSpaces();
58  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
59  throw ImapParserException( "Unable to read more data" );
60  }
61 
62  // literal string
63  // TODO: error handling
64  if ( hasLiteral() ) {
65  while ( !atLiteralEnd() ) {
66  result += readLiteralPart();
67  }
68  return result;
69  }
70 
71  // quoted string
72  return parseQuotedString();
73 }
74 
75 bool ImapStreamParser::hasString()
76 {
77  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
78  throw ImapParserException( "Unable to read more data" );
79  }
80  int savedPos = m_position;
81  stripLeadingSpaces();
82  int pos = m_position;
83  m_position = savedPos;
84  if ( m_data.at( pos ) == '{' ) {
85  return true; //literal string
86  }
87  if ( m_data.at( pos ) == '"' ) {
88  return true; //quoted string
89  }
90  if ( m_data.at( pos ) != ' ' &&
91  m_data.at( pos ) != '(' &&
92  m_data.at( pos ) != ')' &&
93  m_data.at( pos ) != '[' &&
94  m_data.at( pos ) != ']' &&
95  m_data.at( pos ) != '\n' &&
96  m_data.at( pos ) != '\r' ) {
97  return true; //unquoted string
98  }
99 
100  return false; //something else, not a string
101 }
102 
103 bool ImapStreamParser::hasLiteral()
104 {
105  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
106  throw ImapParserException( "Unable to read more data" );
107  }
108  int savedPos = m_position;
109  stripLeadingSpaces();
110  if ( m_data.at( m_position ) == '{' ) {
111  int end = -1;
112  do {
113  end = m_data.indexOf( '}', m_position );
114  if ( !waitForMoreData( end == -1 ) ) {
115  throw ImapParserException( "Unable to read more data" );
116  }
117  } while ( end == -1 );
118  Q_ASSERT( end > m_position );
119  m_literalSize = m_data.mid( m_position + 1, end - m_position - 1 ).toInt();
120  // strip CRLF
121  m_position = end + 1;
122  // ensure that the CRLF is available
123  if ( !waitForMoreData( m_position + 1 >= m_data.length() ) ) {
124  throw ImapParserException( "Unable to read more data" );
125  }
126  if ( m_position < m_data.length() && m_data.at( m_position ) == '\r' ) {
127  ++m_position;
128  }
129  if ( m_position < m_data.length() && m_data.at( m_position ) == '\n' ) {
130  ++m_position;
131  }
132 
133  //FIXME: Makes sense only on the server side?
134  if ( m_isServerModeEnabled && m_literalSize > 0 ) {
135  sendContinuationResponse( m_literalSize );
136  }
137  return true;
138  } else {
139  m_position = savedPos;
140  return false;
141  }
142 }
143 
144 bool ImapStreamParser::atLiteralEnd() const
145 {
146  return ( m_literalSize == 0 );
147 }
148 
149 QByteArray ImapStreamParser::readLiteralPart()
150 {
151  static qint64 maxLiteralPartSize = 4096;
152  int size = qMin(maxLiteralPartSize, m_literalSize);
153 
154  if ( !waitForMoreData( m_data.length() < m_position + size ) ) {
155  throw ImapParserException( "Unable to read more data" );
156  }
157 
158  if ( m_data.length() < m_position + size ) { // Still not enough data
159  // Take what's already there
160  size = m_data.length() - m_position;
161  }
162 
163  QByteArray result = m_data.mid( m_position, size );
164  m_position += size;
165  m_literalSize -= size;
166  Q_ASSERT( m_literalSize >= 0 );
167  trimBuffer();
168 
169  return result;
170 }
171 
172 bool ImapStreamParser::hasList()
173 {
174  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
175  throw ImapParserException( "Unable to read more data" );
176  }
177  int savedPos = m_position;
178  stripLeadingSpaces();
179  int pos = m_position;
180  m_position = savedPos;
181  if ( m_data.at( pos ) == '(' ) {
182  return true;
183  }
184  return false;
185 }
186 
187 bool ImapStreamParser::atListEnd()
188 {
189  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
190  throw ImapParserException( "Unable to read more data" );
191  }
192  int savedPos = m_position;
193  stripLeadingSpaces();
194  int pos = m_position;
195  m_position = savedPos;
196  if ( m_data.at( pos ) == ')' ) {
197  m_position = pos + 1;
198  return true;
199  }
200  return false;
201 }
202 
203 QList<QByteArray> ImapStreamParser::readParenthesizedList()
204 {
205  QList<QByteArray> result;
206  if ( !waitForMoreData( m_data.length() <= m_position ) ) {
207  throw ImapParserException( "Unable to read more data" );
208  }
209 
210  stripLeadingSpaces();
211  if ( m_data.at( m_position ) != '(' ) {
212  return result; //no list found
213  }
214 
215  bool concatToLast = false;
216  int count = 0;
217  int sublistbegin = m_position;
218  int i = m_position + 1;
219  Q_FOREVER {
220  if ( !waitForMoreData( m_data.length() <= i ) ) {
221  m_position = i;
222  throw ImapParserException( "Unable to read more data" );
223  }
224  if ( m_data.at( i ) == '(' ) {
225  ++count;
226  if ( count == 1 ) {
227  sublistbegin = i;
228  }
229  ++i;
230  continue;
231  }
232  if ( m_data.at( i ) == ')' ) {
233  if ( count <= 0 ) {
234  m_position = i + 1;
235  return result;
236  }
237  if ( count == 1 ) {
238  result.append( m_data.mid( sublistbegin, i - sublistbegin + 1 ) );
239  }
240  --count;
241  ++i;
242  continue;
243  }
244  if ( m_data.at( i ) == ' ' ) {
245  ++i;
246  continue;
247  }
248  if ( m_data.at( i ) == '"' ) {
249  if ( count > 0 ) {
250  m_position = i;
251  parseQuotedString();
252  i = m_position;
253  continue;
254  }
255  }
256  if ( m_data.at( i ) == '[' ) {
257  concatToLast = true;
258  if ( result.isEmpty() ) {
259  result.append( QByteArray() );
260  }
261  result.last() += '[';
262  ++i;
263  continue;
264  }
265  if ( m_data.at( i ) == ']' ) {
266  concatToLast = false;
267  result.last() += ']';
268  ++i;
269  continue;
270  }
271  if ( count == 0 ) {
272  m_position = i;
273  QByteArray ba;
274  if ( hasLiteral() ) {
275  while ( !atLiteralEnd() ) {
276  ba += readLiteralPart();
277  }
278  } else {
279  ba = readString();
280  }
281 
282  // We might sometime get some unwanted CRLF, but we're still not at the end
283  // of the list, would make further string reads fail so eat the CRLFs.
284  while ( ( m_position < m_data.size() ) &&
285  ( m_data.at( m_position ) == '\r' || m_data.at( m_position ) == '\n' ) ) {
286  m_position++;
287  }
288 
289  i = m_position - 1;
290  if ( concatToLast ) {
291  result.last() += ba;
292  } else {
293  result.append( ba );
294  }
295  }
296  ++i;
297  }
298 
299  throw ImapParserException( "Something went very very wrong!" );
300 }
301 
302 bool ImapStreamParser::hasResponseCode()
303 {
304  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
305  throw ImapParserException( "Unable to read more data" );
306  }
307  int savedPos = m_position;
308  stripLeadingSpaces();
309  int pos = m_position;
310  m_position = savedPos;
311  if ( m_data.at( pos ) == '[' ) {
312  m_position = pos + 1;
313  return true;
314  }
315  return false;
316 }
317 
318 bool ImapStreamParser::atResponseCodeEnd()
319 {
320  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
321  throw ImapParserException( "Unable to read more data" );
322  }
323  int savedPos = m_position;
324  stripLeadingSpaces();
325  int pos = m_position;
326  m_position = savedPos;
327  if ( m_data.at( pos ) == ']' ) {
328  m_position = pos + 1;
329  return true;
330  }
331  return false;
332 }
333 
334 QByteArray ImapStreamParser::parseQuotedString()
335 {
336  QByteArray result;
337  if ( !waitForMoreData( m_data.length() == 0 ) ) {
338  throw ImapParserException( "Unable to read more data" );
339  }
340  stripLeadingSpaces();
341  int end = m_position;
342  result.clear();
343  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
344  throw ImapParserException( "Unable to read more data" );
345  }
346  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
347  throw ImapParserException( "Unable to read more data" );
348  }
349 
350  bool foundSlash = false;
351  // quoted string
352  if ( m_data.at( m_position ) == '"' ) {
353  ++m_position;
354  int i = m_position;
355  Q_FOREVER {
356  if ( !waitForMoreData( m_data.length() <= i ) ) {
357  m_position = i;
358  throw ImapParserException( "Unable to read more data" );
359  }
360  if ( m_data.at( i ) == '\\' ) {
361  i += 2;
362  foundSlash = true;
363  continue;
364  }
365  if ( m_data.at( i ) == '"' ) {
366  result = m_data.mid( m_position, i - m_position );
367  end = i + 1; // skip the '"'
368  break;
369  }
370  ++i;
371  }
372  }
373 
374  // unquoted string
375  else {
376  bool reachedInputEnd = true;
377  int i = m_position;
378  Q_FOREVER {
379  if ( !waitForMoreData( m_data.length() <= i ) ) {
380  m_position = i;
381  throw ImapParserException( "Unable to read more data" );
382  }
383  if ( m_data.at( i ) == ' ' ||
384  m_data.at( i ) == '(' ||
385  m_data.at( i ) == ')' ||
386  m_data.at( i ) == '[' ||
387  m_data.at( i ) == ']' ||
388  m_data.at( i ) == '\n' ||
389  m_data.at( i ) == '\r' ||
390  m_data.at( i ) == '"' ) {
391  end = i;
392  reachedInputEnd = false;
393  break;
394  }
395  if ( m_data.at( i ) == '\\' ) {
396  foundSlash = true;
397  }
398  i++;
399  }
400  if ( reachedInputEnd ) { //FIXME: how can it get here?
401  end = m_data.length();
402  }
403 
404  result = m_data.mid( m_position, end - m_position );
405  }
406 
407  // strip quotes
408  if ( foundSlash ) {
409  while ( result.contains( "\\\"" ) ) {
410  result.replace( "\\\"", "\"" );
411  }
412  while ( result.contains( "\\\\" ) ) {
413  result.replace( "\\\\", "\\" );
414  }
415  }
416  m_position = end;
417  return result;
418 }
419 
420 qint64 ImapStreamParser::readNumber( bool * ok )
421 {
422  qint64 result;
423  if ( ok ) {
424  *ok = false;
425  }
426  if ( !waitForMoreData( m_data.length() == 0 ) ) {
427  throw ImapParserException( "Unable to read more data" );
428  }
429  stripLeadingSpaces();
430  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
431  throw ImapParserException( "Unable to read more data" );
432  }
433  if ( m_position >= m_data.length() ) {
434  throw ImapParserException( "Unable to read more data" );
435  }
436  int i = m_position;
437  Q_FOREVER {
438  if ( !waitForMoreData( m_data.length() <= i ) ) {
439  m_position = i;
440  throw ImapParserException( "Unable to read more data" );
441  }
442  if ( !isdigit( m_data.at( i ) ) ) {
443  break;
444  }
445  ++i;
446  }
447  const QByteArray tmp = m_data.mid( m_position, i - m_position );
448  result = tmp.toLongLong( ok );
449  m_position = i;
450  return result;
451 }
452 
453 void ImapStreamParser::stripLeadingSpaces()
454 {
455  for ( int i = m_position; i < m_data.length(); ++i ) {
456  if ( m_data.at( i ) != ' ' ) {
457  m_position = i;
458  return;
459  }
460  }
461  m_position = m_data.length();
462 }
463 
464 bool ImapStreamParser::waitForMoreData( bool wait )
465 {
466  if ( wait ) {
467  if ( m_socket->bytesAvailable() > 0 ||
468  m_socket->waitForReadyRead( 30000 ) ) {
469  m_data.append( m_socket->readAll() );
470  } else {
471  return false;
472  }
473  }
474  return true;
475 }
476 
477 void ImapStreamParser::setData( const QByteArray &data )
478 {
479  m_data = data;
480 }
481 
482 QByteArray ImapStreamParser::readRemainingData()
483 {
484  return m_data.mid( m_position );
485 }
486 
487 int ImapStreamParser::availableDataSize() const
488 {
489  return m_socket->bytesAvailable() + m_data.size() - m_position;
490 }
491 
492 bool ImapStreamParser::atCommandEnd()
493 {
494  int savedPos = m_position;
495  do {
496  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
497  throw ImapParserException( "Unable to read more data" );
498  }
499  stripLeadingSpaces();
500  } while ( m_position >= m_data.size() );
501 
502  if ( m_data.at( m_position ) == '\n' || m_data.at( m_position ) == '\r' ) {
503  if ( m_data.at( m_position ) == '\r' ) {
504  ++m_position;
505  }
506  if ( m_position < m_data.length() && m_data.at( m_position ) == '\n' ) {
507  ++m_position;
508  }
509 
510  // We'd better empty m_data from time to time before it grows out of control
511  trimBuffer();
512 
513  return true; //command end
514  }
515  m_position = savedPos;
516  return false; //something else
517 }
518 
519 QByteArray ImapStreamParser::readUntilCommandEnd()
520 {
521  QByteArray result;
522  int i = m_position;
523  int paranthesisBalance = 0;
524  Q_FOREVER {
525  if ( !waitForMoreData( m_data.length() <= i ) ) {
526  m_position = i;
527  throw ImapParserException( "Unable to read more data" );
528  }
529  if ( m_data.at( i ) == '{' ) {
530  m_position = i - 1;
531  hasLiteral(); //init literal size
532  result.append( m_data.mid( i - 1, m_position - i + 1 ) );
533  while ( !atLiteralEnd() ) {
534  result.append( readLiteralPart() );
535  }
536  i = m_position;
537  }
538  if ( m_data.at( i ) == '(' ) {
539  paranthesisBalance++;
540  }
541  if ( m_data.at( i ) == ')' ) {
542  paranthesisBalance--;
543  }
544  if ( ( i == m_data.length() && paranthesisBalance == 0 ) ||
545  m_data.at( i ) == '\n' || m_data.at( i ) == '\r') {
546  break; //command end
547  }
548  result.append( m_data.at( i ) );
549  ++i;
550  }
551  m_position = i;
552  atCommandEnd();
553  return result;
554 }
555 
556 void ImapStreamParser::sendContinuationResponse( qint64 size )
557 {
558  QByteArray block = "+ Ready for literal data (expecting " +
559  QByteArray::number( size ) + " bytes)\r\n";
560  m_socket->write( block );
561  m_socket->waitForBytesWritten( 30000 );
562 }
563 
564 void ImapStreamParser::trimBuffer()
565 {
566  if ( m_position < 4096 ) { // right() is expensive, so don't do it for every line
567  return;
568  }
569  m_data = m_data.right( m_data.size() - m_position );
570  m_position = 0;
571 }
KIMAP::ImapStreamParser::readUntilCommandEnd
QByteArray readUntilCommandEnd()
Return everything that remained from the command.
Definition: imapstreamparser.cpp:519
KIMAP::ImapStreamParser::readNumber
qint64 readNumber(bool *ok=0)
Get the next data as a number.
Definition: imapstreamparser.cpp:420
KIMAP::ImapStreamParser::readLiteralPart
QByteArray readLiteralPart()
Read the next literal sequence.
Definition: imapstreamparser.cpp:149
KIMAP::ImapStreamParser::atLiteralEnd
bool atLiteralEnd() const
Check if the literal data end was reached.
Definition: imapstreamparser.cpp:144
KIMAP::ImapStreamParser::hasList
bool hasList()
Check if the next data is a parenthesized list.
Definition: imapstreamparser.cpp:172
KIMAP::ImapStreamParser::hasString
bool hasString()
Check if the next data is a string or not.
Definition: imapstreamparser.cpp:75
KIMAP::ImapStreamParser::atCommandEnd
bool atCommandEnd()
Check if the command end was reached.
Definition: imapstreamparser.cpp:492
KIMAP::ImapStreamParser::readRemainingData
QByteArray readRemainingData()
Return all the data that was read from the socket, but not processed yet.
Definition: imapstreamparser.cpp:482
KIMAP::ImapStreamParser::hasResponseCode
bool hasResponseCode()
Check if the next data is a response code.
Definition: imapstreamparser.cpp:302
KIMAP::ImapStreamParser::~ImapStreamParser
~ImapStreamParser()
Destructor.
Definition: imapstreamparser.cpp:39
KIMAP::ImapStreamParser::hasLiteral
bool hasLiteral()
Check if the next data is a literal data or not.
Definition: imapstreamparser.cpp:103
KIMAP::ImapStreamParser::readString
QByteArray readString()
Same as above, but without decoding it to utf8.
Definition: imapstreamparser.cpp:51
KIMAP::ImapStreamParser::atListEnd
bool atListEnd()
Check if the next data is a parenthesized list end.
Definition: imapstreamparser.cpp:187
KIMAP::ImapStreamParser::readParenthesizedList
QList< QByteArray > readParenthesizedList()
Get he next parenthesized list.
Definition: imapstreamparser.cpp:203
KIMAP::ImapStreamParser::atResponseCodeEnd
bool atResponseCodeEnd()
Check if the next data is a response code end.
Definition: imapstreamparser.cpp:318
KIMAP::ImapStreamParser::readUtf8String
QString readUtf8String()
Get a string from the message.
Definition: imapstreamparser.cpp:43
KIMAP::ImapStreamParser::ImapStreamParser
ImapStreamParser(QIODevice *socket, bool serverModeEnabled=false)
Construct the parser.
Definition: imapstreamparser.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:08 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



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