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

kioslave/imap4

  • kioslave
  • imap4
mimeheader.cpp
1/***************************************************************************
2 mimeheader.cc - description
3 -------------------
4 begin : Fri Oct 20 2000
5 copyright : (C) 2000 by Sven Carstens
6 email : s.carstens@gmx.de
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "mimeheader.h"
19#include "mimehdrline.h"
20#include "mailheader.h"
21
22#include <QRegExp>
23
24// #include <iostream.h>
25#include <kglobal.h>
26#include <kcomponentdata.h>
27#include <kiconloader.h>
28#include <kmimetype.h>
29#include <kcodecs.h>
30#include <kdebug.h>
31
32#include <kimap/rfccodecs.h>
33using namespace KIMAP;
34
35mimeHeader::mimeHeader ()
36 : typeList (), dispositionList (),
37 _contentType("application/octet-stream"),
38 _contentDisposition(), _contentDescription()
39{
40 // Case insensitive hashes are killing us. Also are they too small?
41 nestedMessage = NULL;
42 contentLength = 0;
43}
44
45mimeHeader::~mimeHeader ()
46{
47}
48
49/*
50QPtrList<mimeHeader> mimeHeader::getAllParts()
51{
52QPtrList<mimeHeader> retVal;
53
54 // caller is responsible for clearing
55 retVal.setAutoDelete( false );
56 nestedParts.setAutoDelete( false );
57
58 // shallow copy
59 retVal = nestedParts;
60
61 // can't have duplicate pointers
62 nestedParts.clear();
63
64 // restore initial state
65 nestedParts.setAutoDelete( true );
66
67 return retVal;
68} */
69
70void
71mimeHeader::addHdrLine (mimeHdrLine * aHdrLine)
72{
73 mimeHdrLine *addLine = new mimeHdrLine( aHdrLine );
74 if ( addLine ) {
75 originalHdrLines.append( addLine );
76 if ( qstrnicmp( addLine->getLabel(), "Content-", 8 ) ) {
77 additionalHdrLines.append( addLine );
78 } else {
79 int skip;
80 const char *aCStr = addLine->getValue().data();
81 QHash < QString, QString > *aList = 0;
82
83 skip = mimeHdrLine::parseSeparator( ';', aCStr );
84 if ( skip > 0 ) {
85 int cut = 0;
86 if ( skip >= 2 ) {
87 if ( aCStr[skip - 1] == '\r' ) {
88 cut++;
89 }
90 if ( aCStr[skip - 1] == '\n' ) {
91 cut++;
92 }
93 if ( aCStr[skip - 2] == '\r' ) {
94 cut++;
95 }
96 if ( aCStr[skip - 1] == ';' ) {
97 cut++;
98 }
99 }
100 QByteArray mimeValue( aCStr, skip - cut );
101
102 if ( !qstricmp( addLine->getLabel(), "Content-Disposition" ) ) {
103 aList = &dispositionList;
104 setDisposition( mimeValue );
105 } else if ( !qstricmp( addLine->getLabel(), "Content-Type" ) ) {
106 aList = &typeList;
107 setType( mimeValue );
108 } else if ( !qstricmp( addLine->getLabel(), "Content-Transfer-Encoding" ) ) {
109 setEncoding( mimeValue );
110 } else if ( !qstricmp( addLine->getLabel(), "Content-ID" ) ) {
111 setID( mimeValue );
112 } else if ( !qstricmp( addLine->getLabel(), "Content-Description" ) ) {
113 setDescription( mimeValue );
114 } else if ( !qstricmp( addLine->getLabel(), "Content-MD5" ) ) {
115 setMD5( mimeValue );
116 } else if ( !qstricmp( addLine->getLabel(), "Content-Length" ) ) {
117 contentLength = mimeValue.toUInt();
118 } else {
119 additionalHdrLines.append( addLine );
120 }
121// cout << addLine->getLabel().data() << ": '" << mimeValue.data() << "'" << endl;
122
123 aCStr += skip;
124 while ( ( skip = mimeHdrLine::parseSeparator( ';', aCStr ) ) ) {
125 if ( skip > 0 ) {
126 if ( aList ) {
127 addParameter( QByteArray( aCStr, skip ).simplified(), *aList );
128 }
129 mimeValue = QByteArray( addLine->getValue().data(), skip );
130 aCStr += skip;
131 } else {
132 break;
133 }
134 }
135 }
136 }
137 }
138}
139
140void
141mimeHeader::addParameter (const QByteArray& aParameter, QHash < QString, QString > &aList)
142{
143 QString aValue;
144 QByteArray aLabel;
145 int pos = aParameter.indexOf( '=' );
146// cout << aParameter.left( pos ).data();
147 aValue = QString::fromLatin1( aParameter.right( aParameter.length() - pos - 1 ) );
148 aLabel = aParameter.left( pos );
149 if ( aValue[0] == '"' ) {
150 aValue = aValue.mid( 1, aValue.length() - 2 );
151 }
152
153 aList.insert( aLabel.toLower(), aValue );
154// cout << "=" << aValue->data() << endl;
155}
156
157QString
158mimeHeader::getDispositionParm (const QByteArray& aStr)
159{
160 return getParameter( aStr, dispositionList );
161}
162
163QString
164mimeHeader::getTypeParm (const QByteArray& aStr)
165{
166 return getParameter( aStr, typeList );
167}
168
169void
170mimeHeader::setDispositionParm (const QByteArray& aLabel, const QString& aValue)
171{
172 setParameter( aLabel, aValue, dispositionList );
173 return;
174}
175
176void
177mimeHeader::setTypeParm (const QByteArray& aLabel, const QString& aValue)
178{
179 setParameter( aLabel, aValue, typeList );
180}
181
182QHashIterator < QString, QString > mimeHeader::getDispositionIterator ()
183{
184 return QHashIterator < QString, QString > ( dispositionList );
185}
186
187QHashIterator < QString, QString > mimeHeader::getTypeIterator ()
188{
189 return QHashIterator < QString, QString > ( typeList );
190}
191
192QListIterator < mimeHdrLine *> mimeHeader::getOriginalIterator ()
193{
194 return QListIterator < mimeHdrLine *> ( originalHdrLines );
195}
196
197QListIterator < mimeHdrLine *> mimeHeader::getAdditionalIterator ()
198{
199 return QListIterator < mimeHdrLine *> ( additionalHdrLines );
200}
201
202void
203mimeHeader::outputHeader (mimeIO & useIO)
204{
205 if ( !getDisposition().isEmpty() ) {
206 useIO.outputMimeLine( QByteArray( "Content-Disposition: " )
207 + getDisposition()
208 + outputParameter( dispositionList ) );
209 }
210
211 if ( !getType().isEmpty() ) {
212 useIO.outputMimeLine( QByteArray( "Content-Type: " )
213 + getType() + outputParameter( typeList ) );
214 }
215 if ( !getDescription().isEmpty() ) {
216 useIO.outputMimeLine( QByteArray( "Content-Description: " ) +
217 getDescription() );
218 }
219 if ( !getID().isEmpty() ) {
220 useIO.outputMimeLine( QByteArray( "Content-ID: " ) + getID() );
221 }
222 if ( !getMD5().isEmpty() ) {
223 useIO.outputMimeLine( QByteArray( "Content-MD5: " ) + getMD5() );
224 }
225 if ( !getEncoding().isEmpty() ) {
226 useIO.outputMimeLine( QByteArray( "Content-Transfer-Encoding: " ) +
227 getEncoding() );
228 }
229
230 QListIterator < mimeHdrLine *> ait = getAdditionalIterator();
231 mimeHdrLine *hdrline;
232 while ( ait.hasNext() ) {
233 hdrline = ait.next();
234 useIO.outputMimeLine( hdrline->getLabel() + ": " +
235 hdrline->getValue() );
236 }
237 useIO.outputMimeLine( QByteArray( "" ) );
238}
239
240QString
241mimeHeader::getParameter (const QByteArray& aStr, QHash < QString, QString > &aDict)
242{
243 QString retVal, found;
244 //see if it is a normal parameter
245 found = aDict.value( aStr );
246 if ( found.isEmpty() ) {
247 //might be a continuated or encoded parameter
248 found = aDict.value( QByteArray(aStr + QByteArray("*")) );
249 if ( found.isEmpty() ) {
250 //continuated parameter
251 QString decoded, encoded;
252 int part = 0;
253
254 do {
255 QByteArray search;
256 search.setNum( part );
257 search = aStr + '*' + search;
258 found = aDict.value( search );
259 if ( found.isEmpty() ) {
260 found = aDict.value( QByteArray(search + QByteArray("*")) );
261 if ( !found.isEmpty() ) {
262 encoded += KIMAP::encodeRFC2231String( found );
263 }
264 } else {
265 encoded += found;
266 }
267 part++;
268 } while ( !found.isEmpty() );
269 if ( encoded.contains( '\'' ) ) {
270 retVal = KIMAP::decodeRFC2231String( encoded.toLocal8Bit() );
271 } else {
272 retVal = KIMAP::decodeRFC2231String( QByteArray(QByteArray( "''" ) + encoded.toLocal8Bit()) );
273 }
274 } else {
275 //simple encoded parameter
276 retVal = KIMAP::decodeRFC2231String( found.toLocal8Bit() );
277 }
278 } else {
279 retVal = found;
280 }
281 return retVal;
282}
283
284void
285mimeHeader::setParameter (const QByteArray& aLabel, const QString& aValue,
286 QHash < QString, QString > &aDict)
287{
288 bool encoded = true;
289 uint vlen, llen;
290 QString val = aValue;
291
292 //see if it needs to get encoded
293 if ( encoded && !aLabel.contains( '*' ) ) {
294 val = KIMAP::encodeRFC2231String( aValue );
295 }
296 //kDebug( 7116 ) << "mimeHeader::setParameter() - val = '" << val << "'";
297 //see if it needs to be truncated
298 vlen = val.length();
299 llen = aLabel.length();
300 if ( vlen + llen + 4 > 80 && llen < 80 - 8 - 2 ) {
301 const int limit = 80 - 8 - 2 - (int)llen;
302 // the -2 is there to allow extending the length of a part of val
303 // by 1 or 2 in order to prevent an encoded character from being
304 // split in half
305 int i = 0;
306 QString shortValue;
307 QByteArray shortLabel;
308
309 while ( !val.isEmpty() ) {
310 int partLen; // the length of the next part of the value
311 if ( limit >= int(vlen) ) {
312 // the rest of the value fits completely into one continued header
313 partLen = vlen;
314 } else {
315 partLen = limit;
316 // make sure that we don't split an encoded char in half
317 if ( val[partLen-1] == '%' ) {
318 partLen += 2;
319 } else if ( partLen > 1 && val[partLen-2] == '%' ) {
320 partLen += 1;
321 }
322 // make sure partLen does not exceed vlen (could happen in case of
323 // an incomplete encoded char)
324 if ( partLen > int(vlen) ) {
325 partLen = vlen;
326 }
327 }
328 shortValue = val.left( partLen );
329 shortLabel.setNum( i );
330 shortLabel = aLabel + '*' + shortLabel;
331 val = val.right( vlen - partLen );
332 vlen = vlen - partLen;
333 if ( encoded ) {
334 if ( i == 0 ) {
335 shortValue = "''" + shortValue;
336 }
337 shortLabel += '*';
338 }
339 //kDebug( 7116 ) << "mimeHeader::setParameter() - shortLabel = '" << shortLabel << "'";
340 //kDebug( 7116 ) << "mimeHeader::setParameter() - shortValue = '" << shortValue << "'";
341 //kDebug( 7116 ) << "mimeHeader::setParameter() - val = '" << val << "'";
342 aDict.insert( shortLabel.toLower(), shortValue );
343 i++;
344 }
345 } else {
346 aDict.insert( aLabel.toLower(), val );
347 }
348}
349
350QByteArray mimeHeader::outputParameter (QHash < QString, QString > &aDict)
351{
352 QByteArray retVal;
353 QHashIterator < QString, QString > it( aDict );
354 while ( it.hasNext() ) {
355 it.next();
356 retVal += ( ";\n\t" + it.key() + '=' ).toLatin1();
357 if ( it.value().indexOf( ' ' ) > 0 || it.value().indexOf( ';' ) > 0 ) {
358 retVal += '"' + it.value().toUtf8() + '"';
359 } else {
360 retVal += it.value().toUtf8();
361 }
362 }
363 retVal += '\n';
364
365 return retVal;
366}
367
368void
369mimeHeader::outputPart (mimeIO & useIO)
370{
371 QListIterator < mimeHeader *> nestedPartsIterator = getNestedIterator();
372 QByteArray boundary;
373 if ( !getTypeParm( "boundary" ).isEmpty() ) {
374 boundary = getTypeParm( "boundary" ).toLatin1();
375 }
376
377 outputHeader( useIO );
378 if ( !getPreBody().isEmpty() ) {
379 useIO.outputMimeLine( getPreBody() );
380 }
381 if ( getNestedMessage() ) {
382 getNestedMessage()->outputPart( useIO );
383 }
384
385 mimeHeader *mimeline;
386 while ( nestedPartsIterator.hasNext() ) {
387 mimeline = nestedPartsIterator.next();
388 if ( !boundary.isEmpty() ) {
389 useIO.outputMimeLine( "--" + boundary );
390 }
391 mimeline->outputPart( useIO );
392 }
393 if ( !boundary.isEmpty() ) {
394 useIO.outputMimeLine( "--" + boundary + "--" );
395 }
396 if ( !getPostBody().isEmpty() ) {
397 useIO.outputMimeLine( getPostBody() );
398 }
399}
400
401#if 0
402int
403mimeHeader::parsePart (mimeIO & useIO, const QString& boundary)
404{
405 int retVal = 0;
406 bool mbox = false;
407 QByteArray preNested, postNested;
408 mbox = parseHeader( useIO );
409
410 kDebug( 7116 ) << "mimeHeader::parsePart - parsing part '" << getType() << "'";
411 if ( !qstrnicmp( getType(), "Multipart", 9 ) ) {
412 retVal = parseBody( useIO, preNested, getTypeParm( "boundary" ) ); //this is a message in mime format stuff
413 setPreBody( preNested );
414 int localRetVal;
415 do {
416 mimeHeader *aHeader = new mimeHeader;
417
418 // set default type for multipart/digest
419 if ( !qstrnicmp( getType(), "Multipart/Digest", 16 ) ) {
420 aHeader->setType( "Message/RFC822" );
421 }
422
423 localRetVal = aHeader->parsePart( useIO, getTypeParm( "boundary" ) );
424 addNestedPart( aHeader );
425 } while ( localRetVal ); //get nested stuff
426 }
427 if ( !qstrnicmp( getType(), "Message/RFC822", 14 ) ) {
428 mailHeader *msgHeader = new mailHeader;
429 retVal = msgHeader->parsePart( useIO, boundary );
430 setNestedMessage( msgHeader );
431 } else {
432 retVal = parseBody( useIO, postNested, boundary, mbox ); //just a simple part remaining
433 setPostBody( postNested );
434 }
435 return retVal;
436}
437
438int
439mimeHeader::parseBody (mimeIO & useIO, QByteArray & messageBody,
440 const QString& boundary, bool mbox)
441{
442 QByteArray inputStr;
443 QByteArray buffer;
444 QString partBoundary;
445 QString partEnd;
446 int retVal = 0; //default is last part
447
448 if ( !boundary.isEmpty() ) {
449 partBoundary = QString( "--" ) + boundary;
450 partEnd = QString( "--" ) + boundary + "--";
451 }
452
453 while ( useIO.inputLine( inputStr ) ) {
454 //check for the end of all parts
455 if ( !partEnd.isEmpty() &&
456 !qstrnicmp( inputStr, partEnd.toLatin1(), partEnd.length() - 1 ) ) {
457 retVal = 0; //end of these parts
458 break;
459 } else if ( !partBoundary.isEmpty() &&
460 !qstrnicmp( inputStr, partBoundary.toLatin1(),
461 partBoundary.length() - 1 ) ) {
462 retVal = 1; //continue with next part
463 break;
464 } else if ( mbox && inputStr.startsWith( "From " ) ) {
465 retVal = 0; // end of mbox
466 break;
467 }
468 buffer += inputStr;
469 if ( buffer.length() > 16384 ) {
470 messageBody += buffer;
471 buffer = "";
472 }
473 }
474
475 messageBody += buffer;
476 return retVal;
477}
478#endif
479
480bool mimeHeader::parseHeader (mimeIO & useIO)
481{
482 bool mbox = false;
483 bool first = true;
484 mimeHdrLine my_line;
485 QByteArray inputStr;
486
487 kDebug( 7116 ) << "mimeHeader::parseHeader - starting parsing";
488 while ( useIO.inputLine( inputStr ) ) {
489 int appended;
490 if ( !inputStr.startsWith( "From " ) || !first ) { //krazy:exclude=strings
491 first = false;
492 appended = my_line.appendStr( inputStr );
493 if ( !appended ) {
494 addHdrLine( &my_line );
495 appended = my_line.setStr( inputStr );
496 }
497 if ( appended <= 0 ) {
498 break;
499 }
500 } else {
501 mbox = true;
502 first = false;
503 }
504 inputStr = QByteArray();
505 }
506
507 kDebug( 7116 ) << "mimeHeader::parseHeader - finished parsing";
508 return mbox;
509}
510
511mimeHeader *
512mimeHeader::bodyPart (const QString & _str)
513{
514 // see if it is nested a little deeper
515 int pt = _str.indexOf( '.' );
516 if ( pt != -1 ) {
517 QString tempStr = _str;
518 mimeHeader *tempPart;
519
520 tempStr = _str.right( _str.length() - pt - 1 );
521 if ( nestedMessage ) {
522 kDebug( 7116 ) << "mimeHeader::bodyPart - recursing message";
523 tempPart = nestedMessage->nestedParts.at( _str.left( pt ).toULong() - 1 );
524 } else {
525 kDebug( 7116 ) << "mimeHeader::bodyPart - recursing mixed";
526 tempPart = nestedParts.at( _str.left( pt ).toULong() - 1 );
527 }
528 if ( tempPart ) {
529 tempPart = tempPart->bodyPart( tempStr );
530 }
531 return tempPart;
532 }
533
534 kDebug( 7116 ) << "mimeHeader::bodyPart - returning part" << _str;
535 // or pick just the plain part
536 if ( nestedMessage ) {
537 kDebug( 7116 ) << "mimeHeader::bodyPart - message";
538 return nestedMessage->nestedParts.at( _str.toULong() - 1 );
539 }
540 kDebug( 7116 ) << "mimeHeader::bodyPart - mixed";
541 return nestedParts.at( _str.toULong() - 1 );
542}
543
544void mimeHeader::serialize(QDataStream& stream)
545{
546 int nestedcount = nestedParts.count();
547 if ( nestedParts.isEmpty() && nestedMessage ) {
548 nestedcount = 1;
549 }
550 stream << nestedcount;
551 stream << _contentType;
552 stream << QString( getTypeParm( "name" ) );
553 stream << _contentDescription;
554 stream << _contentDisposition;
555 stream << _contentEncoding;
556 stream << contentLength;
557 stream << partSpecifier;
558 // serialize nested message
559 if ( nestedMessage ) {
560 nestedMessage->serialize( stream );
561 }
562
563 // serialize nested parts
564 if ( !nestedParts.isEmpty() ) {
565 QListIterator < mimeHeader *> it( nestedParts );
566 mimeHeader* part;
567 while ( it.hasNext() ) {
568 part = it.next();
569 part->serialize( stream );
570 }
571 }
572}
573
574#ifdef KMAIL_COMPATIBLE
575// compatibility subroutines
576QString
577mimeHeader::bodyDecoded ()
578{
579 kDebug( 7116 ) << "mimeHeader::bodyDecoded";
580 QByteArray temp = bodyDecodedBinary();
581 return QString::fromLatin1( temp.data(), temp.count() );
582}
583
584QByteArray
585mimeHeader::bodyDecodedBinary ()
586{
587 QByteArray retVal;
588
589 if ( contentEncoding.startsWith( QLatin1String( "quoted-printable" ), Qt::CaseInsensitive ) ) {
590 retVal = KCodecs::quotedPrintableDecode( postMultipartBody );
591 } else if ( contentEncoding.startsWith( QLatin1String( "base64" ), Qt::CaseInsensitive ) ) {
592 KCodecs::base64Decode( postMultipartBody, retVal );
593 } else {
594 retVal = postMultipartBody;
595 }
596
597 kDebug( 7116 ) << "mimeHeader::bodyDecodedBinary - size is" << retVal.size();
598 return retVal;
599}
600
601void
602mimeHeader::setBodyEncodedBinary (const QByteArray & _arr)
603{
604 setBodyEncoded( _arr );
605}
606
607void
608mimeHeader::setBodyEncoded (const QByteArray & _arr)
609{
610 QByteArray setVal;
611
612 kDebug( 7116 ) << "mimeHeader::setBodyEncoded - in size" << _arr.size();
613 if ( contentEncoding.startsWith( QLatin1String( "quoted-printable" ), Qt::CaseInsensitive ) ) {
614 setVal = KCodecs::quotedPrintableEncode( _arr );
615 } else if ( contentEncoding.startsWith( QLatin1String( "base64" ), Qt::CaseInsensitive ) ) {
616 KCodecs::base64Encode( _arr, setVal );
617 } else {
618 setVal.duplicate( _arr );
619 }
620 kDebug( 7116 ) << "mimeHeader::setBodyEncoded - out size" << setVal.size();
621
622 postMultipartBody.duplicate( setVal );
623 kDebug( 7116 ) << "mimeHeader::setBodyEncoded - out size" << postMultipartBody.size();
624}
625
626QString
627mimeHeader::iconName ()
628{
629 QString fileName =
630 KMimeType::mimeType( contentType.toLower() )->icon( QString(), false );
631 QString iconFileName =
632 KGlobal::mainComponent().iconLoader()->iconPath( fileName, KIconLoader::Desktop );
633// if ( iconFileName.isEmpty() )
634// iconFileName = KGlobal::mainComponent().iconLoader()->iconPath( "unknown", KIconLoader::Desktop );
635 return iconFileName;
636}
637
638void
639mimeHeader::setNestedMessage (mailHeader * inPart, bool destroy)
640{
641// if( nestedMessage && destroy ) delete nestedMessage;
642 nestedMessage = inPart;
643}
644
645QString
646mimeHeader::headerAsString ()
647{
648 mimeIOQString myIO;
649
650 outputHeader( myIO );
651 return myIO.getString();
652}
653
654QString
655mimeHeader::magicSetType (bool aAutoDecode)
656{
657 QByteArray body;
658
659 if ( aAutoDecode ) {
660 body = bodyDecodedBinary();
661 } else {
662 body = postMultipartBody;
663 }
664
665 KMimeType::Ptr mime = KMimeType::findByContent( body );
666 QString mimetype = mime->name();
667 contentType = mimetype;
668 return mimetype;
669}
670#endif
mailHeader
Definition mailheader.h:34
mimeHdrLine
Definition mimehdrline.h:29
mimeHdrLine::parseSeparator
static int parseSeparator(char, const char *)
parses continuated lines
Definition mimehdrline.cpp:321
mimeHdrLine::setStr
int setStr(const char *)
parse a Line into the class and report characters slurped
Definition mimehdrline.cpp:76
mimeHdrLine::getValue
const QByteArray & getValue()
return the value
Definition mimehdrline.cpp:372
mimeHdrLine::getLabel
const QByteArray & getLabel()
return the label
Definition mimehdrline.cpp:366
mimeHeader
Definition mimeheader.h:36
mimeIO
Definition mimeio.h:29
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.

kioslave/imap4

Skip menu "kioslave/imap4"
  • 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