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

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"
  • akonadi
  • 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.4
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