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