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

KMIME Library

  • kmime
kmime_parsers.cpp
1 /*
2  kmime_parsers.cpp
3 
4  KMime, the KDE Internet mail/usenet news message library.
5  Copyright (c) 2001 the KMime authors.
6  See file AUTHORS for details
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public 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
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22 */
23 #include "kmime_parsers.h"
24 
25 #include <QtCore/QRegExp>
26 #include <QtCore/QByteArray>
27 
28 using namespace KMime::Parser;
29 
30 namespace KMime {
31 namespace Parser {
32 
33 MultiPart::MultiPart( const QByteArray &src, const QByteArray &boundary )
34 {
35  s_rc=src;
36  b_oundary=boundary;
37 }
38 
39 bool MultiPart::parse()
40 {
41  QByteArray b = "--" + b_oundary, part;
42  int pos1=0, pos2=0, blen=b.length();
43 
44  p_arts.clear();
45 
46  //find the first valid boundary
47  while ( 1 ) {
48  if ( ( pos1 = s_rc.indexOf( b, pos1 ) ) == -1 || pos1 == 0 ||
49  s_rc[pos1-1] == '\n' ) { //valid boundary found or no boundary at all
50  break;
51  }
52  pos1 += blen; //boundary found but not valid => skip it;
53  }
54 
55  if ( pos1 > -1 ) {
56  pos1 += blen;
57  if ( s_rc[pos1] == '-' && s_rc[pos1+1] == '-' ) {
58  // the only valid boundary is the end-boundary
59  // this message is *really* broken
60  pos1 = -1; //we give up
61  } else if ( ( pos1 - blen ) > 1 ) { //preamble present
62  p_reamble = s_rc.left( pos1 - blen - 1 );
63  }
64  }
65 
66  while ( pos1 > -1 && pos2 > -1 ) {
67 
68  //skip the rest of the line for the first boundary - the message-part starts here
69  if ( ( pos1 = s_rc.indexOf( '\n', pos1 ) ) > -1 ) {
70  //now search the next linebreak
71  //now find the next valid boundary
72  pos2=++pos1; //pos1 and pos2 point now to the beginning of the next line after the boundary
73  while ( 1 ) {
74  if ( ( pos2 = s_rc.indexOf( b, pos2 ) ) == -1 ||
75  s_rc[pos2-1] == '\n' ) { //valid boundary or no more boundaries found
76  break;
77  }
78  pos2 += blen; //boundary is invalid => skip it;
79  }
80 
81  if ( pos2 == -1 ) { // no more boundaries found
82  part = s_rc.mid( pos1, s_rc.length() - pos1 ); //take the rest of the string
83  p_arts.append( part );
84  pos1 = -1;
85  pos2 = -1; //break;
86  } else {
87  part = s_rc.mid( pos1, pos2 - pos1 - 1 ); // pos2 - 1 (\n) is part of the boundary (see RFC 2046, section 5.1.1)
88  p_arts.append( part );
89  pos2 += blen; //pos2 points now to the first character after the boundary
90  if ( s_rc[pos2] == '-' && s_rc[pos2+1] == '-' ) { //end-boundary
91  pos1 = pos2 + 2; //pos1 points now to the character directly after the end-boundary
92 
93  if ( ( pos1 = s_rc.indexOf( '\n', pos1 ) ) > -1 ) { //skip the rest of this line
94  //everything after the end-boundary is considered as the epilouge
95  e_pilouge = s_rc.mid( pos1 + 1, s_rc.length() - pos1 - 1 );
96  }
97  pos1 = -1;
98  pos2 = -1; //break
99  } else {
100  pos1 = pos2; //the search continues ...
101  }
102  }
103  }
104  }
105 
106  return !p_arts.isEmpty();
107 }
108 
109 //=============================================================================
110 
111 NonMimeParser::NonMimeParser( const QByteArray &src ) :
112  s_rc( src ), p_artNr( -1 ), t_otalNr( -1 )
113 {
114 }
115 
119 QByteArray NonMimeParser::guessMimeType( const QByteArray &fileName )
120 {
121  QByteArray tmp, mimeType;
122  int pos;
123 
124  if ( !fileName.isEmpty() ) {
125  pos = fileName.lastIndexOf( '.' );
126  if ( pos++ != -1 ) {
127  tmp = fileName.mid( pos, fileName.length() - pos).toUpper();
128  if ( tmp == "JPG" || tmp=="JPEG" ) {
129  mimeType = "image/jpeg";
130  } else if ( tmp == "GIF") {
131  mimeType = "image/gif";
132  } else if ( tmp == "PNG") {
133  mimeType = "image/png";
134  } else if ( tmp == "TIFF" || tmp == "TIF") {
135  mimeType = "image/tiff";
136  } else if ( tmp == "XPM") {
137  mimeType = "image/x-xpixmap";
138  } else if ( tmp == "XBM") {
139  mimeType = "image/x-xbitmap";
140  } else if ( tmp == "BMP") {
141  mimeType = "image/bmp";
142  } else if ( tmp == "TXT" ||
143  tmp == "ASC" ||
144  tmp == "H" ||
145  tmp == "C" ||
146  tmp == "CC" ||
147  tmp == "CPP") {
148  mimeType = "text/plain";
149  } else if ( tmp == "HTML" || tmp == "HTM" ) {
150  mimeType = "text/html";
151  } else {
152  mimeType = "application/octet-stream";
153  }
154  } else {
155  mimeType = "application/octet-stream";
156  }
157  } else {
158  mimeType = "application/octet-stream";
159  }
160 
161  return mimeType;
162 }
163 
164 //==============================================================================
165 
166 UUEncoded::UUEncoded( const QByteArray &src, const QByteArray &subject ) :
167  NonMimeParser( src ), s_ubject( subject )
168 {}
169 
170 bool UUEncoded::parse()
171 {
172  int currentPos=0;
173  bool success=true, firstIteration=true;
174 
175  while ( success ) {
176  int beginPos=currentPos, uuStart=currentPos, endPos=0, lineCount=0, MCount=0, pos=0, len=0;
177  bool containsBegin=false, containsEnd=false;
178  QByteArray tmp, fileName;
179 
180  if ( ( beginPos = QString::fromLatin1( s_rc ).indexOf( QRegExp( QLatin1String( "begin [0-9][0-9][0-9]" ) ),
181  currentPos ) ) > -1 &&
182  ( beginPos == 0 || s_rc.at( beginPos - 1 ) == '\n') ) {
183  containsBegin = true;
184  uuStart = s_rc.indexOf( '\n', beginPos );
185  if ( uuStart == -1 ) {//no more line breaks found, we give up
186  success = false;
187  break;
188  } else {
189  uuStart++; //points now at the beginning of the next line
190  }
191  } else {
192  beginPos=currentPos;
193  }
194 
195  if ( ( endPos = s_rc.
196  indexOf( "\nend", ( uuStart > 0 ) ? uuStart-1:0 ) ) == -1 ) {
197  endPos = s_rc.length(); //no end found
198  } else {
199  containsEnd = true;
200  }
201 
202  if ( ( containsBegin && containsEnd ) || firstIteration ) {
203 
204  //printf("beginPos=%d , uuStart=%d , endPos=%d\n", beginPos, uuStart, endPos);
205  //all lines in a uuencoded text start with 'M'
206  for ( int idx=uuStart; idx<endPos; idx++ ) {
207  if ( s_rc[idx] == '\n' ) {
208  lineCount++;
209  if ( idx+1 < endPos && s_rc[idx+1] == 'M') {
210  idx++;
211  MCount++;
212  }
213  }
214  }
215 
216  //printf("lineCount=%d , MCount=%d\n", lineCount, MCount);
217  if ( MCount == 0 || ( lineCount - MCount ) > 10 ||
218  ( ( !containsBegin || !containsEnd ) && ( MCount < 15 ) ) ) {
219  // harder check for split-articles
220  success = false;
221  break; //too many "non-M-Lines" found, we give up
222  }
223 
224  if ( ( !containsBegin || !containsEnd ) && !s_ubject.isNull() ) {
225  // message may be split up => parse subject
226  QRegExp rx( QLatin1String( "[0-9]+/[0-9]+") );
227  pos = rx.indexIn( QLatin1String( s_ubject ), 0 );
228  len = rx.matchedLength();
229  if ( pos != -1 ) {
230  tmp = s_ubject.mid( pos, len );
231  pos = tmp.indexOf( '/' );
232  p_artNr = tmp.left( pos ).toInt();
233  t_otalNr = tmp.right( tmp.length() - pos - 1).toInt();
234  } else {
235  success = false;
236  break; //no "part-numbers" found in the subject, we give up
237  }
238  }
239 
240  //everything before "begin" is text
241  if ( beginPos > 0 ) {
242  t_ext.append( s_rc.mid( currentPos, beginPos - currentPos ) );
243  }
244 
245  if ( containsBegin ) {
246  //everything between "begin ### " and the next LF is considered as the filename
247  fileName = s_rc.mid( beginPos + 10, uuStart - beginPos - 11 );
248  } else {
249  fileName = "";
250  }
251  f_ilenames.append( fileName );
252  //everything beetween "begin" and "end" is uuencoded
253  b_ins.append( s_rc.mid( uuStart, endPos - uuStart + 1 ) );
254  m_imeTypes.append( guessMimeType( fileName ) );
255  firstIteration = false;
256 
257  int next = s_rc.indexOf( '\n', endPos + 1 );
258  if ( next == -1 ) { //no more line breaks found, we give up
259  success = false;
260  break;
261  } else {
262  next++; //points now at the beginning of the next line
263  }
264  currentPos = next;
265 
266  } else {
267  success = false;
268  }
269  }
270 
271  // append trailing text part of the article
272  t_ext.append( s_rc.right( s_rc.length() - currentPos ) );
273 
274  return ( ( b_ins.count() > 0 ) || isPartial() );
275 }
276 
277 //==============================================================================
278 
279 YENCEncoded::YENCEncoded( const QByteArray &src ) :
280  NonMimeParser( src )
281 {
282 }
283 
284 bool YENCEncoded::yencMeta( QByteArray &src, const QByteArray &name, int *value )
285 {
286  bool found = false;
287  QByteArray sought=name + '=';
288 
289  int iPos = src.indexOf( sought );
290  if ( iPos > -1 ) {
291  int pos1 = src.indexOf( ' ', iPos );
292  int pos2 = src.indexOf( '\r', iPos );
293  int pos3 = src.indexOf( '\t', iPos );
294  int pos4 = src.indexOf( '\n', iPos );
295  if ( pos2 >= 0 && ( pos1 < 0 || pos1 > pos2 ) ) {
296  pos1 = pos2;
297  }
298  if ( pos3 >= 0 && ( pos1 < 0 || pos1 > pos3 ) ) {
299  pos1 = pos3;
300  }
301  if ( pos4 >= 0 && ( pos1 < 0 || pos1 > pos4 ) ) {
302  pos1 = pos4;
303  }
304  iPos=src.lastIndexOf( '=', pos1 ) + 1;
305  if ( iPos < pos1 ) {
306  char c = src.at( iPos );
307  if ( c>='0' && c<='9' ) {
308  found = true;
309  *value = src.mid( iPos, pos1 - iPos ).toInt();
310  }
311  }
312  }
313  return found;
314 }
315 
316 bool YENCEncoded::parse()
317 {
318  int currentPos=0;
319  bool success=true;
320 
321  while ( success ) {
322  int beginPos=currentPos, yencStart=currentPos;
323  bool containsPart=false;
324  QByteArray fileName, mimeType;
325 
326  if ( ( beginPos = s_rc.
327  indexOf( "=ybegin ", currentPos ) ) > -1 &&
328  ( beginPos == 0 || s_rc.at( beginPos - 1 ) == '\n' ) ) {
329  yencStart = s_rc.indexOf( '\n', beginPos );
330  if ( yencStart == -1 ) { // no more line breaks found, give up
331  success = false;
332  break;
333  } else {
334  yencStart++;
335  if ( s_rc.indexOf( "=ypart", yencStart ) == yencStart ) {
336  containsPart = true;
337  yencStart = s_rc.indexOf( '\n', yencStart );
338  if ( yencStart == -1 ) {
339  success = false;
340  break;
341  }
342  yencStart++;
343  }
344  }
345  // Try to identify yenc meta data
346 
347  // Filenames can contain any embedded chars until end of line
348  QByteArray meta = s_rc.mid( beginPos, yencStart - beginPos );
349  int namePos = meta.indexOf( "name=" );
350  if ( namePos == -1 ) {
351  success = false;
352  break;
353  }
354  int eolPos = meta.indexOf( '\r', namePos );
355  if ( eolPos == -1 ) {
356  eolPos = meta.indexOf( '\n', namePos );
357  }
358  if ( eolPos == -1 ) {
359  success = false;
360  break;
361  }
362  fileName = meta.mid( namePos + 5, eolPos - ( namePos + 5 ) );
363 
364  // Other metadata is integer
365  int yencLine;
366  if ( !yencMeta( meta, "line", &yencLine ) ) {
367  success = false;
368  break;
369  }
370  int yencSize;
371  if ( !yencMeta( meta, "size", &yencSize ) ) {
372  success = false;
373  break;
374  }
375 
376  int partBegin, partEnd;
377  if ( containsPart ) {
378  if ( !yencMeta( meta, "part", &p_artNr ) ) {
379  success = false;
380  break;
381  }
382  if ( !yencMeta( meta, "begin", &partBegin ) ||
383  !yencMeta( meta, "end", &partEnd ) ) {
384  success = false;
385  break;
386  }
387  if ( !yencMeta( meta, "total", &t_otalNr ) ) {
388  t_otalNr = p_artNr + 1;
389  }
390  if ( yencSize == partEnd - partBegin + 1 ) {
391  t_otalNr = 1;
392  } else {
393  yencSize = partEnd - partBegin + 1;
394  }
395  }
396 
397  // We have a valid yenc header; now we extract the binary data
398  int totalSize = 0;
399  int pos = yencStart;
400  int len = s_rc.length();
401  bool lineStart = true;
402  int lineLength = 0;
403  bool containsEnd = false;
404  QByteArray binary;
405  binary.resize( yencSize );
406  while ( pos < len ) {
407  int ch = s_rc.at( pos );
408  if ( ch < 0 ) {
409  ch += 256;
410  }
411  if ( ch == '\r' ) {
412  if ( lineLength != yencLine && totalSize != yencSize ) {
413  break;
414  }
415  pos++;
416  }
417  else if ( ch == '\n' ) {
418  lineStart = true;
419  lineLength = 0;
420  pos++;
421  } else {
422  if ( ch == '=' ) {
423  if ( pos + 1 < len ) {
424  ch = s_rc.at( pos + 1 );
425  if ( lineStart && ch == 'y' ) {
426  containsEnd = true;
427  break;
428  }
429  pos += 2;
430  ch -= 64+42;
431  if ( ch < 0 ) {
432  ch += 256;
433  }
434  if ( totalSize >= yencSize ) {
435  break;
436  }
437  binary[totalSize++] = ch;
438  lineLength++;
439  } else {
440  break;
441  }
442  } else {
443  ch -= 42;
444  if ( ch < 0 ) {
445  ch += 256;
446  }
447  if ( totalSize >= yencSize ) {
448  break;
449  }
450  binary[totalSize++] = ch;
451  lineLength++;
452  pos++;
453  }
454  lineStart = false;
455  }
456  }
457 
458  if ( !containsEnd ) {
459  success = false;
460  break;
461  }
462  if ( totalSize != yencSize ) {
463  success = false;
464  break;
465  }
466 
467  // pos now points to =yend; get end data
468  eolPos = s_rc.indexOf( '\n', pos );
469  if ( eolPos == -1 ) {
470  success = false;
471  break;
472  }
473  meta = s_rc.mid( pos, eolPos - pos );
474  if ( !yencMeta( meta, "size", &totalSize ) ) {
475  success = false;
476  break;
477  }
478  if ( totalSize != yencSize ) {
479  success = false;
480  break;
481  }
482 
483  f_ilenames.append( fileName );
484  m_imeTypes.append( guessMimeType( fileName ) );
485  b_ins.append( binary );
486 
487  //everything before "begin" is text
488  if ( beginPos > 0 ) {
489  t_ext.append( s_rc.mid( currentPos, beginPos - currentPos ) );
490  }
491  currentPos = eolPos + 1;
492 
493  } else {
494  success = false;
495  }
496  }
497 
498  // append trailing text part of the article
499  t_ext.append( s_rc.right( s_rc.length() - currentPos ) );
500 
501  return b_ins.count()>0;
502 }
503 
504 } // namespace Parser
505 
506 } // namespace KMime
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed Nov 28 2012 21:44:57 by doxygen 1.8.1.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KMIME Library

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

kdepimlibs-4.9.3 API Reference

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