summaryrefslogtreecommitdiffstats
path: root/ktnef/lib/ktnefparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ktnef/lib/ktnefparser.cpp')
-rw-r--r--ktnef/lib/ktnefparser.cpp883
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;
+}