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

KIMAP Library

  • 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 
52 QByteArray ImapStreamParser::readString()
53 {
54  QByteArray result;
55  if ( !waitForMoreData( m_data.length() == 0 ) ) {
56  throw ImapParserException( "Unable to read more data" );
57  }
58  stripLeadingSpaces();
59  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
60  throw ImapParserException( "Unable to read more data" );
61  }
62 
63  // literal string
64  // TODO: error handling
65  if ( hasLiteral() ) {
66  while ( !atLiteralEnd() ) {
67  result += readLiteralPart();
68  }
69  return result;
70  }
71 
72  // quoted string
73  return parseQuotedString();
74 }
75 
76 bool ImapStreamParser::hasString()
77 {
78  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
79  throw ImapParserException( "Unable to read more data" );
80  }
81  int savedPos = m_position;
82  stripLeadingSpaces();
83  int pos = m_position;
84  m_position = savedPos;
85  if ( m_data.at( pos ) == '{' ) {
86  return true; //literal string
87  }
88  if ( m_data.at( pos ) == '"' ) {
89  return true; //quoted string
90  }
91  if ( m_data.at( pos ) != ' ' &&
92  m_data.at( pos ) != '(' &&
93  m_data.at( pos ) != ')' &&
94  m_data.at( pos ) != '[' &&
95  m_data.at( pos ) != ']' &&
96  m_data.at( pos ) != '\n' &&
97  m_data.at( pos ) != '\r' ) {
98  return true; //unquoted string
99  }
100 
101  return false; //something else, not a string
102 }
103 
104 bool ImapStreamParser::hasLiteral()
105 {
106  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
107  throw ImapParserException( "Unable to read more data" );
108  }
109  int savedPos = m_position;
110  stripLeadingSpaces();
111  if ( m_data.at( m_position ) == '{' ) {
112  int end = -1;
113  do {
114  end = m_data.indexOf( '}', m_position );
115  if ( !waitForMoreData( end == -1 ) ) {
116  throw ImapParserException( "Unable to read more data" );
117  }
118  } while ( end == -1 );
119  Q_ASSERT( end > m_position );
120  m_literalSize = m_data.mid( m_position + 1, end - m_position - 1 ).toInt();
121  // strip CRLF
122  m_position = end + 1;
123 
124  if ( m_position < m_data.length() && m_data.at( m_position ) == '\r' ) {
125  ++m_position;
126  }
127  if ( m_position < m_data.length() && m_data.at( m_position ) == '\n' ) {
128  ++m_position;
129  }
130 
131  //FIXME: Makes sense only on the server side?
132  if ( m_isServerModeEnabled && m_literalSize > 0 ) {
133  sendContinuationResponse( m_literalSize );
134  }
135  return true;
136  } else {
137  m_position = savedPos;
138  return false;
139  }
140 }
141 
142 bool ImapStreamParser::atLiteralEnd() const
143 {
144  return ( m_literalSize == 0 );
145 }
146 
147 QByteArray ImapStreamParser::readLiteralPart()
148 {
149  static qint64 maxLiteralPartSize = 4096;
150  int size = qMin(maxLiteralPartSize, m_literalSize);
151 
152  if ( !waitForMoreData( m_data.length() < m_position + size ) ) {
153  throw ImapParserException( "Unable to read more data" );
154  }
155 
156  if ( m_data.length() < m_position + size ) { // Still not enough data
157  // Take what's already there
158  size = m_data.length() - m_position;
159  }
160 
161  QByteArray result = m_data.mid( m_position, size );
162  m_position += size;
163  m_literalSize -= size;
164  Q_ASSERT( m_literalSize >= 0 );
165  trimBuffer();
166 
167  return result;
168 }
169 
170 bool ImapStreamParser::hasList()
171 {
172  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
173  throw ImapParserException( "Unable to read more data" );
174  }
175  int savedPos = m_position;
176  stripLeadingSpaces();
177  int pos = m_position;
178  m_position = savedPos;
179  if ( m_data.at( pos ) == '(' ) {
180  return true;
181  }
182  return false;
183 }
184 
185 bool ImapStreamParser::atListEnd()
186 {
187  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
188  throw ImapParserException( "Unable to read more data" );
189  }
190  int savedPos = m_position;
191  stripLeadingSpaces();
192  int pos = m_position;
193  m_position = savedPos;
194  if ( m_data.at( pos ) == ')' ) {
195  m_position = pos + 1;
196  return true;
197  }
198  return false;
199 }
200 
201 QList<QByteArray> ImapStreamParser::readParenthesizedList()
202 {
203  QList<QByteArray> result;
204  if ( !waitForMoreData( m_data.length() <= m_position ) ) {
205  throw ImapParserException( "Unable to read more data" );
206  }
207 
208  stripLeadingSpaces();
209  if ( m_data.at( m_position ) != '(' ) {
210  return result; //no list found
211  }
212 
213  bool concatToLast = false;
214  int count = 0;
215  int sublistbegin = m_position;
216  int i = m_position + 1;
217  Q_FOREVER {
218  if ( !waitForMoreData( m_data.length() <= i ) ) {
219  m_position = i;
220  throw ImapParserException( "Unable to read more data" );
221  }
222  if ( m_data.at( i ) == '(' ) {
223  ++count;
224  if ( count == 1 ) {
225  sublistbegin = i;
226  }
227  ++i;
228  continue;
229  }
230  if ( m_data.at( i ) == ')' ) {
231  if ( count <= 0 ) {
232  m_position = i + 1;
233  return result;
234  }
235  if ( count == 1 ) {
236  result.append( m_data.mid( sublistbegin, i - sublistbegin + 1 ) );
237  }
238  --count;
239  ++i;
240  continue;
241  }
242  if ( m_data.at( i ) == ' ' ) {
243  ++i;
244  continue;
245  }
246  if ( m_data.at( i ) == '"' ) {
247  if ( count > 0 ) {
248  m_position = i;
249  parseQuotedString();
250  i = m_position;
251  continue;
252  }
253  }
254  if ( m_data.at( i ) == '[' ) {
255  concatToLast = true;
256  if ( result.isEmpty() ) {
257  result.append( QByteArray() );
258  }
259  result.last() += '[';
260  ++i;
261  continue;
262  }
263  if ( m_data.at( i ) == ']' ) {
264  concatToLast = false;
265  result.last() += ']';
266  ++i;
267  continue;
268  }
269  if ( count == 0 ) {
270  m_position = i;
271  QByteArray ba;
272  if ( hasLiteral() ) {
273  while ( !atLiteralEnd() ) {
274  ba += readLiteralPart();
275  }
276  } else {
277  ba = readString();
278  }
279 
280  // We might sometime get some unwanted CRLF, but we're still not at the end
281  // of the list, would make further string reads fail so eat the CRLFs.
282  while ( ( m_position < m_data.size() ) &&
283  ( m_data.at( m_position ) == '\r' || m_data.at( m_position ) == '\n' ) ) {
284  m_position++;
285  }
286 
287  i = m_position - 1;
288  if ( concatToLast ) {
289  result.last() += ba;
290  } else {
291  result.append( ba );
292  }
293  }
294  ++i;
295  }
296 
297  throw ImapParserException( "Something went very very wrong!" );
298 }
299 
300 bool ImapStreamParser::hasResponseCode()
301 {
302  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
303  throw ImapParserException( "Unable to read more data" );
304  }
305  int savedPos = m_position;
306  stripLeadingSpaces();
307  int pos = m_position;
308  m_position = savedPos;
309  if ( m_data.at( pos ) == '[' ) {
310  m_position = pos + 1;
311  return true;
312  }
313  return false;
314 }
315 
316 bool ImapStreamParser::atResponseCodeEnd()
317 {
318  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
319  throw ImapParserException( "Unable to read more data" );
320  }
321  int savedPos = m_position;
322  stripLeadingSpaces();
323  int pos = m_position;
324  m_position = savedPos;
325  if ( m_data.at( pos ) == ']' ) {
326  m_position = pos + 1;
327  return true;
328  }
329  return false;
330 }
331 
332 QByteArray ImapStreamParser::parseQuotedString()
333 {
334  QByteArray result;
335  if ( !waitForMoreData( m_data.length() == 0 ) ) {
336  throw ImapParserException( "Unable to read more data" );
337  }
338  stripLeadingSpaces();
339  int end = m_position;
340  result.clear();
341  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
342  throw ImapParserException( "Unable to read more data" );
343  }
344  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
345  throw ImapParserException( "Unable to read more data" );
346  }
347 
348  bool foundSlash = false;
349  // quoted string
350  if ( m_data.at( m_position ) == '"' ) {
351  ++m_position;
352  int i = m_position;
353  Q_FOREVER {
354  if ( !waitForMoreData( m_data.length() <= i ) ) {
355  m_position = i;
356  throw ImapParserException( "Unable to read more data" );
357  }
358  if ( m_data.at( i ) == '\\' ) {
359  i += 2;
360  foundSlash = true;
361  continue;
362  }
363  if ( m_data.at( i ) == '"' ) {
364  result = m_data.mid( m_position, i - m_position );
365  end = i + 1; // skip the '"'
366  break;
367  }
368  ++i;
369  }
370  }
371 
372  // unquoted string
373  else {
374  bool reachedInputEnd = true;
375  int i = m_position;
376  Q_FOREVER {
377  if ( !waitForMoreData( m_data.length() <= i ) ) {
378  m_position = i;
379  throw ImapParserException( "Unable to read more data" );
380  }
381  if ( m_data.at( i ) == ' ' ||
382  m_data.at( i ) == '(' ||
383  m_data.at( i ) == ')' ||
384  m_data.at( i ) == '[' ||
385  m_data.at( i ) == ']' ||
386  m_data.at( i ) == '\n' ||
387  m_data.at( i ) == '\r' ||
388  m_data.at( i ) == '"' ) {
389  end = i;
390  reachedInputEnd = false;
391  break;
392  }
393  if ( m_data.at( i ) == '\\' ) {
394  foundSlash = true;
395  }
396  i++;
397  }
398  if ( reachedInputEnd ) { //FIXME: how can it get here?
399  end = m_data.length();
400  }
401 
402  result = m_data.mid( m_position, end - m_position );
403  }
404 
405  // strip quotes
406  if ( foundSlash ) {
407  while ( result.contains( "\\\"" ) ) {
408  result.replace( "\\\"", "\"" );
409  }
410  while ( result.contains( "\\\\" ) ) {
411  result.replace( "\\\\", "\\" );
412  }
413  }
414  m_position = end;
415  return result;
416 }
417 
418 qint64 ImapStreamParser::readNumber( bool * ok )
419 {
420  qint64 result;
421  if ( ok ) {
422  *ok = false;
423  }
424  if ( !waitForMoreData( m_data.length() == 0 ) ) {
425  throw ImapParserException( "Unable to read more data" );
426  }
427  stripLeadingSpaces();
428  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
429  throw ImapParserException( "Unable to read more data" );
430  }
431  if ( m_position >= m_data.length() ) {
432  throw ImapParserException( "Unable to read more data" );
433  }
434  int i = m_position;
435  Q_FOREVER {
436  if ( !waitForMoreData( m_data.length() <= i ) ) {
437  m_position = i;
438  throw ImapParserException( "Unable to read more data" );
439  }
440  if ( !isdigit( m_data.at( i ) ) ) {
441  break;
442  }
443  ++i;
444  }
445  const QByteArray tmp = m_data.mid( m_position, i - m_position );
446  result = tmp.toLongLong( ok );
447  m_position = i;
448  return result;
449 }
450 
451 void ImapStreamParser::stripLeadingSpaces()
452 {
453  for ( int i = m_position; i < m_data.length(); ++i ) {
454  if ( m_data.at( i ) != ' ' ) {
455  m_position = i;
456  return;
457  }
458  }
459  m_position = m_data.length();
460 }
461 
462 bool ImapStreamParser::waitForMoreData( bool wait )
463 {
464  if ( wait ) {
465  if ( m_socket->bytesAvailable() > 0 ||
466  m_socket->waitForReadyRead( 30000 ) ) {
467  m_data.append( m_socket->readAll() );
468  } else {
469  return false;
470  }
471  }
472  return true;
473 }
474 
475 void ImapStreamParser::setData( const QByteArray &data )
476 {
477  m_data = data;
478 }
479 
480 QByteArray ImapStreamParser::readRemainingData()
481 {
482  return m_data.mid( m_position );
483 }
484 
485 int ImapStreamParser::availableDataSize() const
486 {
487  return m_socket->bytesAvailable() + m_data.size() - m_position;
488 }
489 
490 bool ImapStreamParser::atCommandEnd()
491 {
492  int savedPos = m_position;
493  do {
494  if ( !waitForMoreData( m_position >= m_data.length() ) ) {
495  throw ImapParserException( "Unable to read more data" );
496  }
497  stripLeadingSpaces();
498  } while ( m_position >= m_data.size() );
499 
500  if ( m_data.at( m_position ) == '\n' || m_data.at( m_position ) == '\r' ) {
501  if ( m_data.at( m_position ) == '\r' ) {
502  ++m_position;
503  }
504  if ( m_position < m_data.length() && m_data.at( m_position ) == '\n' ) {
505  ++m_position;
506  }
507 
508  // We'd better empty m_data from time to time before it grows out of control
509  trimBuffer();
510 
511  return true; //command end
512  }
513  m_position = savedPos;
514  return false; //something else
515 }
516 
517 QByteArray ImapStreamParser::readUntilCommandEnd()
518 {
519  QByteArray result;
520  int i = m_position;
521  int paranthesisBalance = 0;
522  Q_FOREVER {
523  if ( !waitForMoreData( m_data.length() <= i ) ) {
524  m_position = i;
525  throw ImapParserException( "Unable to read more data" );
526  }
527  if ( m_data.at( i ) == '{' ) {
528  m_position = i - 1;
529  hasLiteral(); //init literal size
530  result.append( m_data.mid( i - 1, m_position - i + 1 ) );
531  while ( !atLiteralEnd() ) {
532  result.append( readLiteralPart() );
533  }
534  i = m_position;
535  }
536  if ( m_data.at( i ) == '(' ) {
537  paranthesisBalance++;
538  }
539  if ( m_data.at( i ) == ')' ) {
540  paranthesisBalance--;
541  }
542  if ( ( i == m_data.length() && paranthesisBalance == 0 ) ||
543  m_data.at( i ) == '\n' || m_data.at( i ) == '\r') {
544  break; //command end
545  }
546  result.append( m_data.at( i ) );
547  ++i;
548  }
549  m_position = i;
550  atCommandEnd();
551  return result;
552 }
553 
554 void ImapStreamParser::sendContinuationResponse( qint64 size )
555 {
556  QByteArray block = "+ Ready for literal data (expecting " +
557  QByteArray::number( size ) + " bytes)\r\n";
558  m_socket->write( block );
559  m_socket->waitForBytesWritten( 30000 );
560 }
561 
562 void ImapStreamParser::trimBuffer()
563 {
564  if ( m_position < 4096 ) { // right() is expensive, so don't do it for every line
565  return;
566  }
567  m_data = m_data.right( m_data.size() - m_position );
568  m_position = 0;
569 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:25:17 by doxygen 1.8.3.1 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.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • 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