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