38 #include <kmimetype.h>
39 #include <ksavefile.h>
41 #include <QtCore/QDateTime>
42 #include <QtCore/QDataStream>
43 #include <QtCore/QFile>
44 #include <QtCore/QVariant>
45 #include <QtCore/QList>
47 using namespace KTnef;
62 void clearMAPIName( MAPI_value &mapi );
63 void clearMAPIValue( MAPI_value &mapi,
bool clearName =
true );
65 bool align =
true,
int len = -1 );
66 quint16 readMAPIValue(
QDataStream &stream, MAPI_value &mapi );
71 QDateTime formatTime( quint32 lowB, quint32 highB );
82 class KTnef::KTNEFParser::ParserPrivate
87 defaultdir_ =
"/tmp/";
89 deleteDevice_ =
false;
98 bool decodeAttachment();
101 void checkCurrent(
int key );
117 : d( new ParserPrivate )
132 void KTNEFParser::ParserPrivate::deleteDevice()
134 if ( deleteDevice_ ) {
138 deleteDevice_ =
false;
141 bool KTNEFParser::ParserPrivate::decodeMessage()
144 quint16 u, tag, type;
150 tag = ( i1 & 0x0000FFFF );
151 type = ( ( i1 & 0xFFFF0000 ) >> 16 );
155 off = device_->pos() + i2;
162 message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value );
163 kDebug() <<
"Message Owner Appointment ID" <<
"(length=" << i2 <<
")";
168 message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u );
170 kDebug() <<
"Message Request Response" <<
"(length=" << i2 <<
")";
173 value = readTNEFDate( stream_ );
174 message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value );
175 kDebug() <<
"Message Receive Date" <<
"(length=" << i2 <<
")";
178 value = readMAPIString( stream_,
false,
false, i2 );
179 message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value );
180 kDebug() <<
"Message Class" <<
"(length=" << i2 <<
")";
184 message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u );
186 kDebug() <<
"Message Priority" <<
"(length=" << i2 <<
")";
189 kDebug() <<
"Message MAPI Properties" <<
"(length=" << i2 <<
")";
191 int nProps = message_->properties().count();
192 i2 += device_->pos();
193 readMAPIProperties( message_->properties(), 0 );
195 kDebug() <<
"Properties:" << message_->properties().count();
196 value =
QString(
"< %1 properties >" ).
197 arg( message_->properties().count() - nProps );
205 kDebug() <<
"Message TNEF Version" <<
"(length=" << i2 <<
")";
209 message_->addProperty( 0x0024, MAPI_TYPE_STRING8, readTNEFAddress( stream_ ) );
210 device_->seek( device_->pos() - i2 );
211 value = readTNEFData( stream_, i2 );
212 kDebug() <<
"Message From" <<
"(length=" << i2 <<
")";
215 value = readMAPIString( stream_,
false,
false, i2 );
216 message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value );
217 kDebug() <<
"Message Subject" <<
"(length=" << i2 <<
")";
220 value = readTNEFDate( stream_ );
221 message_->addProperty( 0x0039, MAPI_TYPE_TIME, value );
222 kDebug() <<
"Message Date Sent" <<
"(length=" << i2 <<
")";
230 flag |= MSGFLAG_READ;
232 if ( !( c & fmsModified ) ) {
233 flag |= MSGFLAG_UNMODIFIED;
235 if ( c & fmsSubmitted ) {
236 flag |= MSGFLAG_SUBMIT;
238 if ( c & fmsHasAttach ) {
239 flag |= MSGFLAG_HASATTACH;
241 if ( c & fmsLocal ) {
242 flag |= MSGFLAG_UNSENT;
244 message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
247 kDebug() <<
"Message Status" <<
"(length=" << i2 <<
")";
254 for ( uint i=0; i<rows; i++ ) {
256 readMAPIProperties( props, 0 );
257 recipTable << formatRecipient( props );
259 message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable );
260 device_->seek( device_->pos() - i2 );
261 value = readTNEFData( stream_, i2 );
263 kDebug() <<
"Message Recipient Table" <<
"(length=" << i2 <<
")";
266 value = readMAPIString( stream_,
false,
false, i2 );
267 message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value );
268 kDebug() <<
"Message Body" <<
"(length=" << i2 <<
")";
270 case attDATEMODIFIED:
271 value = readTNEFDate( stream_ );
272 message_->addProperty( 0x3008, MAPI_TYPE_TIME, value );
273 kDebug() <<
"Message Date Modified" <<
"(length=" << i2 <<
")";
276 value = readMAPIString( stream_,
false,
false, i2 );
277 message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value );
278 kDebug() <<
"Message ID" <<
"(length=" << i2 <<
")";
281 value = readTNEFData( stream_, i2 );
282 kDebug() <<
"Message OEM Code Page" <<
"(length=" << i2 <<
")";
285 value = readTNEFAttribute( stream_, type, i2 );
290 if ( device_->pos() != off && !device_->seek( off ) ) {
296 message_->addAttribute( tag, type, value,
true );
301 bool KTNEFParser::ParserPrivate::decodeAttachment()
304 quint16 tag, type, u;
309 tag = ( i & 0x0000FFFF );
310 type = ( ( i & 0xFFFF0000 ) >> 16 );
315 value = readMAPIString( stream_,
false,
false, i );
316 current_->setName( value.
toString() );
317 kDebug() <<
"Attachment Title:" << current_->name();
320 current_->setSize( i );
321 current_->setOffset( device_->pos() );
322 device_->seek( device_->pos() + i );
324 kDebug() <<
"Attachment Data: size=" << i;
328 readMAPIProperties( current_->properties(), current_ );
330 current_->setIndex( current_->property( MAPI_TAG_INDEX ).toUInt() );
331 current_->setDisplaySize( current_->property( MAPI_TAG_SIZE ).toUInt() );
332 str = current_->property( MAPI_TAG_DISPLAYNAME ).toString();
334 current_->setDisplayName( str );
336 current_->setFileName( current_->property( MAPI_TAG_FILENAME ).
338 str = current_->property( MAPI_TAG_MIMETAG ).toString();
340 current_->setMimeTag( str );
342 current_->setExtension( current_->property( MAPI_TAG_EXTENSION ).
344 value =
QString(
"< %1 properties >" ).
345 arg( current_->properties().count() );
347 case attATTACHMODDATE:
348 value = readTNEFDate( stream_ );
349 kDebug() <<
"Attachment Modification Date:" << value.
toString();
351 case attATTACHCREATEDATE:
352 value = readTNEFDate( stream_ );
353 kDebug() <<
"Attachment Creation Date:" << value.
toString();
355 case attATTACHMETAFILE:
356 kDebug() <<
"Attachment Metafile: size=" << i;
359 value = readTNEFData( stream_, i );
362 value = readTNEFAttribute( stream_, type, i );
363 kDebug() <<
"Attachment unknown field: tag="
364 << hex << tag <<
", length=" << dec << i;
369 current_->addAttribute( tag, type, value,
true );
377 d->defaultdir_ = dirname;
380 bool KTNEFParser::ParserPrivate::parseDevice()
386 message_->clearAttachments();
390 if ( !device_->open( QIODevice::ReadOnly ) ) {
391 kDebug() <<
"Couldn't open device";
395 stream_.setDevice( device_ );
396 stream_.setByteOrder( QDataStream::LittleEndian );
398 if ( i == TNEF_SIGNATURE ) {
400 kDebug().nospace() <<
"Attachment cross reference key: 0x"
401 << hex << qSetFieldWidth( 4 ) << qSetPadChar(
'0' ) << u;
403 while ( !stream_.atEnd() ) {
407 if ( !decodeMessage() ) {
412 if ( !decodeAttachment() ) {
417 kDebug() <<
"Unknown Level:" << c <<
", at offset" << device_->pos();
422 checkCurrent( attATTACHDATA );
430 kDebug() <<
"This is not a TNEF file";
439 KTNEFAttach *att = d->message_->attachment( filename );
443 return d->extractAttachmentTo( att, d->defaultdir_ );
446 bool KTNEFParser::ParserPrivate::extractAttachmentTo(
KTNEFAttach *att,
449 QString filename = dirname +
'/';
453 filename += att->
name();
459 if ( !device_->isOpen() ) {
462 if ( !device_->seek( att->
offset() ) ) {
465 KSaveFile outfile( filename );
466 if ( !outfile.open() ) {
470 quint32 len = att->
size(), sz( 16384 );
472 char *buf =
new char[sz];
474 while ( ok && len > 0 ) {
475 n = device_->read( buf, qMin( sz, len ) );
480 if ( outfile.write( buf, n ) != n ) {
494 for ( ; it != l.
constEnd(); ++it ) {
495 if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
505 kDebug() <<
"Extracting attachment: filename="
506 << filename <<
", dir=" << dirname;
507 KTNEFAttach *att = d->message_->attachment( filename );
511 return d->extractAttachmentTo( att, dirname );
519 d->device_ =
new QFile( filename );
520 d->deleteDevice_ =
true;
521 return d->parseDevice();
528 return d->parseDevice();
531 void KTNEFParser::ParserPrivate::checkCurrent(
int key )
536 if ( current_->attributes().contains( key ) ) {
537 if ( current_->offset() >= 0 ) {
538 if ( current_->name().isEmpty() ) {
539 current_->setName(
"Unnamed" );
541 if ( current_->mimeTag().isEmpty() ) {
545 KMimeType::Ptr mimetype;
546 if ( !current_->fileName().isEmpty() ) {
547 mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
552 if ( mimetype->name() ==
"application/octet-stream" &&
553 current_->size() > 0 ) {
554 int oldOffset = device_->pos();
555 QByteArray buffer( qMin( 32, current_->size() ),
'\0' );
556 device_->seek( current_->offset() );
557 device_->read( buffer.data(), buffer.size() );
558 mimetype = KMimeType::findByContent( buffer );
559 device_->seek( oldOffset );
561 current_->setMimeTag( mimetype->name() );
563 message_->addAttachment( current_ );
578 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); }
579 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR )
581 void clearMAPIName( MAPI_value &mapi )
583 mapi.name.value.clear();
586 void clearMAPIValue( MAPI_value &mapi,
bool clearName )
590 clearMAPIName( mapi );
594 QDateTime formatTime( quint32 lowB, quint32 highB )
601 u64 -= 116444736000000000LL;
603 if ( u64 <= 0xffffffffU ) {
606 kWarning().nospace() <<
"Invalid date: low byte="
607 << showbase << qSetFieldWidth( 8 ) << qSetPadChar(
'0' )
608 << lowB <<
", high byte=" << highB;
618 if ( ( it = props.
find( 0x3001 ) ) != props.
end() ) {
619 dn = ( *it )->valueString();
621 if ( ( it = props.
find( 0x3003 ) ) != props.
end() ) {
622 addr = ( *it )->valueString();
624 if ( ( it = props.
find( 0x0C15 ) ) != props.
end() ) {
625 switch ( ( *it )->value().toInt() ) {
646 if ( !addr.
isEmpty() && addr != dn ) {
647 s.
append(
" <" + addr +
'>' );
656 quint16 y, m, d, hh, mm, ss, dm;
657 stream >> y >> m >> d >> hh >> mm >> ss >> dm;
663 quint16 totalLen, strLen, addrLen;
665 stream >> totalLen >> totalLen >> strLen >> addrLen;
666 s.
append( readMAPIString( stream,
false,
false, strLen ) );
668 s.
append( readMAPIString( stream,
false,
false, addrLen ) );
671 for (
int i=8+strLen+addrLen; i<totalLen; i++ ) {
691 return readMAPIString( stream,
false,
false, len );
693 return readTNEFDate( stream );
695 return readTNEFData( stream, len );
709 quint32 fullLen = len;
713 buf =
new char[ len ];
716 for ( uint i=len; i<fullLen; i++ ) {
729 quint16 readMAPIValue(
QDataStream &stream, MAPI_value &mapi )
733 clearMAPIValue( mapi );
735 mapi.type = ( d & 0x0000FFFF );
736 mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 );
737 if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
739 stream >> d >> d >> d >> d;
741 stream >> mapi.name.type;
743 if ( mapi.name.type == 0 ) {
746 mapi.name.value.setValue( tmp );
747 }
else if ( mapi.name.type == 1 ) {
748 mapi.name.value.setValue( readMAPIString( stream,
true ) );
754 if ( ISVECTOR( mapi ) ) {
758 for (
int i=0; i<n; i++ ) {
760 switch( mapi.type & 0x0FFF ) {
761 case MAPI_TYPE_UINT16:
765 case MAPI_TYPE_BOOLEAN:
766 case MAPI_TYPE_ULONG:
773 case MAPI_TYPE_FLOAT:
777 case MAPI_TYPE_DOUBLE:
787 stream >> lowB >> highB;
788 value = formatTime( lowB, highB );
791 case MAPI_TYPE_USTRING:
792 case MAPI_TYPE_STRING8:
795 if ( ISVECTOR( mapi ) ) {
800 for ( uint i=0; i<d; i++ ) {
802 value.
setValue( readMAPIString( stream,( mapi.type & 0x0FFF ) == MAPI_TYPE_USTRING ) );
805 case MAPI_TYPE_OBJECT:
806 case MAPI_TYPE_BINARY:
807 if ( ISVECTOR( mapi ) ) {
812 for ( uint i=0; i<d; i++ ) {
822 for (
int i=len; i<fullLen; i++ ) {
830 mapi.type = MAPI_TYPE_NONE;
833 if ( ISVECTOR( mapi ) ) {
836 mapi.
value.setValue( lst );
852 bool foundAttachment =
false;
855 mapi.type = MAPI_TYPE_NONE;
860 kDebug() <<
"MAPI Properties:" << n;
861 for ( uint i=0; i<n; i++ ) {
862 if ( stream_.atEnd() ) {
863 clearMAPIValue( mapi );
866 readMAPIValue( stream_, mapi );
867 if ( mapi.type == MAPI_TYPE_NONE ) {
868 kDebug().nospace() <<
"MAPI unsupported: tag="
869 << hex << mapi.tag <<
", type=" << mapi.type;
870 clearMAPIValue( mapi );
874 switch ( mapi.tag ) {
877 if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
879 int len = data.
size();
881 device_->seek( device_->pos()-len );
882 quint32 interface_ID;
883 stream_ >> interface_ID;
884 if ( interface_ID == MAPI_IID_IMessage ) {
889 attach->
setMimeTag(
"application/vnd.ms-tnef" );
891 kDebug() <<
"MAPI Embedded Message: size=" << data.
size();
893 device_->seek( device_->pos() + ( len-4 ) );
895 }
else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->
offset() < 0 ) {
896 foundAttachment =
true;
897 int len = mapi.value.toByteArray().size();
900 attach->
setOffset( device_->pos() - len );
901 attach->
addAttribute( attATTACHDATA, atpBYTE,
QString(
"< size=%1 >" ).arg( len ),
false );
904 kDebug() <<
"MAPI data: size=" << mapi.value.toByteArray().size();
909 if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
910 if ( mapi.name.type == 0 ) {
911 mapiname =
QString().
sprintf(
" [name = 0x%04x]", mapi.name.value.toUInt() );
913 mapiname =
QString(
" [name = %1]" ).
arg( mapi.name.value.toString() );
916 switch ( mapi.type & 0x0FFF ) {
917 case MAPI_TYPE_UINT16:
918 kDebug().nospace() <<
"(tag="
921 <<
":" << hex << mapi.value.toUInt();
923 case MAPI_TYPE_ULONG:
924 kDebug().nospace() <<
"(tag="
927 <<
":" << hex << mapi.value.toUInt();
929 case MAPI_TYPE_BOOLEAN:
930 kDebug().nospace() <<
"(tag="
933 <<
":" << mapi.value.toBool();
936 kDebug().nospace() <<
"(tag="
939 <<
":" << mapi.value.toString().toLatin1().data();
941 case MAPI_TYPE_USTRING:
942 case MAPI_TYPE_STRING8:
943 kDebug().nospace() <<
"(tag="
946 <<
":size=" << mapi.value.toByteArray().size()
947 << mapi.value.toString();
949 case MAPI_TYPE_BINARY:
950 kDebug().nospace() <<
"(tag="
953 <<
":size=" << mapi.value.toByteArray().size();
962 mapi.value, mapi.name.value );
963 props[ p->
key() ] = p;
968 if ( foundAttachment && attach ) {
QByteArray toByteArray() const
void setOffset(int offset)
Sets the offset value of this attachment to offset.
QVariant property(int key) const
Returns the property associcated with the specified key.
QString & append(QChar ch)
void setDisplaySize(int size)
Sets the display size of the attachment to size.
bool extractAll()
Extracts all TNEF attachments into the default directory.
int key() const
Returns the integer key of the property.
~KTNEFParser()
Destroys the TNEF parser object.
void setDefaultExtractDir(const QString &dirname)
Sets the default extraction directory to dirname.
const_iterator constFind(const Key &key) const
void setExtension(const QString &str)
Sets the filename extension of this attachment to str.
This file is part of the API for handling TNEF data and defines the KTNEFMessage class.
bool extractFileTo(const QString &filename, const QString &dirname) const
Extracts a TNEF attachment having filename filename into the directory dirname.
int readRawData(char *s, int len)
bool extractFile(const QString &filename) const
Extracts a TNEF attachment having filename filename into the default directory.
Represents a TNEF message.
void addAttribute(int key, int type, const QVariant &value, bool overwrite=false)
Adds a TNEF attribute.
This file is part of the API for handling TNEF data and provides some basic definitions for general u...
uint toUInt(bool *ok) const
QString fromUtf16(const ushort *unicode, int size)
This file is part of the API for handling TNEF data and defines the KTNEFParser class.
int size() const
Returns the size of the attachment.
const_iterator constEnd() const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
QString fileName() const
Returns the filename of the attachment.
void setMimeTag(const QString &str)
Sets the MIME tag of this attachment to str.
void setFileName(const QString &str)
Sets the filename of this attachment to str.
void unsetDataParser()
Unsets the DataParsed flag for this attachment.
Represents a TNEF attachment.
void setValue(const T &value)
bool openDevice(QIODevice *device)
Opens the QIODevice device for parsing.
bool openFile(const QString &filename) const
Opens the filename for parsing.
void setName(const QString &str)
Sets the name of this attachment to str.
QByteArray toLatin1() const
char * toString(const T &value)
This file is part of the API for handling TNEF data and defines the KTNEFProperty class...
QString & sprintf(const char *cformat,...)
void setSize(int size)
Sets the size of the attachment to size.
QString fromLatin1(const char *str, int size)
int offset() const
Returns the offset value of the attachment.
const_iterator constEnd() const
const_iterator constBegin() const
KTNEFParser()
Constructs a TNEF parser object.
This file is part of the API for handling TNEF data and defines the KTNEFAttach class.
void setIndex(int indx)
Sets the index of this attachment to indx.
KTNEFMessage * message() const
Returns the KTNEFMessage used in the parsing process.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString name() const
Returns the name of the attachment.
Interface for setting MAPI properties.
iterator find(const Key &key)
void setDisplayName(const QString &str)
Sets the display name of this attachment to str.
void setTime_t(uint seconds)
const T value(const Key &key) const