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 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 //kDebug() << "stream:" << device_->pos(); 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 ); // this line has the effect to append the 00425 // attachment, if it has data. If not it does 00426 // nothing, and the attachment will be discarded 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 // No mime type defined in the TNEF structure, 00536 // try to find it from the attachment filename 00537 // and/or content (using at most 32 bytes) 00538 KMimeType::Ptr mimetype; 00539 if ( !current_->fileName().isEmpty() ) { 00540 mimetype = KMimeType::findByPath( current_->fileName(), 0, true ); 00541 } 00542 if ( !mimetype ) { 00543 return; // FIXME 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 // invalid attachment, skip it 00560 delete current_; 00561 current_ = 0; 00562 } 00563 current_ = new KTNEFAttach(); 00564 } 00565 } 00566 } 00567 00568 //------------------------------------------------------------------------------ 00569 00570 //@cond IGNORE 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 // 14-bytes long 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 // skip GUID 00732 stream >> d >> d >> d >> d; 00733 // name type 00734 stream >> mapi.name.type; 00735 // name 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 // FIXME: Don't we have to set the value here 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 // in case of a vector'ed value, the number of elements 00786 // has already been read in the upper for-loop 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 // FIXME: Shouldn't we do something with the value??? 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 //@endcond 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 // some initializations 00850 mapi.type = MAPI_TYPE_NONE; 00851 mapi.value.clear(); 00852 00853 // get number of properties 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 // embedded TNEF file 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 // do not remove potential existing similar entry 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 //kDebug() << "stream:" << device_->pos(); 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 }