00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #ifdef HAVE_CONFIG_H
00018 #include <config.h>
00019 #endif
00020
00021 #include "kmime_util.h"
00022
00023 #include <kmdcodec.h>
00024 #include <kglobal.h>
00025 #include <klocale.h>
00026 #include <kcharsets.h>
00027 #include <kdeversion.h>
00028 #if KDE_IS_VERSION( 3, 1, 90 )
00029 #include <kcalendarsystem.h>
00030 #endif
00031
00032 #include <qtextcodec.h>
00033 #include <qstrlist.h>
00034 #include <qregexp.h>
00035
00036 #include <stdlib.h>
00037 #include <ctype.h>
00038 #include <time.h>
00039 #include <unistd.h>
00040
00041 using namespace KMime;
00042
00043 namespace KMime {
00044
00045 QStrIList c_harsetCache;
00046 QStrIList l_anguageCache;
00047
00048 const char* cachedCharset(const QCString &name)
00049 {
00050 int idx=c_harsetCache.find(name.data());
00051 if(idx>-1)
00052 return c_harsetCache.at(idx);
00053
00054 c_harsetCache.append(name.upper().data());
00055
00056 return c_harsetCache.last();
00057 }
00058
00059 const char* cachedLanguage(const QCString &name)
00060 {
00061 int idx=l_anguageCache.find(name.data());
00062 if(idx>-1)
00063 return l_anguageCache.at(idx);
00064
00065 l_anguageCache.append(name.upper().data());
00066
00067 return l_anguageCache.last();
00068 }
00069
00070 bool isUsAscii(const QString &s)
00071 {
00072 for (uint i=0; i<s.length(); i++)
00073 if (s.at(i).latin1()<=0)
00074 return false;
00075
00076 return true;
00077 }
00078
00079
00080 const uchar specialsMap[16] = {
00081 0x00, 0x00, 0x00, 0x00,
00082 0x20, 0xCA, 0x00, 0x3A,
00083 0x80, 0x00, 0x00, 0x1C,
00084 0x00, 0x00, 0x00, 0x00
00085 };
00086
00087
00088 const uchar tSpecialsMap[16] = {
00089 0x00, 0x00, 0x00, 0x00,
00090 0x20, 0xC9, 0x00, 0x3F,
00091 0x80, 0x00, 0x00, 0x1C,
00092 0x00, 0x00, 0x00, 0x00
00093 };
00094
00095
00096 const uchar aTextMap[16] = {
00097 0x00, 0x00, 0x00, 0x00,
00098 0x5F, 0x35, 0xFF, 0xC5,
00099 0x7F, 0xFF, 0xFF, 0xE3,
00100 0xFF, 0xFF, 0xFF, 0xFE
00101 };
00102
00103
00104 const uchar tTextMap[16] = {
00105 0x00, 0x00, 0x00, 0x00,
00106 0x5F, 0x36, 0xFF, 0xC0,
00107 0x7F, 0xFF, 0xFF, 0xE3,
00108 0xFF, 0xFF, 0xFF, 0xFE
00109 };
00110
00111
00112 const uchar eTextMap[16] = {
00113 0x00, 0x00, 0x00, 0x00,
00114 0x40, 0x35, 0xFF, 0xC0,
00115 0x7F, 0xFF, 0xFF, 0xE0,
00116 0x7F, 0xFF, 0xFF, 0xE0
00117 };
00118
00119 #if defined(_AIX) && defined(truncate)
00120 #undef truncate
00121 #endif
00122
00123 QString decodeRFC2047String(const QCString &src, const char **usedCS,
00124 const QCString &defaultCS, bool forceCS)
00125 {
00126 QCString result, str;
00127 QCString declaredCS;
00128 char *pos, *dest, *beg, *end, *mid, *endOfLastEncWord=0;
00129 char encoding = '\0';
00130 bool valid, onlySpacesSinceLastWord=false;
00131 const int maxLen=400;
00132 int i;
00133
00134 if(src.find("=?") < 0)
00135 result = src.copy();
00136 else {
00137 result.truncate(src.length());
00138 for (pos=src.data(), dest=result.data(); *pos; pos++)
00139 {
00140 if (pos[0]!='=' || pos[1]!='?')
00141 {
00142 *dest++ = *pos;
00143 if (onlySpacesSinceLastWord)
00144 onlySpacesSinceLastWord = (pos[0]==' ' || pos[1]=='\t');
00145 continue;
00146 }
00147 beg = pos+2;
00148 end = beg;
00149 valid = TRUE;
00150
00151 declaredCS="";
00152 for (i=2,pos+=2; i<maxLen && (*pos!='?'&&(ispunct(*pos)||isalnum(*pos))); i++) {
00153 declaredCS+=(*pos);
00154 pos++;
00155 }
00156 if (*pos!='?' || i<4 || i>=maxLen) valid = FALSE;
00157 else
00158 {
00159
00160 encoding = toupper(pos[1]);
00161 if (pos[2]!='?' || (encoding!='Q' && encoding!='B'))
00162 valid = FALSE;
00163 pos+=3;
00164 i+=3;
00165 }
00166 if (valid)
00167 {
00168 mid = pos;
00169
00170 while (i<maxLen && *pos && !(*pos=='?' && *(pos+1)=='='))
00171 {
00172 i++;
00173 pos++;
00174 }
00175 end = pos+2;
00176 if (i>=maxLen || !*pos) valid = FALSE;
00177 }
00178
00179 if (valid) {
00180
00181 if (onlySpacesSinceLastWord)
00182 dest=endOfLastEncWord;
00183
00184 if (mid < pos) {
00185 str = QCString(mid, (int)(pos - mid + 1));
00186 if (encoding == 'Q')
00187 {
00188
00189 for (i=str.length()-1; i>=0; i--)
00190 if (str[i]=='_') str[i]=' ';
00191 str = KCodecs::quotedPrintableDecode(str);
00192 }
00193 else
00194 {
00195 str = KCodecs::base64Decode(str);
00196 }
00197 for (i=0; str[i]; i++) {
00198 *dest++ = str[i];
00199 }
00200 }
00201
00202 endOfLastEncWord=dest;
00203 onlySpacesSinceLastWord=true;
00204
00205 pos = end -1;
00206 }
00207 else
00208 {
00209 pos = beg - 2;
00210 *dest++ = *pos++;
00211 *dest++ = *pos;
00212 }
00213 }
00214 *dest = '\0';
00215 }
00216
00217
00218 QTextCodec *codec=0;
00219 bool ok=true;
00220 if (forceCS || declaredCS.isEmpty()) {
00221 codec=KGlobal::charsets()->codecForName(defaultCS);
00222 (*usedCS)=cachedCharset(defaultCS);
00223 }
00224 else {
00225 codec=KGlobal::charsets()->codecForName(declaredCS, ok);
00226 if(!ok) {
00227 codec=KGlobal::charsets()->codecForName(defaultCS);
00228 (*usedCS)=cachedCharset(defaultCS);
00229 }
00230 else
00231 (*usedCS)=cachedCharset(declaredCS);
00232 }
00233
00234 return codec->toUnicode(result.data(), result.length());
00235 }
00236
00237
00238 QCString encodeRFC2047String(const QString &src, const char *charset,
00239 bool addressHeader, bool allow8BitHeaders)
00240 {
00241 QCString encoded8Bit, result, usedCS;
00242 unsigned int start=0,end=0;
00243 bool nonAscii=false, ok=true, useQEncoding=false;
00244 QTextCodec *codec=0;
00245
00246 usedCS=charset;
00247 codec=KGlobal::charsets()->codecForName(usedCS, ok);
00248
00249 if(!ok) {
00250
00251 usedCS=KGlobal::locale()->encoding();
00252 codec=KGlobal::charsets()->codecForName(usedCS, ok);
00253 }
00254
00255 if (usedCS.find("8859-")>=0)
00256 useQEncoding=true;
00257
00258 encoded8Bit=codec->fromUnicode(src);
00259
00260 if(allow8BitHeaders)
00261 return encoded8Bit;
00262
00263 for (unsigned int i=0; i<encoded8Bit.length(); i++) {
00264 if (encoded8Bit[i]==' ')
00265 start = i+1;
00266
00267
00268 if (((signed char)encoded8Bit[i]<0) || (encoded8Bit[i] == '\033') ||
00269 (addressHeader && (strchr("\"()<>@,.;:\\[]=",encoded8Bit[i])!=0))) {
00270 end = start;
00271 nonAscii=true;
00272 break;
00273 }
00274 }
00275
00276 if (nonAscii) {
00277 while ((end<encoded8Bit.length())&&(encoded8Bit[end]!=' '))
00278 end++;
00279
00280 for (unsigned int x=end;x<encoded8Bit.length();x++)
00281 if (((signed char)encoded8Bit[x]<0) || (encoded8Bit[x] == '\033') ||
00282 (addressHeader && (strchr("\"()<>@,.;:\\[]=",encoded8Bit[x])!=0))) {
00283 end = encoded8Bit.length();
00284
00285 while ((end<encoded8Bit.length())&&(encoded8Bit[end]!=' '))
00286 end++;
00287 }
00288
00289 result = encoded8Bit.left(start)+"=?"+usedCS;
00290
00291 if (useQEncoding) {
00292 result += "?Q?";
00293
00294 char c,hexcode;
00295 for (unsigned int i=start;i<end;i++) {
00296 c = encoded8Bit[i];
00297 if (c == ' ')
00298 result+='_';
00299 else
00300 if (((c>='a')&&(c<='z'))||
00301 ((c>='A')&&(c<='Z'))||
00302 ((c>='0')&&(c<='9')))
00303 result+=c;
00304 else {
00305 result += "=";
00306 hexcode = ((c & 0xF0) >> 4) + 48;
00307 if (hexcode >= 58) hexcode += 7;
00308 result += hexcode;
00309 hexcode = (c & 0x0F) + 48;
00310 if (hexcode >= 58) hexcode += 7;
00311 result += hexcode;
00312 }
00313 }
00314 } else {
00315 result += "?B?"+KCodecs::base64Encode(encoded8Bit.mid(start,end-start), false);
00316 }
00317
00318 result +="?=";
00319 result += encoded8Bit.right(encoded8Bit.length()-end);
00320 }
00321 else
00322 result = encoded8Bit;
00323
00324 return result;
00325 }
00326
00327 QCString uniqueString()
00328 {
00329 static char chars[] = "0123456789abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00330 time_t now;
00331 QCString ret;
00332 char p[11];
00333 int pos, ran;
00334 unsigned int timeval;
00335
00336 p[10]='\0';
00337 now=time(0);
00338 ran=1+(int) (1000.0*rand()/(RAND_MAX+1.0));
00339 timeval=(now/ran)+getpid();
00340
00341 for(int i=0; i<10; i++){
00342 pos=(int) (61.0*rand()/(RAND_MAX+1.0));
00343
00344 p[i]=chars[pos];
00345 }
00346 ret.sprintf("%d.%s", timeval, p);
00347
00348 return ret;
00349 }
00350
00351
00352 QCString multiPartBoundary()
00353 {
00354 QCString ret;
00355 ret="nextPart"+uniqueString();
00356 return ret;
00357 }
00358
00359 QCString extractHeader(const QCString &src, const char *name)
00360 {
00361 QCString n=QCString(name)+": ";
00362 int pos1=-1, pos2=0, len=src.length()-1;
00363 bool folded(false);
00364
00365 if (n.lower() == src.left(n.length()).lower()) {
00366 pos1 = 0;
00367 } else {
00368 n.prepend("\n");
00369 pos1 = src.find(n,0,false);
00370 }
00371
00372 if (pos1>-1) {
00373 pos1+=n.length();
00374 pos2=pos1;
00375
00376 if (src[pos2]!='\n') {
00377 while(1) {
00378 pos2=src.find("\n", pos2+1);
00379 if(pos2==-1 || pos2==len || ( src[pos2+1]!=' ' && src[pos2+1]!='\t') )
00380 break;
00381 else
00382 folded = true;
00383 }
00384 }
00385
00386 if(pos2<0) pos2=len+1;
00387
00388 if (!folded)
00389 return src.mid(pos1, pos2-pos1);
00390 else
00391 return (src.mid(pos1, pos2-pos1).replace(QRegExp("\\s*\\n\\s*")," "));
00392 }
00393 else {
00394 return QCString(0);
00395 }
00396 }
00397
00398
00399 QCString CRLFtoLF(const QCString &s)
00400 {
00401 QCString ret=s.copy();
00402 ret.replace(QRegExp("\\r\\n"), "\n");
00403 return ret;
00404 }
00405
00406
00407 QCString CRLFtoLF(const char *s)
00408 {
00409 QCString ret=s;
00410 ret.replace(QRegExp("\\r\\n"), "\n");
00411 return ret;
00412 }
00413
00414
00415 QCString LFtoCRLF(const QCString &s)
00416 {
00417 QCString ret=s.copy();
00418 ret.replace(QRegExp("\\n"), "\r\n");
00419 return ret;
00420 }
00421
00422
00423 void removeQuots(QCString &str)
00424 {
00425 bool inQuote=false;
00426
00427 for (int i=0; i < (int)str.length(); i++) {
00428 if (str[i] == '"') {
00429 str.remove(i,1);
00430 i--;
00431 inQuote = !inQuote;
00432 } else {
00433 if (inQuote && (str[i] == '\\'))
00434 str.remove(i,1);
00435 }
00436 }
00437 }
00438
00439
00440 void removeQuots(QString &str)
00441 {
00442 bool inQuote=false;
00443
00444 for (int i=0; i < (int)str.length(); i++) {
00445 if (str[i] == '"') {
00446 str.remove(i,1);
00447 i--;
00448 inQuote = !inQuote;
00449 } else {
00450 if (inQuote && (str[i] == '\\'))
00451 str.remove(i,1);
00452 }
00453 }
00454 }
00455
00456
00457 void addQuotes(QCString &str, bool forceQuotes)
00458 {
00459 bool needsQuotes=false;
00460 for (unsigned int i=0; i < str.length(); i++) {
00461 if (strchr("()<>@,.;:[]=\\\"",str[i])!=0)
00462 needsQuotes = true;
00463 if (str[i]=='\\' || str[i]=='\"') {
00464 str.insert(i, '\\');
00465 i++;
00466 }
00467 }
00468
00469 if (needsQuotes || forceQuotes) {
00470 str.insert(0,'\"');
00471 str.append("\"");
00472 }
00473 }
00474
00475 int DateFormatter::mDaylight = -1;
00476 DateFormatter::DateFormatter(FormatType fType)
00477 : mFormat( fType ), mCurrentTime( 0 )
00478 {
00479
00480 }
00481
00482 DateFormatter::~DateFormatter()
00483 {}
00484
00485 DateFormatter::FormatType
00486 DateFormatter::getFormat() const
00487 {
00488 return mFormat;
00489 }
00490
00491 void
00492 DateFormatter::setFormat( FormatType t )
00493 {
00494 mFormat = t;
00495 }
00496
00497 QString
00498 DateFormatter::dateString( time_t otime , const QString& lang ,
00499 bool shortFormat, bool includeSecs ) const
00500 {
00501 switch ( mFormat ) {
00502 case Fancy:
00503 return fancy( otime );
00504 break;
00505 case Localized:
00506 return localized( otime, shortFormat, includeSecs, lang );
00507 break;
00508 case CTime:
00509 return cTime( otime );
00510 break;
00511 case Iso:
00512 return isoDate( otime );
00513 break;
00514 case Custom:
00515 return custom( otime );
00516 break;
00517 }
00518 return QString::null;
00519 }
00520
00521 QString
00522 DateFormatter::dateString(const QDateTime& dtime, const QString& lang,
00523 bool shortFormat, bool includeSecs ) const
00524 {
00525 return DateFormatter::dateString( qdateToTimeT(dtime), lang, shortFormat, includeSecs );
00526 }
00527
00528 QCString
00529 DateFormatter::rfc2822(time_t otime) const
00530 {
00531 QDateTime tmp;
00532 QCString ret;
00533
00534 tmp.setTime_t(otime);
00535
00536 ret = tmp.toString("ddd, dd MMM yyyy hh:mm:ss ").latin1();
00537 ret += zone(otime);
00538
00539 return ret;
00540 }
00541
00542 QString
00543 DateFormatter::custom(time_t t) const
00544 {
00545 if ( mCustomFormat.isEmpty() )
00546 return QString::null;
00547
00548 int z = mCustomFormat.find("Z");
00549 QDateTime d;
00550 QString ret = mCustomFormat;
00551
00552 d.setTime_t(t);
00553 if ( z != -1 ) {
00554 ret.replace(z,1,zone(t));
00555 }
00556
00557 ret = d.toString(ret);
00558
00559 return ret;
00560 }
00561
00562 void
00563 DateFormatter::setCustomFormat(const QString& format)
00564 {
00565 mCustomFormat = format;
00566 mFormat = Custom;
00567 }
00568
00569 QString
00570 DateFormatter::getCustomFormat() const
00571 {
00572 return mCustomFormat;
00573 }
00574
00575
00576 QCString
00577 DateFormatter::zone(time_t otime) const
00578 {
00579 QCString ret;
00580 #if defined(HAVE_TIMEZONE) || defined(HAVE_TM_GMTOFF)
00581 struct tm *local = localtime( &otime );
00582 #endif
00583
00584 #if defined(HAVE_TIMEZONE)
00585
00586
00587 int secs = abs(timezone);
00588 int neg = (timezone>0)?1:0;
00589 int hours = secs/3600;
00590 int mins = (secs - hours*3600)/60;
00591
00592
00593 if ( local->tm_isdst > 0 ) {
00594 mDaylight = 1;
00595 if ( neg )
00596 --hours;
00597 else
00598 ++hours;
00599 } else
00600 mDaylight = 0;
00601
00602 ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins);
00603
00604 #elif defined(HAVE_TM_GMTOFF)
00605
00606 int secs = abs( local->tm_gmtoff );
00607 int neg = (local->tm_gmtoff<0)?1:0;
00608 int hours = secs/3600;
00609 int mins = (secs - hours*3600)/60;
00610
00611 if ( local->tm_isdst > 0 )
00612 mDaylight = 1;
00613 else
00614 mDaylight = 0;
00615
00616 ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins);
00617
00618 #else
00619
00620 QDateTime d1 = QDateTime::fromString( asctime(gmtime(&otime)) );
00621 QDateTime d2 = QDateTime::fromString( asctime(localtime(&otime)) );
00622 int secs = d1.secsTo(d2);
00623 int neg = (secs<0)?1:0;
00624 secs = abs(secs);
00625 int hours = secs/3600;
00626 int mins = (secs - hours*3600)/60;
00627
00628 ret.sprintf("%c%.2d%.2d",(neg)?'-':'+', hours, mins);
00629
00630 #endif
00631
00632 return ret;
00633 }
00634
00635 time_t
00636 DateFormatter::qdateToTimeT(const QDateTime& dt) const
00637 {
00638 QDateTime epoch( QDate(1970, 1,1), QTime(00,00,00) );
00639 time_t otime;
00640 time( &otime );
00641
00642 QDateTime d1 = QDateTime::fromString( asctime(gmtime(&otime)) );
00643 QDateTime d2 = QDateTime::fromString( asctime(localtime(&otime)) );
00644 time_t drf = epoch.secsTo( dt ) - d1.secsTo( d2 );
00645
00646 return drf;
00647 }
00648
00649 QString
00650 DateFormatter::fancy(time_t otime) const
00651 {
00652 KLocale *locale = KGlobal::locale();
00653
00654 if ( otime <= 0 )
00655 return i18n( "unknown" );
00656
00657 if ( !mCurrentTime ) {
00658 time( &mCurrentTime );
00659 mDate.setTime_t( mCurrentTime );
00660 }
00661
00662 QDateTime old;
00663 old.setTime_t( otime );
00664
00665
00666 if ( mCurrentTime + 60 * 60 >= otime ) {
00667 time_t diff = mCurrentTime - otime;
00668
00669 if ( diff < 24 * 60 * 60 ) {
00670 if ( old.date().year() == mDate.date().year() &&
00671 old.date().dayOfYear() == mDate.date().dayOfYear() )
00672 return i18n( "Today %1" ).arg( locale->
00673 formatTime( old.time(), true ) );
00674 }
00675 if ( diff < 2 * 24 * 60 * 60 ) {
00676 QDateTime yesterday( mDate.addDays( -1 ) );
00677 if ( old.date().year() == yesterday.date().year() &&
00678 old.date().dayOfYear() == yesterday.date().dayOfYear() )
00679 return i18n( "Yesterday %1" ).arg( locale->
00680 formatTime( old.time(), true) );
00681 }
00682 for ( int i = 3; i < 7; i++ )
00683 if ( diff < i * 24 * 60 * 60 ) {
00684 QDateTime weekday( mDate.addDays( -i + 1 ) );
00685 if ( old.date().year() == weekday.date().year() &&
00686 old.date().dayOfYear() == weekday.date().dayOfYear() )
00687 return i18n( "1. weekday, 2. time", "%1 %2" ).
00688 #if KDE_IS_VERSION( 3, 1, 90 )
00689 arg( locale->calendar()->weekDayName( old.date() ) ).
00690 #else
00691 arg( locale->weekDayName( old.date().dayOfWeek() ) ).
00692 #endif
00693 arg( locale->formatTime( old.time(), true) );
00694 }
00695 }
00696
00697 return locale->formatDateTime( old );
00698
00699 }
00700
00701 QString
00702 DateFormatter::localized(time_t otime, bool shortFormat, bool includeSecs,
00703 const QString& localeLanguage ) const
00704 {
00705 QDateTime tmp;
00706 QString ret;
00707 KLocale *locale = KGlobal::locale();
00708
00709 tmp.setTime_t( otime );
00710
00711
00712 if ( !localeLanguage.isEmpty() ) {
00713 QString olang = locale->language();
00714 locale->setLanguage( localeLanguage );
00715 ret = locale->formatDateTime( tmp, shortFormat, includeSecs );
00716 locale->setLanguage( olang );
00717 } else {
00718 ret = locale->formatDateTime( tmp, shortFormat, includeSecs );
00719 }
00720
00721 return ret;
00722 }
00723
00724 QString
00725 DateFormatter::cTime(time_t otime) const
00726 {
00727 return QString::fromLatin1( ctime( &otime ) ).stripWhiteSpace() ;
00728 }
00729
00730 QString
00731 DateFormatter::isoDate(time_t otime) const
00732 {
00733 char cstr[64];
00734 strftime( cstr, 63, "%Y-%m-%d %H:%M:%S", localtime(&otime) );
00735 return QString( cstr );
00736 }
00737
00738
00739 void
00740 DateFormatter::reset()
00741 {
00742 mCurrentTime = 0;
00743 }
00744
00745 QString
00746 DateFormatter::formatDate(DateFormatter::FormatType t, time_t otime,
00747 const QString& data, bool shortFormat, bool includeSecs )
00748 {
00749 DateFormatter f( t );
00750 if ( t == DateFormatter::Custom ) {
00751 f.setCustomFormat( data );
00752 }
00753 return f.dateString( otime, data, shortFormat, includeSecs );
00754 }
00755
00756 QString
00757 DateFormatter::formatCurrentDate( DateFormatter::FormatType t, const QString& data,
00758 bool shortFormat, bool includeSecs )
00759 {
00760 DateFormatter f( t );
00761 if ( t == DateFormatter::Custom ) {
00762 f.setCustomFormat( data );
00763 }
00764 return f.dateString( time(0), data, shortFormat, includeSecs );
00765 }
00766
00767 QCString
00768 DateFormatter::rfc2822FormatDate( time_t t )
00769 {
00770 DateFormatter f;
00771 return f.rfc2822( t );
00772 }
00773
00774 bool
00775 DateFormatter::isDaylight()
00776 {
00777 if ( mDaylight == -1 ) {
00778 time_t ntime = time( 0 );
00779 struct tm *local = localtime( &ntime );
00780 if ( local->tm_isdst > 0 ) {
00781 mDaylight = 1;
00782 return true;
00783 } else {
00784 mDaylight = 0;
00785 return false;
00786 }
00787 } else if ( mDaylight != 0 )
00788 return true;
00789 else
00790 return false;
00791 }
00792
00793 }