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