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

KIMAP Library

  • sources
  • kde-4.14
  • kdepimlibs
  • kimap
rfccodecs.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * rfccodecs.cpp - handler for various rfc/mime encodings
4  * Copyright (C) 2000 s.carstens@gmx.de
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  *********************************************************************/
33 #include "rfccodecs.h"
34 
35 #include <ctype.h>
36 #include <sys/types.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 
41 #include <QtCore/QTextCodec>
42 #include <QtCore/QBuffer>
43 #include <QtCore/QRegExp>
44 #include <QtCore/QByteArray>
45 #include <QtCore/QLatin1Char>
46 #include <kcodecs.h>
47 
48 using namespace KIMAP;
49 
50 // This part taken from rfc 2192 IMAP URL Scheme. C. Newman. September 1997.
51 // adapted to QT-Toolkit by Sven Carstens <s.carstens@gmx.de> 2000
52 
53 //@cond PRIVATE
54 static const unsigned char base64chars[] =
55  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
56 #define UNDEFINED 64
57 #define MAXLINE 76
58 static const char especials[17] = "()<>@,;:\"/[]?.= ";
59 
60 /* UTF16 definitions */
61 #define UTF16MASK 0x03FFUL
62 #define UTF16SHIFT 10
63 #define UTF16BASE 0x10000UL
64 #define UTF16HIGHSTART 0xD800UL
65 #define UTF16HIGHEND 0xDBFFUL
66 #define UTF16LOSTART 0xDC00UL
67 #define UTF16LOEND 0xDFFFUL
68 //@endcond
69 
70 //-----------------------------------------------------------------------------
71 QByteArray KIMAP::decodeImapFolderName( const QByteArray &inSrc )
72 {
73  unsigned char c, i, bitcount;
74  unsigned long ucs4, utf16, bitbuf;
75  unsigned char base64[256], utf8[6];
76  unsigned int srcPtr = 0;
77  QByteArray dst;
78  QByteArray src = inSrc;
79  uint srcLen = inSrc.length();
80 
81  /* initialize modified base64 decoding table */
82  memset( base64, UNDEFINED, sizeof( base64 ) );
83  for ( i = 0; i < sizeof( base64chars ); ++i ) {
84  base64[(int)base64chars[i]] = i;
85  }
86 
87  /* loop until end of string */
88  while ( srcPtr < srcLen ) {
89  c = src[srcPtr++];
90  /* deal with literal characters and &- */
91  if ( c != '&' || src[srcPtr] == '-' ) {
92  /* encode literally */
93  dst += c;
94  /* skip over the '-' if this is an &- sequence */
95  if ( c == '&' ) {
96  srcPtr++;
97  }
98  } else {
99  /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
100  bitbuf = 0;
101  bitcount = 0;
102  ucs4 = 0;
103  while ( ( c = base64[(unsigned char)src[srcPtr]] ) != UNDEFINED ) {
104  ++srcPtr;
105  bitbuf = ( bitbuf << 6 ) | c;
106  bitcount += 6;
107  /* enough bits for a UTF-16 character? */
108  if ( bitcount >= 16 ) {
109  bitcount -= 16;
110  utf16 = ( bitcount ? bitbuf >> bitcount : bitbuf ) & 0xffff;
111  /* convert UTF16 to UCS4 */
112  if ( utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND ) {
113  ucs4 = ( utf16 - UTF16HIGHSTART ) << UTF16SHIFT;
114  continue;
115  } else if ( utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND ) {
116  ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
117  } else {
118  ucs4 = utf16;
119  }
120  /* convert UTF-16 range of UCS4 to UTF-8 */
121  if ( ucs4 <= 0x7fUL ) {
122  utf8[0] = ucs4;
123  i = 1;
124  } else if ( ucs4 <= 0x7ffUL ) {
125  utf8[0] = 0xc0 | ( ucs4 >> 6 );
126  utf8[1] = 0x80 | ( ucs4 & 0x3f );
127  i = 2;
128  } else if ( ucs4 <= 0xffffUL ) {
129  utf8[0] = 0xe0 | ( ucs4 >> 12 );
130  utf8[1] = 0x80 | ( ( ucs4 >> 6 ) & 0x3f );
131  utf8[2] = 0x80 | ( ucs4 & 0x3f );
132  i = 3;
133  } else {
134  utf8[0] = 0xf0 | ( ucs4 >> 18 );
135  utf8[1] = 0x80 | ( ( ucs4 >> 12 ) & 0x3f );
136  utf8[2] = 0x80 | ( ( ucs4 >> 6 ) & 0x3f );
137  utf8[3] = 0x80 | ( ucs4 & 0x3f );
138  i = 4;
139  }
140  /* copy it */
141  for ( c = 0; c < i; ++c ) {
142  dst += utf8[c];
143  }
144  }
145  }
146  /* skip over trailing '-' in modified UTF-7 encoding */
147  if ( src[srcPtr] == '-' ) {
148  ++srcPtr;
149  }
150  }
151  }
152  return dst;
153 }
154 
155 QString KIMAP::decodeImapFolderName( const QString &inSrc )
156 {
157  return QString::fromUtf8( decodeImapFolderName( inSrc.toUtf8() ).data() );
158 }
159 
160 //-----------------------------------------------------------------------------
161 
162 QByteArray KIMAP::quoteIMAP( const QByteArray &src )
163 {
164  uint len = src.length();
165  QByteArray result;
166  result.reserve( 2 * len );
167  for ( unsigned int i = 0; i < len; i++ ) {
168  if ( src[i] == '"' || src[i] == '\\' ) {
169  result += '\\';
170  }
171  result += src[i];
172  }
173  result.squeeze();
174  return result;
175 }
176 
177 QString KIMAP::quoteIMAP( const QString &src )
178 {
179  uint len = src.length();
180  QString result;
181  result.reserve( 2 * len );
182  for ( unsigned int i = 0; i < len; i++ ) {
183  if ( src[i] == QLatin1Char('"') || src[i] == QLatin1Char('\\') ) {
184  result += QLatin1Char('\\');
185  }
186  result += src[i];
187  }
188  //result.squeeze(); - unnecessary and slow
189  return result;
190 }
191 
192 //-----------------------------------------------------------------------------
193 QString KIMAP::encodeImapFolderName( const QString &inSrc )
194 {
195  return QString::fromUtf8( encodeImapFolderName( inSrc.toUtf8() ).data() );
196 }
197 
198 QByteArray KIMAP::encodeImapFolderName( const QByteArray &inSrc )
199 {
200  unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
201  unsigned int ucs4, bitbuf;
202  QByteArray src = inSrc;
203  QByteArray dst;
204 
205  int srcPtr = 0;
206  utf7mode = 0;
207  utf8total = 0;
208  bitstogo = 0;
209  utf8pos = 0;
210  bitbuf = 0;
211  ucs4 = 0;
212  while ( srcPtr < src.length () ) {
213  c = (unsigned char)src[srcPtr++];
214  /* normal character? */
215  if ( c >= ' ' && c <= '~' ) {
216  /* switch out of UTF-7 mode */
217  if ( utf7mode ) {
218  if ( bitstogo ) {
219  dst += base64chars[( bitbuf << ( 6 - bitstogo ) ) & 0x3F];
220  bitstogo = 0;
221  }
222  dst += '-';
223  utf7mode = 0;
224  }
225  dst += c;
226  /* encode '&' as '&-' */
227  if ( c == '&' ) {
228  dst += '-';
229  }
230  continue;
231  }
232  /* switch to UTF-7 mode */
233  if ( !utf7mode ) {
234  dst += '&';
235  utf7mode = 1;
236  }
237  /* Encode US-ASCII characters as themselves */
238  if ( c < 0x80 ) {
239  ucs4 = c;
240  utf8total = 1;
241  } else if ( utf8total ) {
242  /* save UTF8 bits into UCS4 */
243  ucs4 = ( ucs4 << 6 ) | ( c & 0x3FUL );
244  if ( ++utf8pos < utf8total ) {
245  continue;
246  }
247  } else {
248  utf8pos = 1;
249  if ( c < 0xE0 ) {
250  utf8total = 2;
251  ucs4 = c & 0x1F;
252  } else if ( c < 0xF0 ) {
253  utf8total = 3;
254  ucs4 = c & 0x0F;
255  } else {
256  /* NOTE: can't convert UTF8 sequences longer than 4 */
257  utf8total = 4;
258  ucs4 = c & 0x03;
259  }
260  continue;
261  }
262  /* loop to split ucs4 into two utf16 chars if necessary */
263  utf8total = 0;
264  do
265  {
266  if ( ucs4 >= UTF16BASE ) {
267  ucs4 -= UTF16BASE;
268  bitbuf =
269  ( bitbuf << 16 ) | ( ( ucs4 >> UTF16SHIFT ) + UTF16HIGHSTART );
270  ucs4 = ( ucs4 & UTF16MASK ) + UTF16LOSTART;
271  utf16flag = 1;
272  } else {
273  bitbuf = ( bitbuf << 16 ) | ucs4;
274  utf16flag = 0;
275  }
276  bitstogo += 16;
277  /* spew out base64 */
278  while ( bitstogo >= 6 ) {
279  bitstogo -= 6;
280  dst +=
281  base64chars[( bitstogo ? ( bitbuf >> bitstogo ) : bitbuf ) & 0x3F];
282  }
283  }
284  while ( utf16flag );
285  }
286  /* if in UTF-7 mode, finish in ASCII */
287  if ( utf7mode ) {
288  if ( bitstogo ) {
289  dst += base64chars[( bitbuf << ( 6 - bitstogo ) ) & 0x3F];
290  }
291  dst += '-';
292  }
293  return quoteIMAP( dst );
294 }
295 
296 //-----------------------------------------------------------------------------
297 QTextCodec *KIMAP::codecForName( const QString &str )
298 {
299  if ( str.isEmpty () ) {
300  return 0;
301  }
302  return QTextCodec::codecForName ( str.toLower ().
303  replace ( QLatin1String("windows"), QLatin1String("cp") ).toLatin1 () );
304 }
305 
306 //-----------------------------------------------------------------------------
307 const QString KIMAP::decodeRFC2047String( const QString &str )
308 {
309  QString throw_away;
310 
311  return decodeRFC2047String( str, throw_away );
312 }
313 
314 //-----------------------------------------------------------------------------
315 const QString KIMAP::decodeRFC2047String( const QString &str,
316  QString &charset )
317 {
318  QString throw_away;
319 
320  return decodeRFC2047String( str, charset, throw_away );
321 }
322 
323 //-----------------------------------------------------------------------------
324 const QString KIMAP::decodeRFC2047String( const QString &str,
325  QString &charset,
326  QString &language )
327 {
328  //do we have a rfc string
329  if ( !str.contains( QLatin1String("=?") ) ) {
330  return str;
331  }
332 
333  // FIXME get rid of the conversion?
334  QByteArray aStr = str.toLatin1 (); // QString.length() means Unicode chars
335  QByteArray result;
336  char *pos, *beg, *end, *mid = 0;
337  QByteArray cstr;
338  char encoding = 0, ch;
339  bool valid;
340  const int maxLen = 200;
341  int i;
342 
343 // result.truncate(aStr.length());
344  for ( pos = aStr.data (); *pos; pos++ ) {
345  if ( pos[0] != '=' || pos[1] != '?' ) {
346  result += *pos;
347  continue;
348  }
349  beg = pos + 2;
350  end = beg;
351  valid = true;
352  // parse charset name
353  for ( i = 2, pos += 2;
354  i < maxLen &&
355  ( *pos != '?' && ( ispunct( *pos ) || isalnum ( *pos ) ) );
356  i++ ) {
357  pos++;
358  }
359  if ( *pos != '?' || i < 4 || i >= maxLen ) {
360  valid = false;
361  } else {
362  charset = QLatin1String(QByteArray( beg, i - 1 )); // -2 + 1 for the zero
363  int pt = charset.lastIndexOf( QLatin1Char('*') );
364  if ( pt != -1 ) {
365  // save language for later usage
366  language = charset.right( charset.length () - pt - 1 );
367 
368  // tie off language as defined in rfc2047
369  charset.truncate( pt );
370  }
371  // get encoding and check delimiting question marks
372  encoding = toupper( pos[1] );
373  if ( pos[2] != '?' ||
374  ( encoding != 'Q' && encoding != 'B' &&
375  encoding != 'q' && encoding != 'b' ) ) {
376  valid = false;
377  }
378  pos += 3;
379  i += 3;
380 // kDebug() << "Charset:" << charset << "- Language:" << language << "-'" << pos << "'";
381  }
382  if ( valid ) {
383  mid = pos;
384  // search for end of encoded part
385  while ( i < maxLen && *pos && !( *pos == '?' && *( pos + 1 ) == '=' ) ) {
386  i++;
387  pos++;
388  }
389  end = pos + 2;//end now points to the first char after the encoded string
390  if ( i >= maxLen || !*pos ) {
391  valid = false;
392  }
393  }
394  if ( valid ) {
395  ch = *pos;
396  *pos = '\0';
397  cstr = QByteArray (mid).left( (int)( mid - pos - 1 ) );
398  if ( encoding == 'Q' ) {
399  // decode quoted printable text
400  for ( i = cstr.length () - 1; i >= 0; --i ) {
401  if ( cstr[i] == '_' ) {
402  cstr[i] = ' ';
403  }
404  }
405 // kDebug() << "before QP '"
406 // << cstr << "'";
407  cstr = KCodecs::quotedPrintableDecode( cstr );
408 // kDebug() << "after QP '"
409 // << cstr << "'";
410  } else {
411  // decode base64 text
412  cstr = QByteArray::fromBase64( cstr );
413  }
414  *pos = ch;
415  int len = cstr.length();
416  for ( i = 0; i < len; ++i ) {
417  result += cstr[i];
418  }
419 
420  pos = end - 1;
421  } else {
422 // kDebug() << "invalid";
423  //result += "=?";
424  //pos = beg -1; // because pos gets increased shortly afterwards
425  pos = beg - 2;
426  result += *pos++;
427  result += *pos;
428  }
429  }
430  if ( !charset.isEmpty () ) {
431  QTextCodec *aCodec = codecForName( QLatin1String(charset.toLatin1 ()) );
432  if ( aCodec ) {
433 // kDebug() << "Codec is" << aCodec->name();
434  return aCodec->toUnicode( result );
435  }
436  }
437  return QLatin1String(result);
438 }
439 
440 //-----------------------------------------------------------------------------
441 const QString KIMAP::encodeRFC2047String( const QString &str )
442 {
443  return QLatin1String(encodeRFC2047String( str.toLatin1() ));
444 }
445 
446 //-----------------------------------------------------------------------------
447 const QByteArray KIMAP::encodeRFC2047String( const QByteArray &str )
448 {
449  if ( str.isEmpty () ) {
450  return str;
451  }
452 
453  const signed char *latin =
454  reinterpret_cast<const signed char *>
455  ( str.data() ), *l, *start, *stop;
456  char hexcode;
457  int numQuotes, i;
458  int rptr = 0;
459  // My stats show this number results in 12 resize() out of 73,000
460  int resultLen = 3 * str.length() / 2;
461  QByteArray result( resultLen, '\0' );
462 
463  while ( *latin ) {
464  l = latin;
465  start = latin;
466  while ( *l ) {
467  if ( *l == 32 ) {
468  start = l + 1;
469  }
470  if ( *l < 0 ) {
471  break;
472  }
473  l++;
474  }
475  if ( *l ) {
476  numQuotes = 1;
477  while ( *l ) {
478  /* The encoded word must be limited to 75 character */
479  for ( i = 0; i < 16; ++i ) {
480  if ( *l == especials[i] ) {
481  numQuotes++;
482  }
483  }
484  if ( *l < 0 ) {
485  numQuotes++;
486  }
487  /* Stop after 58 = 75 - 17 characters or at "<user@host..." */
488  if ( l - start + 2 * numQuotes >= 58 || *l == 60 ) {
489  break;
490  }
491  l++;
492  }
493  if ( *l ) {
494  stop = l - 1;
495  while ( stop >= start && *stop != 32 ) {
496  stop--;
497  }
498  if ( stop <= start ) {
499  stop = l;
500  }
501  } else {
502  stop = l;
503  }
504  if ( resultLen - rptr - 1 <= start - latin + 1 + 16 ) {
505  // =?iso-88...
506  resultLen += ( start - latin + 1 ) * 2 + 20; // more space
507  result.resize( resultLen );
508  }
509  while ( latin < start ) {
510  result[rptr++] = *latin;
511  latin++;
512  }
513  result.replace( rptr, 15, "=?iso-8859-1?q?" );
514  rptr += 15;
515  if ( resultLen - rptr - 1 <= 3 * ( stop - latin + 1 ) ) {
516  resultLen += ( stop - latin + 1 ) * 4 + 20; // more space
517  result.resize( resultLen );
518  }
519  while ( latin < stop ) {
520  // can add up to 3 chars/iteration
521  numQuotes = 0;
522  for ( i = 0; i < 16; ++i ) {
523  if ( *latin == especials[i] ) {
524  numQuotes = 1;
525  }
526  }
527  if ( *latin < 0 ) {
528  numQuotes = 1;
529  }
530  if ( numQuotes ) {
531  result[rptr++] = '=';
532  hexcode = ( ( *latin & 0xF0 ) >> 4 ) + 48;
533  if ( hexcode >= 58 ) {
534  hexcode += 7;
535  }
536  result[rptr++] = hexcode;
537  hexcode = ( *latin & 0x0F ) + 48;
538  if ( hexcode >= 58 ) {
539  hexcode += 7;
540  }
541  result[rptr++] = hexcode;
542  } else {
543  result[rptr++] = *latin;
544  }
545  latin++;
546  }
547  result[rptr++] = '?';
548  result[rptr++] = '=';
549  } else {
550  while ( *latin ) {
551  if ( rptr == resultLen - 1 ) {
552  resultLen += 30;
553  result.resize( resultLen );
554  }
555  result[rptr++] = *latin;
556  latin++;
557  }
558  }
559  }
560  result[rptr] = 0;
561  return result;
562 }
563 
564 //-----------------------------------------------------------------------------
565 const QString KIMAP::encodeRFC2231String( const QString &str )
566 {
567  if ( str.isEmpty () ) {
568  return str;
569  }
570 
571  signed char *latin = (signed char *)calloc( 1, str.length () + 1 );
572  char *latin_us = (char *)latin;
573  strcpy( latin_us, str.toLatin1 () );
574  signed char *l = latin;
575  char hexcode;
576  int i;
577  bool quote;
578  while ( *l ) {
579  if ( *l < 0 ) {
580  break;
581  }
582  l++;
583  }
584  if ( !*l ) {
585  free( latin );
586  return str;
587  }
588  QByteArray result;
589  l = latin;
590  while ( *l ) {
591  quote = *l < 0;
592  for ( i = 0; i < 16; ++i ) {
593  if ( *l == especials[i] ) {
594  quote = true;
595  }
596  }
597  if ( quote ) {
598  result += '%';
599  hexcode = ( ( *l & 0xF0 ) >> 4 ) + 48;
600  if ( hexcode >= 58 ) {
601  hexcode += 7;
602  }
603  result += hexcode;
604  hexcode = ( *l & 0x0F ) + 48;
605  if ( hexcode >= 58 ) {
606  hexcode += 7;
607  }
608  result += hexcode;
609  } else {
610  result += *l;
611  }
612  l++;
613  }
614  free( latin );
615  return QLatin1String(result);
616 }
617 
618 //-----------------------------------------------------------------------------
619 const QString KIMAP::decodeRFC2231String( const QString &str )
620 {
621  int p = str.indexOf ( QLatin1Char('\'') );
622 
623  //see if it is an rfc string
624  if ( p < 0 ) {
625  return str;
626  }
627 
628  int l = str.lastIndexOf( QLatin1Char('\'') );
629 
630  //second is language
631  if ( p >= l ) {
632  return str;
633  }
634 
635  //first is charset or empty
636  //QString charset = str.left ( p );
637  QString st = str.mid ( l + 1 );
638  //QString language = str.mid ( p + 1, l - p - 1 );
639 
640  //kDebug() << "Charset:" << charset << "Language:" << language;
641 
642  char ch, ch2;
643  p = 0;
644  while ( p < (int) st.length () ) {
645  if ( st.at( p ) == 37 ) {
646  ch = st.at( p + 1 ).toLatin1 () - 48;
647  if ( ch > 16 ) {
648  ch -= 7;
649  }
650  ch2 = st.at( p + 2 ).toLatin1 () - 48;
651  if ( ch2 > 16 ) {
652  ch2 -= 7;
653  }
654  st.replace( p, 1, ch * 16 + ch2 );
655  st.remove ( p + 1, 2 );
656  }
657  p++;
658  }
659  return st;
660 }
QByteArray::squeeze
void squeeze()
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
rfccodecs.h
This file is part of the IMAP support library and defines the RfcCodecs class.
QString::truncate
void truncate(int position)
QByteArray::reserve
void reserve(int size)
QByteArray
QByteArray::isEmpty
bool isEmpty() const
QByteArray::length
int length() const
QString::remove
QString & remove(int position, int n)
QString::lastIndexOf
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QByteArray::resize
void resize(int size)
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::isEmpty
bool isEmpty() const
QByteArray::replace
QByteArray & replace(int pos, int len, const char *after)
QString
QTextCodec
QString::right
QString right(int n) const
QString::toLower
QString toLower() const
QString::contains
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QLatin1Char
QChar::toLatin1
char toLatin1() const
QString::replace
QString & replace(int position, int n, QChar after)
QByteArray::left
QByteArray left(int len) const
QString::toLatin1
QByteArray toLatin1() const
QString::mid
QString mid(int position, int n) const
QLatin1String
QByteArray::fromBase64
QByteArray fromBase64(const QByteArray &base64)
QString::at
const QChar at(int position) const
QTextCodec::codecForName
QTextCodec * codecForName(const QByteArray &name)
QString::length
int length() const
QString::reserve
void reserve(int size)
QByteArray::data
char * data()
QTextCodec::toUnicode
QString toUnicode(const QByteArray &a) const
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:37:03 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIMAP Library

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

kdepimlibs API Reference

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

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