• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KTNEF Library

ktnefparser.cpp

Go to the documentation of this file.
00001 /*
00002     ktnefparser.cpp
00003 
00004     Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be>
00005 
00006     This file is part of KTNEF, the KDE TNEF support library/program.
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022  */
00031 #include "ktnefparser.h"
00032 #include "ktnefattach.h"
00033 #include "ktnefproperty.h"
00034 #include "ktnefmessage.h"
00035 #include "ktnefdefs.h"
00036 
00037 #include <kdebug.h>
00038 #include <kmimetype.h>
00039 #include <ksavefile.h>
00040 
00041 #include <QtCore/QDateTime>
00042 #include <QtCore/QDataStream>
00043 #include <QtCore/QFile>
00044 #include <QtCore/QVariant>
00045 #include <QtCore/QList>
00046 
00047 using namespace KTnef;
00048 
00049 //@cond PRIVATE
00050 typedef struct {
00051   quint16 type;
00052   quint16 tag;
00053   QVariant value;
00054   struct {
00055     quint32 type;
00056     QVariant value;
00057   } name;
00058 } MAPI_value;
00059 //@endcond
00060 
00061 //@cond IGNORE
00062 void clearMAPIName( MAPI_value &mapi );
00063 void clearMAPIValue( MAPI_value &mapi, bool clearName = true );
00064 QString readMAPIString( QDataStream &stream, bool isUnicode = false,
00065                         bool align = true, int len = -1 );
00066 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi );
00067 QDateTime readTNEFDate( QDataStream &stream );
00068 QString readTNEFAddress( QDataStream &stream );
00069 QByteArray readTNEFData( QDataStream &stream, quint32 len );
00070 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len );
00071 QDateTime formatTime( quint32 lowB, quint32 highB );
00072 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props );
00073 //@endcond
00074 
00075 //------------------------------------------------------------------------------
00076 
00081 //@cond PRIVATE
00082 class KTnef::KTNEFParser::ParserPrivate
00083 {
00084 public:
00085   ParserPrivate()
00086   {
00087     defaultdir_ = "/tmp/";
00088     current_ = 0;
00089     deleteDevice_ = false;
00090     device_ = 0;
00091     message_ = new KTNEFMessage;
00092   }
00093   ~ParserPrivate()
00094   {
00095     delete message_;
00096   }
00097 
00098   bool decodeAttachment();
00099   bool decodeMessage();
00100   bool extractAttachmentTo( KTNEFAttach *att, const QString &dirname );
00101   void checkCurrent( int key );
00102   bool readMAPIProperties( QMap<int,KTNEFProperty*>& props,
00103                            KTNEFAttach *attach = 0 );
00104   bool parseDevice();
00105   void deleteDevice();
00106 
00107   QDataStream  stream_;
00108   QIODevice    *device_;
00109   bool         deleteDevice_;
00110   QString      defaultdir_;
00111   KTNEFAttach  *current_;
00112   KTNEFMessage *message_;
00113 };
00114 //@endcond
00115 
00116 KTNEFParser::KTNEFParser()
00117   : d( new ParserPrivate )
00118 {
00119 }
00120 
00121 KTNEFParser::~KTNEFParser()
00122 {
00123   d->deleteDevice();
00124   delete d;
00125 }
00126 
00127 KTNEFMessage *KTNEFParser::message() const
00128 {
00129   return d->message_;
00130 }
00131 
00132 void KTNEFParser::ParserPrivate::deleteDevice()
00133 {
00134   if ( deleteDevice_ ) {
00135     delete device_;
00136   }
00137   device_ = 0;
00138   deleteDevice_ = false;
00139 }
00140 
00141 bool KTNEFParser::ParserPrivate::decodeMessage()
00142 {
00143   quint32  i1, i2, off;
00144   quint16  u, tag, type;
00145   QVariant value;
00146 
00147   // read (type+name)
00148   stream_ >> i1;
00149   u = 0;
00150   tag = ( i1 & 0x0000FFFF );
00151   type = ( ( i1 & 0xFFFF0000 ) >> 16 );
00152   // read data length
00153   stream_ >> i2;
00154   // offset after reading the value
00155   off = device_->pos() + i2;
00156   switch ( tag ) {
00157   case attAIDOWNER:
00158     {
00159       uint tmp;
00160       stream_ >> tmp;
00161       value.setValue( tmp );
00162       message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value );
00163       kDebug(5975) << "Message Owner Appointment ID"
00164                << "(length=" << i2 << ")";
00165       break;
00166     }
00167   case attREQUESTRES:
00168     stream_ >> u;
00169     message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u );
00170     value = ( bool )u;
00171     kDebug(5975) << "Message Request Response" << "(length=" << i2 << ")";
00172     break;
00173   case attDATERECD:
00174     value = readTNEFDate( stream_ );
00175     message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value );
00176     kDebug(5975) << "Message Receive Date" << "(length=" << i2 << ")";
00177     break;
00178   case attMSGCLASS:
00179     value = readMAPIString( stream_, false, false, i2 );
00180     message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value );
00181     kDebug(5975) << "Message Class" << "(length=" << i2 << ")";
00182     break;
00183   case attMSGPRIORITY:
00184     stream_ >> u;
00185     message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u );
00186     value = u;
00187     kDebug(5975) << "Message Priority" << "(length=" << i2 << ")";
00188     break;
00189   case attMAPIPROPS:
00190     kDebug(5975) << "Message MAPI Properties" << "(length=" << i2 << ")";
00191     {
00192       int nProps = message_->properties().count();
00193       i2 += device_->pos();
00194       readMAPIProperties( message_->properties(), 0 );
00195       device_->seek( i2 );
00196       kDebug(5975) << "Properties:" << message_->properties().count();
00197       value = QString( "< %1 properties >" ).
00198               arg( message_->properties().count() - nProps );
00199     }
00200     break;
00201   case attTNEFVERSION:
00202     {
00203       uint tmp;
00204       stream_ >> tmp;
00205       value.setValue( tmp );
00206       kDebug(5975) << "Message TNEF Version" << "(length=" << i2 << ")";
00207     }
00208     break;
00209   case attFROM:
00210     message_->addProperty( 0x0024, MAPI_TYPE_STRING8,
00211                               readTNEFAddress( stream_ ) );
00212     device_->seek( device_->pos() - i2 );
00213     value = readTNEFData( stream_, i2 );
00214     kDebug(5975) << "Message From" << "(length=" << i2 << ")";
00215     break;
00216   case attSUBJECT:
00217     value = readMAPIString( stream_, false, false, i2 );
00218     message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value );
00219     kDebug(5975) << "Message Subject" << "(length=" << i2 << ")";
00220     break;
00221   case attDATESENT:
00222     value = readTNEFDate( stream_ );
00223     message_->addProperty( 0x0039, MAPI_TYPE_TIME, value );
00224     kDebug(5975) << "Message Date Sent" << "(length=" << i2 << ")";
00225     break;
00226   case attMSGSTATUS:
00227     {
00228       quint8 c;
00229       quint32 flag = 0;
00230       stream_ >> c;
00231       if ( c & fmsRead ) flag |= MSGFLAG_READ;
00232       if ( !( c & fmsModified ) ) flag |= MSGFLAG_UNMODIFIED;
00233       if ( c & fmsSubmitted ) flag |= MSGFLAG_SUBMIT;
00234       if ( c & fmsHasAttach ) flag |= MSGFLAG_HASATTACH;
00235       if ( c & fmsLocal ) flag |= MSGFLAG_UNSENT;
00236       message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
00237       value = c;
00238     }
00239     kDebug(5975) << "Message Status" << "(length=" << i2 << ")";
00240     break;
00241   case attRECIPTABLE:
00242     {
00243       quint32 rows;
00244       QList<QVariant> recipTable;
00245       stream_ >> rows;
00246       for ( uint i=0; i<rows; i++ ) {
00247         QMap<int,KTNEFProperty*> props;
00248         readMAPIProperties( props, 0 );
00249         recipTable << formatRecipient( props );
00250       }
00251       message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable );
00252       device_->seek( device_->pos() - i2 );
00253       value = readTNEFData( stream_, i2 );
00254     }
00255     kDebug(5975) << "Message Recipient Table" << "(length=" << i2 << ")";
00256     break;
00257   case attBODY:
00258     value = readMAPIString( stream_, false, false, i2 );
00259     message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value );
00260     kDebug(5975) << "Message Body" << "(length=" << i2 << ")";
00261     break;
00262   case attDATEMODIFIED:
00263     value = readTNEFDate( stream_ );
00264     message_->addProperty( 0x3008, MAPI_TYPE_TIME, value );
00265     kDebug(5975) << "Message Date Modified" << "(length=" << i2 << ")";
00266     break;
00267   case attMSGID:
00268     value = readMAPIString( stream_, false, false, i2 );
00269     message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value );
00270     kDebug(5975) << "Message ID" << "(length=" << i2 << ")";
00271     break;
00272   case attOEMCODEPAGE:
00273     value = readTNEFData( stream_, i2 );
00274     kDebug(5975) << "Message OEM Code Page" << "(length=" << i2 << ")";
00275     break;
00276   default:
00277     value = readTNEFAttribute( stream_, type, i2 );
00278     //kDebug(5975).form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u );
00279     break;
00280   }
00281   // skip data
00282   if ( device_->pos() != off && !device_->seek( off ) ) {
00283     return false;
00284   }
00285   // get checksum
00286   stream_ >> u;
00287   // add TNEF attribute
00288   message_->addAttribute( tag, type, value, true );
00289   //kDebug(5975) << "stream:" << device_->pos();
00290   return true;
00291 }
00292 
00293 bool KTNEFParser::ParserPrivate::decodeAttachment()
00294 {
00295   quint32  i;
00296   quint16  tag, type, u;
00297   QVariant value;
00298   QString  str;
00299 
00300   stream_ >> i;     // i <- attribute type & name
00301   tag = ( i & 0x0000FFFF );
00302   type = ( ( i & 0xFFFF0000 ) >> 16 );
00303   stream_ >> i;     // i <- data length
00304   checkCurrent( tag );
00305   switch ( tag ) {
00306   case attATTACHTITLE:
00307     value = readMAPIString( stream_, false, false, i );
00308     current_->setName( value.toString() );
00309     kDebug(5975) << "Attachment Title:" << current_->name();
00310     break;
00311   case attATTACHDATA:
00312     current_->setSize( i );
00313     current_->setOffset( device_->pos() );
00314     device_->seek( device_->pos() + i );
00315     value = QString( "< size=%1 >" ).arg( i );
00316     kDebug(5975) << "Attachment Data: size=" << i;
00317     break;
00318   case attATTACHMENT:   // try to get attachment info
00319     i += device_->pos();
00320     readMAPIProperties( current_->properties(), current_ );
00321     device_->seek( i );
00322     current_->setIndex( current_->property( MAPI_TAG_INDEX ).toUInt() );
00323     current_->setDisplaySize( current_->property( MAPI_TAG_SIZE ).toUInt() );
00324     str = current_->property( MAPI_TAG_DISPLAYNAME ).toString();
00325     if ( !str.isEmpty() ) {
00326       current_->setDisplayName( str );
00327     }
00328     current_->setFileName( current_->property( MAPI_TAG_FILENAME ).
00329                               toString() );
00330     str = current_->property( MAPI_TAG_MIMETAG ).toString();
00331     if ( !str.isEmpty() ) {
00332       current_->setMimeTag( str );
00333     }
00334     current_->setExtension( current_->property( MAPI_TAG_EXTENSION ).
00335                                toString() );
00336     value = QString( "< %1 properties >" ).
00337             arg( current_->properties().count() );
00338     break;
00339   case attATTACHMODDATE:
00340     value = readTNEFDate( stream_ );
00341     kDebug(5975) << "Attachment Modification Date:" << value.toString();
00342     break;
00343   case attATTACHCREATEDATE:
00344     value = readTNEFDate( stream_ );
00345     kDebug(5975) << "Attachment Creation Date:" << value.toString();
00346     break;
00347   case attATTACHMETAFILE:
00348     kDebug(5975) << "Attachment Metafile: size=" << i;
00349     //value = QString( "< size=%1 >" ).arg( i );
00350     //device_->seek( device_->pos()+i );
00351     value = readTNEFData( stream_, i );
00352     break;
00353   default:
00354     value = readTNEFAttribute( stream_, type, i );
00355     kDebug(5975) << "Attachment unknown field:         tag="
00356                  << hex << tag << ", length=" << dec << i;
00357     break;
00358   }
00359   stream_ >> u; // u <- checksum
00360   // add TNEF attribute
00361   current_->addAttribute( tag, type, value, true );
00362   //kDebug(5975) << "stream:" << device_->pos();
00363 
00364   return true;
00365 }
00366 
00367 void KTNEFParser::setDefaultExtractDir( const QString &dirname )
00368 {
00369   d->defaultdir_ = dirname;
00370 }
00371 
00372 bool KTNEFParser::ParserPrivate::parseDevice()
00373 {
00374   quint16 u;
00375   quint32 i;
00376   quint8  c;
00377 
00378   message_->clearAttachments();
00379   if ( current_ ) {
00380     delete current_;
00381     current_ = 0;
00382   }
00383 
00384   if ( !device_->open( QIODevice::ReadOnly ) ) {
00385     kDebug(5975) << "Couldn't open device";
00386     return false;
00387   }
00388 
00389   stream_.setDevice( device_ );
00390   stream_.setByteOrder( QDataStream::LittleEndian );
00391   stream_ >> i;
00392   if ( i == TNEF_SIGNATURE ) {
00393     stream_ >> u;
00394     kDebug(5975).nospace() << "Attachment cross reference key: 0x"
00395                            << hex << qSetFieldWidth(4) << qSetPadChar('0') << u;
00396     //kDebug(5975) << "stream:" << device_->pos();
00397     while (!stream_.atEnd()) {
00398       stream_ >> c;
00399       switch( c ) {
00400       case LVL_MESSAGE:
00401         if ( !decodeMessage() ) goto end;
00402         break;
00403       case LVL_ATTACHMENT:
00404         if ( !decodeAttachment() ) goto end;
00405         break;
00406       default:
00407         kDebug(5975) << "Unknown Level:" << c << ", at offset"
00408                  << device_->pos();
00409         goto end;
00410       }
00411     }
00412     if ( current_ ) {
00413       checkCurrent( attATTACHDATA );  // this line has the effect to append the
00414       // attachment, if it has data. If not it does
00415       // nothing, and the attachment will be discarded
00416       delete current_;
00417       current_ = 0;
00418     }
00419     return true;
00420   } else {
00421     kDebug(5975) << "This is not a TNEF file";
00422   end:
00423     device_->close();
00424     return false;
00425   }
00426 }
00427 
00428 bool KTNEFParser::extractFile( const QString &filename ) const
00429 {
00430   KTNEFAttach *att = d->message_->attachment( filename );
00431   if ( !att ) {
00432     return false;
00433   }
00434   return d->extractAttachmentTo( att, d->defaultdir_ );
00435 }
00436 
00437 bool KTNEFParser::ParserPrivate::extractAttachmentTo( KTNEFAttach *att,
00438                                                       const QString &dirname )
00439 {
00440   QString filename = dirname + '/' + att->name();
00441   if ( !device_->isOpen() ) {
00442     return false;
00443   }
00444   if ( !device_->seek( att->offset() ) ) {
00445     return false;
00446   }
00447   KSaveFile outfile( filename );
00448   if ( !outfile.open() ) {
00449     return false;
00450   }
00451 
00452   quint32 len = att->size(), sz( 16384 );
00453   int     n( 0 );
00454   char    *buf = new char[sz];
00455   bool    ok( true );
00456   while ( ok && len > 0 ) {
00457     n = device_->read( buf, qMin( sz, len ) );
00458     if ( n < 0 ) {
00459       ok = false;
00460     } else {
00461       len -= n;
00462       if ( outfile.write( buf, n ) != n ) {
00463         ok = false;
00464       }
00465     }
00466   }
00467   delete [] buf;
00468 
00469   return ok;
00470 }
00471 
00472 bool KTNEFParser::extractAll()
00473 {
00474   QList<KTNEFAttach*> l = d->message_->attachmentList();
00475   QList<KTNEFAttach*>::const_iterator it = l.begin();
00476   for ( ; it != l.end(); ++it ) {
00477     if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
00478       return false;
00479     }
00480   }
00481   return true;
00482 }
00483 
00484 bool KTNEFParser::extractFileTo( const QString &filename,
00485                                  const QString &dirname ) const
00486 {
00487   kDebug(5975) << "Extracting attachment: filename="
00488            << filename << ", dir=" << dirname;
00489   KTNEFAttach *att = d->message_->attachment( filename );
00490   if ( !att ) {
00491     return false;
00492   }
00493   return d->extractAttachmentTo( att, dirname );
00494 }
00495 
00496 bool KTNEFParser::openFile( const QString &filename ) const
00497 {
00498   d->deleteDevice();
00499   delete d->message_;
00500   d->message_ = new KTNEFMessage();
00501   d->device_ = new QFile( filename );
00502   d->deleteDevice_ = true;
00503   return d->parseDevice();
00504 }
00505 
00506 bool KTNEFParser::openDevice( QIODevice *device )
00507 {
00508   d->deleteDevice();
00509   d->device_ = device;
00510   return d->parseDevice();
00511 }
00512 
00513 void KTNEFParser::ParserPrivate::checkCurrent( int key )
00514 {
00515   if ( !current_ ) {
00516     current_ = new KTNEFAttach();
00517   } else {
00518     if ( current_->attributes().contains( key ) ) {
00519       if (current_->offset() >= 0 ) {
00520         if (current_->name().isEmpty()) {
00521           current_->setName( "Unnamed" );
00522         }
00523         if ( current_->mimeTag().isEmpty() ) {
00524           // No mime type defined in the TNEF structure,
00525           // try to find it from the attachment filename
00526           // and/or content (using at most 32 bytes)
00527           KMimeType::Ptr mimetype;
00528           if ( !current_->fileName().isEmpty() ) {
00529             mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
00530           }
00531           if ( !mimetype ) return; // FIXME
00532           if ( mimetype->name() == "application/octet-stream" &&
00533                current_->size() > 0 ) {
00534             int oldOffset = device_->pos();
00535             QByteArray buffer( qMin( 32, current_->size() ), '\0' );
00536             device_->seek( current_->offset() );
00537             device_->read( buffer.data(), buffer.size() );
00538             mimetype = KMimeType::findByContent( buffer );
00539             device_->seek( oldOffset );
00540           }
00541           current_->setMimeTag( mimetype->name() );
00542         }
00543         message_->addAttachment( current_ );
00544         current_ = 0;
00545       } else {
00546         // invalid attachment, skip it
00547         delete current_;
00548         current_ = 0;
00549       }
00550       current_ = new KTNEFAttach();
00551     }
00552   }
00553 }
00554 
00555 //------------------------------------------------------------------------------
00556 
00557 //@cond IGNORE
00558 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); }
00559 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR )
00560 
00561 void clearMAPIName( MAPI_value &mapi )
00562 {
00563   mapi.name.value.clear();
00564 }
00565 
00566 void clearMAPIValue( MAPI_value &mapi, bool clearName )
00567 {
00568   mapi.value.clear();
00569   if ( clearName ) {
00570     clearMAPIName( mapi );
00571   }
00572 }
00573 
00574 QDateTime formatTime( quint32 lowB, quint32 highB )
00575 {
00576   QDateTime dt;
00577   quint64 u64;
00578   u64 = highB;
00579   u64 <<= 32;
00580   u64 |= lowB;
00581   u64 -= 116444736000000000LL;
00582   u64 /= 10000000;
00583   if ( u64 <= 0xffffffffU ) {
00584     dt.setTime_t( ( unsigned int )u64 );
00585   } else {
00586     kWarning().nospace() << "Invalid date: low byte="
00587                          << showbase << qSetFieldWidth(8) << qSetPadChar('0')
00588                          << lowB << ", high byte=" << highB;
00589     dt.setTime_t( 0xffffffffU );
00590   }
00591   return dt;
00592 }
00593 
00594 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props )
00595 {
00596   QString s, dn, addr, t;
00597   QMap<int,KTnef::KTNEFProperty*>::ConstIterator it;
00598   if ( ( it = props.find( 0x3001 ) ) != props.end() ) {
00599     dn = ( *it )->valueString();
00600   }
00601   if ( ( it = props.find( 0x3003 ) ) != props.end() ) {
00602     addr = ( *it )->valueString();
00603   }
00604   if ( ( it = props.find( 0x0C15 ) ) != props.end() ) {
00605     switch ( ( *it )->value().toInt() ) {
00606     case 0: t = "From:"; break;
00607     case 1: t = "To:"; break;
00608     case 2: t = "Cc:"; break;
00609     case 3: t = "Bcc:"; break;
00610     }
00611   }
00612   if ( !t.isEmpty() ) {
00613     s.append( t );
00614   }
00615   if ( !dn.isEmpty() ) {
00616     s.append( ' ' + dn );
00617   }
00618   if ( !addr.isEmpty() && addr != dn ) {
00619     s.append( " <" + addr + '>' );
00620   }
00621 
00622   return s.trimmed();
00623 }
00624 
00625 QDateTime readTNEFDate( QDataStream &stream )
00626 {
00627   // 14-bytes long
00628   quint16 y, m, d, hh, mm, ss, dm;
00629   stream >> y >> m >> d >> hh >> mm >> ss >> dm;
00630   return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) );
00631 }
00632 
00633 QString readTNEFAddress( QDataStream &stream )
00634 {
00635   quint16 totalLen, strLen, addrLen;
00636   QString s;
00637   stream >> totalLen >> totalLen >> strLen >> addrLen;
00638   s.append( readMAPIString( stream, false, false, strLen ) );
00639   s.append( " <" );
00640   s.append( readMAPIString( stream, false, false, addrLen ) );
00641   s.append( ">" );
00642   quint8 c;
00643   for ( int i=8+strLen+addrLen; i<totalLen; i++ ) {
00644     stream >> c;
00645   }
00646   return s;
00647 }
00648 
00649 QByteArray readTNEFData( QDataStream &stream, quint32 len )
00650 {
00651   QByteArray array( len, '\0' );
00652   if ( len > 0 ) {
00653     stream.readRawData( array.data(), len );
00654   }
00655   return array;
00656 }
00657 
00658 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len )
00659 {
00660   switch ( type ) {
00661   case atpTEXT:
00662   case atpSTRING:
00663     return readMAPIString( stream, false, false, len );
00664   case atpDATE:
00665     return readTNEFDate( stream );
00666   default:
00667     return readTNEFData( stream, len );
00668   }
00669 }
00670 
00671 QString readMAPIString( QDataStream &stream, bool isUnicode, bool align,
00672                         int len_ )
00673 {
00674   quint32 len;
00675   char *buf = 0;
00676   if ( len_ == -1 ) {
00677     stream >> len;
00678   } else {
00679     len = len_;
00680   }
00681   quint32 fullLen = len;
00682   if ( align ) {
00683     ALIGN( fullLen, 4 );
00684   }
00685   buf = new char[ len ];
00686   stream.readRawData( buf, len );
00687   quint8 c;
00688   for ( uint i=len; i<fullLen; i++ ) {
00689     stream >> c;
00690   }
00691   QString res;
00692   if ( isUnicode ) {
00693     res = QString::fromUtf16( ( const unsigned short *)buf );
00694   } else {
00695     res = QString::fromLocal8Bit( buf );
00696   }
00697   delete [] buf;
00698   return res;
00699 }
00700 
00701 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi )
00702 {
00703   quint32 d;
00704 
00705   clearMAPIValue( mapi );
00706   stream >> d;
00707   mapi.type =  ( d & 0x0000FFFF );
00708   mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 );
00709   if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00710     // skip GUID
00711     stream >> d >> d >> d >> d;
00712     // name type
00713     stream >> mapi.name.type;
00714     // name
00715     if ( mapi.name.type == 0 ) {
00716       uint tmp;
00717       stream >> tmp;
00718       mapi.name.value.setValue( tmp );
00719     } else if ( mapi.name.type == 1 ) {
00720       mapi.name.value.setValue( readMAPIString( stream, true ) );
00721     }
00722   }
00723 
00724   int n = 1;
00725   QVariant value;
00726   if ( ISVECTOR( mapi ) ) {
00727     stream >> n;
00728     mapi.value = QList<QVariant>();
00729   }
00730   for ( int i=0; i<n; i++ ) {
00731     value.clear();
00732     switch( mapi.type & 0x0FFF ) {
00733     case MAPI_TYPE_UINT16:
00734       stream >> d;
00735       value.setValue( d & 0x0000FFFF );
00736       break;
00737     case MAPI_TYPE_BOOLEAN:
00738     case MAPI_TYPE_ULONG:
00739       {
00740         uint tmp;
00741         stream >> tmp;
00742         value.setValue( tmp );
00743       }
00744       break;
00745     case MAPI_TYPE_FLOAT:
00746       // FIXME: Don't we have to set the value here
00747       stream >> d;
00748       break;
00749     case MAPI_TYPE_DOUBLE:
00750       {
00751         double tmp;
00752         stream >> tmp;
00753         value.setValue( tmp );
00754       }
00755       break;
00756     case MAPI_TYPE_TIME:
00757       {
00758         quint32 lowB, highB;
00759         stream >> lowB >> highB;
00760         value = formatTime( lowB, highB );
00761       }
00762       break;
00763     case MAPI_TYPE_STRING8:
00764       // in case of a vector'ed value, the number of elements
00765       // has already been read in the upper for-loop
00766       if ( ISVECTOR( mapi ) ) {
00767         d = 1;
00768       } else {
00769         stream >> d;
00770       }
00771       for ( uint i=0; i<d; i++ ) {
00772         value.clear();
00773         value.setValue( readMAPIString( stream ) );
00774       }
00775       break;
00776     case MAPI_TYPE_USTRING:
00777       mapi.type = MAPI_TYPE_NONE;
00778       break;
00779     case MAPI_TYPE_OBJECT:
00780     case MAPI_TYPE_BINARY:
00781       if ( ISVECTOR( mapi ) ) {
00782         d = 1;
00783       } else {
00784         stream >> d;
00785       }
00786       for ( uint i=0; i<d; i++ ) {
00787         value.clear();
00788         quint32 len;
00789         stream >> len;
00790         value = QByteArray( len, '\0' );
00791         if ( len > 0 ) {
00792           int fullLen = len;
00793           ALIGN( fullLen, 4 );
00794           stream.readRawData( value.toByteArray().data(), len );
00795           quint8 c;
00796           for ( int i=len; i<fullLen; i++ ) {
00797             stream >> c;
00798           }
00799           // FIXME: Shouldn't we do something with the value???
00800         }
00801       }
00802       break;
00803     default:
00804       mapi.type = MAPI_TYPE_NONE;
00805       break;
00806     }
00807     if ( ISVECTOR( mapi ) ) {
00808       QList <QVariant> lst = mapi.value.toList();
00809       lst << value;
00810       mapi.value.setValue( lst );
00811     } else {
00812       mapi.value = value;
00813     }
00814   }
00815   return mapi.tag;
00816 }
00817 //@endcond
00818 
00819 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
00820                                                      KTNEFAttach *attach )
00821 {
00822   quint32       n;
00823   MAPI_value    mapi;
00824   KTNEFProperty *p;
00825   QMap<int,KTNEFProperty*>::ConstIterator it;
00826   bool foundAttachment = false;
00827 
00828   // some initializations
00829   mapi.type = MAPI_TYPE_NONE;
00830   mapi.value.clear();
00831 
00832   // get number of properties
00833   stream_ >> n;
00834   kDebug(5975) << "MAPI Properties:" << n;
00835   for ( uint i=0; i<n; i++ ) {
00836     if ( stream_.atEnd() ) {
00837       clearMAPIValue( mapi );
00838       return false;
00839     }
00840     readMAPIValue( stream_, mapi );
00841     if ( mapi.type == MAPI_TYPE_NONE ) {
00842       kDebug(5975).nospace() << "MAPI unsupported:         tag="
00843                              << hex << mapi.tag << ", type=" << mapi.type;
00844       clearMAPIValue( mapi );
00845       return false;
00846     }
00847     int key = mapi.tag;
00848     switch ( mapi.tag ) {
00849     case MAPI_TAG_DATA:
00850     {
00851       if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
00852         QByteArray data = mapi.value.toByteArray();
00853         int len = data.size();
00854         ALIGN( len, 4 );
00855         device_->seek( device_->pos()-len );
00856         quint32 interface_ID;
00857         stream_ >> interface_ID;
00858         if ( interface_ID == MAPI_IID_IMessage ) {
00859           // embedded TNEF file
00860           attach->unsetDataParser();
00861           attach->setOffset( device_->pos()+12 );
00862           attach->setSize( data.size()-16 );
00863           attach->setMimeTag( "application/vnd.ms-tnef" );
00864           attach->setDisplayName( "Embedded Message" );
00865           kDebug(5975) << "MAPI Embedded Message: size=" << data.size();
00866         }
00867         device_->seek( device_->pos() + ( len-4 ) );
00868         break;
00869       } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) {
00870         foundAttachment = true;
00871         int len = mapi.value.toByteArray().size();
00872         ALIGN( len, 4 );
00873         attach->setSize( len );
00874         attach->setOffset( device_->pos() - len );
00875         attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
00876       }
00877     }
00878     kDebug(5975) << "MAPI data: size=" << mapi.value.toByteArray().size();
00879     break;
00880     default:
00881     {
00882       QString mapiname = "";
00883       if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00884         if ( mapi.name.type == 0 ) {
00885           mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
00886         } else {
00887           mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() );
00888         }
00889       }
00890       switch ( mapi.type & 0x0FFF ) {
00891       case MAPI_TYPE_UINT16:
00892         kDebug(5975).nospace() << "(tag="
00893                                << hex << mapi.tag
00894                                << ") MAPI short" <<  mapiname.toAscii().data()
00895                                << ":" << hex << mapi.value.toUInt();
00896         break;
00897       case MAPI_TYPE_ULONG:
00898         kDebug(5975).nospace() << "(tag="
00899                                << hex << mapi.tag
00900                                << ") MAPI long" <<  mapiname.toAscii().data()
00901                                << ":" << hex << mapi.value.toUInt();
00902         break;
00903       case MAPI_TYPE_BOOLEAN:
00904         kDebug(5975).nospace() << "(tag="
00905                                << hex << mapi.tag
00906                                << ") MAPI boolean" <<  mapiname.toAscii().data()
00907                                << ":" << mapi.value.toBool();
00908         break;
00909       case MAPI_TYPE_TIME:
00910         kDebug(5975).nospace() << "(tag="
00911                                << hex << mapi.tag
00912                                << ") MAPI time" <<  mapiname.toAscii().data()
00913                                << ":" << mapi.value.toString().toAscii().data();
00914         break;
00915       case MAPI_TYPE_USTRING:
00916       case MAPI_TYPE_STRING8:
00917         kDebug(5975).nospace() << "(tag="
00918                                << hex << mapi.tag
00919                                << ") MAPI string" <<  mapiname.toAscii().data()
00920                                << ":size=" << mapi.value.toByteArray().size()
00921                                << mapi.value.toString();
00922         break;
00923       case MAPI_TYPE_BINARY:
00924         kDebug(5975).nospace() << "(tag="
00925                                << hex << mapi.tag
00926                                << ") MAPI binary" <<  mapiname.toAscii().data()
00927                                << ":size=" << mapi.value.toByteArray().size();
00928         break;
00929       }
00930     }
00931     break;
00932     }
00933     // do not remove potential existing similar entry
00934     if ( ( it = props.find( key ) ) == props.end() ) {
00935       p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
00936                              mapi.value, mapi.name.value );
00937       props[ p->key() ] = p;
00938     }
00939     //kDebug(5975) << "stream:" << device_->pos();
00940   }
00941 
00942   if ( foundAttachment && attach ) {
00943     attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
00944     attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
00945     QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString();
00946     if ( !str.isEmpty() )
00947       attach->setDisplayName( str );
00948     attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() );
00949     str = attach->property( MAPI_TAG_MIMETAG ).toString();
00950     if ( !str.isEmpty() )
00951       attach->setMimeTag( str );
00952     attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() );
00953     if ( attach->name().isEmpty() )
00954       attach->setName( attach->fileName() );
00955   }
00956 
00957   return true;
00958 }

KTNEF Library

Skip menu "KTNEF Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal