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

KDE's Doxygen guidelines are available online.

KMIME Library

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

kdepimlibs-4.14.10 API Reference

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