diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /ktnef/lib/ktnefparser.cpp | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'ktnef/lib/ktnefparser.cpp')
-rw-r--r-- | ktnef/lib/ktnefparser.cpp | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/ktnef/lib/ktnefparser.cpp b/ktnef/lib/ktnefparser.cpp new file mode 100644 index 000000000..0c9881400 --- /dev/null +++ b/ktnef/lib/ktnefparser.cpp @@ -0,0 +1,883 @@ +/* + ktnefparser.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + This file is part of KTNEF, the KDE TNEF support library/program. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include "ktnef/ktnefparser.h" +#include "ktnef/ktnefattach.h" +#include "ktnef/ktnefproperty.h" +#include "ktnef/ktnefmessage.h" + +#include <qdatetime.h> +#include <qdatastream.h> +#include <qfile.h> +#include <qvariant.h> +#include <kdebug.h> +#include <kmimetype.h> +#include <ksavefile.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif /* HAVE_INTTYPES_H */ + +#include "ktnef/ktnefdefs.h" + + +typedef struct { + Q_UINT16 type; + Q_UINT16 tag; + QVariant value; + struct { + Q_UINT32 type; + QVariant value; + } name; +} MAPI_value; + +void clearMAPIName( MAPI_value& mapi ); +void clearMAPIValue(MAPI_value& mapi, bool clearName = true); +QString readMAPIString( QDataStream& stream, bool isUnicode = false, bool align = true, int len = -1 ); +Q_UINT16 readMAPIValue(QDataStream& stream, MAPI_value& mapi); +QDateTime readTNEFDate( QDataStream& stream ); +QString readTNEFAddress( QDataStream& stream ); +QByteArray readTNEFData( QDataStream& stream, Q_UINT32 len ); +QVariant readTNEFAttribute( QDataStream& stream, Q_UINT16 type, Q_UINT32 len ); +QDateTime formatTime( Q_UINT32 lowB, Q_UINT32 highB ); +QString formatRecipient( const QMap<int,KTNEFProperty*>& props ); + +//------------------------------------------------------------------------------------ + +class KTNEFParser::ParserPrivate +{ +public: + ParserPrivate() + { + defaultdir_ = "/tmp/"; + current_ = 0; + deleteDevice_ = false; + device_ = 0; + message_ = new KTNEFMessage; + } + ~ParserPrivate() + { + delete message_; + } + + QDataStream stream_; + QIODevice *device_; + bool deleteDevice_; + QString defaultdir_; + KTNEFAttach *current_; + KTNEFMessage *message_; +}; + +KTNEFParser::KTNEFParser() +{ + d = new ParserPrivate; +} + +KTNEFParser::~KTNEFParser() +{ + deleteDevice(); + delete d; +} + +KTNEFMessage* KTNEFParser::message() const +{ + return d->message_; +} + +void KTNEFParser::deleteDevice() +{ + if ( d->deleteDevice_ ) + delete d->device_; + d->device_ = 0; + d->deleteDevice_ = false; +} + +bool KTNEFParser::decodeMessage() +{ + Q_UINT32 i1, i2, off; + Q_UINT16 u, tag, type; + QVariant value; + + // read (type+name) + d->stream_ >> i1; + u = 0; + tag = ( i1 & 0x0000FFFF ); + type = ( ( i1 & 0xFFFF0000 ) >> 16 ); + // read data length + d->stream_ >> i2; + // offset after reading the value + off = d->device_->at() + i2; + switch ( tag ) + { + case attAIDOWNER: + d->stream_ >> value.asUInt(); + d->message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value ); + kdDebug() << "Message Owner Appointment ID" << " (length=" << i2 << ")" << endl; + break; + case attREQUESTRES: + d->stream_ >> u; + d->message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u ); + value = ( bool )u; + kdDebug() << "Message Request Response" << " (length=" << i2 << ")" << endl; + break; + case attDATERECD: + value = readTNEFDate( d->stream_ ); + d->message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value ); + kdDebug() << "Message Receive Date" << " (length=" << i2 << ")" << endl; + break; + case attMSGCLASS: + value = readMAPIString( d->stream_, false, false, i2 ); + d->message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value ); + kdDebug() << "Message Class" << " (length=" << i2 << ")" << endl; + break; + case attMSGPRIORITY: + d->stream_ >> u; + d->message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u ); + value = u; + kdDebug() << "Message Priority" << " (length=" << i2 << ")" << endl; + break; + case attMAPIPROPS: + kdDebug() << "Message MAPI Properties" << " (length=" << i2 << ")" << endl; + { + int nProps = d->message_->properties().count(); + i2 += d->device_->at(); + readMAPIProperties( d->message_->properties(), 0 ); + d->device_->at( i2 ); + kdDebug() << "Properties: " << d->message_->properties().count() << endl; + value = QString( "< %1 properties >" ).arg( d->message_->properties().count() - nProps ); + } + break; + case attTNEFVERSION: + d->stream_ >> value.asUInt(); + kdDebug() << "Message TNEF Version" << " (length=" << i2 << ")" << endl; + break; + case attFROM: + d->message_->addProperty( 0x0024, MAPI_TYPE_STRING8, readTNEFAddress( d->stream_ ) ); + d->device_->at( d->device_->at() - i2 ); + value = readTNEFData( d->stream_, i2 ); + kdDebug() << "Message From" << " (length=" << i2 << ")" << endl; + break; + case attSUBJECT: + value = readMAPIString( d->stream_, false, false, i2 ); + d->message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value ); + kdDebug() << "Message Subject" << " (length=" << i2 << ")" << endl; + break; + case attDATESENT: + value = readTNEFDate( d->stream_ ); + d->message_->addProperty( 0x0039, MAPI_TYPE_TIME, value ); + kdDebug() << "Message Date Sent" << " (length=" << i2 << ")" << endl; + break; + case attMSGSTATUS: + { + Q_UINT8 c; + Q_UINT32 flag = 0; + d->stream_ >> c; + if ( c & fmsRead ) flag |= MSGFLAG_READ; + if ( !( c & fmsModified ) ) flag |= MSGFLAG_UNMODIFIED; + if ( c & fmsSubmitted ) flag |= MSGFLAG_SUBMIT; + if ( c & fmsHasAttach ) flag |= MSGFLAG_HASATTACH; + if ( c & fmsLocal ) flag |= MSGFLAG_UNSENT; + d->message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag ); + value = c; + } + kdDebug() << "Message Status" << " (length=" << i2 << ")" << endl; + break; + case attRECIPTABLE: + { + Q_UINT32 rows; + QValueList<QVariant> recipTable; + d->stream_ >> rows; + for ( uint i=0; i<rows; i++ ) + { + QMap<int,KTNEFProperty*> props; + readMAPIProperties( props, 0 ); + recipTable << formatRecipient( props ); + } + d->message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable ); + d->device_->at( d->device_->at() - i2 ); + value = readTNEFData( d->stream_, i2 ); + } + kdDebug() << "Message Recipient Table" << " (length=" << i2 << ")" << endl; + break; + case attBODY: + value = readMAPIString( d->stream_, false, false, i2 ); + d->message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value ); + kdDebug() << "Message Body" << " (length=" << i2 << ")" << endl; + break; + case attDATEMODIFIED: + value = readTNEFDate( d->stream_ ); + d->message_->addProperty( 0x3008, MAPI_TYPE_TIME, value ); + kdDebug() << "Message Date Modified" << " (length=" << i2 << ")" << endl; + break; + case attMSGID: + value = readMAPIString( d->stream_, false, false, i2 ); + d->message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value ); + kdDebug() << "Message ID" << " (length=" << i2 << ")" << endl; + break; + case attOEMCODEPAGE: + value = readTNEFData( d->stream_, i2 ); + kdDebug() << "Message OEM Code Page" << " (length=" << i2 << ")" << endl; + break; + default: + value = readTNEFAttribute( d->stream_, type, i2 ); + kdDebug().form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u ); + break; + } + // skip data + if ( d->device_->at() != off && !d->device_->at( off ) ) + return false; + // get checksum + d->stream_ >> u; + // add TNEF attribute + d->message_->addAttribute( tag, type, value, true ); + //kdDebug() << "stream: " << d->device_->at() << endl; + return true; +} + +bool KTNEFParser::decodeAttachment() +{ + Q_UINT32 i; + Q_UINT16 tag, type, u; + QVariant value; + QString str; + + d->stream_ >> i; // i <- attribute type & name + tag = ( i & 0x0000FFFF ); + type = ( ( i & 0xFFFF0000 ) >> 16 ); + d->stream_ >> i; // i <- data length + checkCurrent( tag ); + switch (tag) + { + case attATTACHTITLE: + value = readMAPIString( d->stream_, false, false, i ); + d->current_->setName( value.toString() ); + kdDebug() << "Attachment Title: " << d->current_->name() << endl; + break; + case attATTACHDATA: + d->current_->setSize( i ); + d->current_->setOffset( d->device_->at() ); + d->device_->at( d->device_->at() + i ); + value = QString( "< size=%1 >" ).arg( i ); + kdDebug() << "Attachment Data: size=" << i << endl; + break; + case attATTACHMENT: // try to get attachment info + i += d->device_->at(); + readMAPIProperties( d->current_->properties(), d->current_ ); + d->device_->at( i ); + d->current_->setIndex( d->current_->property( MAPI_TAG_INDEX ).toUInt() ); + d->current_->setDisplaySize( d->current_->property( MAPI_TAG_SIZE ).toUInt() ); + str = d->current_->property( MAPI_TAG_DISPLAYNAME ).toString(); + if ( !str.isEmpty() ) + d->current_->setDisplayName( str ); + d->current_->setFileName( d->current_->property( MAPI_TAG_FILENAME ).toString() ); + str = d->current_->property( MAPI_TAG_MIMETAG ).toString(); + if ( !str.isEmpty() ) + d->current_->setMimeTag( str ); + d->current_->setExtension( d->current_->property( MAPI_TAG_EXTENSION ).toString() ); + value = QString( "< %1 properties >" ).arg( d->current_->properties().count() ); + break; + case attATTACHMODDATE: + value = readTNEFDate( d->stream_ ); + kdDebug() << "Attachment Modification Date: " << value.toString() << endl; + break; + case attATTACHCREATEDATE: + value = readTNEFDate( d->stream_ ); + kdDebug() << "Attachment Creation Date: " << value.toString() << endl; + break; + case attATTACHMETAFILE: + kdDebug() << "Attachment Metafile: size=" << i << endl; + //value = QString( "< size=%1 >" ).arg( i ); + //d->device_->at( d->device_->at()+i ); + value = readTNEFData( d->stream_, i ); + break; + default: + value = readTNEFAttribute( d->stream_, type, i ); + kdDebug().form( "Attachment unknown field: tag=%x, length=%d\n", tag, i); + break; + } + d->stream_ >> u; // u <- checksum + // add TNEF attribute + d->current_->addAttribute( tag, type, value, true ); + //kdDebug() << "stream: " << d->device_->at() << endl; + + return true; +} + +void KTNEFParser::setDefaultExtractDir(const QString& dirname) +{ + d->defaultdir_ = dirname; +} + +bool KTNEFParser::parseDevice() +{ + Q_UINT16 u; + Q_UINT32 i; + Q_UINT8 c; + + d->message_->clearAttachments(); + if (d->current_) + { + delete d->current_; + d->current_ = 0; + } + + if ( !d->device_->open( IO_ReadOnly ) ) { + kdDebug() << "Couldn't open device" << endl; + return false; + } + + d->stream_.setDevice( d->device_ ); + d->stream_.setByteOrder( QDataStream::LittleEndian ); + d->stream_ >> i; + if (i == TNEF_SIGNATURE) + { + d->stream_ >> u; + kdDebug().form( "Attachment cross reference key: 0x%04x\n",u ); + //kdDebug() << "stream: " << d->device_->at() << endl; + while (!d->stream_.eof()) + { + d->stream_ >> c; + switch (c) + { + case LVL_MESSAGE: + if (!decodeMessage()) goto end; + break; + case LVL_ATTACHMENT: + if (!decodeAttachment()) goto end; + break; + default: + kdDebug() << "Unknown Level: " << c << ", at offset " << d->device_->at() << endl; + goto end; + } + } + if (d->current_) + { + checkCurrent(attATTACHDATA); // this line has the effect to append the + // attachment, if it has data. If not it does + // nothing, and the attachment will be discarded + delete d->current_; + d->current_ = 0; + } + return true; + } + else + { + kdDebug() << "This is not a TNEF file" << endl; +end: d->device_->close(); + return false; + } +} + +bool KTNEFParser::extractFile(const QString& filename) +{ + KTNEFAttach *att = d->message_->attachment(filename); + if (!att) return false; + return extractAttachmentTo(att, d->defaultdir_); +} + +bool KTNEFParser::extractAttachmentTo(KTNEFAttach *att, const QString& dirname) +{ + QString filename = dirname + "/" + att->name(); + if (!d->device_->isOpen()) + return false; + if (!d->device_->at(att->offset())) + return false; + KSaveFile saveFile( filename ); + QFile *outfile = saveFile.file(); + if ( !outfile ) + return false; + + Q_UINT32 len = att->size(), sz(16384); + int n(0); + char *buf = new char[sz]; + bool ok(true); + while (ok && len > 0) + { + n = d->device_->readBlock(buf,QMIN(sz,len)); + if (n < 0) + ok = false; + else + { + len -= n; + if (outfile->writeBlock(buf,n) != n) + ok = false; + } + } + delete [] buf; + + return ok; +} + +bool KTNEFParser::extractAll() +{ + QPtrListIterator<KTNEFAttach> it(d->message_->attachmentList()); + for (;it.current();++it) + if (!extractAttachmentTo(it.current(),d->defaultdir_)) return false; + return true; +} + +bool KTNEFParser::extractFileTo(const QString& filename, const QString& dirname) +{ + kdDebug() << "Extracting attachment: filename=" << filename << ", dir=" << dirname << endl; + KTNEFAttach *att = d->message_->attachment(filename); + if (!att) return false; + return extractAttachmentTo(att, dirname); +} + +bool KTNEFParser::openFile(const QString& filename) +{ + deleteDevice(); + delete d->message_; + d->message_ = new KTNEFMessage(); + d->device_ = new QFile( filename ); + d->deleteDevice_ = true; + return parseDevice(); +} + +bool KTNEFParser::openDevice( QIODevice *device ) +{ + deleteDevice(); + d->device_ = device; + return parseDevice(); +} + +void KTNEFParser::checkCurrent( int key ) +{ + if ( !d->current_ ) + d->current_ = new KTNEFAttach(); + else + { + if ( d->current_->attributes().contains( key ) ) + { + if (d->current_->offset() >= 0 ) + { + if (d->current_->name().isEmpty()) + d->current_->setName("Unnamed"); + if ( d->current_->mimeTag().isEmpty() ) + { + // No mime type defined in the TNEF structure, + // try to find it from the attachment filename + // and/or content (using at most 32 bytes) + KMimeType::Ptr mimetype; + if ( !d->current_->fileName().isEmpty() ) + mimetype = KMimeType::findByPath( d->current_->fileName(), 0, true ); + if (!mimetype) return; // FIXME + if ( mimetype->name() == "application/octet-stream" && d->current_->size() > 0 ) + { + int oldOffset = d->device_->at(); + QByteArray buffer( QMIN( 32, d->current_->size() ) ); + d->device_->at( d->current_->offset() ); + d->device_->readBlock( buffer.data(), buffer.size() ); + mimetype = KMimeType::findByContent( buffer ); + d->device_->at( oldOffset ); + } + d->current_->setMimeTag( mimetype->name() ); + } + d->message_->addAttachment( d->current_ ); + d->current_ = 0; + } + else + { // invalid attachment, skip it + delete d->current_; + d->current_ = 0; + } + d->current_ = new KTNEFAttach(); + } + } +} + +//---------------------------------------------------------------------------------------- + +#define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); } +#define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR ) + +void clearMAPIName( MAPI_value& mapi ) +{ + mapi.name.value.clear(); +} + +void clearMAPIValue(MAPI_value& mapi, bool clearName) +{ + mapi.value.clear(); + if ( clearName ) + clearMAPIName( mapi ); +} + +QDateTime formatTime( Q_UINT32 lowB, Q_UINT32 highB ) +{ + QDateTime dt; +#if ( SIZEOF_UINT64_T == 8 ) + uint64_t u64; +#elif ( SIZEOF_UNSIGNED_LONG_LONG == 8 ) + unsigned long long u64; +#elif ( SIZEOF_UNSIGNED_LONG == 8 ) + unsigned long u64; +#else + kdWarning() << "Unable to perform date conversion on this system, no 64-bits integer found" << endl; + dt.setTime_t( 0xffffffffU ); + return dt; +#endif + u64 = highB; + u64 <<= 32; + u64 |= lowB; + u64 -= 116444736000000000LL; + u64 /= 10000000; + if ( u64 <= 0xffffffffU ) + dt.setTime_t( ( unsigned int )u64 ); + else + { + kdWarning().form( "Invalid date: low byte=0x%08X, high byte=0x%08X\n", lowB, highB ); + dt.setTime_t( 0xffffffffU ); + } + return dt; +} + +QString formatRecipient( const QMap<int,KTNEFProperty*>& props ) +{ + QString s, dn, addr, t; + QMap<int,KTNEFProperty*>::ConstIterator it; + if ( ( it = props.find( 0x3001 ) ) != props.end() ) + dn = ( *it )->valueString(); + if ( ( it = props.find( 0x3003 ) ) != props.end() ) + addr = ( *it )->valueString(); + if ( ( it = props.find( 0x0C15 ) ) != props.end() ) + switch ( ( *it )->value().toInt() ) + { + case 0: t = "From:"; break; + case 1: t = "To:"; break; + case 2: t = "Cc:"; break; + case 3: t = "Bcc:"; break; + } + + if ( !t.isEmpty() ) + s.append( t ); + if ( !dn.isEmpty() ) + s.append( " " + dn ); + if ( !addr.isEmpty() && addr != dn ) + s.append( " <" + addr + ">" ); + + return s.stripWhiteSpace(); +} + +QDateTime readTNEFDate( QDataStream& stream ) +{ + // 14-bytes long + Q_UINT16 y, m, d, hh, mm, ss, dm; + stream >> y >> m >> d >> hh >> mm >> ss >> dm; + return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) ); +} + +QString readTNEFAddress( QDataStream& stream ) +{ + Q_UINT16 totalLen, strLen, addrLen; + QString s; + stream >> totalLen >> totalLen >> strLen >> addrLen; + s.append( readMAPIString( stream, false, false, strLen ) ); + s.append( " <" ); + s.append( readMAPIString( stream, false, false, addrLen ) ); + s.append( ">" ); + Q_UINT8 c; + for ( int i=8+strLen+addrLen; i<totalLen; i++ ) + stream >> c; + return s; +} + +QByteArray readTNEFData( QDataStream& stream, Q_UINT32 len ) +{ + QByteArray array( len ); + if ( len > 0 ) + stream.readRawBytes( array.data(), len ); + return array; +} + +QVariant readTNEFAttribute( QDataStream& stream, Q_UINT16 type, Q_UINT32 len ) +{ + switch ( type ) + { + case atpTEXT: + case atpSTRING: + return readMAPIString( stream, false, false, len ); + case atpDATE: + return readTNEFDate( stream ); + default: + return readTNEFData( stream, len ); + } +} + +QString readMAPIString( QDataStream& stream, bool isUnicode, bool align, int len_ ) +{ + Q_UINT32 len; + char *buf = 0; + if ( len_ == -1 ) + stream >> len; + else + len = len_; + Q_UINT32 fullLen = len; + if ( align ) + ALIGN( fullLen, 4 ); + buf = new char[ len ]; + stream.readRawBytes( buf, len ); + Q_UINT8 c; + for ( uint i=len; i<fullLen; i++ ) + stream >> c; + QString res; + if ( isUnicode ) + res = QString::fromUcs2( ( const unsigned short* )buf ); + else + res = QString::fromLocal8Bit( buf ); + delete [] buf; + return res; +} + +Q_UINT16 readMAPIValue(QDataStream& stream, MAPI_value& mapi) +{ + Q_UINT32 d; + + clearMAPIValue(mapi); + stream >> d; + mapi.type = (d & 0x0000FFFF); + mapi.tag = ((d & 0xFFFF0000) >> 16); + if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) + { + // skip GUID + stream >> d >> d >> d >> d; + // name type + stream >> mapi.name.type; + // name + if ( mapi.name.type == 0 ) + stream >> mapi.name.value.asUInt(); + else if ( mapi.name.type == 1 ) + mapi.name.value.asString() = readMAPIString( stream, true ); + } + + int n = 1; + QVariant value; + if ( ISVECTOR( mapi ) ) + { + stream >> n; + mapi.value = QValueList<QVariant>(); + } + for ( int i=0; i<n; i++ ) + { + value.clear(); + switch(mapi.type & 0x0FFF) + { + case MAPI_TYPE_UINT16: + stream >> d; + value.asUInt() = ( d & 0x0000FFFF ); + break; + case MAPI_TYPE_BOOLEAN: + case MAPI_TYPE_ULONG: + stream >> value.asUInt(); + break; + case MAPI_TYPE_FLOAT: + stream >> d; + break; + case MAPI_TYPE_DOUBLE: + stream >> value.asDouble(); + break; + case MAPI_TYPE_TIME: + { + Q_UINT32 lowB, highB; + stream >> lowB >> highB; + value = formatTime( lowB, highB ); + } + break; + case MAPI_TYPE_STRING8: + // in case of a vector'ed value, the number of elements + // has already been read in the upper for-loop + if ( ISVECTOR( mapi ) ) + d = 1; + else + stream >> d; + for (uint i=0;i<d;i++) + { + value.clear(); + value.asString() = readMAPIString( stream ); + } + break; + case MAPI_TYPE_USTRING: + mapi.type = MAPI_TYPE_NONE; + break; + case MAPI_TYPE_OBJECT: + case MAPI_TYPE_BINARY: + if ( ISVECTOR( mapi ) ) + d = 1; + else + stream >> d; + for (uint i=0;i<d;i++) + { + value.clear(); + Q_UINT32 len; + stream >> len; + value = QByteArray( len ); + if (len > 0) + { + int fullLen = len; + ALIGN(fullLen, 4); + stream.readRawBytes(value.asByteArray().data(), len); + Q_UINT8 c; + for ( int i=len; i<fullLen; i++ ) + stream >> c; + } + } + break; + default: + mapi.type = MAPI_TYPE_NONE; + break; + } + if ( ISVECTOR( mapi ) ) + mapi.value.asList().append( value ); + else + mapi.value = value; + } + return mapi.tag; +} + +bool KTNEFParser::readMAPIProperties( QMap<int,KTNEFProperty*>& props, KTNEFAttach *attach ) +{ + Q_UINT32 n; + MAPI_value mapi; + KTNEFProperty *p; + QMap<int,KTNEFProperty*>::ConstIterator it; + bool foundAttachment = false; + + // some initializations + mapi.type = MAPI_TYPE_NONE; + mapi.value.clear(); + + // get number of properties + d->stream_ >> n; + kdDebug() << "MAPI Properties: " << n << endl; + for (uint i=0;i<n;i++) + { + if (d->stream_.eof()) + { + clearMAPIValue(mapi); + return false; + } + readMAPIValue(d->stream_, mapi); + if (mapi.type == MAPI_TYPE_NONE) + { + kdDebug().form( "MAPI unsupported: tag=%x, type=%x\n", mapi.tag, mapi.type ); + clearMAPIValue(mapi); + return false; + } + int key = mapi.tag; + switch (mapi.tag) + { + case MAPI_TAG_DATA: + { + if ( mapi.type == MAPI_TYPE_OBJECT && attach ) + { + QByteArray data = mapi.value.toByteArray(); + int len = data.size(); + ALIGN( len, 4 ); + d->device_->at( d->device_->at()-len ); + Q_UINT32 interface_ID; + d->stream_ >> interface_ID; + if ( interface_ID == MAPI_IID_IMessage ) + { + // embedded TNEF file + attach->unsetDataParser(); + attach->setOffset( d->device_->at()+12 ); + attach->setSize( data.size()-16 ); + attach->setMimeTag( "application/ms-tnef" ); + attach->setDisplayName( "Embedded Message" ); + kdDebug() << "MAPI Embedded Message: size=" << data.size() << endl; + } + d->device_->at( d->device_->at() + ( len-4 ) ); + break; + } + else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) + { + foundAttachment = true; + int len = mapi.value.toByteArray().size(); + ALIGN( len, 4 ) + attach->setSize( len ); + attach->setOffset( d->device_->at() - len ); + attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false ); + } + } + kdDebug().form( "MAPI data: size=%d\n", mapi.value.toByteArray().size() ); + break; + default: + { + QString mapiname = ""; + if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) + { + if ( mapi.name.type == 0 ) + mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() ); + else + mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() ); + } + switch ( mapi.type & 0x0FFF ) + { + case MAPI_TYPE_UINT16: + kdDebug().form( "(tag=%04x) MAPI short%s: 0x%x\n", mapi.tag, mapiname.ascii(), mapi.value.toUInt() ); + break; + case MAPI_TYPE_ULONG: + kdDebug().form( "(tag=%04x) MAPI long%s: 0x%x\n", mapi.tag, mapiname.ascii(), mapi.value.toUInt() ); + break; + case MAPI_TYPE_BOOLEAN: + kdDebug().form( "(tag=%04x) MAPI boolean%s: %s\n", mapi.tag, mapiname.ascii(), ( mapi.value.toBool() ? "true" : "false" ) ); + break; + case MAPI_TYPE_TIME: + kdDebug().form( "(tag=%04x) MAPI time%s: %s\n", mapi.tag, mapiname.ascii(), mapi.value.toString().ascii() ); + break; + case MAPI_TYPE_USTRING: + case MAPI_TYPE_STRING8: + kdDebug().form( "(tag=%04x) MAPI string%s: size=%d \"%s\"\n", mapi.tag, mapiname.ascii(), mapi.value.toByteArray().size(), mapi.value.toString().ascii() ); + break; + case MAPI_TYPE_BINARY: + kdDebug().form( "(tag=%04x) MAPI binary%s: size=%d\n", mapi.tag, mapiname.ascii(), mapi.value.toByteArray().size() ); + break; + } + } + break; + } + // do not remove potential existing similar entry + if ( ( it = props.find( key ) ) == props.end() ) + { + p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ), mapi.value, mapi.name.value ); + props[ p->key() ] = p; + } + //kdDebug() << "stream: " << d->device_->at() << endl; + } + + if ( foundAttachment && attach ) + { + attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() ); + attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() ); + QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString(); + if ( !str.isEmpty() ) + attach->setDisplayName( str ); + attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() ); + str = attach->property( MAPI_TAG_MIMETAG ).toString(); + if ( !str.isEmpty() ) + attach->setMimeTag( str ); + attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() ); + if ( attach->name().isEmpty() ) + attach->setName( attach->fileName() ); + } + + return true; +} |