kde-playground/kdepimlibs/ktnef/ktnefparser.cpp

988 lines
27 KiB
C++
Raw Permalink Normal View History

/*
ktnefparser.cpp
Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be>
This file is part of KTNEF, the KDE TNEF support library/program.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
/**
* @file
* This file is part of the API for handling TNEF data and
* defines the KTNEFParser class.
*
* @author Michael Goffioul
*/
#include "ktnefparser.h"
#include "ktnefattach.h"
#include "ktnefproperty.h"
#include "ktnefmessage.h"
#include "ktnefdefs.h"
#include <kdebug.h>
#include <kmimetype.h>
#include <ksavefile.h>
#include <QtCore/QDateTime>
#include <QtCore/QDataStream>
#include <QtCore/QFile>
#include <QtCore/QVariant>
#include <QtCore/QList>
using namespace KTnef;
//@cond PRIVATE
typedef struct {
quint16 type;
quint16 tag;
QVariant value;
struct {
quint32 type;
QVariant value;
} name;
} MAPI_value;
//@endcond
//@cond IGNORE
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 );
quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi );
QDateTime readTNEFDate( QDataStream &stream );
QString readTNEFAddress( QDataStream &stream );
QByteArray readTNEFData( QDataStream &stream, quint32 len );
QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len );
QDateTime formatTime( quint32 lowB, quint32 highB );
QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props );
//@endcond
//------------------------------------------------------------------------------
/**
* Private class that helps to provide binary compatibility between releases.
* @internal
*/
//@cond PRIVATE
class KTnef::KTNEFParser::ParserPrivate
{
public:
ParserPrivate()
{
defaultdir_ = "/tmp/";
current_ = 0;
deleteDevice_ = false;
device_ = 0;
message_ = new KTNEFMessage;
}
~ParserPrivate()
{
delete message_;
}
bool decodeAttachment();
bool decodeMessage();
bool extractAttachmentTo( KTNEFAttach *att, const QString &dirname );
void checkCurrent( int key );
bool readMAPIProperties( QMap<int,KTNEFProperty*>& props,
KTNEFAttach *attach = 0 );
bool parseDevice();
void deleteDevice();
QDataStream stream_;
QIODevice *device_;
bool deleteDevice_;
QString defaultdir_;
KTNEFAttach *current_;
KTNEFMessage *message_;
};
//@endcond
KTNEFParser::KTNEFParser()
: d( new ParserPrivate )
{
}
KTNEFParser::~KTNEFParser()
{
d->deleteDevice();
delete d;
}
KTNEFMessage *KTNEFParser::message() const
{
return d->message_;
}
void KTNEFParser::ParserPrivate::deleteDevice()
{
if ( deleteDevice_ ) {
delete device_;
}
device_ = 0;
deleteDevice_ = false;
}
bool KTNEFParser::ParserPrivate::decodeMessage()
{
quint32 i1, i2, off;
quint16 u, tag, type;
QVariant value;
// read (type+name)
stream_ >> i1;
u = 0;
tag = ( i1 & 0x0000FFFF );
type = ( ( i1 & 0xFFFF0000 ) >> 16 );
// read data length
stream_ >> i2;
// offset after reading the value
off = device_->pos() + i2;
switch ( tag ) {
case attAIDOWNER:
{
uint tmp;
stream_ >> tmp;
value.setValue( tmp );
message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value );
kDebug() << "Message Owner Appointment ID" << "(length=" << i2 << ")";
break;
}
case attREQUESTRES:
stream_ >> u;
message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u );
value = ( bool )u;
kDebug() << "Message Request Response" << "(length=" << i2 << ")";
break;
case attDATERECD:
value = readTNEFDate( stream_ );
message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value );
kDebug() << "Message Receive Date" << "(length=" << i2 << ")";
break;
case attMSGCLASS:
value = readMAPIString( stream_, false, false, i2 );
message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value );
kDebug() << "Message Class" << "(length=" << i2 << ")";
break;
case attMSGPRIORITY:
stream_ >> u;
message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u );
value = u;
kDebug() << "Message Priority" << "(length=" << i2 << ")";
break;
case attMAPIPROPS:
kDebug() << "Message MAPI Properties" << "(length=" << i2 << ")";
{
int nProps = message_->properties().count();
i2 += device_->pos();
readMAPIProperties( message_->properties(), 0 );
device_->seek( i2 );
kDebug() << "Properties:" << message_->properties().count();
value = QString( "< %1 properties >" ).
arg( message_->properties().count() - nProps );
}
break;
case attTNEFVERSION:
{
uint tmp;
stream_ >> tmp;
value.setValue( tmp );
kDebug() << "Message TNEF Version" << "(length=" << i2 << ")";
}
break;
case attFROM:
message_->addProperty( 0x0024, MAPI_TYPE_STRING8, readTNEFAddress( stream_ ) );
device_->seek( device_->pos() - i2 );
value = readTNEFData( stream_, i2 );
kDebug() << "Message From" << "(length=" << i2 << ")";
break;
case attSUBJECT:
value = readMAPIString( stream_, false, false, i2 );
message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value );
kDebug() << "Message Subject" << "(length=" << i2 << ")";
break;
case attDATESENT:
value = readTNEFDate( stream_ );
message_->addProperty( 0x0039, MAPI_TYPE_TIME, value );
kDebug() << "Message Date Sent" << "(length=" << i2 << ")";
break;
case attMSGSTATUS:
{
quint8 c;
quint32 flag = 0;
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;
}
message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
value = c;
}
kDebug() << "Message Status" << "(length=" << i2 << ")";
break;
case attRECIPTABLE:
{
quint32 rows;
QList<QVariant> recipTable;
stream_ >> rows;
for ( uint i=0; i<rows; i++ ) {
QMap<int,KTNEFProperty*> props;
readMAPIProperties( props, 0 );
recipTable << formatRecipient( props );
}
message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable );
device_->seek( device_->pos() - i2 );
value = readTNEFData( stream_, i2 );
}
kDebug() << "Message Recipient Table" << "(length=" << i2 << ")";
break;
case attBODY:
value = readMAPIString( stream_, false, false, i2 );
message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value );
kDebug() << "Message Body" << "(length=" << i2 << ")";
break;
case attDATEMODIFIED:
value = readTNEFDate( stream_ );
message_->addProperty( 0x3008, MAPI_TYPE_TIME, value );
kDebug() << "Message Date Modified" << "(length=" << i2 << ")";
break;
case attMSGID:
value = readMAPIString( stream_, false, false, i2 );
message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value );
kDebug() << "Message ID" << "(length=" << i2 << ")";
break;
case attOEMCODEPAGE:
value = readTNEFData( stream_, i2 );
kDebug() << "Message OEM Code Page" << "(length=" << i2 << ")";
break;
default:
value = readTNEFAttribute( stream_, type, i2 );
//kDebug().form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u );
break;
}
// skip data
if ( device_->pos() != off && !device_->seek( off ) ) {
return false;
}
// get checksum
stream_ >> u;
// add TNEF attribute
message_->addAttribute( tag, type, value, true );
//kDebug() << "stream:" << device_->pos();
return true;
}
bool KTNEFParser::ParserPrivate::decodeAttachment()
{
quint32 i;
quint16 tag, type, u;
QVariant value;
QString str;
stream_ >> i; // i <- attribute type & name
tag = ( i & 0x0000FFFF );
type = ( ( i & 0xFFFF0000 ) >> 16 );
stream_ >> i; // i <- data length
checkCurrent( tag );
switch ( tag ) {
case attATTACHTITLE:
value = readMAPIString( stream_, false, false, i );
current_->setName( value.toString() );
kDebug() << "Attachment Title:" << current_->name();
break;
case attATTACHDATA:
current_->setSize( i );
current_->setOffset( device_->pos() );
device_->seek( device_->pos() + i );
value = QString( "< size=%1 >" ).arg( i );
kDebug() << "Attachment Data: size=" << i;
break;
case attATTACHMENT: // try to get attachment info
i += device_->pos();
readMAPIProperties( current_->properties(), current_ );
device_->seek( i );
current_->setIndex( current_->property( MAPI_TAG_INDEX ).toUInt() );
current_->setDisplaySize( current_->property( MAPI_TAG_SIZE ).toUInt() );
str = current_->property( MAPI_TAG_DISPLAYNAME ).toString();
if ( !str.isEmpty() ) {
current_->setDisplayName( str );
}
current_->setFileName( current_->property( MAPI_TAG_FILENAME ).
toString() );
str = current_->property( MAPI_TAG_MIMETAG ).toString();
if ( !str.isEmpty() ) {
current_->setMimeTag( str );
}
current_->setExtension( current_->property( MAPI_TAG_EXTENSION ).
toString() );
value = QString( "< %1 properties >" ).
arg( current_->properties().count() );
break;
case attATTACHMODDATE:
value = readTNEFDate( stream_ );
kDebug() << "Attachment Modification Date:" << value.toString();
break;
case attATTACHCREATEDATE:
value = readTNEFDate( stream_ );
kDebug() << "Attachment Creation Date:" << value.toString();
break;
case attATTACHMETAFILE:
kDebug() << "Attachment Metafile: size=" << i;
//value = QString( "< size=%1 >" ).arg( i );
//device_->seek( device_->pos()+i );
value = readTNEFData( stream_, i );
break;
default:
value = readTNEFAttribute( stream_, type, i );
kDebug() << "Attachment unknown field: tag="
<< hex << tag << ", length=" << dec << i;
break;
}
stream_ >> u; // u <- checksum
// add TNEF attribute
current_->addAttribute( tag, type, value, true );
//kDebug() << "stream:" << device_->pos();
return true;
}
void KTNEFParser::setDefaultExtractDir( const QString &dirname )
{
d->defaultdir_ = dirname;
}
bool KTNEFParser::ParserPrivate::parseDevice()
{
quint16 u;
quint32 i;
quint8 c;
message_->clearAttachments();
delete current_;
current_ = 0;
if ( !device_->open( QIODevice::ReadOnly ) ) {
kDebug() << "Couldn't open device";
return false;
}
stream_.setDevice( device_ );
stream_.setByteOrder( QDataStream::LittleEndian );
stream_ >> i;
if ( i == TNEF_SIGNATURE ) {
stream_ >> u;
kDebug().nospace() << "Attachment cross reference key: 0x"
<< hex << qSetFieldWidth( 4 ) << qSetPadChar( '0' ) << u;
//kDebug() << "stream:" << device_->pos();
while ( !stream_.atEnd() ) {
stream_ >> c;
switch( c ) {
case LVL_MESSAGE:
if ( !decodeMessage() ) {
goto end;
}
break;
case LVL_ATTACHMENT:
if ( !decodeAttachment() ) {
goto end;
}
break;
default:
kDebug() << "Unknown Level:" << c << ", at offset" << device_->pos();
goto end;
}
}
if ( 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 current_;
current_ = 0;
}
return true;
} else {
kDebug() << "This is not a TNEF file";
end:
device_->close();
return false;
}
}
bool KTNEFParser::extractFile( const QString &filename ) const
{
KTNEFAttach *att = d->message_->attachment( filename );
if ( !att ) {
return false;
}
return d->extractAttachmentTo( att, d->defaultdir_ );
}
bool KTNEFParser::ParserPrivate::extractAttachmentTo( KTNEFAttach *att,
const QString &dirname )
{
QString filename = dirname + '/';
if ( !att->fileName().isEmpty()) {
filename += att->fileName();
} else {
filename += att->name();
}
if ( filename.endsWith( '/') ) {
return false;
}
if ( !device_->isOpen() ) {
return false;
}
if ( !device_->seek( att->offset() ) ) {
return false;
}
KSaveFile outfile( filename );
if ( !outfile.open() ) {
return false;
}
quint32 len = att->size(), sz( 16384 );
int n( 0 );
char *buf = new char[sz];
bool ok( true );
while ( ok && len > 0 ) {
n = device_->read( buf, qMin( sz, len ) );
if ( n < 0 ) {
ok = false;
} else {
len -= n;
if ( outfile.write( buf, n ) != n ) {
ok = false;
}
}
}
delete [] buf;
return ok;
}
bool KTNEFParser::extractAll()
{
QList<KTNEFAttach*> l = d->message_->attachmentList();
QList<KTNEFAttach*>::const_iterator it = l.constBegin();
for ( ; it != l.constEnd(); ++it ) {
if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
return false;
}
}
return true;
}
bool KTNEFParser::extractFileTo( const QString &filename,
const QString &dirname ) const
{
kDebug() << "Extracting attachment: filename="
<< filename << ", dir=" << dirname;
KTNEFAttach *att = d->message_->attachment( filename );
if ( !att ) {
return false;
}
return d->extractAttachmentTo( att, dirname );
}
bool KTNEFParser::openFile( const QString &filename ) const
{
d->deleteDevice();
delete d->message_;
d->message_ = new KTNEFMessage();
d->device_ = new QFile( filename );
d->deleteDevice_ = true;
return d->parseDevice();
}
bool KTNEFParser::openDevice( QIODevice *device )
{
d->deleteDevice();
d->device_ = device;
return d->parseDevice();
}
void KTNEFParser::ParserPrivate::checkCurrent( int key )
{
if ( !current_ ) {
current_ = new KTNEFAttach();
} else {
if ( current_->attributes().contains( key ) ) {
if ( current_->offset() >= 0 ) {
if ( current_->name().isEmpty() ) {
current_->setName( "Unnamed" );
}
if ( 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 ( !current_->fileName().isEmpty() ) {
mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
}
if ( !mimetype ) {
return; // FIXME
}
if ( mimetype->name() == "application/octet-stream" &&
current_->size() > 0 ) {
int oldOffset = device_->pos();
QByteArray buffer( qMin( 32, current_->size() ), '\0' );
device_->seek( current_->offset() );
device_->read( buffer.data(), buffer.size() );
mimetype = KMimeType::findByContent( buffer );
device_->seek( oldOffset );
}
current_->setMimeTag( mimetype->name() );
}
message_->addAttachment( current_ );
current_ = 0;
} else {
// invalid attachment, skip it
delete current_;
current_ = 0;
}
current_ = new KTNEFAttach();
}
}
}
//------------------------------------------------------------------------------
//@cond IGNORE
#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( quint32 lowB, quint32 highB )
{
QDateTime dt;
quint64 u64;
u64 = highB;
u64 <<= 32;
u64 |= lowB;
u64 -= 116444736000000000LL;
u64 /= 10000000;
if ( u64 <= 0xffffffffU ) {
dt.setTime_t( ( unsigned int )u64 );
} else {
kWarning().nospace() << "Invalid date: low byte="
<< showbase << qSetFieldWidth( 8 ) << qSetPadChar( '0' )
<< lowB << ", high byte=" << highB;
dt.setTime_t( 0xffffffffU );
}
return dt;
}
QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props )
{
QString s, dn, addr, t;
QMap<int,KTnef::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.trimmed();
}
QDateTime readTNEFDate( QDataStream &stream )
{
// 14-bytes long
quint16 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 )
{
quint16 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( ">" );
quint8 c;
for ( int i=8+strLen+addrLen; i<totalLen; i++ ) {
stream >> c;
}
return s;
}
QByteArray readTNEFData( QDataStream &stream, quint32 len )
{
QByteArray array( len, '\0' );
if ( len > 0 ) {
stream.readRawData( array.data(), len );
}
return array;
}
QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 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_ )
{
quint32 len;
char *buf = 0;
if ( len_ == -1 ) {
stream >> len;
} else {
len = len_;
}
quint32 fullLen = len;
if ( align ) {
ALIGN( fullLen, 4 );
}
buf = new char[ len ];
stream.readRawData( buf, len );
quint8 c;
for ( uint i=len; i<fullLen; i++ ) {
stream >> c;
}
QString res;
if ( isUnicode ) {
res = QString::fromUtf16( ( const unsigned short *)buf );
} else {
res = QString::fromLocal8Bit( buf );
}
delete [] buf;
return res;
}
quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi )
{
quint32 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 ) {
uint tmp;
stream >> tmp;
mapi.name.value.setValue( tmp );
} else if ( mapi.name.type == 1 ) {
mapi.name.value.setValue( readMAPIString( stream, true ) );
}
}
int n = 1;
QVariant value;
if ( ISVECTOR( mapi ) ) {
stream >> n;
mapi.value = QList<QVariant>();
}
for ( int i=0; i<n; i++ ) {
value.clear();
switch( mapi.type & 0x0FFF ) {
case MAPI_TYPE_UINT16:
stream >> d;
value.setValue( d & 0x0000FFFF );
break;
case MAPI_TYPE_BOOLEAN:
case MAPI_TYPE_ULONG:
{
uint tmp;
stream >> tmp;
value.setValue( tmp );
}
break;
case MAPI_TYPE_FLOAT:
// FIXME: Don't we have to set the value here
stream >> d;
break;
case MAPI_TYPE_DOUBLE:
{
double tmp;
stream >> tmp;
value.setValue( tmp );
}
break;
case MAPI_TYPE_TIME:
{
quint32 lowB, highB;
stream >> lowB >> highB;
value = formatTime( lowB, highB );
}
break;
case MAPI_TYPE_USTRING:
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.setValue( readMAPIString( stream,( mapi.type & 0x0FFF ) == MAPI_TYPE_USTRING ) );
}
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();
quint32 len;
stream >> len;
value = QByteArray( len, '\0' );
if ( len > 0 ) {
int fullLen = len;
ALIGN( fullLen, 4 );
stream.readRawData( value.toByteArray().data(), len );
quint8 c;
for ( int i=len; i<fullLen; i++ ) {
stream >> c;
}
// FIXME: Shouldn't we do something with the value???
}
}
break;
default:
mapi.type = MAPI_TYPE_NONE;
break;
}
if ( ISVECTOR( mapi ) ) {
QList <QVariant> lst = mapi.value.toList();
lst << value;
mapi.value.setValue( lst );
} else {
mapi.value = value;
}
}
return mapi.tag;
}
//@endcond
bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
KTNEFAttach *attach )
{
quint32 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
stream_ >> n;
kDebug() << "MAPI Properties:" << n;
for ( uint i=0; i<n; i++ ) {
if ( stream_.atEnd() ) {
clearMAPIValue( mapi );
return false;
}
readMAPIValue( stream_, mapi );
if ( mapi.type == MAPI_TYPE_NONE ) {
kDebug().nospace() << "MAPI unsupported: tag="
<< hex << mapi.tag << ", type=" << 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 );
device_->seek( device_->pos()-len );
quint32 interface_ID;
stream_ >> interface_ID;
if ( interface_ID == MAPI_IID_IMessage ) {
// embedded TNEF file
attach->unsetDataParser();
attach->setOffset( device_->pos()+12 );
attach->setSize( data.size()-16 );
attach->setMimeTag( "application/vnd.ms-tnef" );
attach->setDisplayName( "Embedded Message" );
kDebug() << "MAPI Embedded Message: size=" << data.size();
}
device_->seek( device_->pos() + ( 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( device_->pos() - len );
attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
}
}
kDebug() << "MAPI data: size=" << 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:
kDebug().nospace() << "(tag="
<< hex << mapi.tag
<< ") MAPI short" << mapiname.toLatin1().data()
<< ":" << hex << mapi.value.toUInt();
break;
case MAPI_TYPE_ULONG:
kDebug().nospace() << "(tag="
<< hex << mapi.tag
<< ") MAPI long" << mapiname.toLatin1().data()
<< ":" << hex << mapi.value.toUInt();
break;
case MAPI_TYPE_BOOLEAN:
kDebug().nospace() << "(tag="
<< hex << mapi.tag
<< ") MAPI boolean" << mapiname.toLatin1().data()
<< ":" << mapi.value.toBool();
break;
case MAPI_TYPE_TIME:
kDebug().nospace() << "(tag="
<< hex << mapi.tag
<< ") MAPI time" << mapiname.toLatin1().data()
<< ":" << mapi.value.toString().toLatin1().data();
break;
case MAPI_TYPE_USTRING:
case MAPI_TYPE_STRING8:
kDebug().nospace() << "(tag="
<< hex << mapi.tag
<< ") MAPI string" << mapiname.toLatin1().data()
<< ":size=" << mapi.value.toByteArray().size()
<< mapi.value.toString();
break;
case MAPI_TYPE_BINARY:
kDebug().nospace() << "(tag="
<< hex << mapi.tag
<< ") MAPI binary" << mapiname.toLatin1().data()
<< ":size=" << mapi.value.toByteArray().size();
break;
}
}
break;
}
// do not remove potential existing similar entry
if ( ( it = props.constFind( key ) ) == props.constEnd() ) {
p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
mapi.value, mapi.name.value );
props[ p->key() ] = p;
}
//kDebug() << "stream:" << device_->pos();
}
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;
}