You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/ktnef/lib/ktnefparser.cpp

884 lines
24 KiB

/*
ktnefparser.cpp
Copyright (C) 2002 Michael Goffioul <tdeprint@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 <tqdatetime.h>
#include <tqdatastream.h>
#include <tqfile.h>
#include <tqvariant.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 {
TQ_UINT16 type;
TQ_UINT16 tag;
TQVariant value;
struct {
TQ_UINT32 type;
TQVariant value;
} name;
} MAPI_value;
void clearMAPIName( MAPI_value& mapi );
void clearMAPIValue(MAPI_value& mapi, bool clearName = true);
TQString readMAPIString( TQDataStream& stream, bool isUnicode = false, bool align = true, int len = -1 );
TQ_UINT16 readMAPIValue(TQDataStream& stream, MAPI_value& mapi);
TQDateTime readTNEFDate( TQDataStream& stream );
TQString readTNEFAddress( TQDataStream& stream );
TQByteArray readTNEFData( TQDataStream& stream, TQ_UINT32 len );
TQVariant readTNEFAttribute( TQDataStream& stream, TQ_UINT16 type, TQ_UINT32 len );
TQDateTime formatTime( TQ_UINT32 lowB, TQ_UINT32 highB );
TQString formatRecipient( const TQMap<int,KTNEFProperty*>& props );
//------------------------------------------------------------------------------------
class KTNEFParser::ParserPrivate
{
public:
ParserPrivate()
{
defaultdir_ = "/tmp/";
current_ = 0;
deleteDevice_ = false;
tqdevice_ = 0;
message_ = new KTNEFMessage;
}
~ParserPrivate()
{
delete message_;
}
TQDataStream stream_;
TQIODevice *tqdevice_;
bool deleteDevice_;
TQString 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->tqdevice_;
d->tqdevice_ = 0;
d->deleteDevice_ = false;
}
bool KTNEFParser::decodeMessage()
{
TQ_UINT32 i1, i2, off;
TQ_UINT16 u, tag, type;
TQVariant 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->tqdevice_->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->tqdevice_->at();
readMAPIProperties( d->message_->properties(), 0 );
d->tqdevice_->at( i2 );
kdDebug() << "Properties: " << d->message_->properties().count() << endl;
value = TQString( "< %1 properties >" ).tqarg( 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->tqdevice_->at( d->tqdevice_->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:
{
TQ_UINT8 c;
TQ_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:
{
TQ_UINT32 rows;
TQValueList<TQVariant> recipTable;
d->stream_ >> rows;
for ( uint i=0; i<rows; i++ )
{
TQMap<int,KTNEFProperty*> props;
readMAPIProperties( props, 0 );
recipTable << formatRecipient( props );
}
d->message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable );
d->tqdevice_->at( d->tqdevice_->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->tqdevice_->at() != off && !d->tqdevice_->at( off ) )
return false;
// get checksum
d->stream_ >> u;
// add TNEF attribute
d->message_->addAttribute( tag, type, value, true );
//kdDebug() << "stream: " << d->tqdevice_->at() << endl;
return true;
}
bool KTNEFParser::decodeAttachment()
{
TQ_UINT32 i;
TQ_UINT16 tag, type, u;
TQVariant value;
TQString 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->tqdevice_->at() );
d->tqdevice_->at( d->tqdevice_->at() + i );
value = TQString( "< size=%1 >" ).tqarg( i );
kdDebug() << "Attachment Data: size=" << i << endl;
break;
case attATTACHMENT: // try to get attachment info
i += d->tqdevice_->at();
readMAPIProperties( d->current_->properties(), d->current_ );
d->tqdevice_->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 = TQString( "< %1 properties >" ).tqarg( 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 = TQString( "< size=%1 >" ).tqarg( i );
//d->tqdevice_->at( d->tqdevice_->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->tqdevice_->at() << endl;
return true;
}
void KTNEFParser::setDefaultExtractDir(const TQString& dirname)
{
d->defaultdir_ = dirname;
}
bool KTNEFParser::parseDevice()
{
TQ_UINT16 u;
TQ_UINT32 i;
TQ_UINT8 c;
d->message_->clearAttachments();
if (d->current_)
{
delete d->current_;
d->current_ = 0;
}
if ( !d->tqdevice_->open( IO_ReadOnly ) ) {
kdDebug() << "Couldn't open tqdevice" << endl;
return false;
}
d->stream_.setDevice( d->tqdevice_ );
d->stream_.setByteOrder( TQDataStream::LittleEndian );
d->stream_ >> i;
if (i == TNEF_SIGNATURE)
{
d->stream_ >> u;
kdDebug().form( "Attachment cross reference key: 0x%04x\n",u );
//kdDebug() << "stream: " << d->tqdevice_->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->tqdevice_->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->tqdevice_->close();
return false;
}
}
bool KTNEFParser::extractFile(const TQString& filename)
{
KTNEFAttach *att = d->message_->attachment(filename);
if (!att) return false;
return extractAttachmentTo(att, d->defaultdir_);
}
bool KTNEFParser::extractAttachmentTo(KTNEFAttach *att, const TQString& dirname)
{
TQString filename = dirname + "/" + att->name();
if (!d->tqdevice_->isOpen())
return false;
if (!d->tqdevice_->at(att->offset()))
return false;
KSaveFile saveFile( filename );
TQFile *outfile = saveFile.file();
if ( !outfile )
return false;
TQ_UINT32 len = att->size(), sz(16384);
int n(0);
char *buf = new char[sz];
bool ok(true);
while (ok && len > 0)
{
n = d->tqdevice_->readBlock(buf,TQMIN(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()
{
TQPtrListIterator<KTNEFAttach> it(d->message_->attachmentList());
for (;it.current();++it)
if (!extractAttachmentTo(it.current(),d->defaultdir_)) return false;
return true;
}
bool KTNEFParser::extractFileTo(const TQString& filename, const TQString& 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 TQString& filename)
{
deleteDevice();
delete d->message_;
d->message_ = new KTNEFMessage();
d->tqdevice_ = TQT_TQIODEVICE(new TQFile( filename ));
d->deleteDevice_ = true;
return parseDevice();
}
bool KTNEFParser::openDevice( TQIODevice *tqdevice )
{
deleteDevice();
d->tqdevice_ = tqdevice;
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->tqdevice_->at();
TQByteArray buffer( TQMIN( 32, d->current_->size() ) );
d->tqdevice_->at( d->current_->offset() );
d->tqdevice_->readBlock( buffer.data(), buffer.size() );
mimetype = KMimeType::findByContent( buffer );
d->tqdevice_->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 );
}
TQDateTime formatTime( TQ_UINT32 lowB, TQ_UINT32 highB )
{
TQDateTime 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;
}
TQString formatRecipient( const TQMap<int,KTNEFProperty*>& props )
{
TQString s, dn, addr, t;
TQMap<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();
}
TQDateTime readTNEFDate( TQDataStream& stream )
{
// 14-bytes long
TQ_UINT16 y, m, d, hh, mm, ss, dm;
stream >> y >> m >> d >> hh >> mm >> ss >> dm;
return TQDateTime( TQDate( y, m, d ), TQTime( hh, mm, ss ) );
}
TQString readTNEFAddress( TQDataStream& stream )
{
TQ_UINT16 totalLen, strLen, addrLen;
TQString s;
stream >> totalLen >> totalLen >> strLen >> addrLen;
s.append( readMAPIString( stream, false, false, strLen ) );
s.append( " <" );
s.append( readMAPIString( stream, false, false, addrLen ) );
s.append( ">" );
TQ_UINT8 c;
for ( int i=8+strLen+addrLen; i<totalLen; i++ )
stream >> c;
return s;
}
TQByteArray readTNEFData( TQDataStream& stream, TQ_UINT32 len )
{
TQByteArray array( len );
if ( len > 0 )
stream.readRawBytes( array.data(), len );
return array;
}
TQVariant readTNEFAttribute( TQDataStream& stream, TQ_UINT16 type, TQ_UINT32 len )
{
switch ( type )
{
case atpTEXT:
case atpSTRING:
return readMAPIString( stream, false, false, len );
case atpDATE:
return readTNEFDate( stream );
default:
return readTNEFData( stream, len );
}
}
TQString readMAPIString( TQDataStream& stream, bool isUnicode, bool align, int len_ )
{
TQ_UINT32 len;
char *buf = 0;
if ( len_ == -1 )
stream >> len;
else
len = len_;
TQ_UINT32 fullLen = len;
if ( align )
ALIGN( fullLen, 4 );
buf = new char[ len ];
stream.readRawBytes( buf, len );
TQ_UINT8 c;
for ( uint i=len; i<fullLen; i++ )
stream >> c;
TQString res;
if ( isUnicode )
res = TQString::fromUcs2( ( const unsigned short* )buf );
else
res = TQString::fromLocal8Bit( buf );
delete [] buf;
return res;
}
TQ_UINT16 readMAPIValue(TQDataStream& stream, MAPI_value& mapi)
{
TQ_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;
TQVariant value;
if ( ISVECTOR( mapi ) )
{
stream >> n;
mapi.value = TQValueList<TQVariant>();
}
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:
{
TQ_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();
TQ_UINT32 len;
stream >> len;
value = TQByteArray( len );
if (len > 0)
{
int fullLen = len;
ALIGN(fullLen, 4);
stream.readRawBytes(value.asByteArray().data(), len);
TQ_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( TQMap<int,KTNEFProperty*>& props, KTNEFAttach *attach )
{
TQ_UINT32 n;
MAPI_value mapi;
KTNEFProperty *p;
TQMap<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 )
{
TQByteArray data = mapi.value.toByteArray();
int len = data.size();
ALIGN( len, 4 );
d->tqdevice_->at( d->tqdevice_->at()-len );
TQ_UINT32 interface_ID;
d->stream_ >> interface_ID;
if ( interface_ID == MAPI_IID_IMessage )
{
// embedded TNEF file
attach->unsetDataParser();
attach->setOffset( d->tqdevice_->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->tqdevice_->at( d->tqdevice_->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->tqdevice_->at() - len );
attach->addAttribute( attATTACHDATA, atpBYTE, TQString( "< size=%1 >" ).tqarg( len ), false );
}
}
kdDebug().form( "MAPI data: size=%d\n", mapi.value.toByteArray().size() );
break;
default:
{
TQString mapiname = "";
if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE )
{
if ( mapi.name.type == 0 )
mapiname = TQString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
else
mapiname = TQString( " [name = %1]" ).tqarg( 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->tqdevice_->at() << endl;
}
if ( foundAttachment && attach )
{
attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
TQString 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;
}