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

akonadi

  • sources
  • kde-4.12
  • kdepimlibs
  • akonadi
  • notes
noteutils.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2011 Christian Mollekopf <chrigi_1@fastmail.fm>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 
20 #include "noteutils.h"
21 
22 #include <klocalizedstring.h>
23 #include <kdatetime.h>
24 #include <kmime/kmime_message.h>
25 #include <kdebug.h>
26 
27 #include <qstring.h>
28 #include <quuid.h>
29 #include <qdom.h>
30 
31 namespace Akonadi {
32 namespace NoteUtils {
33 
34 #define X_NOTES_UID_HEADER "X-Akonotes-UID"
35 #define X_NOTES_LASTMODIFIED_HEADER "X-Akonotes-LastModified"
36 #define X_NOTES_CLASSIFICATION_HEADER "X-Akonotes-Classification"
37 #define X_NOTES_CUSTOM_HEADER "X-Akonotes-Custom"
38 
39 #define CLASSIFICATION_PUBLIC "Public"
40 #define CLASSIFICATION_PRIVATE "Private"
41 #define CLASSIFICATION_CONFIDENTIAL "Confidential"
42 
43 #define X_NOTES_URL_HEADER "X-Akonotes-Url"
44 #define X_NOTES_LABEL_HEADER "X-Akonotes-Label"
45 #define X_NOTES_CONTENTTYPE_HEADER "X-Akonotes-Type"
46 #define CONTENT_TYPE_CUSTOM "custom"
47 #define CONTENT_TYPE_ATTACHMENT "attachment"
48 
49 #define ENCODING "utf-8"
50 
51 class Attachment::AttachmentPrivate
52 {
53  public:
54  AttachmentPrivate( const QUrl& url, const QString& mimetype )
55  : mUrl( url ),
56  mMimetype( mimetype )
57  {}
58 
59  AttachmentPrivate( const QByteArray& data, const QString& mimetype )
60  : mData( data ),
61  mMimetype( mimetype )
62  {}
63 
64  AttachmentPrivate( const AttachmentPrivate &other )
65  {
66  *this = other;
67  }
68 
69  QUrl mUrl;
70  QByteArray mData;
71  QString mMimetype;
72  QString mLabel;
73 };
74 
75 Attachment::Attachment( const QUrl& url, const QString& mimetype )
76 : d_ptr( new Attachment::AttachmentPrivate( url, mimetype ) )
77 {
78 }
79 
80 Attachment::Attachment( const QByteArray& data, const QString& mimetype )
81 : d_ptr( new Attachment::AttachmentPrivate( data, mimetype ) )
82 {
83 }
84 
85 Attachment::Attachment( const Attachment &other )
86 : d_ptr(new AttachmentPrivate(*other.d_func()) )
87 {
88 
89 }
90 
91 Attachment::~Attachment()
92 {
93  delete d_ptr;
94 }
95 
96 bool Attachment::operator==( const Attachment &a ) const
97 {
98  const Q_D( Attachment );
99  if ( d->mUrl.isEmpty() ) {
100  return d->mUrl == a.d_func()->mUrl &&
101  d->mMimetype == a.d_func()->mMimetype &&
102  d->mLabel == a.d_func()->mLabel;
103  }
104  return d->mData == a.d_func()->mData &&
105  d->mMimetype == a.d_func()->mMimetype &&
106  d->mLabel == a.d_func()->mLabel;
107 }
108 
109 void Attachment::operator=( const Attachment &a )
110 {
111  *d_ptr = *a.d_ptr;
112 }
113 
114 QUrl Attachment::url() const
115 {
116  const Q_D( Attachment );
117  return d->mUrl;
118 }
119 
120 QByteArray Attachment::data() const
121 {
122  const Q_D( Attachment );
123  return d->mData;
124 }
125 
126 QString Attachment::mimetype() const
127 {
128  const Q_D( Attachment );
129  return d->mMimetype;
130 }
131 
132 void Attachment::setLabel( const QString& label )
133 {
134  Q_D( Attachment );
135  d->mLabel = label;
136 }
137 
138 QString Attachment::label() const
139 {
140  const Q_D( Attachment );
141  return d->mLabel;
142 }
143 
144 class NoteMessageWrapper::NoteMessageWrapperPrivate
145 {
146  public:
147  NoteMessageWrapperPrivate()
148  : classification( Public )
149  {
150  }
151 
152  NoteMessageWrapperPrivate( const KMime::Message::Ptr &msg )
153  : textFormat( Qt::PlainText ),
154  classification( Public )
155  {
156  readMimeMessage(msg);
157  }
158 
159  void readMimeMessage(const KMime::Message::Ptr &msg );
160 
161  KMime::Content* createCustomPart() const;
162  void parseCustomPart( KMime::Content * );
163 
164  KMime::Content* createAttachmentPart( const Attachment & ) const;
165  void parseAttachmentPart( KMime::Content * );
166 
167  QString uid;
168  QString title;
169  QString text;
170  Qt::TextFormat textFormat;
171  QString from;
172  KDateTime creationDate;
173  KDateTime lastModifiedDate;
174  QMap< QString, QString > custom;
175  QList<Attachment> attachments;
176  Classification classification;
177 };
178 
179 void NoteMessageWrapper::NoteMessageWrapperPrivate::readMimeMessage(const KMime::Message::Ptr& msg)
180 {
181  if (!msg.get()) {
182  kWarning() << "Empty message";
183  return;
184  }
185  title = msg->subject( true )->asUnicodeString();
186  text = msg->mainBodyPart()->decodedText( true ); //remove trailing whitespace, so we get rid of " " in empty notes
187  if ( msg->from( false ) )
188  from = msg->from( false )->asUnicodeString();
189  creationDate = msg->date( true )->dateTime();
190  if ( msg->contentType( false ) && msg->contentType( false )->asUnicodeString() == QLatin1String("text/html") ) {
191  textFormat = Qt::RichText;
192  }
193 
194  if (KMime::Headers::Base *lastmod = msg->headerByType(X_NOTES_LASTMODIFIED_HEADER)) {
195  const QByteArray &s = lastmod->asUnicodeString().toLatin1();
196  const char *cursor = s.constData();
197  if (!KMime::HeaderParsing::parseDateTime( cursor, cursor + s.length(), lastModifiedDate)) {
198  kWarning() << "failed to parse lastModifiedDate";
199  }
200  }
201 
202  if (KMime::Headers::Base *uidHeader = msg->headerByType(X_NOTES_UID_HEADER)) {
203  uid = uidHeader->asUnicodeString();
204  }
205 
206  if (KMime::Headers::Base *classificationHeader = msg->headerByType(X_NOTES_CLASSIFICATION_HEADER)) {
207  const QString &c = classificationHeader->asUnicodeString();
208  if ( c == CLASSIFICATION_PRIVATE ) {
209  classification = Private;
210  } else if ( c == CLASSIFICATION_CONFIDENTIAL ) {
211  classification = Confidential;
212  }
213  }
214 
215  const KMime::Content::List list = msg->contents();
216  Q_FOREACH(KMime::Content *c, msg->contents()) {
217  if (KMime::Headers::Base *typeHeader = c->headerByType(X_NOTES_CONTENTTYPE_HEADER)) {
218  const QString &type = typeHeader->asUnicodeString();
219  if ( type == CONTENT_TYPE_CUSTOM ) {
220  parseCustomPart(c);
221  } else if ( type == CONTENT_TYPE_ATTACHMENT ) {
222  parseAttachmentPart(c);
223  } else {
224  qWarning() << "unknown type " << type;
225  }
226  }
227  }
228 }
229 
230 QDomDocument createXMLDocument()
231 {
232  QDomDocument document;
233  QString p = "version=\"1.0\" encoding=\"UTF-8\"";
234  document.appendChild(document.createProcessingInstruction( "xml", p ) );
235  return document;
236 }
237 
238 QDomDocument loadDocument(KMime::Content *part)
239 {
240  QString errorMsg;
241  int errorLine, errorColumn;
242  QDomDocument document;
243  bool ok = document.setContent( part->body(), &errorMsg, &errorLine, &errorColumn );
244  if ( !ok ) {
245  kWarning() << part->body();
246  qWarning( "Error loading document: %s, line %d, column %d", qPrintable( errorMsg ), errorLine, errorColumn );
247  return QDomDocument();
248  }
249  return document;
250 }
251 
252 KMime::Content* NoteMessageWrapper::NoteMessageWrapperPrivate::createCustomPart() const
253 {
254  KMime::Content* content = new KMime::Content();
255  content->appendHeader( new KMime::Headers::Generic( X_NOTES_CONTENTTYPE_HEADER, content, CONTENT_TYPE_CUSTOM, ENCODING ) );
256  QDomDocument document = createXMLDocument();
257  QDomElement element = document.createElement( "custom" );
258  element.setAttribute( "version", "1.0" );
259  for ( QMap <QString, QString >::const_iterator it = custom.begin(); it != custom.end(); ++it ) {
260  QDomElement e = element.ownerDocument().createElement( it.key() );
261  QDomText t = element.ownerDocument().createTextNode( it.value() );
262  e.appendChild( t );
263  element.appendChild( e );
264  document.appendChild( element );
265  }
266  content->setBody( document.toString().toLatin1() );
267  return content;
268 }
269 
270 void NoteMessageWrapper::NoteMessageWrapperPrivate::parseCustomPart( KMime::Content* part )
271 {
272  QDomDocument document = loadDocument( part );
273  if (document.isNull()) {
274  return;
275  }
276  QDomElement top = document.documentElement();
277  if ( top.tagName() != "custom" ) {
278  qWarning( "XML error: Top tag was %s instead of the expected custom",
279  top.tagName().toLatin1().data() );
280  return;
281  }
282 
283  for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
284  if ( n.isElement() ) {
285  QDomElement e = n.toElement();
286  custom.insert(e.tagName(), e.text());
287  } else {
288  kDebug() <<"Node is not an element";
289  Q_ASSERT(false);
290  }
291  }
292 }
293 
294 KMime::Content* NoteMessageWrapper::NoteMessageWrapperPrivate::createAttachmentPart( const Attachment &a ) const
295 {
296  KMime::Content* content = new KMime::Content();
297  content->appendHeader( new KMime::Headers::Generic( X_NOTES_CONTENTTYPE_HEADER, content, CONTENT_TYPE_ATTACHMENT, ENCODING ) );
298  if (a.url().isValid()) {
299  content->appendHeader( new KMime::Headers::Generic( X_NOTES_URL_HEADER, content, a.url().toString().toLatin1(), ENCODING ) );
300  } else {
301  content->setBody( a.data() );
302  }
303  content->contentType()->setMimeType( a.mimetype().toLatin1() );
304  if (!a.label().isEmpty()) {
305  content->appendHeader( new KMime::Headers::Generic( X_NOTES_LABEL_HEADER, content, a.label().toLatin1(), ENCODING ) );
306  }
307  content->contentTransferEncoding()->setEncoding( KMime::Headers::CEbase64 );
308  content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
309  content->contentDisposition()->setFilename( "attachment" );
310  return content;
311 }
312 
313 void NoteMessageWrapper::NoteMessageWrapperPrivate::parseAttachmentPart( KMime::Content *part )
314 {
315  QString label;
316  if ( KMime::Headers::Base *labelHeader = part->headerByType( X_NOTES_LABEL_HEADER ) ) {
317  label = labelHeader->asUnicodeString();
318  }
319  if ( KMime::Headers::Base *header = part->headerByType( X_NOTES_URL_HEADER ) ) {
320  Attachment attachment( QUrl( header->asUnicodeString() ), part->contentType()->mimeType() );
321  attachment.setLabel( label );
322  attachments.append(attachment);
323  } else {
324  Attachment attachment( part->decodedContent(), part->contentType()->mimeType() );
325  attachment.setLabel( label );
326  attachments.append(attachment);
327  }
328 }
329 
330 NoteMessageWrapper::NoteMessageWrapper()
331 : d_ptr( new NoteMessageWrapperPrivate() )
332 {
333 }
334 
335 NoteMessageWrapper::NoteMessageWrapper( const KMime::Message::Ptr &msg )
336 : d_ptr( new NoteMessageWrapperPrivate(msg) )
337 {
338 }
339 
340 NoteMessageWrapper::~NoteMessageWrapper()
341 {
342  delete d_ptr;
343 }
344 
345 KMime::Message::Ptr NoteMessageWrapper::message() const
346 {
347  const Q_D( NoteMessageWrapper );
348  KMime::Message::Ptr msg = KMime::Message::Ptr( new KMime::Message() );
349 
350  QString title = i18nc( "The default name for new notes.", "New Note" );
351  if ( !d->title.isEmpty() ) {
352  title = d->title;
353  }
354  // Need a non-empty body part so that the serializer regards this as a valid message.
355  QString text = QLatin1String(" ");
356  if ( !d->text.isEmpty() ) {
357  text = d->text;
358  }
359 
360  KDateTime creationDate = KDateTime::currentLocalDateTime();
361  if ( d->creationDate.isValid() ) {
362  creationDate = d->creationDate;
363  }
364 
365  KDateTime lastModifiedDate = KDateTime::currentLocalDateTime();
366  if ( d->lastModifiedDate.isValid() ) {
367  lastModifiedDate = d->lastModifiedDate;
368  }
369 
370  QString uid;
371  if ( !d->uid.isEmpty() ) {
372  uid = d->uid;
373  } else {
374  uid = QUuid::createUuid();
375  }
376 
377  msg->subject( true )->fromUnicodeString( title, ENCODING );
378  msg->contentType( true )->setMimeType( d->textFormat == Qt::RichText ? "text/html" : "text/plain" );
379  msg->date( true )->setDateTime( creationDate );
380  msg->from( true )->fromUnicodeString( d->from, ENCODING );
381  msg->mainBodyPart()->fromUnicodeString( text );
382  msg->appendHeader( new KMime::Headers::Generic(X_NOTES_LASTMODIFIED_HEADER, msg.get(), lastModifiedDate.toString( KDateTime::RFCDateDay ).toLatin1(), ENCODING ) );
383  msg->appendHeader( new KMime::Headers::Generic( X_NOTES_UID_HEADER, msg.get(), uid, ENCODING ) );
384 
385  QString classification = QString::fromLatin1(CLASSIFICATION_PUBLIC);
386  switch ( d->classification ) {
387  case Private:
388  classification = QString::fromLatin1(CLASSIFICATION_PRIVATE);
389  break;
390  case Confidential:
391  classification = QString::fromLatin1(CLASSIFICATION_CONFIDENTIAL);
392  break;
393  default:
394  //do nothing
395  break;
396  }
397  msg->appendHeader( new KMime::Headers::Generic( X_NOTES_CLASSIFICATION_HEADER, msg.get(), classification, ENCODING ) );
398 
399  foreach (const Attachment &a, d->attachments) {
400  msg->addContent( d->createAttachmentPart(a) );
401  }
402 
403  if ( !d->custom.isEmpty() ) {
404  msg->addContent( d->createCustomPart() );
405  }
406 
407  msg->assemble();
408  return msg;
409 }
410 
411 void NoteMessageWrapper::setUid( const QString &uid )
412 {
413  Q_D( NoteMessageWrapper );
414  d->uid = uid;
415 }
416 
417 QString NoteMessageWrapper::uid() const
418 {
419  const Q_D( NoteMessageWrapper );
420  return d->uid;
421 }
422 
423 void NoteMessageWrapper::setClassification( NoteMessageWrapper::Classification classification )
424 {
425  Q_D( NoteMessageWrapper );
426  d->classification = classification;
427 }
428 
429 NoteMessageWrapper::Classification NoteMessageWrapper::classification() const
430 {
431  const Q_D( NoteMessageWrapper );
432  return d->classification;
433 }
434 
435 void NoteMessageWrapper::setLastModifiedDate( const KDateTime& lastModifiedDate )
436 {
437  Q_D( NoteMessageWrapper );
438  d->lastModifiedDate = lastModifiedDate;
439 }
440 
441 KDateTime NoteMessageWrapper::lastModifiedDate() const
442 {
443  const Q_D( NoteMessageWrapper );
444  return d->lastModifiedDate;
445 }
446 
447 void NoteMessageWrapper::setCreationDate( const KDateTime &creationDate )
448 {
449  Q_D( NoteMessageWrapper );
450  d->creationDate = creationDate;
451 }
452 
453 KDateTime NoteMessageWrapper::creationDate() const
454 {
455  const Q_D( NoteMessageWrapper );
456  return d->creationDate;
457 }
458 
459 void NoteMessageWrapper::setFrom( const QString &from )
460 {
461  Q_D( NoteMessageWrapper );
462  d->from = from;
463 }
464 
465 QString NoteMessageWrapper::from() const
466 {
467  const Q_D( NoteMessageWrapper );
468  return d->from;
469 }
470 
471 void NoteMessageWrapper::setTitle( const QString &title )
472 {
473  Q_D( NoteMessageWrapper );
474  d->title = title;
475 }
476 
477 QString NoteMessageWrapper::title() const
478 {
479  const Q_D( NoteMessageWrapper );
480  return d->title;
481 }
482 
483 void NoteMessageWrapper::setText( const QString &text, Qt::TextFormat format )
484 {
485  Q_D( NoteMessageWrapper );
486  d->text = text;
487  d->textFormat = format;
488 }
489 
490 QString NoteMessageWrapper::text() const
491 {
492  const Q_D( NoteMessageWrapper );
493  return d->text;
494 }
495 
496 Qt::TextFormat NoteMessageWrapper::textFormat() const
497 {
498  const Q_D( NoteMessageWrapper );
499  return d->textFormat;
500 }
501 
502 QString NoteMessageWrapper::toPlainText() const
503 {
504  const Q_D( NoteMessageWrapper );
505  if ( d->textFormat == Qt::PlainText ) {
506  return d->text;
507  }
508 
509  //From cleanHtml in kdepimlibs/kcalutils/incidenceformatter.cpp
510  QRegExp rx( QLatin1String("<body[^>]*>(.*)</body>"), Qt::CaseInsensitive );
511  rx.indexIn( d->text );
512  QString body = rx.cap( 1 );
513 
514  return Qt::escape( body.remove( QRegExp( QLatin1String("<[^>]*>") ) ).trimmed() );
515 }
516 
517 QList<Attachment> &NoteMessageWrapper::attachments()
518 {
519  Q_D( NoteMessageWrapper );
520  return d->attachments;
521 }
522 
523 QMap< QString, QString > &NoteMessageWrapper::custom()
524 {
525  Q_D( NoteMessageWrapper );
526  return d->custom;
527 }
528 
529 QString noteIconName()
530 {
531  return QString::fromLatin1( "text-plain" );
532 }
533 
534 QString noteMimeType()
535 {
536  return QString::fromLatin1( "text/x-vnd.akonadi.note" );
537 }
538 
539 } //End Namepsace
540 } //End Namepsace
Akonadi::NoteUtils::NoteMessageWrapper::setFrom
void setFrom(const QString &from)
Set the origin (creator) of the note (stored in the mime header) This is usually the application crea...
Definition: noteutils.cpp:459
Akonadi::NoteUtils::NoteMessageWrapper::classification
Classification classification() const
Returns the classification of the note.
Definition: noteutils.cpp:429
Akonadi::NoteUtils::NoteMessageWrapper::text
QString text() const
Returns the text of the note.
Definition: noteutils.cpp:490
Akonadi::NoteUtils::NoteMessageWrapper::textFormat
Qt::TextFormat textFormat() const
Definition: noteutils.cpp:496
Akonadi::NoteUtils::NoteMessageWrapper::uid
QString uid() const
Returns the uid of the note.
Definition: noteutils.cpp:417
Akonadi::NoteUtils::NoteMessageWrapper::setTitle
void setTitle(const QString &title)
Set the title of the note.
Definition: noteutils.cpp:471
Akonadi::NoteUtils::Attachment::data
QByteArray data() const
Returns the date for inline attachments.
Definition: noteutils.cpp:120
Akonadi::NoteUtils::NoteMessageWrapper::setText
void setText(const QString &text, Qt::TextFormat format=Qt::PlainText)
Set the text of the note.
Definition: noteutils.cpp:483
Akonadi::NoteUtils::NoteMessageWrapper::setClassification
void setClassification(Classification)
Set the classification of the note.
Definition: noteutils.cpp:423
Akonadi::NoteUtils::NoteMessageWrapper::setLastModifiedDate
void setLastModifiedDate(const KDateTime &lastModifiedDate)
Set the lastModified-date of the note.
Definition: noteutils.cpp:435
Akonadi::NoteUtils::NoteMessageWrapper::setUid
void setUid(const QString &uid)
Set the uid of the note.
Definition: noteutils.cpp:411
Akonadi::NoteUtils::NoteMessageWrapper::lastModifiedDate
KDateTime lastModifiedDate() const
Returns the lastModified-date of the note.
Definition: noteutils.cpp:441
Akonadi::NoteUtils::Attachment::url
QUrl url() const
Returns the url for url-only attachments.
Definition: noteutils.cpp:114
Akonadi::NoteUtils::NoteMessageWrapper::setCreationDate
void setCreationDate(const KDateTime &creationDate)
Set the creation date of the note (stored in the mime header)
Definition: noteutils.cpp:447
Akonadi::NoteUtils::NoteMessageWrapper::toPlainText
QString toPlainText() const
Definition: noteutils.cpp:502
Akonadi::NoteUtils::Attachment::setLabel
void setLabel(const QString &label)
Sets the label to be presented to the user.
Definition: noteutils.cpp:132
Akonadi::NoteUtils::NoteMessageWrapper
A convenience wrapper around KMime::Message::Ptr for notes.
Definition: noteutils.h:145
Akonadi::NoteUtils::Attachment::Attachment
Attachment(const QUrl &url, const QString &mimetype)
Create an attachment referencing a url only.
Definition: noteutils.cpp:75
Akonadi::NoteUtils::NoteMessageWrapper::from
QString from() const
Returns the origin (creator) of the note.
Definition: noteutils.cpp:465
Akonadi::NoteUtils::Attachment
An attachment for a note.
Definition: noteutils.h:58
Akonadi::NoteUtils::NoteMessageWrapper::title
QString title() const
Returns the title of the note.
Definition: noteutils.cpp:477
Akonadi::NoteUtils::NoteMessageWrapper::custom
QMap< QString, QString > & custom()
Returns a reference to the custom-value map.
Definition: noteutils.cpp:523
Akonadi::NoteUtils::Attachment::label
QString label() const
Returns the label of the attachment.
Definition: noteutils.cpp:138
Akonadi::NoteUtils::NoteMessageWrapper::attachments
QList< Attachment > & attachments()
Returns a reference to the list of attachments of the note.
Definition: noteutils.cpp:517
Akonadi::NoteUtils::Attachment::mimetype
QString mimetype() const
Returns the mimetype.
Definition: noteutils.cpp:126
Akonadi::NoteUtils::NoteMessageWrapper::creationDate
KDateTime creationDate() const
Returns the creation date of the note.
Definition: noteutils.cpp:453
Akonadi::NoteUtils::NoteMessageWrapper::message
KMime::MessagePtr message() const
Assemble a KMime message with the given values.
Definition: noteutils.cpp:345
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:27 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



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