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

Skip menu "kdepimlibs-4.9.5 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