00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00031 #include "ktnefparser.h"
00032 #include "ktnefattach.h"
00033 #include "ktnefproperty.h"
00034 #include "ktnefmessage.h"
00035 #include "ktnefdefs.h"
00036
00037 #include <kdebug.h>
00038 #include <kmimetype.h>
00039 #include <ksavefile.h>
00040
00041 #include <QtCore/QDateTime>
00042 #include <QtCore/QDataStream>
00043 #include <QtCore/QFile>
00044 #include <QtCore/QVariant>
00045 #include <QtCore/QList>
00046
00047 using namespace KTnef;
00048
00049
00050 typedef struct {
00051 quint16 type;
00052 quint16 tag;
00053 QVariant value;
00054 struct {
00055 quint32 type;
00056 QVariant value;
00057 } name;
00058 } MAPI_value;
00059
00060
00061
00062 void clearMAPIName( MAPI_value &mapi );
00063 void clearMAPIValue( MAPI_value &mapi, bool clearName = true );
00064 QString readMAPIString( QDataStream &stream, bool isUnicode = false,
00065 bool align = true, int len = -1 );
00066 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi );
00067 QDateTime readTNEFDate( QDataStream &stream );
00068 QString readTNEFAddress( QDataStream &stream );
00069 QByteArray readTNEFData( QDataStream &stream, quint32 len );
00070 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len );
00071 QDateTime formatTime( quint32 lowB, quint32 highB );
00072 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props );
00073
00074
00075
00076
00081
00082 class KTnef::KTNEFParser::ParserPrivate
00083 {
00084 public:
00085 ParserPrivate()
00086 {
00087 defaultdir_ = "/tmp/";
00088 current_ = 0;
00089 deleteDevice_ = false;
00090 device_ = 0;
00091 message_ = new KTNEFMessage;
00092 }
00093 ~ParserPrivate()
00094 {
00095 delete message_;
00096 }
00097
00098 bool decodeAttachment();
00099 bool decodeMessage();
00100 bool extractAttachmentTo( KTNEFAttach *att, const QString &dirname );
00101 void checkCurrent( int key );
00102 bool readMAPIProperties( QMap<int,KTNEFProperty*>& props,
00103 KTNEFAttach *attach = 0 );
00104 bool parseDevice();
00105 void deleteDevice();
00106
00107 QDataStream stream_;
00108 QIODevice *device_;
00109 bool deleteDevice_;
00110 QString defaultdir_;
00111 KTNEFAttach *current_;
00112 KTNEFMessage *message_;
00113 };
00114
00115
00116 KTNEFParser::KTNEFParser()
00117 : d( new ParserPrivate )
00118 {
00119 }
00120
00121 KTNEFParser::~KTNEFParser()
00122 {
00123 d->deleteDevice();
00124 delete d;
00125 }
00126
00127 KTNEFMessage *KTNEFParser::message() const
00128 {
00129 return d->message_;
00130 }
00131
00132 void KTNEFParser::ParserPrivate::deleteDevice()
00133 {
00134 if ( deleteDevice_ ) {
00135 delete device_;
00136 }
00137 device_ = 0;
00138 deleteDevice_ = false;
00139 }
00140
00141 bool KTNEFParser::ParserPrivate::decodeMessage()
00142 {
00143 quint32 i1, i2, off;
00144 quint16 u, tag, type;
00145 QVariant value;
00146
00147
00148 stream_ >> i1;
00149 u = 0;
00150 tag = ( i1 & 0x0000FFFF );
00151 type = ( ( i1 & 0xFFFF0000 ) >> 16 );
00152
00153 stream_ >> i2;
00154
00155 off = device_->pos() + i2;
00156 switch ( tag ) {
00157 case attAIDOWNER:
00158 {
00159 uint tmp;
00160 stream_ >> tmp;
00161 value.setValue( tmp );
00162 message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value );
00163 kDebug() << "Message Owner Appointment ID" << "(length=" << i2 << ")";
00164 break;
00165 }
00166 case attREQUESTRES:
00167 stream_ >> u;
00168 message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u );
00169 value = ( bool )u;
00170 kDebug() << "Message Request Response" << "(length=" << i2 << ")";
00171 break;
00172 case attDATERECD:
00173 value = readTNEFDate( stream_ );
00174 message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value );
00175 kDebug() << "Message Receive Date" << "(length=" << i2 << ")";
00176 break;
00177 case attMSGCLASS:
00178 value = readMAPIString( stream_, false, false, i2 );
00179 message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value );
00180 kDebug() << "Message Class" << "(length=" << i2 << ")";
00181 break;
00182 case attMSGPRIORITY:
00183 stream_ >> u;
00184 message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u );
00185 value = u;
00186 kDebug() << "Message Priority" << "(length=" << i2 << ")";
00187 break;
00188 case attMAPIPROPS:
00189 kDebug() << "Message MAPI Properties" << "(length=" << i2 << ")";
00190 {
00191 int nProps = message_->properties().count();
00192 i2 += device_->pos();
00193 readMAPIProperties( message_->properties(), 0 );
00194 device_->seek( i2 );
00195 kDebug() << "Properties:" << message_->properties().count();
00196 value = QString( "< %1 properties >" ).
00197 arg( message_->properties().count() - nProps );
00198 }
00199 break;
00200 case attTNEFVERSION:
00201 {
00202 uint tmp;
00203 stream_ >> tmp;
00204 value.setValue( tmp );
00205 kDebug() << "Message TNEF Version" << "(length=" << i2 << ")";
00206 }
00207 break;
00208 case attFROM:
00209 message_->addProperty( 0x0024, MAPI_TYPE_STRING8, readTNEFAddress( stream_ ) );
00210 device_->seek( device_->pos() - i2 );
00211 value = readTNEFData( stream_, i2 );
00212 kDebug() << "Message From" << "(length=" << i2 << ")";
00213 break;
00214 case attSUBJECT:
00215 value = readMAPIString( stream_, false, false, i2 );
00216 message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value );
00217 kDebug() << "Message Subject" << "(length=" << i2 << ")";
00218 break;
00219 case attDATESENT:
00220 value = readTNEFDate( stream_ );
00221 message_->addProperty( 0x0039, MAPI_TYPE_TIME, value );
00222 kDebug() << "Message Date Sent" << "(length=" << i2 << ")";
00223 break;
00224 case attMSGSTATUS:
00225 {
00226 quint8 c;
00227 quint32 flag = 0;
00228 stream_ >> c;
00229 if ( c & fmsRead ) {
00230 flag |= MSGFLAG_READ;
00231 }
00232 if ( !( c & fmsModified ) ) {
00233 flag |= MSGFLAG_UNMODIFIED;
00234 }
00235 if ( c & fmsSubmitted ) {
00236 flag |= MSGFLAG_SUBMIT;
00237 }
00238 if ( c & fmsHasAttach ) {
00239 flag |= MSGFLAG_HASATTACH;
00240 }
00241 if ( c & fmsLocal ) {
00242 flag |= MSGFLAG_UNSENT;
00243 }
00244 message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
00245 value = c;
00246 }
00247 kDebug() << "Message Status" << "(length=" << i2 << ")";
00248 break;
00249 case attRECIPTABLE:
00250 {
00251 quint32 rows;
00252 QList<QVariant> recipTable;
00253 stream_ >> rows;
00254 for ( uint i=0; i<rows; i++ ) {
00255 QMap<int,KTNEFProperty*> props;
00256 readMAPIProperties( props, 0 );
00257 recipTable << formatRecipient( props );
00258 }
00259 message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable );
00260 device_->seek( device_->pos() - i2 );
00261 value = readTNEFData( stream_, i2 );
00262 }
00263 kDebug() << "Message Recipient Table" << "(length=" << i2 << ")";
00264 break;
00265 case attBODY:
00266 value = readMAPIString( stream_, false, false, i2 );
00267 message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value );
00268 kDebug() << "Message Body" << "(length=" << i2 << ")";
00269 break;
00270 case attDATEMODIFIED:
00271 value = readTNEFDate( stream_ );
00272 message_->addProperty( 0x3008, MAPI_TYPE_TIME, value );
00273 kDebug() << "Message Date Modified" << "(length=" << i2 << ")";
00274 break;
00275 case attMSGID:
00276 value = readMAPIString( stream_, false, false, i2 );
00277 message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value );
00278 kDebug() << "Message ID" << "(length=" << i2 << ")";
00279 break;
00280 case attOEMCODEPAGE:
00281 value = readTNEFData( stream_, i2 );
00282 kDebug() << "Message OEM Code Page" << "(length=" << i2 << ")";
00283 break;
00284 default:
00285 value = readTNEFAttribute( stream_, type, i2 );
00286
00287 break;
00288 }
00289
00290 if ( device_->pos() != off && !device_->seek( off ) ) {
00291 return false;
00292 }
00293
00294 stream_ >> u;
00295
00296 message_->addAttribute( tag, type, value, true );
00297
00298 return true;
00299 }
00300
00301 bool KTNEFParser::ParserPrivate::decodeAttachment()
00302 {
00303 quint32 i;
00304 quint16 tag, type, u;
00305 QVariant value;
00306 QString str;
00307
00308 stream_ >> i;
00309 tag = ( i & 0x0000FFFF );
00310 type = ( ( i & 0xFFFF0000 ) >> 16 );
00311 stream_ >> i;
00312 checkCurrent( tag );
00313 switch ( tag ) {
00314 case attATTACHTITLE:
00315 value = readMAPIString( stream_, false, false, i );
00316 current_->setName( value.toString() );
00317 kDebug() << "Attachment Title:" << current_->name();
00318 break;
00319 case attATTACHDATA:
00320 current_->setSize( i );
00321 current_->setOffset( device_->pos() );
00322 device_->seek( device_->pos() + i );
00323 value = QString( "< size=%1 >" ).arg( i );
00324 kDebug() << "Attachment Data: size=" << i;
00325 break;
00326 case attATTACHMENT:
00327 i += device_->pos();
00328 readMAPIProperties( current_->properties(), current_ );
00329 device_->seek( i );
00330 current_->setIndex( current_->property( MAPI_TAG_INDEX ).toUInt() );
00331 current_->setDisplaySize( current_->property( MAPI_TAG_SIZE ).toUInt() );
00332 str = current_->property( MAPI_TAG_DISPLAYNAME ).toString();
00333 if ( !str.isEmpty() ) {
00334 current_->setDisplayName( str );
00335 }
00336 current_->setFileName( current_->property( MAPI_TAG_FILENAME ).
00337 toString() );
00338 str = current_->property( MAPI_TAG_MIMETAG ).toString();
00339 if ( !str.isEmpty() ) {
00340 current_->setMimeTag( str );
00341 }
00342 current_->setExtension( current_->property( MAPI_TAG_EXTENSION ).
00343 toString() );
00344 value = QString( "< %1 properties >" ).
00345 arg( current_->properties().count() );
00346 break;
00347 case attATTACHMODDATE:
00348 value = readTNEFDate( stream_ );
00349 kDebug() << "Attachment Modification Date:" << value.toString();
00350 break;
00351 case attATTACHCREATEDATE:
00352 value = readTNEFDate( stream_ );
00353 kDebug() << "Attachment Creation Date:" << value.toString();
00354 break;
00355 case attATTACHMETAFILE:
00356 kDebug() << "Attachment Metafile: size=" << i;
00357
00358
00359 value = readTNEFData( stream_, i );
00360 break;
00361 default:
00362 value = readTNEFAttribute( stream_, type, i );
00363 kDebug() << "Attachment unknown field: tag="
00364 << hex << tag << ", length=" << dec << i;
00365 break;
00366 }
00367 stream_ >> u;
00368
00369 current_->addAttribute( tag, type, value, true );
00370
00371
00372 return true;
00373 }
00374
00375 void KTNEFParser::setDefaultExtractDir( const QString &dirname )
00376 {
00377 d->defaultdir_ = dirname;
00378 }
00379
00380 bool KTNEFParser::ParserPrivate::parseDevice()
00381 {
00382 quint16 u;
00383 quint32 i;
00384 quint8 c;
00385
00386 message_->clearAttachments();
00387 if ( current_ ) {
00388 delete current_;
00389 current_ = 0;
00390 }
00391
00392 if ( !device_->open( QIODevice::ReadOnly ) ) {
00393 kDebug() << "Couldn't open device";
00394 return false;
00395 }
00396
00397 stream_.setDevice( device_ );
00398 stream_.setByteOrder( QDataStream::LittleEndian );
00399 stream_ >> i;
00400 if ( i == TNEF_SIGNATURE ) {
00401 stream_ >> u;
00402 kDebug().nospace() << "Attachment cross reference key: 0x"
00403 << hex << qSetFieldWidth( 4 ) << qSetPadChar( '0' ) << u;
00404
00405 while ( !stream_.atEnd() ) {
00406 stream_ >> c;
00407 switch( c ) {
00408 case LVL_MESSAGE:
00409 if ( !decodeMessage() ) {
00410 goto end;
00411 }
00412 break;
00413 case LVL_ATTACHMENT:
00414 if ( !decodeAttachment() ) {
00415 goto end;
00416 }
00417 break;
00418 default:
00419 kDebug() << "Unknown Level:" << c << ", at offset" << device_->pos();
00420 goto end;
00421 }
00422 }
00423 if ( current_ ) {
00424 checkCurrent( attATTACHDATA );
00425
00426
00427 delete current_;
00428 current_ = 0;
00429 }
00430 return true;
00431 } else {
00432 kDebug() << "This is not a TNEF file";
00433 end:
00434 device_->close();
00435 return false;
00436 }
00437 }
00438
00439 bool KTNEFParser::extractFile( const QString &filename ) const
00440 {
00441 KTNEFAttach *att = d->message_->attachment( filename );
00442 if ( !att ) {
00443 return false;
00444 }
00445 return d->extractAttachmentTo( att, d->defaultdir_ );
00446 }
00447
00448 bool KTNEFParser::ParserPrivate::extractAttachmentTo( KTNEFAttach *att,
00449 const QString &dirname )
00450 {
00451 QString filename = dirname + '/' + att->name();
00452 if ( !device_->isOpen() ) {
00453 return false;
00454 }
00455 if ( !device_->seek( att->offset() ) ) {
00456 return false;
00457 }
00458 KSaveFile outfile( filename );
00459 if ( !outfile.open() ) {
00460 return false;
00461 }
00462
00463 quint32 len = att->size(), sz( 16384 );
00464 int n( 0 );
00465 char *buf = new char[sz];
00466 bool ok( true );
00467 while ( ok && len > 0 ) {
00468 n = device_->read( buf, qMin( sz, len ) );
00469 if ( n < 0 ) {
00470 ok = false;
00471 } else {
00472 len -= n;
00473 if ( outfile.write( buf, n ) != n ) {
00474 ok = false;
00475 }
00476 }
00477 }
00478 delete [] buf;
00479
00480 return ok;
00481 }
00482
00483 bool KTNEFParser::extractAll()
00484 {
00485 QList<KTNEFAttach*> l = d->message_->attachmentList();
00486 QList<KTNEFAttach*>::const_iterator it = l.constBegin();
00487 for ( ; it != l.constEnd(); ++it ) {
00488 if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
00489 return false;
00490 }
00491 }
00492 return true;
00493 }
00494
00495 bool KTNEFParser::extractFileTo( const QString &filename,
00496 const QString &dirname ) const
00497 {
00498 kDebug() << "Extracting attachment: filename="
00499 << filename << ", dir=" << dirname;
00500 KTNEFAttach *att = d->message_->attachment( filename );
00501 if ( !att ) {
00502 return false;
00503 }
00504 return d->extractAttachmentTo( att, dirname );
00505 }
00506
00507 bool KTNEFParser::openFile( const QString &filename ) const
00508 {
00509 d->deleteDevice();
00510 delete d->message_;
00511 d->message_ = new KTNEFMessage();
00512 d->device_ = new QFile( filename );
00513 d->deleteDevice_ = true;
00514 return d->parseDevice();
00515 }
00516
00517 bool KTNEFParser::openDevice( QIODevice *device )
00518 {
00519 d->deleteDevice();
00520 d->device_ = device;
00521 return d->parseDevice();
00522 }
00523
00524 void KTNEFParser::ParserPrivate::checkCurrent( int key )
00525 {
00526 if ( !current_ ) {
00527 current_ = new KTNEFAttach();
00528 } else {
00529 if ( current_->attributes().contains( key ) ) {
00530 if ( current_->offset() >= 0 ) {
00531 if ( current_->name().isEmpty() ) {
00532 current_->setName( "Unnamed" );
00533 }
00534 if ( current_->mimeTag().isEmpty() ) {
00535
00536
00537
00538 KMimeType::Ptr mimetype;
00539 if ( !current_->fileName().isEmpty() ) {
00540 mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
00541 }
00542 if ( !mimetype ) {
00543 return;
00544 }
00545 if ( mimetype->name() == "application/octet-stream" &&
00546 current_->size() > 0 ) {
00547 int oldOffset = device_->pos();
00548 QByteArray buffer( qMin( 32, current_->size() ), '\0' );
00549 device_->seek( current_->offset() );
00550 device_->read( buffer.data(), buffer.size() );
00551 mimetype = KMimeType::findByContent( buffer );
00552 device_->seek( oldOffset );
00553 }
00554 current_->setMimeTag( mimetype->name() );
00555 }
00556 message_->addAttachment( current_ );
00557 current_ = 0;
00558 } else {
00559
00560 delete current_;
00561 current_ = 0;
00562 }
00563 current_ = new KTNEFAttach();
00564 }
00565 }
00566 }
00567
00568
00569
00570
00571 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); }
00572 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR )
00573
00574 void clearMAPIName( MAPI_value &mapi )
00575 {
00576 mapi.name.value.clear();
00577 }
00578
00579 void clearMAPIValue( MAPI_value &mapi, bool clearName )
00580 {
00581 mapi.value.clear();
00582 if ( clearName ) {
00583 clearMAPIName( mapi );
00584 }
00585 }
00586
00587 QDateTime formatTime( quint32 lowB, quint32 highB )
00588 {
00589 QDateTime dt;
00590 quint64 u64;
00591 u64 = highB;
00592 u64 <<= 32;
00593 u64 |= lowB;
00594 u64 -= 116444736000000000LL;
00595 u64 /= 10000000;
00596 if ( u64 <= 0xffffffffU ) {
00597 dt.setTime_t( ( unsigned int )u64 );
00598 } else {
00599 kWarning().nospace() << "Invalid date: low byte="
00600 << showbase << qSetFieldWidth( 8 ) << qSetPadChar( '0' )
00601 << lowB << ", high byte=" << highB;
00602 dt.setTime_t( 0xffffffffU );
00603 }
00604 return dt;
00605 }
00606
00607 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props )
00608 {
00609 QString s, dn, addr, t;
00610 QMap<int,KTnef::KTNEFProperty*>::ConstIterator it;
00611 if ( ( it = props.find( 0x3001 ) ) != props.end() ) {
00612 dn = ( *it )->valueString();
00613 }
00614 if ( ( it = props.find( 0x3003 ) ) != props.end() ) {
00615 addr = ( *it )->valueString();
00616 }
00617 if ( ( it = props.find( 0x0C15 ) ) != props.end() ) {
00618 switch ( ( *it )->value().toInt() ) {
00619 case 0:
00620 t = "From:";
00621 break;
00622 case 1:
00623 t = "To:";
00624 break;
00625 case 2:
00626 t = "Cc:";
00627 break;
00628 case 3:
00629 t = "Bcc:";
00630 break;
00631 }
00632 }
00633 if ( !t.isEmpty() ) {
00634 s.append( t );
00635 }
00636 if ( !dn.isEmpty() ) {
00637 s.append( ' ' + dn );
00638 }
00639 if ( !addr.isEmpty() && addr != dn ) {
00640 s.append( " <" + addr + '>' );
00641 }
00642
00643 return s.trimmed();
00644 }
00645
00646 QDateTime readTNEFDate( QDataStream &stream )
00647 {
00648
00649 quint16 y, m, d, hh, mm, ss, dm;
00650 stream >> y >> m >> d >> hh >> mm >> ss >> dm;
00651 return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) );
00652 }
00653
00654 QString readTNEFAddress( QDataStream &stream )
00655 {
00656 quint16 totalLen, strLen, addrLen;
00657 QString s;
00658 stream >> totalLen >> totalLen >> strLen >> addrLen;
00659 s.append( readMAPIString( stream, false, false, strLen ) );
00660 s.append( " <" );
00661 s.append( readMAPIString( stream, false, false, addrLen ) );
00662 s.append( ">" );
00663 quint8 c;
00664 for ( int i=8+strLen+addrLen; i<totalLen; i++ ) {
00665 stream >> c;
00666 }
00667 return s;
00668 }
00669
00670 QByteArray readTNEFData( QDataStream &stream, quint32 len )
00671 {
00672 QByteArray array( len, '\0' );
00673 if ( len > 0 ) {
00674 stream.readRawData( array.data(), len );
00675 }
00676 return array;
00677 }
00678
00679 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len )
00680 {
00681 switch ( type ) {
00682 case atpTEXT:
00683 case atpSTRING:
00684 return readMAPIString( stream, false, false, len );
00685 case atpDATE:
00686 return readTNEFDate( stream );
00687 default:
00688 return readTNEFData( stream, len );
00689 }
00690 }
00691
00692 QString readMAPIString( QDataStream &stream, bool isUnicode, bool align,
00693 int len_ )
00694 {
00695 quint32 len;
00696 char *buf = 0;
00697 if ( len_ == -1 ) {
00698 stream >> len;
00699 } else {
00700 len = len_;
00701 }
00702 quint32 fullLen = len;
00703 if ( align ) {
00704 ALIGN( fullLen, 4 );
00705 }
00706 buf = new char[ len ];
00707 stream.readRawData( buf, len );
00708 quint8 c;
00709 for ( uint i=len; i<fullLen; i++ ) {
00710 stream >> c;
00711 }
00712 QString res;
00713 if ( isUnicode ) {
00714 res = QString::fromUtf16( ( const unsigned short *)buf );
00715 } else {
00716 res = QString::fromLocal8Bit( buf );
00717 }
00718 delete [] buf;
00719 return res;
00720 }
00721
00722 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi )
00723 {
00724 quint32 d;
00725
00726 clearMAPIValue( mapi );
00727 stream >> d;
00728 mapi.type = ( d & 0x0000FFFF );
00729 mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 );
00730 if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00731
00732 stream >> d >> d >> d >> d;
00733
00734 stream >> mapi.name.type;
00735
00736 if ( mapi.name.type == 0 ) {
00737 uint tmp;
00738 stream >> tmp;
00739 mapi.name.value.setValue( tmp );
00740 } else if ( mapi.name.type == 1 ) {
00741 mapi.name.value.setValue( readMAPIString( stream, true ) );
00742 }
00743 }
00744
00745 int n = 1;
00746 QVariant value;
00747 if ( ISVECTOR( mapi ) ) {
00748 stream >> n;
00749 mapi.value = QList<QVariant>();
00750 }
00751 for ( int i=0; i<n; i++ ) {
00752 value.clear();
00753 switch( mapi.type & 0x0FFF ) {
00754 case MAPI_TYPE_UINT16:
00755 stream >> d;
00756 value.setValue( d & 0x0000FFFF );
00757 break;
00758 case MAPI_TYPE_BOOLEAN:
00759 case MAPI_TYPE_ULONG:
00760 {
00761 uint tmp;
00762 stream >> tmp;
00763 value.setValue( tmp );
00764 }
00765 break;
00766 case MAPI_TYPE_FLOAT:
00767
00768 stream >> d;
00769 break;
00770 case MAPI_TYPE_DOUBLE:
00771 {
00772 double tmp;
00773 stream >> tmp;
00774 value.setValue( tmp );
00775 }
00776 break;
00777 case MAPI_TYPE_TIME:
00778 {
00779 quint32 lowB, highB;
00780 stream >> lowB >> highB;
00781 value = formatTime( lowB, highB );
00782 }
00783 break;
00784 case MAPI_TYPE_STRING8:
00785
00786
00787 if ( ISVECTOR( mapi ) ) {
00788 d = 1;
00789 } else {
00790 stream >> d;
00791 }
00792 for ( uint i=0; i<d; i++ ) {
00793 value.clear();
00794 value.setValue( readMAPIString( stream ) );
00795 }
00796 break;
00797 case MAPI_TYPE_USTRING:
00798 mapi.type = MAPI_TYPE_NONE;
00799 break;
00800 case MAPI_TYPE_OBJECT:
00801 case MAPI_TYPE_BINARY:
00802 if ( ISVECTOR( mapi ) ) {
00803 d = 1;
00804 } else {
00805 stream >> d;
00806 }
00807 for ( uint i=0; i<d; i++ ) {
00808 value.clear();
00809 quint32 len;
00810 stream >> len;
00811 value = QByteArray( len, '\0' );
00812 if ( len > 0 ) {
00813 int fullLen = len;
00814 ALIGN( fullLen, 4 );
00815 stream.readRawData( value.toByteArray().data(), len );
00816 quint8 c;
00817 for ( int i=len; i<fullLen; i++ ) {
00818 stream >> c;
00819 }
00820
00821 }
00822 }
00823 break;
00824 default:
00825 mapi.type = MAPI_TYPE_NONE;
00826 break;
00827 }
00828 if ( ISVECTOR( mapi ) ) {
00829 QList <QVariant> lst = mapi.value.toList();
00830 lst << value;
00831 mapi.value.setValue( lst );
00832 } else {
00833 mapi.value = value;
00834 }
00835 }
00836 return mapi.tag;
00837 }
00838
00839
00840 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
00841 KTNEFAttach *attach )
00842 {
00843 quint32 n;
00844 MAPI_value mapi;
00845 KTNEFProperty *p;
00846 QMap<int,KTNEFProperty*>::ConstIterator it;
00847 bool foundAttachment = false;
00848
00849
00850 mapi.type = MAPI_TYPE_NONE;
00851 mapi.value.clear();
00852
00853
00854 stream_ >> n;
00855 kDebug() << "MAPI Properties:" << n;
00856 for ( uint i=0; i<n; i++ ) {
00857 if ( stream_.atEnd() ) {
00858 clearMAPIValue( mapi );
00859 return false;
00860 }
00861 readMAPIValue( stream_, mapi );
00862 if ( mapi.type == MAPI_TYPE_NONE ) {
00863 kDebug().nospace() << "MAPI unsupported: tag="
00864 << hex << mapi.tag << ", type=" << mapi.type;
00865 clearMAPIValue( mapi );
00866 return false;
00867 }
00868 int key = mapi.tag;
00869 switch ( mapi.tag ) {
00870 case MAPI_TAG_DATA:
00871 {
00872 if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
00873 QByteArray data = mapi.value.toByteArray();
00874 int len = data.size();
00875 ALIGN( len, 4 );
00876 device_->seek( device_->pos()-len );
00877 quint32 interface_ID;
00878 stream_ >> interface_ID;
00879 if ( interface_ID == MAPI_IID_IMessage ) {
00880
00881 attach->unsetDataParser();
00882 attach->setOffset( device_->pos()+12 );
00883 attach->setSize( data.size()-16 );
00884 attach->setMimeTag( "application/vnd.ms-tnef" );
00885 attach->setDisplayName( "Embedded Message" );
00886 kDebug() << "MAPI Embedded Message: size=" << data.size();
00887 }
00888 device_->seek( device_->pos() + ( len-4 ) );
00889 break;
00890 } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) {
00891 foundAttachment = true;
00892 int len = mapi.value.toByteArray().size();
00893 ALIGN( len, 4 );
00894 attach->setSize( len );
00895 attach->setOffset( device_->pos() - len );
00896 attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
00897 }
00898 }
00899 kDebug() << "MAPI data: size=" << mapi.value.toByteArray().size();
00900 break;
00901 default:
00902 {
00903 QString mapiname = "";
00904 if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00905 if ( mapi.name.type == 0 ) {
00906 mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
00907 } else {
00908 mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() );
00909 }
00910 }
00911 switch ( mapi.type & 0x0FFF ) {
00912 case MAPI_TYPE_UINT16:
00913 kDebug().nospace() << "(tag="
00914 << hex << mapi.tag
00915 << ") MAPI short" << mapiname.toAscii().data()
00916 << ":" << hex << mapi.value.toUInt();
00917 break;
00918 case MAPI_TYPE_ULONG:
00919 kDebug().nospace() << "(tag="
00920 << hex << mapi.tag
00921 << ") MAPI long" << mapiname.toAscii().data()
00922 << ":" << hex << mapi.value.toUInt();
00923 break;
00924 case MAPI_TYPE_BOOLEAN:
00925 kDebug().nospace() << "(tag="
00926 << hex << mapi.tag
00927 << ") MAPI boolean" << mapiname.toAscii().data()
00928 << ":" << mapi.value.toBool();
00929 break;
00930 case MAPI_TYPE_TIME:
00931 kDebug().nospace() << "(tag="
00932 << hex << mapi.tag
00933 << ") MAPI time" << mapiname.toAscii().data()
00934 << ":" << mapi.value.toString().toAscii().data();
00935 break;
00936 case MAPI_TYPE_USTRING:
00937 case MAPI_TYPE_STRING8:
00938 kDebug().nospace() << "(tag="
00939 << hex << mapi.tag
00940 << ") MAPI string" << mapiname.toAscii().data()
00941 << ":size=" << mapi.value.toByteArray().size()
00942 << mapi.value.toString();
00943 break;
00944 case MAPI_TYPE_BINARY:
00945 kDebug().nospace() << "(tag="
00946 << hex << mapi.tag
00947 << ") MAPI binary" << mapiname.toAscii().data()
00948 << ":size=" << mapi.value.toByteArray().size();
00949 break;
00950 }
00951 }
00952 break;
00953 }
00954
00955 if ( ( it = props.constFind( key ) ) == props.constEnd() ) {
00956 p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
00957 mapi.value, mapi.name.value );
00958 props[ p->key() ] = p;
00959 }
00960
00961 }
00962
00963 if ( foundAttachment && attach ) {
00964 attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
00965 attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
00966 QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString();
00967 if ( !str.isEmpty() ) {
00968 attach->setDisplayName( str );
00969 }
00970 attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() );
00971 str = attach->property( MAPI_TAG_MIMETAG ).toString();
00972 if ( !str.isEmpty() ) {
00973 attach->setMimeTag( str );
00974 }
00975 attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() );
00976 if ( attach->name().isEmpty() ) {
00977 attach->setName( attach->fileName() );
00978 }
00979 }
00980
00981 return true;
00982 }