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