KTNEF Library
ktnefparser.cpp
Go to the documentation of this file.
00001 /* 00002 ktnefparser.cpp 00003 00004 Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> 00005 00006 This file is part of KTNEF, the KDE TNEF support library/program. 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 */ 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 //@cond PRIVATE 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 //@endcond 00060 00061 //@cond IGNORE 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 //@endcond 00074 00075 //------------------------------------------------------------------------------ 00076 00081 //@cond PRIVATE 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 //@endcond 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 // read (type+name) 00148 stream_ >> i1; 00149 u = 0; 00150 tag = ( i1 & 0x0000FFFF ); 00151 type = ( ( i1 & 0xFFFF0000 ) >> 16 ); 00152 // read data length 00153 stream_ >> i2; 00154 // offset after reading the value 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 //kDebug().form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u ); 00287 break; 00288 } 00289 // skip data 00290 if ( device_->pos() != off && !device_->seek( off ) ) { 00291 return false; 00292 } 00293 // get checksum 00294 stream_ >> u; 00295 // add TNEF attribute 00296 message_->addAttribute( tag, type, value, true ); 00297 //kDebug() << "stream:" << device_->pos(); 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; // i <- attribute type & name 00309 tag = ( i & 0x0000FFFF ); 00310 type = ( ( i & 0xFFFF0000 ) >> 16 ); 00311 stream_ >> i; // i <- data length 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: // try to get attachment info 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 //value = QString( "< size=%1 >" ).arg( i ); 00358 //device_->seek( device_->pos()+i ); 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; // u <- checksum 00368 // add TNEF attribute 00369 current_->addAttribute( tag, type, value, true ); 00370 //kDebug() << "stream:" << device_->pos(); 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 delete current_; 00388 current_ = 0; 00389 00390 if ( !device_->open( QIODevice::ReadOnly ) ) { 00391 kDebug() << "Couldn't open device"; 00392 return false; 00393 } 00394 00395 stream_.setDevice( device_ ); 00396 stream_.setByteOrder( QDataStream::LittleEndian ); 00397 stream_ >> i; 00398 if ( i == TNEF_SIGNATURE ) { 00399 stream_ >> u; 00400 kDebug().nospace() << "Attachment cross reference key: 0x" 00401 << hex << qSetFieldWidth( 4 ) << qSetPadChar( '0' ) << u; 00402 //kDebug() << "stream:" << device_->pos(); 00403 while ( !stream_.atEnd() ) { 00404 stream_ >> c; 00405 switch( c ) { 00406 case LVL_MESSAGE: 00407 if ( !decodeMessage() ) { 00408 goto end; 00409 } 00410 break; 00411 case LVL_ATTACHMENT: 00412 if ( !decodeAttachment() ) { 00413 goto end; 00414 } 00415 break; 00416 default: 00417 kDebug() << "Unknown Level:" << c << ", at offset" << device_->pos(); 00418 goto end; 00419 } 00420 } 00421 if ( current_ ) { 00422 checkCurrent( attATTACHDATA ); // this line has the effect to append the 00423 // attachment, if it has data. If not it does 00424 // nothing, and the attachment will be discarded 00425 delete current_; 00426 current_ = 0; 00427 } 00428 return true; 00429 } else { 00430 kDebug() << "This is not a TNEF file"; 00431 end: 00432 device_->close(); 00433 return false; 00434 } 00435 } 00436 00437 bool KTNEFParser::extractFile( const QString &filename ) const 00438 { 00439 KTNEFAttach *att = d->message_->attachment( filename ); 00440 if ( !att ) { 00441 return false; 00442 } 00443 return d->extractAttachmentTo( att, d->defaultdir_ ); 00444 } 00445 00446 bool KTNEFParser::ParserPrivate::extractAttachmentTo( KTNEFAttach *att, 00447 const QString &dirname ) 00448 { 00449 QString filename = dirname + '/' + att->name(); 00450 if ( !device_->isOpen() ) { 00451 return false; 00452 } 00453 if ( !device_->seek( att->offset() ) ) { 00454 return false; 00455 } 00456 KSaveFile outfile( filename ); 00457 if ( !outfile.open() ) { 00458 return false; 00459 } 00460 00461 quint32 len = att->size(), sz( 16384 ); 00462 int n( 0 ); 00463 char *buf = new char[sz]; 00464 bool ok( true ); 00465 while ( ok && len > 0 ) { 00466 n = device_->read( buf, qMin( sz, len ) ); 00467 if ( n < 0 ) { 00468 ok = false; 00469 } else { 00470 len -= n; 00471 if ( outfile.write( buf, n ) != n ) { 00472 ok = false; 00473 } 00474 } 00475 } 00476 delete [] buf; 00477 00478 return ok; 00479 } 00480 00481 bool KTNEFParser::extractAll() 00482 { 00483 QList<KTNEFAttach*> l = d->message_->attachmentList(); 00484 QList<KTNEFAttach*>::const_iterator it = l.constBegin(); 00485 for ( ; it != l.constEnd(); ++it ) { 00486 if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) { 00487 return false; 00488 } 00489 } 00490 return true; 00491 } 00492 00493 bool KTNEFParser::extractFileTo( const QString &filename, 00494 const QString &dirname ) const 00495 { 00496 kDebug() << "Extracting attachment: filename=" 00497 << filename << ", dir=" << dirname; 00498 KTNEFAttach *att = d->message_->attachment( filename ); 00499 if ( !att ) { 00500 return false; 00501 } 00502 return d->extractAttachmentTo( att, dirname ); 00503 } 00504 00505 bool KTNEFParser::openFile( const QString &filename ) const 00506 { 00507 d->deleteDevice(); 00508 delete d->message_; 00509 d->message_ = new KTNEFMessage(); 00510 d->device_ = new QFile( filename ); 00511 d->deleteDevice_ = true; 00512 return d->parseDevice(); 00513 } 00514 00515 bool KTNEFParser::openDevice( QIODevice *device ) 00516 { 00517 d->deleteDevice(); 00518 d->device_ = device; 00519 return d->parseDevice(); 00520 } 00521 00522 void KTNEFParser::ParserPrivate::checkCurrent( int key ) 00523 { 00524 if ( !current_ ) { 00525 current_ = new KTNEFAttach(); 00526 } else { 00527 if ( current_->attributes().contains( key ) ) { 00528 if ( current_->offset() >= 0 ) { 00529 if ( current_->name().isEmpty() ) { 00530 current_->setName( "Unnamed" ); 00531 } 00532 if ( current_->mimeTag().isEmpty() ) { 00533 // No mime type defined in the TNEF structure, 00534 // try to find it from the attachment filename 00535 // and/or content (using at most 32 bytes) 00536 KMimeType::Ptr mimetype; 00537 if ( !current_->fileName().isEmpty() ) { 00538 mimetype = KMimeType::findByPath( current_->fileName(), 0, true ); 00539 } 00540 if ( !mimetype ) { 00541 return; // FIXME 00542 } 00543 if ( mimetype->name() == "application/octet-stream" && 00544 current_->size() > 0 ) { 00545 int oldOffset = device_->pos(); 00546 QByteArray buffer( qMin( 32, current_->size() ), '\0' ); 00547 device_->seek( current_->offset() ); 00548 device_->read( buffer.data(), buffer.size() ); 00549 mimetype = KMimeType::findByContent( buffer ); 00550 device_->seek( oldOffset ); 00551 } 00552 current_->setMimeTag( mimetype->name() ); 00553 } 00554 message_->addAttachment( current_ ); 00555 current_ = 0; 00556 } else { 00557 // invalid attachment, skip it 00558 delete current_; 00559 current_ = 0; 00560 } 00561 current_ = new KTNEFAttach(); 00562 } 00563 } 00564 } 00565 00566 //------------------------------------------------------------------------------ 00567 00568 //@cond IGNORE 00569 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); } 00570 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR ) 00571 00572 void clearMAPIName( MAPI_value &mapi ) 00573 { 00574 mapi.name.value.clear(); 00575 } 00576 00577 void clearMAPIValue( MAPI_value &mapi, bool clearName ) 00578 { 00579 mapi.value.clear(); 00580 if ( clearName ) { 00581 clearMAPIName( mapi ); 00582 } 00583 } 00584 00585 QDateTime formatTime( quint32 lowB, quint32 highB ) 00586 { 00587 QDateTime dt; 00588 quint64 u64; 00589 u64 = highB; 00590 u64 <<= 32; 00591 u64 |= lowB; 00592 u64 -= 116444736000000000LL; 00593 u64 /= 10000000; 00594 if ( u64 <= 0xffffffffU ) { 00595 dt.setTime_t( ( unsigned int )u64 ); 00596 } else { 00597 kWarning().nospace() << "Invalid date: low byte=" 00598 << showbase << qSetFieldWidth( 8 ) << qSetPadChar( '0' ) 00599 << lowB << ", high byte=" << highB; 00600 dt.setTime_t( 0xffffffffU ); 00601 } 00602 return dt; 00603 } 00604 00605 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props ) 00606 { 00607 QString s, dn, addr, t; 00608 QMap<int,KTnef::KTNEFProperty*>::ConstIterator it; 00609 if ( ( it = props.find( 0x3001 ) ) != props.end() ) { 00610 dn = ( *it )->valueString(); 00611 } 00612 if ( ( it = props.find( 0x3003 ) ) != props.end() ) { 00613 addr = ( *it )->valueString(); 00614 } 00615 if ( ( it = props.find( 0x0C15 ) ) != props.end() ) { 00616 switch ( ( *it )->value().toInt() ) { 00617 case 0: 00618 t = "From:"; 00619 break; 00620 case 1: 00621 t = "To:"; 00622 break; 00623 case 2: 00624 t = "Cc:"; 00625 break; 00626 case 3: 00627 t = "Bcc:"; 00628 break; 00629 } 00630 } 00631 if ( !t.isEmpty() ) { 00632 s.append( t ); 00633 } 00634 if ( !dn.isEmpty() ) { 00635 s.append( ' ' + dn ); 00636 } 00637 if ( !addr.isEmpty() && addr != dn ) { 00638 s.append( " <" + addr + '>' ); 00639 } 00640 00641 return s.trimmed(); 00642 } 00643 00644 QDateTime readTNEFDate( QDataStream &stream ) 00645 { 00646 // 14-bytes long 00647 quint16 y, m, d, hh, mm, ss, dm; 00648 stream >> y >> m >> d >> hh >> mm >> ss >> dm; 00649 return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) ); 00650 } 00651 00652 QString readTNEFAddress( QDataStream &stream ) 00653 { 00654 quint16 totalLen, strLen, addrLen; 00655 QString s; 00656 stream >> totalLen >> totalLen >> strLen >> addrLen; 00657 s.append( readMAPIString( stream, false, false, strLen ) ); 00658 s.append( " <" ); 00659 s.append( readMAPIString( stream, false, false, addrLen ) ); 00660 s.append( ">" ); 00661 quint8 c; 00662 for ( int i=8+strLen+addrLen; i<totalLen; i++ ) { 00663 stream >> c; 00664 } 00665 return s; 00666 } 00667 00668 QByteArray readTNEFData( QDataStream &stream, quint32 len ) 00669 { 00670 QByteArray array( len, '\0' ); 00671 if ( len > 0 ) { 00672 stream.readRawData( array.data(), len ); 00673 } 00674 return array; 00675 } 00676 00677 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len ) 00678 { 00679 switch ( type ) { 00680 case atpTEXT: 00681 case atpSTRING: 00682 return readMAPIString( stream, false, false, len ); 00683 case atpDATE: 00684 return readTNEFDate( stream ); 00685 default: 00686 return readTNEFData( stream, len ); 00687 } 00688 } 00689 00690 QString readMAPIString( QDataStream &stream, bool isUnicode, bool align, 00691 int len_ ) 00692 { 00693 quint32 len; 00694 char *buf = 0; 00695 if ( len_ == -1 ) { 00696 stream >> len; 00697 } else { 00698 len = len_; 00699 } 00700 quint32 fullLen = len; 00701 if ( align ) { 00702 ALIGN( fullLen, 4 ); 00703 } 00704 buf = new char[ len ]; 00705 stream.readRawData( buf, len ); 00706 quint8 c; 00707 for ( uint i=len; i<fullLen; i++ ) { 00708 stream >> c; 00709 } 00710 QString res; 00711 if ( isUnicode ) { 00712 res = QString::fromUtf16( ( const unsigned short *)buf ); 00713 } else { 00714 res = QString::fromLocal8Bit( buf ); 00715 } 00716 delete [] buf; 00717 return res; 00718 } 00719 00720 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi ) 00721 { 00722 quint32 d; 00723 00724 clearMAPIValue( mapi ); 00725 stream >> d; 00726 mapi.type = ( d & 0x0000FFFF ); 00727 mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 ); 00728 if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) { 00729 // skip GUID 00730 stream >> d >> d >> d >> d; 00731 // name type 00732 stream >> mapi.name.type; 00733 // name 00734 if ( mapi.name.type == 0 ) { 00735 uint tmp; 00736 stream >> tmp; 00737 mapi.name.value.setValue( tmp ); 00738 } else if ( mapi.name.type == 1 ) { 00739 mapi.name.value.setValue( readMAPIString( stream, true ) ); 00740 } 00741 } 00742 00743 int n = 1; 00744 QVariant value; 00745 if ( ISVECTOR( mapi ) ) { 00746 stream >> n; 00747 mapi.value = QList<QVariant>(); 00748 } 00749 for ( int i=0; i<n; i++ ) { 00750 value.clear(); 00751 switch( mapi.type & 0x0FFF ) { 00752 case MAPI_TYPE_UINT16: 00753 stream >> d; 00754 value.setValue( d & 0x0000FFFF ); 00755 break; 00756 case MAPI_TYPE_BOOLEAN: 00757 case MAPI_TYPE_ULONG: 00758 { 00759 uint tmp; 00760 stream >> tmp; 00761 value.setValue( tmp ); 00762 } 00763 break; 00764 case MAPI_TYPE_FLOAT: 00765 // FIXME: Don't we have to set the value here 00766 stream >> d; 00767 break; 00768 case MAPI_TYPE_DOUBLE: 00769 { 00770 double tmp; 00771 stream >> tmp; 00772 value.setValue( tmp ); 00773 } 00774 break; 00775 case MAPI_TYPE_TIME: 00776 { 00777 quint32 lowB, highB; 00778 stream >> lowB >> highB; 00779 value = formatTime( lowB, highB ); 00780 } 00781 break; 00782 case MAPI_TYPE_STRING8: 00783 // in case of a vector'ed value, the number of elements 00784 // has already been read in the upper for-loop 00785 if ( ISVECTOR( mapi ) ) { 00786 d = 1; 00787 } else { 00788 stream >> d; 00789 } 00790 for ( uint i=0; i<d; i++ ) { 00791 value.clear(); 00792 value.setValue( readMAPIString( stream ) ); 00793 } 00794 break; 00795 case MAPI_TYPE_USTRING: 00796 mapi.type = MAPI_TYPE_NONE; 00797 break; 00798 case MAPI_TYPE_OBJECT: 00799 case MAPI_TYPE_BINARY: 00800 if ( ISVECTOR( mapi ) ) { 00801 d = 1; 00802 } else { 00803 stream >> d; 00804 } 00805 for ( uint i=0; i<d; i++ ) { 00806 value.clear(); 00807 quint32 len; 00808 stream >> len; 00809 value = QByteArray( len, '\0' ); 00810 if ( len > 0 ) { 00811 int fullLen = len; 00812 ALIGN( fullLen, 4 ); 00813 stream.readRawData( value.toByteArray().data(), len ); 00814 quint8 c; 00815 for ( int i=len; i<fullLen; i++ ) { 00816 stream >> c; 00817 } 00818 // FIXME: Shouldn't we do something with the value??? 00819 } 00820 } 00821 break; 00822 default: 00823 mapi.type = MAPI_TYPE_NONE; 00824 break; 00825 } 00826 if ( ISVECTOR( mapi ) ) { 00827 QList <QVariant> lst = mapi.value.toList(); 00828 lst << value; 00829 mapi.value.setValue( lst ); 00830 } else { 00831 mapi.value = value; 00832 } 00833 } 00834 return mapi.tag; 00835 } 00836 //@endcond 00837 00838 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props, 00839 KTNEFAttach *attach ) 00840 { 00841 quint32 n; 00842 MAPI_value mapi; 00843 KTNEFProperty *p; 00844 QMap<int,KTNEFProperty*>::ConstIterator it; 00845 bool foundAttachment = false; 00846 00847 // some initializations 00848 mapi.type = MAPI_TYPE_NONE; 00849 mapi.value.clear(); 00850 00851 // get number of properties 00852 stream_ >> n; 00853 kDebug() << "MAPI Properties:" << n; 00854 for ( uint i=0; i<n; i++ ) { 00855 if ( stream_.atEnd() ) { 00856 clearMAPIValue( mapi ); 00857 return false; 00858 } 00859 readMAPIValue( stream_, mapi ); 00860 if ( mapi.type == MAPI_TYPE_NONE ) { 00861 kDebug().nospace() << "MAPI unsupported: tag=" 00862 << hex << mapi.tag << ", type=" << mapi.type; 00863 clearMAPIValue( mapi ); 00864 return false; 00865 } 00866 int key = mapi.tag; 00867 switch ( mapi.tag ) { 00868 case MAPI_TAG_DATA: 00869 { 00870 if ( mapi.type == MAPI_TYPE_OBJECT && attach ) { 00871 QByteArray data = mapi.value.toByteArray(); 00872 int len = data.size(); 00873 ALIGN( len, 4 ); 00874 device_->seek( device_->pos()-len ); 00875 quint32 interface_ID; 00876 stream_ >> interface_ID; 00877 if ( interface_ID == MAPI_IID_IMessage ) { 00878 // embedded TNEF file 00879 attach->unsetDataParser(); 00880 attach->setOffset( device_->pos()+12 ); 00881 attach->setSize( data.size()-16 ); 00882 attach->setMimeTag( "application/vnd.ms-tnef" ); 00883 attach->setDisplayName( "Embedded Message" ); 00884 kDebug() << "MAPI Embedded Message: size=" << data.size(); 00885 } 00886 device_->seek( device_->pos() + ( len-4 ) ); 00887 break; 00888 } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) { 00889 foundAttachment = true; 00890 int len = mapi.value.toByteArray().size(); 00891 ALIGN( len, 4 ); 00892 attach->setSize( len ); 00893 attach->setOffset( device_->pos() - len ); 00894 attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false ); 00895 } 00896 } 00897 kDebug() << "MAPI data: size=" << mapi.value.toByteArray().size(); 00898 break; 00899 default: 00900 { 00901 QString mapiname = ""; 00902 if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) { 00903 if ( mapi.name.type == 0 ) { 00904 mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() ); 00905 } else { 00906 mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() ); 00907 } 00908 } 00909 switch ( mapi.type & 0x0FFF ) { 00910 case MAPI_TYPE_UINT16: 00911 kDebug().nospace() << "(tag=" 00912 << hex << mapi.tag 00913 << ") MAPI short" << mapiname.toAscii().data() 00914 << ":" << hex << mapi.value.toUInt(); 00915 break; 00916 case MAPI_TYPE_ULONG: 00917 kDebug().nospace() << "(tag=" 00918 << hex << mapi.tag 00919 << ") MAPI long" << mapiname.toAscii().data() 00920 << ":" << hex << mapi.value.toUInt(); 00921 break; 00922 case MAPI_TYPE_BOOLEAN: 00923 kDebug().nospace() << "(tag=" 00924 << hex << mapi.tag 00925 << ") MAPI boolean" << mapiname.toAscii().data() 00926 << ":" << mapi.value.toBool(); 00927 break; 00928 case MAPI_TYPE_TIME: 00929 kDebug().nospace() << "(tag=" 00930 << hex << mapi.tag 00931 << ") MAPI time" << mapiname.toAscii().data() 00932 << ":" << mapi.value.toString().toAscii().data(); 00933 break; 00934 case MAPI_TYPE_USTRING: 00935 case MAPI_TYPE_STRING8: 00936 kDebug().nospace() << "(tag=" 00937 << hex << mapi.tag 00938 << ") MAPI string" << mapiname.toAscii().data() 00939 << ":size=" << mapi.value.toByteArray().size() 00940 << mapi.value.toString(); 00941 break; 00942 case MAPI_TYPE_BINARY: 00943 kDebug().nospace() << "(tag=" 00944 << hex << mapi.tag 00945 << ") MAPI binary" << mapiname.toAscii().data() 00946 << ":size=" << mapi.value.toByteArray().size(); 00947 break; 00948 } 00949 } 00950 break; 00951 } 00952 // do not remove potential existing similar entry 00953 if ( ( it = props.constFind( key ) ) == props.constEnd() ) { 00954 p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ), 00955 mapi.value, mapi.name.value ); 00956 props[ p->key() ] = p; 00957 } 00958 //kDebug() << "stream:" << device_->pos(); 00959 } 00960 00961 if ( foundAttachment && attach ) { 00962 attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() ); 00963 attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() ); 00964 QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString(); 00965 if ( !str.isEmpty() ) { 00966 attach->setDisplayName( str ); 00967 } 00968 attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() ); 00969 str = attach->property( MAPI_TAG_MIMETAG ).toString(); 00970 if ( !str.isEmpty() ) { 00971 attach->setMimeTag( str ); 00972 } 00973 attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() ); 00974 if ( attach->name().isEmpty() ) { 00975 attach->setName( attach->fileName() ); 00976 } 00977 } 00978 00979 return true; 00980 }