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

KMIME Library

  • kmime
kmime_content.cpp
Go to the documentation of this file.
1 /*
2  kmime_content.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  Copyright (c) 2006 Volker Krause <vkrause@kde.org>
8  Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Library General Public
12  License as published by the Free Software Foundation; either
13  version 2 of the License, or (at your option) any later version.
14 
15  This library is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Library General Public License for more details.
19 
20  You should have received a copy of the GNU Library General Public License
21  along with this library; see the file COPYING.LIB. If not, write to
22  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  Boston, MA 02110-1301, USA.
24 */
37 #include "kmime_content.h"
38 #include "kmime_content_p.h"
39 #include "kmime_codecs.h"
40 #include "kmime_message.h"
41 #include "kmime_header_parsing.h"
42 #include "kmime_header_parsing_p.h"
43 #include "kmime_parsers.h"
44 #include "kmime_util_p.h"
45 
46 #include <kcharsets.h>
47 #include <kcodecs.h>
48 #include <kglobal.h>
49 #include <klocale.h>
50 #include <kdebug.h>
51 
52 #include <QtCore/QTextCodec>
53 #include <QtCore/QTextStream>
54 #include <QtCore/QByteArray>
55 
56 using namespace KMime;
57 
58 namespace KMime {
59 
60 Content::Content()
61  : d_ptr( new ContentPrivate( this ) )
62 {
63 }
64 
65 Content::Content( Content *parent )
66  : d_ptr( new ContentPrivate( this ) )
67 {
68  d_ptr->parent = parent;
69 }
70 
71 Content::Content( const QByteArray &h, const QByteArray &b )
72  : d_ptr( new ContentPrivate( this ) )
73 {
74  d_ptr->head = h;
75  d_ptr->body = b;
76 }
77 
78 Content::Content( const QByteArray &h, const QByteArray &b, Content *parent )
79  : d_ptr( new ContentPrivate( this ) )
80 {
81  d_ptr->head = h;
82  d_ptr->body = b;
83  d_ptr->parent = parent;
84 }
85 
86 Content::Content( ContentPrivate *d )
87  : d_ptr( d )
88 {
89 }
90 
91 Content::~Content()
92 {
93  qDeleteAll( h_eaders );
94  h_eaders.clear();
95  delete d_ptr;
96  d_ptr = 0;
97 }
98 
99 bool Content::hasContent() const
100 {
101  return !d_ptr->head.isEmpty() || !d_ptr->body.isEmpty() || !d_ptr->contents().isEmpty();
102 }
103 
104 void Content::setContent( const QList<QByteArray> &l )
105 {
106  Q_D( Content );
107  //qDebug("Content::setContent( const QList<QByteArray> &l ) : start");
108  d->head.clear();
109  d->body.clear();
110 
111  //usage of textstreams is much faster than simply appending the strings
112  QTextStream hts( &( d->head ), QIODevice::WriteOnly );
113  QTextStream bts( &( d->body ), QIODevice::WriteOnly );
114  hts.setCodec( "ISO 8859-1" );
115  bts.setCodec( "ISO 8859-1" );
116 
117  bool isHead = true;
118  foreach ( const QByteArray& line, l ) {
119  if ( isHead && line.isEmpty() ) {
120  isHead = false;
121  continue;
122  }
123  if ( isHead ) {
124  hts << line << "\n";
125  } else {
126  bts << line << "\n";
127  }
128  }
129 
130  //qDebug("Content::setContent( const QList<QByteArray> & l ) : finished");
131 }
132 
133 void Content::setContent( const QByteArray &s )
134 {
135  Q_D( Content );
136  KMime::HeaderParsing::extractHeaderAndBody( s, d->head, d->body );
137 }
138 
139 QByteArray Content::head() const
140 {
141  return d_ptr->head;
142 }
143 
144 void Content::setHead( const QByteArray &head )
145 {
146  d_ptr->head = head;
147  if ( !head.endsWith( '\n' ) ) {
148  d_ptr->head += '\n';
149  }
150 }
151 
152 QByteArray Content::body() const
153 {
154  return d_ptr->body;
155 }
156 
157 void Content::setBody( const QByteArray &body )
158 {
159  d_ptr->body = body;
160 }
161 
162 QByteArray Content::preamble() const
163 {
164  return d_ptr->preamble;
165 }
166 
167 void Content::setPreamble( const QByteArray &preamble )
168 {
169  d_ptr->preamble = preamble;
170 }
171 
172 
173 QByteArray Content::epilogue() const
174 {
175  return d_ptr->epilogue;
176 }
177 
178 void Content::setEpilogue( const QByteArray &epilogue )
179 {
180  d_ptr->epilogue = epilogue;
181 }
182 
183 void Content::parse()
184 {
185  Q_D( Content );
186 
187  // Clean up old headers and parse them again.
188  qDeleteAll( h_eaders );
189  h_eaders.clear();
190  h_eaders = HeaderParsing::parseHeaders( d->head );
191  foreach ( Headers::Base *h, h_eaders ) {
192  h->setParent( this );
193  }
194 
195  // If we are frozen, save the body as-is. This is done because parsing
196  // changes the content (it loses preambles and epilogues, converts uuencode->mime, etc.)
197  if ( d->frozen ) {
198  d->frozenBody = d->body;
199  }
200 
201  // Clean up old sub-Contents and parse them again.
202  qDeleteAll( d->multipartContents );
203  d->multipartContents.clear();
204  d->clearBodyMessage();
205  Headers::ContentType *ct = contentType();
206  if ( ct->isText() ) {
207  // This content is either text, or of unknown type.
208 
209  if ( d->parseUuencoded() ) {
210  // This is actually uuencoded content generated by broken software.
211  } else if ( d->parseYenc() ) {
212  // This is actually yenc content generated by broken software.
213  } else {
214  // This is just plain text.
215  }
216  } else if ( ct->isMultipart() ) {
217  // This content claims to be MIME multipart.
218 
219  if ( d->parseMultipart() ) {
220  // This is actual MIME multipart content.
221  } else {
222  // Parsing failed; treat this content as "text/plain".
223  ct->setMimeType( "text/plain" );
224  ct->setCharset( "US-ASCII" );
225  }
226  } else {
227  // This content is something else, like an encapsulated message or a binary attachment
228  // or something like that
229  if ( bodyIsMessage() ) {
230  d->bodyAsMessage = Message::Ptr( new Message );
231  d->bodyAsMessage->setContent( d->body );
232  d->bodyAsMessage->setFrozen( d->frozen );
233  d->bodyAsMessage->parse();
234  d->bodyAsMessage->d_ptr->parent = this;
235 
236  // Clear the body, as it is now represented by d->bodyAsMessage. This is the same behavior
237  // as with multipart contents, since parseMultipart() clears the body as well
238  d->body.clear();
239  }
240  }
241 }
242 
243 bool Content::isFrozen() const
244 {
245  return d_ptr->frozen;
246 }
247 
248 void Content::setFrozen( bool frozen )
249 {
250  d_ptr->frozen = frozen;
251 }
252 
253 void Content::assemble()
254 {
255  Q_D( Content );
256  if ( d->frozen ) {
257  return;
258  }
259 
260  d->head = assembleHeaders();
261  foreach ( Content *c, contents() ) {
262  c->assemble();
263  }
264 }
265 
266 QByteArray Content::assembleHeaders()
267 {
268  QByteArray newHead;
269  foreach ( const Headers::Base *h, h_eaders ) {
270  if ( !h->isEmpty() ) {
271  newHead += h->as7BitString() + '\n';
272  }
273  }
274 
275  return newHead;
276 }
277 
278 void Content::clear()
279 {
280  Q_D( Content );
281  qDeleteAll( h_eaders );
282  h_eaders.clear();
283  clearContents();
284  d->head.clear();
285  d->body.clear();
286 }
287 
288 void Content::clearContents( bool del )
289 {
290  Q_D( Content );
291  if ( del ) {
292  qDeleteAll( d->multipartContents );
293  }
294  d->multipartContents.clear();
295  d->clearBodyMessage();
296 }
297 
298 QByteArray Content::encodedContent( bool useCrLf )
299 {
300  Q_D( Content );
301  QByteArray e;
302 
303  // Head.
304  e = d->head;
305  e += '\n';
306  e += encodedBody();
307 
308  if ( useCrLf ) {
309  return LFtoCRLF( e );
310  } else {
311  return e;
312  }
313 }
314 
315 QByteArray Content::encodedBody()
316 {
317  Q_D( Content );
318  QByteArray e;
319  // Body.
320  if ( d->frozen ) {
321  // This Content is frozen.
322  if ( d->frozenBody.isEmpty() ) {
323  // This Content has never been parsed.
324  e += d->body;
325  } else {
326  // Use the body as it was before parsing.
327  e += d->frozenBody;
328  }
329  } else if ( bodyIsMessage() && d->bodyAsMessage ) {
330  // This is an encapsulated message
331  // No encoding needed, as the ContentTransferEncoding can only be 7bit
332  // for encapsulated messages
333  e += d->bodyAsMessage->encodedContent();
334  } else if ( !d->body.isEmpty() ) {
335  // This is a single-part Content.
336  Headers::ContentTransferEncoding *enc = contentTransferEncoding();
337 
338  if ( enc->needToEncode() ) {
339  if ( enc->encoding() == Headers::CEquPr ) {
340  e += KCodecs::quotedPrintableEncode( d->body, false );
341  } else {
342  e += KCodecs::base64Encode( d->body, true );
343  e += '\n';
344  }
345  } else {
346  e += d->body;
347  }
348  }
349 
350  if ( !d->frozen && !d->multipartContents.isEmpty() ) {
351  // This is a multipart Content.
352  Headers::ContentType *ct=contentType();
353  QByteArray boundary = "\n--" + ct->boundary();
354 
355  if ( !d->preamble.isEmpty() ) {
356  e += d->preamble;
357  }
358 
359  //add all (encoded) contents separated by boundaries
360  foreach ( Content *c, d->multipartContents ) {
361  e += boundary + '\n';
362  e += c->encodedContent( false ); // don't convert LFs here, we do that later!!!!!
363  }
364  //finally append the closing boundary
365  e += boundary+"--\n";
366 
367  if ( !d->epilogue.isEmpty() ) {
368  e += d->epilogue;
369  }
370  }
371  return e;
372 }
373 
374 QByteArray Content::decodedContent()
375 {
376  QByteArray ret;
377  Headers::ContentTransferEncoding *ec=contentTransferEncoding();
378  bool removeTrailingNewline=false;
379 
380  if ( d_ptr->body.length() == 0 ) {
381  return ret;
382  }
383 
384  if ( ec->decoded() ) {
385  ret = d_ptr->body;
386  //Laurent Fix bug #311267
387  //removeTrailingNewline = true;
388  } else {
389  switch ( ec->encoding() ) {
390  case Headers::CEbase64 :
391  {
392  KMime::Codec *codec = KMime::Codec::codecForName( "base64" );
393  Q_ASSERT( codec );
394  ret.resize( codec->maxDecodedSizeFor( d_ptr->body.size() ) );
395  KMime::Decoder* decoder = codec->makeDecoder();
396  QByteArray::const_iterator inputIt = d_ptr->body.constBegin();
397  QByteArray::iterator resultIt = ret.begin();
398  decoder->decode( inputIt, d_ptr->body.constEnd(), resultIt, ret.end() );
399  ret.truncate( resultIt - ret.begin() );
400  break;
401  }
402  case Headers::CEquPr :
403  ret = KCodecs::quotedPrintableDecode( d_ptr->body );
404  removeTrailingNewline = true;
405  break;
406  case Headers::CEuuenc :
407  KCodecs::uudecode( d_ptr->body, ret );
408  break;
409  case Headers::CEbinary :
410  ret = d_ptr->body;
411  removeTrailingNewline = false;
412  break;
413  default :
414  ret = d_ptr->body;
415  removeTrailingNewline = true;
416  }
417  }
418 
419  if ( removeTrailingNewline && ( ret.size() > 0 ) && ( ret[ret.size() - 1] == '\n' ) ) {
420  ret.resize( ret.size() - 1 );
421  }
422 
423  return ret;
424 }
425 
426 QString Content::decodedText( bool trimText, bool removeTrailingNewlines )
427 {
428  if ( !decodeText() ) { //this is not a text content !!
429  return QString();
430  }
431 
432  bool ok = true;
433  QTextCodec *codec =
434  KGlobal::charsets()->codecForName( QLatin1String( contentType()->charset() ), ok );
435  if ( !ok || codec == NULL ) { // no suitable codec found => try local settings and hope the best ;-)
436  codec = KGlobal::locale()->codecForEncoding();
437  QByteArray chset = KGlobal::locale()->encoding();
438  contentType()->setCharset( chset );
439  }
440 
441  QString s = codec->toUnicode( d_ptr->body.data(), d_ptr->body.length() );
442 
443  if ( trimText || removeTrailingNewlines ) {
444  int i;
445  for ( i = s.length() - 1; i >= 0; --i ) {
446  if ( trimText ) {
447  if ( !s[i].isSpace() ) {
448  break;
449  }
450  }
451  else {
452  if ( s[i] != QLatin1Char( '\n' ) ) {
453  break;
454  }
455  }
456  }
457  s.truncate( i + 1 );
458  } else {
459  if ( s.right( 1 ) == QLatin1String( "\n" ) ) {
460  s.truncate( s.length() - 1 ); // remove trailing new-line
461  }
462  }
463 
464  return s;
465 }
466 
467 void Content::fromUnicodeString( const QString &s )
468 {
469  bool ok = true;
470  QTextCodec *codec =
471  KGlobal::charsets()->codecForName( QLatin1String( contentType()->charset() ), ok );
472 
473  if ( !ok ) { // no suitable codec found => try local settings and hope the best ;-)
474  codec = KGlobal::locale()->codecForEncoding();
475  QByteArray chset = KGlobal::locale()->encoding();
476  contentType()->setCharset( chset );
477  }
478 
479  d_ptr->body = codec->fromUnicode( s );
480  contentTransferEncoding()->setDecoded( true ); //text is always decoded
481 }
482 
483 Content *Content::textContent()
484 {
485  Content *ret=0;
486 
487  //return the first content with mimetype=text/*
488  if ( contentType()->isText() ) {
489  ret = this;
490  } else {
491  foreach ( Content *c, d_ptr->contents() ) {
492  if ( ( ret = c->textContent() ) != 0 ) {
493  break;
494  }
495  }
496  }
497  return ret;
498 }
499 
500 Content::List Content::attachments( bool incAlternatives )
501 {
502  List attachments;
503  if ( d_ptr->contents().isEmpty() ) {
504  attachments.append( this );
505  } else {
506  foreach ( Content *c, d_ptr->contents() ) {
507  if ( !incAlternatives &&
508  c->contentType()->category() == Headers::CCalternativePart ) {
509  continue;
510  } else {
511  attachments += c->attachments( incAlternatives );
512  }
513  }
514  }
515 
516  if ( isTopLevel() ) {
517  Content *text = textContent();
518  if ( text ) {
519  attachments.removeAll( text );
520  }
521  }
522  return attachments;
523 }
524 
525 Content::List Content::contents() const
526 {
527  return d_ptr->contents();
528 }
529 
530 void Content::addContent( Content *c, bool prepend )
531 {
532  Q_D( Content );
533 
534  // This method makes no sense for encapsulated messages
535  Q_ASSERT( !bodyIsMessage() );
536 
537  // If this message is single-part; make it multipart first.
538  if( d->multipartContents.isEmpty() && !contentType()->isMultipart() ) {
539  // The current body will be our first sub-Content.
540  Content *main = new Content( this );
541 
542  // Move the MIME headers to the newly created sub-Content.
543  // NOTE: The other headers (RFC5322 headers like From:, To:, as well as X-headers
544  // are not moved to the subcontent; they remain with the top-level content.
545  for ( Headers::Base::List::iterator it = h_eaders.begin();
546  it != h_eaders.end(); ) {
547  if ( (*it)->isMimeHeader() ) {
548  // Add to new content.
549  main->setHeader( *it );
550  // Remove from this content.
551  it = h_eaders.erase( it );
552  } else {
553  ++it;
554  }
555  }
556 
557  // Adjust the Content-Type of the newly created sub-Content.
558  main->contentType()->setCategory( Headers::CCmixedPart );
559 
560  // Move the body to the new subcontent.
561  main->setBody( d->body );
562  d->body.clear();
563 
564  // Add the subcontent.
565  d->multipartContents.append( main );
566 
567  // Convert this content to "multipart/mixed".
568  Headers::ContentType *ct = contentType();
569  ct->setMimeType( "multipart/mixed" );
570  ct->setBoundary( multiPartBoundary() );
571  ct->setCategory( Headers::CCcontainer );
572  contentTransferEncoding()->clear(); // 7Bit, decoded.
573  }
574 
575  // Add the new content.
576  if( prepend ) {
577  d->multipartContents.prepend( c );
578  } else {
579  d->multipartContents.append( c );
580  }
581 
582  if( c->parent() != this ) {
583  // If the content was part of something else, this will remove it from there.
584  c->setParent( this );
585  }
586 }
587 
588 void Content::removeContent( Content *c, bool del )
589 {
590  Q_D( Content );
591  if ( d->multipartContents.isEmpty() || !d->multipartContents.contains( c ) ) {
592  return;
593  }
594 
595  // This method makes no sense for encapsulated messages.
596  // Should be covered by the above assert already, though.
597  Q_ASSERT( !bodyIsMessage() );
598 
599  d->multipartContents.removeAll( c );
600  if ( del ) {
601  delete c;
602  } else {
603  c->d_ptr->parent = 0;
604  }
605 
606  // If only one content is left, turn this content into a single-part.
607  if( d->multipartContents.count() == 1 ) {
608  Content *main = d->multipartContents.first();
609 
610  // Move all headers from the old subcontent to ourselves.
611  // NOTE: This also sets the new Content-Type.
612  foreach( Headers::Base *h, main->h_eaders ) {
613  setHeader( h ); // Will remove the old one if present.
614  }
615  main->h_eaders.clear();
616 
617  // Move the body.
618  d->body = main->body();
619 
620  // Delete the old subcontent.
621  delete main;
622  d->multipartContents.clear();
623  }
624 }
625 
626 void Content::changeEncoding( Headers::contentEncoding e )
627 {
628  // This method makes no sense for encapsulated messages, they are always 7bit
629  // encoded.
630  Q_ASSERT( !bodyIsMessage() );
631 
632  Headers::ContentTransferEncoding *enc = contentTransferEncoding();
633  if( enc->encoding() == e ) {
634  // Nothing to do.
635  return;
636  }
637 
638  if( decodeText() ) {
639  // This is textual content. Textual content is stored decoded.
640  Q_ASSERT( enc->decoded() );
641  enc->setEncoding( e );
642  } else {
643  // This is non-textual content. Re-encode it.
644  if( e == Headers::CEbase64 ) {
645  d_ptr->body = KCodecs::base64Encode( decodedContent(), true );
646  d_ptr->body.append( "\n" );
647  enc->setEncoding( e );
648  enc->setDecoded( false );
649  } else {
650  // It only makes sense to convert binary stuff to base64.
651  Q_ASSERT( false );
652  }
653  }
654 }
655 
656 void Content::toStream( QTextStream &ts, bool scrambleFromLines )
657 {
658  QByteArray ret = encodedContent( false );
659 
660  if ( scrambleFromLines ) {
661  // FIXME Why are only From lines with a preceding empty line considered?
662  // And, of course, all lines starting with >*From have to be escaped
663  // because otherwise the transformation is not revertable.
664  ret.replace( "\n\nFrom ", "\n\n>From ");
665  }
666  ts << ret;
667 }
668 
669 Headers::Generic *Content::getNextHeader( QByteArray &head )
670 {
671  return d_ptr->nextHeader( head );
672 }
673 
674 Headers::Generic *Content::nextHeader( QByteArray &head )
675 {
676  return d_ptr->nextHeader( head );
677 }
678 
679 Headers::Generic *ContentPrivate::nextHeader( QByteArray &_head )
680 {
681  Headers::Base *header = HeaderParsing::extractFirstHeader( _head );
682  if ( !header ) {
683  return 0;
684  }
685  // Convert it from the real class to Generic.
686  Headers::Generic *ret = new Headers::Generic( header->type(), q_ptr );
687  ret->from7BitString( header->as7BitString() );
688  return ret;
689 }
690 
691 Headers::Base *Content::getHeaderByType( const char *type )
692 {
693  return headerByType( type );
694 }
695 
696 Headers::Base *Content::headerByType( const char *type )
697 {
698  Q_ASSERT( type && *type );
699 
700  foreach( Headers::Base *h, h_eaders ) {
701  if( h->is( type ) ) {
702  return h; // Found.
703  }
704  }
705 
706  return 0; // Not found.
707 }
708 
709 Headers::Base::List Content::headersByType( const char *type )
710 {
711  Q_ASSERT( type && *type );
712 
713  Headers::Base::List result;
714 
715  foreach( Headers::Base *h, h_eaders ) {
716  if( h->is( type ) ) {
717  result << h;
718  }
719  }
720 
721  return result;
722 }
723 
724 void Content::setHeader( Headers::Base *h )
725 {
726  Q_ASSERT( h );
727  removeHeader( h->type() );
728  appendHeader( h );
729 }
730 
731 void Content::appendHeader( Headers::Base *h )
732 {
733  h_eaders.append( h );
734  h->setParent( this );
735 }
736 
737 void Content::prependHeader( Headers::Base *h )
738 {
739  h_eaders.prepend( h );
740  h->setParent( this );
741 }
742 
743 bool Content::removeHeader( const char *type )
744 {
745  for ( Headers::Base::List::iterator it = h_eaders.begin();
746  it != h_eaders.end(); ++it )
747  if ( (*it)->is(type) ) {
748  delete (*it);
749  h_eaders.erase( it );
750  return true;
751  }
752 
753  return false;
754 }
755 
756 bool Content::hasHeader( const char *type )
757 {
758  return headerByType( type ) != 0;
759 }
760 
761 int Content::size()
762 {
763  int ret = d_ptr->body.length();
764 
765  if ( contentTransferEncoding()->encoding() == Headers::CEbase64 ) {
766  KMime::Codec *codec = KMime::Codec::codecForName( "base64" );
767  return codec->maxEncodedSizeFor(ret);
768  }
769 
770  // Not handling quoted-printable here since that requires actually
771  // converting the content, and that is O(size_of_content).
772  // For quoted-printable, this is only an approximate size.
773 
774  return ret;
775 }
776 
777 int Content::storageSize() const
778 {
779  const Q_D( Content );
780  int s = d->head.size();
781 
782  if ( d->contents().isEmpty() ) {
783  s += d->body.size();
784  } else {
785 
786  // FIXME: This should take into account the boundary headers that are added in
787  // encodedContent!
788  foreach ( Content *c, d->contents() ) {
789  s += c->storageSize();
790  }
791  }
792 
793  return s;
794 }
795 
796 int Content::lineCount() const
797 {
798  const Q_D( Content );
799  int ret = 0;
800  if ( !isTopLevel() ) {
801  ret += d->head.count( '\n' );
802  }
803  ret += d->body.count( '\n' );
804 
805  foreach ( Content *c, d->contents() ) {
806  ret += c->lineCount();
807  }
808 
809  return ret;
810 }
811 
812 QByteArray Content::rawHeader( const char *name ) const
813 {
814  return KMime::extractHeader( d_ptr->head, name );
815 }
816 
817 QList<QByteArray> Content::rawHeaders( const char *name ) const
818 {
819  return KMime::extractHeaders( d_ptr->head, name );
820 }
821 
822 bool Content::decodeText()
823 {
824  Q_D( Content );
825  Headers::ContentTransferEncoding *enc = contentTransferEncoding();
826 
827  if ( !contentType()->isText() ) {
828  return false; //non textual data cannot be decoded here => use decodedContent() instead
829  }
830  if ( enc->decoded() ) {
831  return true; //nothing to do
832  }
833 
834  switch( enc->encoding() )
835  {
836  case Headers::CEbase64 :
837  d->body = KCodecs::base64Decode( d->body );
838  d->body.append( "\n" );
839  break;
840  case Headers::CEquPr :
841  d->body = KCodecs::quotedPrintableDecode( d->body );
842  break;
843  case Headers::CEuuenc :
844  d->body = KCodecs::uudecode( d->body );
845  d->body.append( "\n" );
846  break;
847  case Headers::CEbinary :
848  // nothing to decode
849  d->body.append( "\n" );
850  default :
851  break;
852  }
853  enc->setDecoded( true );
854  return true;
855 }
856 
857 QByteArray Content::defaultCharset() const
858 {
859  return d_ptr->defaultCS;
860 }
861 
862 void Content::setDefaultCharset( const QByteArray &cs )
863 {
864  d_ptr->defaultCS = KMime::cachedCharset( cs );
865 
866  foreach ( Content *c, d_ptr->contents() ) {
867  c->setDefaultCharset( cs );
868  }
869 
870  // reparse the part and its sub-parts in order
871  // to clear cached header values
872  parse();
873 }
874 
875 bool Content::forceDefaultCharset() const
876 {
877  return d_ptr->forceDefaultCS;
878 }
879 
880 void Content::setForceDefaultCharset( bool b )
881 {
882  d_ptr->forceDefaultCS = b;
883 
884  foreach ( Content *c, d_ptr->contents() ) {
885  c->setForceDefaultCharset( b );
886  }
887 
888  // reparse the part and its sub-parts in order
889  // to clear cached header values
890  parse();
891 }
892 
893 Content * KMime::Content::content( const ContentIndex &index ) const
894 {
895  if ( !index.isValid() ) {
896  return const_cast<KMime::Content*>( this );
897  }
898  ContentIndex idx = index;
899  unsigned int i = idx.pop() - 1; // one-based -> zero-based index
900  if ( i < (unsigned int)d_ptr->contents().size() ) {
901  return d_ptr->contents()[i]->content( idx );
902  } else {
903  return 0;
904  }
905 }
906 
907 ContentIndex KMime::Content::indexForContent( Content * content ) const
908 {
909  int i = d_ptr->contents().indexOf( content );
910  if ( i >= 0 ) {
911  ContentIndex ci;
912  ci.push( i + 1 ); // zero-based -> one-based index
913  return ci;
914  }
915  // not found, we need to search recursively
916  for ( int i = 0; i < d_ptr->contents().size(); ++i ) {
917  ContentIndex ci = d_ptr->contents()[i]->indexForContent( content );
918  if ( ci.isValid() ) {
919  // found it
920  ci.push( i + 1 ); // zero-based -> one-based index
921  return ci;
922  }
923  }
924  return ContentIndex(); // not found
925 }
926 
927 bool Content::isTopLevel() const
928 {
929  return d_ptr->parent == 0;
930 }
931 
932 void Content::setParent( Content *parent )
933 {
934  // Make sure the Content is only in the contents list of one parent object
935  Content *oldParent = d_ptr->parent;
936  if ( oldParent ) {
937  if ( !oldParent->contents().isEmpty() && oldParent->contents().contains( this ) ) {
938  oldParent->removeContent( this );
939  }
940  }
941 
942  d_ptr->parent = parent;
943  if ( parent ) {
944  if ( !parent->contents().isEmpty() && !parent->contents().contains( this ) ) {
945  parent->addContent( this );
946  }
947  }
948 }
949 
950 Content *Content::parent() const
951 {
952  return d_ptr->parent;
953 }
954 
955 Content *Content::topLevel() const
956 {
957  Content *top = const_cast<Content*>(this);
958  Content *c = parent();
959  while ( c ) {
960  top = c;
961  c = c->parent();
962  }
963 
964  return top;
965 }
966 
967 ContentIndex Content::index() const
968 {
969  Content* top = topLevel();
970  if ( top ) {
971  return top->indexForContent( const_cast<Content*>(this) );
972  }
973 
974  return indexForContent( const_cast<Content*>(this) );
975 }
976 
977 Message::Ptr Content::bodyAsMessage() const
978 {
979  if ( bodyIsMessage() && d_ptr->bodyAsMessage ) {
980  return d_ptr->bodyAsMessage;
981  } else {
982  return Message::Ptr();
983  }
984 }
985 
986 bool Content::bodyIsMessage() const
987 {
988  // Use const_case here to work around API issue that neither header() nor hasHeader() are
989  // const, even though they should be
990  return const_cast<Content*>( this )->header<Headers::ContentType>( false ) &&
991  const_cast<Content*>( this )->header<Headers::ContentType>( true )
992  ->mimeType().toLower() == "message/rfc822";
993 }
994 
995 // @cond PRIVATE
996 #define kmime_mk_header_accessor( type, method ) \
997 Headers::type *Content::method( bool create ) { \
998  return header<Headers::type>( create ); \
999 }
1000 
1001 kmime_mk_header_accessor( ContentType, contentType )
1002 kmime_mk_header_accessor( ContentTransferEncoding, contentTransferEncoding )
1003 kmime_mk_header_accessor( ContentDisposition, contentDisposition )
1004 kmime_mk_header_accessor( ContentDescription, contentDescription )
1005 kmime_mk_header_accessor( ContentLocation, contentLocation )
1006 kmime_mk_header_accessor( ContentID, contentID )
1007 
1008 #undef kmime_mk_header_accessor
1009 // @endcond
1010 
1011 
1012 void ContentPrivate::clearBodyMessage()
1013 {
1014  bodyAsMessage.reset();
1015 }
1016 
1017 Content::List ContentPrivate::contents() const
1018 {
1019  Q_ASSERT( multipartContents.isEmpty() || !bodyAsMessage );
1020  if ( bodyAsMessage )
1021  return Content::List() << bodyAsMessage.get();
1022  else
1023  return multipartContents;
1024 }
1025 
1026 bool ContentPrivate::parseUuencoded()
1027 {
1028  Q_Q( Content );
1029  Parser::UUEncoded uup( body, KMime::extractHeader( head, "Subject" ) );
1030  if( !uup.parse() ) {
1031  return false; // Parsing failed.
1032  }
1033 
1034  Headers::ContentType *ct = q->contentType();
1035  ct->clear();
1036 
1037  if( uup.isPartial() ) {
1038  // This seems to be only a part of the message, so we treat it as "message/partial".
1039  ct->setMimeType( "message/partial" );
1040  //ct->setId( uniqueString() ); not needed yet
1041  ct->setPartialParams( uup.partialCount(), uup.partialNumber() );
1042  q->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
1043  } else {
1044  // This is a complete message, so treat it as "multipart/mixed".
1045  body.clear();
1046  ct->setMimeType( "multipart/mixed" );
1047  ct->setBoundary( multiPartBoundary() );
1048  ct->setCategory( Headers::CCcontainer );
1049  q->contentTransferEncoding()->clear(); // 7Bit, decoded.
1050 
1051  // Add the plain text part first.
1052  Q_ASSERT( multipartContents.count() == 0 );
1053  {
1054  Content *c = new Content( q );
1055  c->contentType()->setMimeType( "text/plain" );
1056  c->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
1057  c->setBody( uup.textPart() );
1058  multipartContents.append( c );
1059  }
1060 
1061  // Now add each of the binary parts as sub-Contents.
1062  for( int i = 0; i < uup.binaryParts().count(); ++i ) {
1063  Content *c = new Content( q );
1064  c->contentType()->setMimeType( uup.mimeTypes().at( i ) );
1065  c->contentType()->setName( QLatin1String( uup.filenames().at( i ) ), QByteArray( /*charset*/ ) );
1066  c->contentTransferEncoding()->setEncoding( Headers::CEuuenc );
1067  c->contentTransferEncoding()->setDecoded( false );
1068  c->contentDisposition()->setDisposition( Headers::CDattachment );
1069  c->contentDisposition()->setFilename( QLatin1String( uup.filenames().at( i ) ) );
1070  c->setBody( uup.binaryParts().at( i ) );
1071  c->changeEncoding( Headers::CEbase64 ); // Convert to base64.
1072  multipartContents.append( c );
1073  }
1074  }
1075 
1076  return true; // Parsing successful.
1077 }
1078 
1079 bool ContentPrivate::parseYenc()
1080 {
1081  Q_Q( Content );
1082  Parser::YENCEncoded yenc( body );
1083  if ( !yenc.parse() ) {
1084  return false; // Parsing failed.
1085  }
1086 
1087  Headers::ContentType *ct = q->contentType();
1088  ct->clear();
1089 
1090  if ( yenc.isPartial() ) {
1091  // Assume there is exactly one decoded part. Treat this as "message/partial".
1092  ct->setMimeType( "message/partial" );
1093  //ct->setId( uniqueString() ); not needed yet
1094  ct->setPartialParams( yenc.partialCount(), yenc.partialNumber() );
1095  q->contentTransferEncoding()->setEncoding( Headers::CEbinary );
1096  q->changeEncoding( Headers::CEbase64 ); // Convert to base64.
1097  } else {
1098  // This is a complete message, so treat it as "multipart/mixed".
1099  body.clear();
1100  ct->setMimeType( "multipart/mixed" );
1101  ct->setBoundary( multiPartBoundary() );
1102  ct->setCategory( Headers::CCcontainer );
1103  q->contentTransferEncoding()->clear(); // 7Bit, decoded.
1104 
1105  // Add the plain text part first.
1106  Q_ASSERT( multipartContents.count() == 0 );
1107  {
1108  Content *c = new Content( q );
1109  c->contentType()->setMimeType( "text/plain" );
1110  c->contentTransferEncoding()->setEncoding( Headers::CE7Bit );
1111  c->setBody( yenc.textPart() );
1112  multipartContents.append( c );
1113  }
1114 
1115  // Now add each of the binary parts as sub-Contents.
1116  for ( int i=0; i<yenc.binaryParts().count(); i++ ) {
1117  Content *c = new Content( q );
1118  c->contentType()->setMimeType( yenc.mimeTypes().at( i ) );
1119  c->contentType()->setName( QLatin1String( yenc.filenames().at( i ) ), QByteArray( /*charset*/ ) );
1120  c->contentTransferEncoding()->setEncoding( Headers::CEbinary );
1121  c->contentDisposition()->setDisposition( Headers::CDattachment );
1122  c->contentDisposition()->setFilename( QLatin1String( yenc.filenames().at( i ) ) );
1123  c->setBody( yenc.binaryParts().at( i ) ); // Yenc bodies are binary.
1124  c->changeEncoding( Headers::CEbase64 ); // Convert to base64.
1125  multipartContents.append( c );
1126  }
1127  }
1128 
1129  return true; // Parsing successful.
1130 }
1131 
1132 bool ContentPrivate::parseMultipart()
1133 {
1134  Q_Q( Content );
1135  const Headers::ContentType *ct = q->contentType();
1136  const QByteArray boundary = ct->boundary();
1137  if ( boundary.isEmpty() ) {
1138  return false; // Parsing failed; invalid multipart content.
1139  }
1140  Parser::MultiPart mpp( body, boundary );
1141  if ( !mpp.parse() ) {
1142  return false; // Parsing failed.
1143  }
1144 
1145  preamble = mpp.preamble();
1146  epilogue = mpp.epilouge();
1147 
1148  // Determine the category of the subparts (used in attachments()).
1149  Headers::contentCategory cat;
1150  if ( ct->isSubtype( "alternative" ) ) {
1151  cat = Headers::CCalternativePart;
1152  } else {
1153  cat = Headers::CCmixedPart; // Default to "mixed".
1154  }
1155 
1156  // Create a sub-Content for every part.
1157  Q_ASSERT( multipartContents.isEmpty() );
1158  body.clear();
1159  QList<QByteArray> parts = mpp.parts();
1160  foreach ( const QByteArray &part, mpp.parts() ) {
1161  Content *c = new Content( q );
1162  c->setContent( part );
1163  c->setFrozen( frozen );
1164  c->parse();
1165  c->contentType()->setCategory( cat );
1166  multipartContents.append( c );
1167  }
1168 
1169  return true; // Parsing successful.
1170 }
1171 
1172 } // namespace KMime
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:25:45 by doxygen 1.8.3.1 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.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