• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KMIME Library

kmime_util.cpp

00001 /*
00002   kmime_util.cpp
00003 
00004   KMime, the KDE internet mail/usenet news message library.
00005   Copyright (c) 2001 the KMime authors.
00006   See file AUTHORS for details
00007 
00008   This library is free software; you can redistribute it and/or
00009   modify it under the terms of the GNU Library General Public
00010   License as published by the Free Software Foundation; either
00011   version 2 of the License, or (at your option) any later version.
00012 
00013   This library is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016   Library General Public License for more details.
00017 
00018   You should have received a copy of the GNU Library General Public License
00019   along with this library; see the file COPYING.LIB.  If not, write to
00020   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021   Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "kmime_util.h"
00025 #include "kmime_util_p.h"
00026 #include "kmime_header_parsing.h"
00027 
00028 #include <config-kmime.h>
00029 #include <kdefakes.h> // for strcasestr
00030 #include <kglobal.h>
00031 #include <klocale.h>
00032 #include <kcharsets.h>
00033 #include <kcodecs.h>
00034 
00035 #include <QtCore/QList>
00036 #include <QtCore/QString>
00037 #include <QtCore/QTextCodec>
00038 
00039 #include <ctype.h>
00040 #include <time.h>
00041 #include <stdlib.h>
00042 #include <unistd.h>
00043 
00044 using namespace KMime;
00045 
00046 namespace KMime {
00047 
00048 QList<QByteArray> c_harsetCache;
00049 QList<QByteArray> l_anguageCache;
00050 
00051 QByteArray cachedCharset( const QByteArray &name )
00052 {
00053   foreach ( QByteArray charset, c_harsetCache ) {
00054     if ( qstricmp( name.data(), charset.data() ) == 0 ) {
00055       return charset;
00056     }
00057   }
00058 
00059   c_harsetCache.append( name.toUpper() );
00060   //kDebug(5320) << "KNMimeBase::cachedCharset() number of cs" << c_harsetCache.count();
00061   return c_harsetCache.last();
00062 }
00063 
00064 QByteArray cachedLanguage( const QByteArray &name )
00065 {
00066   foreach ( QByteArray language, l_anguageCache ) {
00067     if ( qstricmp( name.data(), language.data() ) == 0 ) {
00068       return language;
00069     }
00070   }
00071 
00072   l_anguageCache.append( name.toUpper() );
00073   //kDebug(5320) << "KNMimeBase::cachedCharset() number of cs" << c_harsetCache.count();
00074   return l_anguageCache.last();
00075 }
00076 
00077 bool isUsAscii( const QString &s )
00078 {
00079   uint sLength = s.length();
00080   for ( uint i=0; i<sLength; i++ ) {
00081     if ( s.at( i ).toLatin1() <= 0 ) { // c==0: non-latin1, c<0: non-us-ascii
00082       return false;
00083     }
00084   }
00085   return true;
00086 }
00087 
00088 // "(),.:;<>@[\]
00089 const uchar specialsMap[16] = {
00090   0x00, 0x00, 0x00, 0x00, // CTLs
00091   0x20, 0xCA, 0x00, 0x3A, // SPACE ... '?'
00092   0x80, 0x00, 0x00, 0x1C, // '@' ... '_'
00093   0x00, 0x00, 0x00, 0x00  // '`' ... DEL
00094 };
00095 
00096 // "(),:;<>@[\]/=?
00097 const uchar tSpecialsMap[16] = {
00098   0x00, 0x00, 0x00, 0x00, // CTLs
00099   0x20, 0xC9, 0x00, 0x3F, // SPACE ... '?'
00100   0x80, 0x00, 0x00, 0x1C, // '@' ... '_'
00101   0x00, 0x00, 0x00, 0x00  // '`' ... DEL
00102 };
00103 
00104 // all except specials, CTLs, SPACE.
00105 const uchar aTextMap[16] = {
00106   0x00, 0x00, 0x00, 0x00,
00107   0x5F, 0x35, 0xFF, 0xC5,
00108   0x7F, 0xFF, 0xFF, 0xE3,
00109   0xFF, 0xFF, 0xFF, 0xFE
00110 };
00111 
00112 // all except tspecials, CTLs, SPACE.
00113 const uchar tTextMap[16] = {
00114   0x00, 0x00, 0x00, 0x00,
00115   0x5F, 0x36, 0xFF, 0xC0,
00116   0x7F, 0xFF, 0xFF, 0xE3,
00117   0xFF, 0xFF, 0xFF, 0xFE
00118 };
00119 
00120 // none except a-zA-Z0-9!*+-/
00121 const uchar eTextMap[16] = {
00122   0x00, 0x00, 0x00, 0x00,
00123   0x40, 0x35, 0xFF, 0xC0,
00124   0x7F, 0xFF, 0xFF, 0xE0,
00125   0x7F, 0xFF, 0xFF, 0xE0
00126 };
00127 
00128 QString decodeRFC2047String( const QByteArray &src, QByteArray &usedCS,
00129                              const QByteArray &defaultCS, bool forceCS )
00130 {
00131   QString result;
00132   QByteArray spaceBuffer;
00133   const char *scursor = src.constData();
00134   const char *send = scursor + src.length();
00135   bool onlySpacesSinceLastWord = false;
00136 
00137   while ( scursor != send ) {
00138      // space
00139     if ( isspace( *scursor ) && onlySpacesSinceLastWord ) {
00140       spaceBuffer += *scursor++;
00141       continue;
00142     }
00143 
00144     // possible start of an encoded word
00145     if ( *scursor == '=' ) {
00146       QByteArray language;
00147       QString decoded;
00148       ++scursor;
00149       const char *start = scursor;
00150       if ( HeaderParsing::parseEncodedWord( scursor, send, decoded, language, usedCS, defaultCS, forceCS ) ) {
00151         result += decoded;
00152         onlySpacesSinceLastWord = true;
00153         spaceBuffer.clear();
00154       } else {
00155         if ( onlySpacesSinceLastWord ) {
00156           result += QString::fromLatin1( spaceBuffer );
00157           onlySpacesSinceLastWord = false;
00158         }
00159         result += QLatin1Char( '=' );
00160         scursor = start; // reset cursor after parsing failure
00161       }
00162       continue;
00163     } else {
00164       // unencoded data
00165       if ( onlySpacesSinceLastWord ) {
00166         result += QString::fromLatin1( spaceBuffer );
00167         onlySpacesSinceLastWord = false;
00168       }
00169       result += QLatin1Char( *scursor );
00170       ++scursor;
00171     }
00172   }
00173 
00174   return result;
00175 }
00176 
00177 QByteArray encodeRFC2047String( const QString &src, const QByteArray &charset,
00178                                 bool addressHeader, bool allow8BitHeaders )
00179 {
00180   QByteArray encoded8Bit, result, usedCS;
00181   int start=0, end=0;
00182   bool nonAscii=false, ok=true, useQEncoding=false;
00183   QTextCodec *codec=0;
00184 
00185   usedCS = charset;
00186   codec = KGlobal::charsets()->codecForName( usedCS, ok );
00187 
00188   if ( !ok ) {
00189     //no codec available => try local8Bit and hope the best ;-)
00190     usedCS = KGlobal::locale()->encoding();
00191     codec = KGlobal::charsets()->codecForName( usedCS, ok );
00192   }
00193 
00194   if ( usedCS.contains( "8859-" ) ) { // use "B"-Encoding for non iso-8859-x charsets
00195     useQEncoding = true;
00196   }
00197 
00198   encoded8Bit = codec->fromUnicode( src );
00199 
00200   if ( allow8BitHeaders ) {
00201     return encoded8Bit;
00202   }
00203 
00204   uint encoded8BitLength = encoded8Bit.length();
00205   for ( unsigned int i=0; i<encoded8BitLength; i++ ) {
00206     if ( encoded8Bit[i] == ' ' ) { // encoding starts at word boundaries
00207       start = i + 1;
00208     }
00209 
00210     // encode escape character, for japanese encodings...
00211     if ( ( (signed char)encoded8Bit[i] < 0 ) || ( encoded8Bit[i] == '\033' ) ||
00212          ( addressHeader && ( strchr( "\"()<>@,.;:\\[]=", encoded8Bit[i] ) != 0 ) ) ) {
00213       end = start;   // non us-ascii char found, now we determine where to stop encoding
00214       nonAscii = true;
00215       break;
00216     }
00217   }
00218 
00219   if ( nonAscii ) {
00220     while ( ( end < encoded8Bit.length() ) && ( encoded8Bit[end] != ' ' ) ) {
00221       // we encode complete words
00222       end++;
00223     }
00224 
00225     for ( int x=end; x<encoded8Bit.length(); x++ ) {
00226       if ( ( (signed char)encoded8Bit[x]<0) || ( encoded8Bit[x] == '\033' ) ||
00227            ( addressHeader && ( strchr("\"()<>@,.;:\\[]=",encoded8Bit[x]) != 0 ) ) ) {
00228         end = encoded8Bit.length();     // we found another non-ascii word
00229 
00230         while ( ( end < encoded8Bit.length() ) && ( encoded8Bit[end] != ' ' ) ) {
00231           // we encode complete words
00232           end++;
00233         }
00234       }
00235     }
00236 
00237     result = encoded8Bit.left( start ) + "=?" + usedCS;
00238 
00239     if ( useQEncoding ) {
00240       result += "?Q?";
00241 
00242       char c, hexcode;// "Q"-encoding implementation described in RFC 2047
00243       for ( int i=start; i<end; i++ ) {
00244         c = encoded8Bit[i];
00245         if ( c == ' ' ) { // make the result readable with not MIME-capable readers
00246           result += '_';
00247         } else {
00248           if ( ( ( c >= 'a' ) && ( c <= 'z' ) ) || // paranoid mode, encode *all* special chars to avoid problems
00249               ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||  // with "From" & "To" headers
00250               ( ( c >= '0' ) && ( c <= '9' ) ) ) {
00251             result += c;
00252           } else {
00253             result += '=';                 // "stolen" from KMail ;-)
00254             hexcode = ((c & 0xF0) >> 4) + 48;
00255             if ( hexcode >= 58 ) {
00256               hexcode += 7;
00257             }
00258             result += hexcode;
00259             hexcode = (c & 0x0F) + 48;
00260             if ( hexcode >= 58 ) {
00261               hexcode += 7;
00262             }
00263             result += hexcode;
00264           }
00265         }
00266       }
00267     } else {
00268       result += "?B?" + encoded8Bit.mid( start, end - start ).toBase64();
00269     }
00270 
00271     result +="?=";
00272     result += encoded8Bit.right( encoded8Bit.length() - end );
00273   } else {
00274     result = encoded8Bit;
00275   }
00276 
00277   return result;
00278 }
00279 
00280 QByteArray uniqueString()
00281 {
00282   static char chars[] = "0123456789abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00283   time_t now;
00284   char p[11];
00285   int pos, ran;
00286   unsigned int timeval;
00287 
00288   p[10] = '\0';
00289   now = time( 0 );
00290   ran = 1 + (int)(1000.0*rand() / (RAND_MAX + 1.0));
00291   timeval = (now / ran) + getpid();
00292 
00293   for ( int i=0; i<10; i++ ) {
00294     pos = (int) (61.0*rand() / (RAND_MAX + 1.0));
00295     //kDebug(5320) << pos;
00296     p[i] = chars[pos];
00297   }
00298 
00299   QByteArray ret;
00300   ret.setNum( timeval );
00301   ret += '.';
00302   ret += p;
00303 
00304   return ret;
00305 }
00306 
00307 QByteArray multiPartBoundary()
00308 {
00309   return "nextPart" + uniqueString();
00310 }
00311 
00312 QByteArray unfoldHeader( const QByteArray &header )
00313 {
00314   QByteArray result;
00315   int pos = 0, foldBegin = 0, foldMid = 0, foldEnd = 0;
00316   while ( ( foldMid = header.indexOf( '\n', pos ) ) >= 0 ) {
00317     foldBegin = foldEnd = foldMid;
00318     // find the first space before the line-break
00319     while ( foldBegin > 0 ) {
00320       if ( !QChar( header[foldBegin - 1] ).isSpace() ) {
00321         break;
00322       }
00323       --foldBegin;
00324     }
00325     // find the first non-space after the line-break
00326     while ( foldEnd < header.length() - 1 ) {
00327       if ( !QChar( header[foldEnd] ).isSpace() ) {
00328         break;
00329       }
00330       ++foldEnd;
00331     }
00332     result += header.mid( pos, foldBegin - pos );
00333     result += ' ';
00334     pos = foldEnd;
00335   }
00336   result += header.mid( pos, header.length() - pos );
00337   return result;
00338 }
00339 
00340 int indexOfHeader( const QByteArray &src, const QByteArray &name, int &end, int &dataBegin, bool *folded )
00341 {
00342   QByteArray n = name;
00343   n.append( ':' );
00344   int begin = -1;
00345 
00346   if ( qstrnicmp( n.constData(), src.constData(), n.length() ) == 0 ) {
00347     begin = 0;
00348   } else {
00349     n.prepend('\n');
00350     const char *p = strcasestr( src.constData(), n.constData() );
00351     if ( !p ) {
00352       begin = -1;
00353     } else {
00354       begin = p - src.constData();
00355       ++begin;
00356     }
00357   }
00358 
00359   if ( begin > -1) {     //there is a header with the given name
00360     dataBegin = begin + name.length() + 1; //skip the name
00361     // skip the usual space after the colon
00362     if ( src.at( dataBegin ) == ' ' ) {
00363       ++dataBegin;
00364     }
00365     end = dataBegin;
00366     int len = src.length() - 1;
00367     if ( folded )
00368       *folded = false;
00369 
00370     if ( src.at(end) != '\n' ) {  // check if the header is not empty
00371       while ( true ) {
00372         end = src.indexOf( '\n', end + 1 );
00373         if ( end == -1 || end == len ||
00374              ( src[end+1] != ' ' && src[end+1] != '\t' ) ) {
00375           //break if we reach the end of the string, honor folded lines
00376           break;
00377         } else {
00378           if ( folded )
00379             *folded = true;
00380         }
00381       }
00382     }
00383 
00384     if ( end < 0 ) {
00385       end = len + 1; //take the rest of the string
00386     }
00387     return begin;
00388 
00389   } else {
00390     dataBegin = -1;
00391     return -1; //header not found
00392   }
00393 }
00394 
00395 QByteArray extractHeader( const QByteArray &src, const QByteArray &name )
00396 {
00397   int begin, end;
00398   bool folded;
00399   indexOfHeader( src, name, end, begin, &folded );
00400 
00401   if ( begin >= 0 ) {
00402     if ( !folded ) {
00403       return src.mid( begin, end - begin );
00404     } else {
00405       QByteArray hdrValue = src.mid( begin, end - begin );
00406       return unfoldHeader( hdrValue );
00407     }
00408   } else {
00409     return QByteArray(); //header not found
00410   }
00411 }
00412 
00413 void removeHeader( QByteArray &header, const QByteArray &name )
00414 {
00415   int begin, end, dummy;
00416   begin = indexOfHeader( header, name, end, dummy );
00417   if ( begin >= 0 ) {
00418     header.remove( begin, end - begin + 1 );
00419   }
00420 }
00421 
00422 QByteArray CRLFtoLF( const QByteArray &s )
00423 {
00424   QByteArray ret = s;
00425   ret.replace( "\r\n", "\n" );
00426   return ret;
00427 }
00428 
00429 QByteArray LFtoCRLF( const QByteArray &s )
00430 {
00431   QByteArray ret = s;
00432   ret.replace( "\n", "\r\n" );
00433   return ret;
00434 }
00435 
00436 namespace {
00437 template < typename T > void removeQuotesGeneric( T & str )
00438 {
00439   bool inQuote = false;
00440   for ( int i = 0; i < str.length(); ++i ) {
00441     if ( str[i] == '"' ) {
00442       str.remove( i, 1 );
00443       i--;
00444       inQuote = !inQuote;
00445     } else {
00446       if ( inQuote && ( str[i] == '\\' ) ) {
00447         str.remove( i, 1 );
00448       }
00449     }
00450   }
00451 }
00452 }
00453 
00454 void removeQuots( QByteArray &str )
00455 {
00456   removeQuotesGeneric( str );
00457 }
00458 
00459 void removeQuots( QString &str )
00460 {
00461   removeQuotesGeneric( str );
00462 }
00463 
00464 void addQuotes( QByteArray &str, bool forceQuotes )
00465 {
00466   bool needsQuotes=false;
00467   for ( int i=0; i < str.length(); i++ ) {
00468     if ( strchr("()<>@,.;:[]=\\\"", str[i] ) != 0 ) {
00469       needsQuotes = true;
00470     }
00471     if ( str[i] == '\\' || str[i] == '\"' ) {
00472       str.insert( i, '\\' );
00473       i++;
00474     }
00475   }
00476 
00477   if ( needsQuotes || forceQuotes ) {
00478     str.insert( 0, '\"' );
00479     str.append( "\"" );
00480   }
00481 }
00482 
00483 } // namespace KMime

KMIME Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal