• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • 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   delete current_;
00388   current_ = 0;
00389 
00390   if ( !device_->open( QIODevice::ReadOnly ) ) {
00391     kDebug() << "Couldn't open device";
00392     return false;
00393   }
00394 
00395   stream_.setDevice( device_ );
00396   stream_.setByteOrder( QDataStream::LittleEndian );
00397   stream_ >> i;
00398   if ( i == TNEF_SIGNATURE ) {
00399     stream_ >> u;
00400     kDebug().nospace() << "Attachment cross reference key: 0x"
00401                        << hex << qSetFieldWidth( 4 ) << qSetPadChar( '0' ) << u;
00402     //kDebug() << "stream:" << device_->pos();
00403     while ( !stream_.atEnd() ) {
00404       stream_ >> c;
00405       switch( c ) {
00406       case LVL_MESSAGE:
00407         if ( !decodeMessage() ) {
00408           goto end;
00409         }
00410         break;
00411       case LVL_ATTACHMENT:
00412         if ( !decodeAttachment() ) {
00413           goto end;
00414         }
00415         break;
00416       default:
00417         kDebug() << "Unknown Level:" << c << ", at offset" << device_->pos();
00418         goto end;
00419       }
00420     }
00421     if ( current_ ) {
00422       checkCurrent( attATTACHDATA );  // this line has the effect to append the
00423       // attachment, if it has data. If not it does
00424       // nothing, and the attachment will be discarded
00425       delete current_;
00426       current_ = 0;
00427     }
00428     return true;
00429   } else {
00430     kDebug() << "This is not a TNEF file";
00431   end:
00432     device_->close();
00433     return false;
00434   }
00435 }
00436 
00437 bool KTNEFParser::extractFile( const QString &filename ) const
00438 {
00439   KTNEFAttach *att = d->message_->attachment( filename );
00440   if ( !att ) {
00441     return false;
00442   }
00443   return d->extractAttachmentTo( att, d->defaultdir_ );
00444 }
00445 
00446 bool KTNEFParser::ParserPrivate::extractAttachmentTo( KTNEFAttach *att,
00447                                                       const QString &dirname )
00448 {
00449   QString filename = dirname + '/' + att->name();
00450   if ( !device_->isOpen() ) {
00451     return false;
00452   }
00453   if ( !device_->seek( att->offset() ) ) {
00454     return false;
00455   }
00456   KSaveFile outfile( filename );
00457   if ( !outfile.open() ) {
00458     return false;
00459   }
00460 
00461   quint32 len = att->size(), sz( 16384 );
00462   int     n( 0 );
00463   char    *buf = new char[sz];
00464   bool    ok( true );
00465   while ( ok && len > 0 ) {
00466     n = device_->read( buf, qMin( sz, len ) );
00467     if ( n < 0 ) {
00468       ok = false;
00469     } else {
00470       len -= n;
00471       if ( outfile.write( buf, n ) != n ) {
00472         ok = false;
00473       }
00474     }
00475   }
00476   delete [] buf;
00477 
00478   return ok;
00479 }
00480 
00481 bool KTNEFParser::extractAll()
00482 {
00483   QList<KTNEFAttach*> l = d->message_->attachmentList();
00484   QList<KTNEFAttach*>::const_iterator it = l.constBegin();
00485   for ( ; it != l.constEnd(); ++it ) {
00486     if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
00487       return false;
00488     }
00489   }
00490   return true;
00491 }
00492 
00493 bool KTNEFParser::extractFileTo( const QString &filename,
00494                                  const QString &dirname ) const
00495 {
00496   kDebug() << "Extracting attachment: filename="
00497            << filename << ", dir=" << dirname;
00498   KTNEFAttach *att = d->message_->attachment( filename );
00499   if ( !att ) {
00500     return false;
00501   }
00502   return d->extractAttachmentTo( att, dirname );
00503 }
00504 
00505 bool KTNEFParser::openFile( const QString &filename ) const
00506 {
00507   d->deleteDevice();
00508   delete d->message_;
00509   d->message_ = new KTNEFMessage();
00510   d->device_ = new QFile( filename );
00511   d->deleteDevice_ = true;
00512   return d->parseDevice();
00513 }
00514 
00515 bool KTNEFParser::openDevice( QIODevice *device )
00516 {
00517   d->deleteDevice();
00518   d->device_ = device;
00519   return d->parseDevice();
00520 }
00521 
00522 void KTNEFParser::ParserPrivate::checkCurrent( int key )
00523 {
00524   if ( !current_ ) {
00525     current_ = new KTNEFAttach();
00526   } else {
00527     if ( current_->attributes().contains( key ) ) {
00528       if ( current_->offset() >= 0 ) {
00529         if ( current_->name().isEmpty() ) {
00530           current_->setName( "Unnamed" );
00531         }
00532         if ( current_->mimeTag().isEmpty() ) {
00533           // No mime type defined in the TNEF structure,
00534           // try to find it from the attachment filename
00535           // and/or content (using at most 32 bytes)
00536           KMimeType::Ptr mimetype;
00537           if ( !current_->fileName().isEmpty() ) {
00538             mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
00539           }
00540           if ( !mimetype ) {
00541             return; // FIXME
00542           }
00543           if ( mimetype->name() == "application/octet-stream" &&
00544                current_->size() > 0 ) {
00545             int oldOffset = device_->pos();
00546             QByteArray buffer( qMin( 32, current_->size() ), '\0' );
00547             device_->seek( current_->offset() );
00548             device_->read( buffer.data(), buffer.size() );
00549             mimetype = KMimeType::findByContent( buffer );
00550             device_->seek( oldOffset );
00551           }
00552           current_->setMimeTag( mimetype->name() );
00553         }
00554         message_->addAttachment( current_ );
00555         current_ = 0;
00556       } else {
00557         // invalid attachment, skip it
00558         delete current_;
00559         current_ = 0;
00560       }
00561       current_ = new KTNEFAttach();
00562     }
00563   }
00564 }
00565 
00566 //------------------------------------------------------------------------------
00567 
00568 //@cond IGNORE
00569 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); }
00570 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR )
00571 
00572 void clearMAPIName( MAPI_value &mapi )
00573 {
00574   mapi.name.value.clear();
00575 }
00576 
00577 void clearMAPIValue( MAPI_value &mapi, bool clearName )
00578 {
00579   mapi.value.clear();
00580   if ( clearName ) {
00581     clearMAPIName( mapi );
00582   }
00583 }
00584 
00585 QDateTime formatTime( quint32 lowB, quint32 highB )
00586 {
00587   QDateTime dt;
00588   quint64 u64;
00589   u64 = highB;
00590   u64 <<= 32;
00591   u64 |= lowB;
00592   u64 -= 116444736000000000LL;
00593   u64 /= 10000000;
00594   if ( u64 <= 0xffffffffU ) {
00595     dt.setTime_t( ( unsigned int )u64 );
00596   } else {
00597     kWarning().nospace() << "Invalid date: low byte="
00598                          << showbase << qSetFieldWidth( 8 ) << qSetPadChar( '0' )
00599                          << lowB << ", high byte=" << highB;
00600     dt.setTime_t( 0xffffffffU );
00601   }
00602   return dt;
00603 }
00604 
00605 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props )
00606 {
00607   QString s, dn, addr, t;
00608   QMap<int,KTnef::KTNEFProperty*>::ConstIterator it;
00609   if ( ( it = props.find( 0x3001 ) ) != props.end() ) {
00610     dn = ( *it )->valueString();
00611   }
00612   if ( ( it = props.find( 0x3003 ) ) != props.end() ) {
00613     addr = ( *it )->valueString();
00614   }
00615   if ( ( it = props.find( 0x0C15 ) ) != props.end() ) {
00616     switch ( ( *it )->value().toInt() ) {
00617     case 0:
00618       t = "From:";
00619       break;
00620     case 1:
00621       t = "To:";
00622       break;
00623     case 2:
00624       t = "Cc:";
00625       break;
00626     case 3:
00627       t = "Bcc:";
00628       break;
00629     }
00630   }
00631   if ( !t.isEmpty() ) {
00632     s.append( t );
00633   }
00634   if ( !dn.isEmpty() ) {
00635     s.append( ' ' + dn );
00636   }
00637   if ( !addr.isEmpty() && addr != dn ) {
00638     s.append( " <" + addr + '>' );
00639   }
00640 
00641   return s.trimmed();
00642 }
00643 
00644 QDateTime readTNEFDate( QDataStream &stream )
00645 {
00646   // 14-bytes long
00647   quint16 y, m, d, hh, mm, ss, dm;
00648   stream >> y >> m >> d >> hh >> mm >> ss >> dm;
00649   return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) );
00650 }
00651 
00652 QString readTNEFAddress( QDataStream &stream )
00653 {
00654   quint16 totalLen, strLen, addrLen;
00655   QString s;
00656   stream >> totalLen >> totalLen >> strLen >> addrLen;
00657   s.append( readMAPIString( stream, false, false, strLen ) );
00658   s.append( " <" );
00659   s.append( readMAPIString( stream, false, false, addrLen ) );
00660   s.append( ">" );
00661   quint8 c;
00662   for ( int i=8+strLen+addrLen; i<totalLen; i++ ) {
00663     stream >> c;
00664   }
00665   return s;
00666 }
00667 
00668 QByteArray readTNEFData( QDataStream &stream, quint32 len )
00669 {
00670   QByteArray array( len, '\0' );
00671   if ( len > 0 ) {
00672     stream.readRawData( array.data(), len );
00673   }
00674   return array;
00675 }
00676 
00677 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len )
00678 {
00679   switch ( type ) {
00680   case atpTEXT:
00681   case atpSTRING:
00682     return readMAPIString( stream, false, false, len );
00683   case atpDATE:
00684     return readTNEFDate( stream );
00685   default:
00686     return readTNEFData( stream, len );
00687   }
00688 }
00689 
00690 QString readMAPIString( QDataStream &stream, bool isUnicode, bool align,
00691                         int len_ )
00692 {
00693   quint32 len;
00694   char *buf = 0;
00695   if ( len_ == -1 ) {
00696     stream >> len;
00697   } else {
00698     len = len_;
00699   }
00700   quint32 fullLen = len;
00701   if ( align ) {
00702     ALIGN( fullLen, 4 );
00703   }
00704   buf = new char[ len ];
00705   stream.readRawData( buf, len );
00706   quint8 c;
00707   for ( uint i=len; i<fullLen; i++ ) {
00708     stream >> c;
00709   }
00710   QString res;
00711   if ( isUnicode ) {
00712     res = QString::fromUtf16( ( const unsigned short *)buf );
00713   } else {
00714     res = QString::fromLocal8Bit( buf );
00715   }
00716   delete [] buf;
00717   return res;
00718 }
00719 
00720 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi )
00721 {
00722   quint32 d;
00723 
00724   clearMAPIValue( mapi );
00725   stream >> d;
00726   mapi.type =  ( d & 0x0000FFFF );
00727   mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 );
00728   if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00729     // skip GUID
00730     stream >> d >> d >> d >> d;
00731     // name type
00732     stream >> mapi.name.type;
00733     // name
00734     if ( mapi.name.type == 0 ) {
00735       uint tmp;
00736       stream >> tmp;
00737       mapi.name.value.setValue( tmp );
00738     } else if ( mapi.name.type == 1 ) {
00739       mapi.name.value.setValue( readMAPIString( stream, true ) );
00740     }
00741   }
00742 
00743   int n = 1;
00744   QVariant value;
00745   if ( ISVECTOR( mapi ) ) {
00746     stream >> n;
00747     mapi.value = QList<QVariant>();
00748   }
00749   for ( int i=0; i<n; i++ ) {
00750     value.clear();
00751     switch( mapi.type & 0x0FFF ) {
00752     case MAPI_TYPE_UINT16:
00753       stream >> d;
00754       value.setValue( d & 0x0000FFFF );
00755       break;
00756     case MAPI_TYPE_BOOLEAN:
00757     case MAPI_TYPE_ULONG:
00758       {
00759         uint tmp;
00760         stream >> tmp;
00761         value.setValue( tmp );
00762       }
00763       break;
00764     case MAPI_TYPE_FLOAT:
00765       // FIXME: Don't we have to set the value here
00766       stream >> d;
00767       break;
00768     case MAPI_TYPE_DOUBLE:
00769       {
00770         double tmp;
00771         stream >> tmp;
00772         value.setValue( tmp );
00773       }
00774       break;
00775     case MAPI_TYPE_TIME:
00776       {
00777         quint32 lowB, highB;
00778         stream >> lowB >> highB;
00779         value = formatTime( lowB, highB );
00780       }
00781       break;
00782     case MAPI_TYPE_STRING8:
00783       // in case of a vector'ed value, the number of elements
00784       // has already been read in the upper for-loop
00785       if ( ISVECTOR( mapi ) ) {
00786         d = 1;
00787       } else {
00788         stream >> d;
00789       }
00790       for ( uint i=0; i<d; i++ ) {
00791         value.clear();
00792         value.setValue( readMAPIString( stream ) );
00793       }
00794       break;
00795     case MAPI_TYPE_USTRING:
00796       mapi.type = MAPI_TYPE_NONE;
00797       break;
00798     case MAPI_TYPE_OBJECT:
00799     case MAPI_TYPE_BINARY:
00800       if ( ISVECTOR( mapi ) ) {
00801         d = 1;
00802       } else {
00803         stream >> d;
00804       }
00805       for ( uint i=0; i<d; i++ ) {
00806         value.clear();
00807         quint32 len;
00808         stream >> len;
00809         value = QByteArray( len, '\0' );
00810         if ( len > 0 ) {
00811           int fullLen = len;
00812           ALIGN( fullLen, 4 );
00813           stream.readRawData( value.toByteArray().data(), len );
00814           quint8 c;
00815           for ( int i=len; i<fullLen; i++ ) {
00816             stream >> c;
00817           }
00818           // FIXME: Shouldn't we do something with the value???
00819         }
00820       }
00821       break;
00822     default:
00823       mapi.type = MAPI_TYPE_NONE;
00824       break;
00825     }
00826     if ( ISVECTOR( mapi ) ) {
00827       QList <QVariant> lst = mapi.value.toList();
00828       lst << value;
00829       mapi.value.setValue( lst );
00830     } else {
00831       mapi.value = value;
00832     }
00833   }
00834   return mapi.tag;
00835 }
00836 //@endcond
00837 
00838 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
00839                                                      KTNEFAttach *attach )
00840 {
00841   quint32       n;
00842   MAPI_value    mapi;
00843   KTNEFProperty *p;
00844   QMap<int,KTNEFProperty*>::ConstIterator it;
00845   bool foundAttachment = false;
00846 
00847   // some initializations
00848   mapi.type = MAPI_TYPE_NONE;
00849   mapi.value.clear();
00850 
00851   // get number of properties
00852   stream_ >> n;
00853   kDebug() << "MAPI Properties:" << n;
00854   for ( uint i=0; i<n; i++ ) {
00855     if ( stream_.atEnd() ) {
00856       clearMAPIValue( mapi );
00857       return false;
00858     }
00859     readMAPIValue( stream_, mapi );
00860     if ( mapi.type == MAPI_TYPE_NONE ) {
00861       kDebug().nospace() << "MAPI unsupported:         tag="
00862                          << hex << mapi.tag << ", type=" << mapi.type;
00863       clearMAPIValue( mapi );
00864       return false;
00865     }
00866     int key = mapi.tag;
00867     switch ( mapi.tag ) {
00868     case MAPI_TAG_DATA:
00869     {
00870       if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
00871         QByteArray data = mapi.value.toByteArray();
00872         int len = data.size();
00873         ALIGN( len, 4 );
00874         device_->seek( device_->pos()-len );
00875         quint32 interface_ID;
00876         stream_ >> interface_ID;
00877         if ( interface_ID == MAPI_IID_IMessage ) {
00878           // embedded TNEF file
00879           attach->unsetDataParser();
00880           attach->setOffset( device_->pos()+12 );
00881           attach->setSize( data.size()-16 );
00882           attach->setMimeTag( "application/vnd.ms-tnef" );
00883           attach->setDisplayName( "Embedded Message" );
00884           kDebug() << "MAPI Embedded Message: size=" << data.size();
00885         }
00886         device_->seek( device_->pos() + ( len-4 ) );
00887         break;
00888       } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) {
00889         foundAttachment = true;
00890         int len = mapi.value.toByteArray().size();
00891         ALIGN( len, 4 );
00892         attach->setSize( len );
00893         attach->setOffset( device_->pos() - len );
00894         attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
00895       }
00896     }
00897     kDebug() << "MAPI data: size=" << mapi.value.toByteArray().size();
00898     break;
00899     default:
00900     {
00901       QString mapiname = "";
00902       if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00903         if ( mapi.name.type == 0 ) {
00904           mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
00905         } else {
00906           mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() );
00907         }
00908       }
00909       switch ( mapi.type & 0x0FFF ) {
00910       case MAPI_TYPE_UINT16:
00911         kDebug().nospace() << "(tag="
00912                            << hex << mapi.tag
00913                            << ") MAPI short" <<  mapiname.toAscii().data()
00914                            << ":" << hex << mapi.value.toUInt();
00915         break;
00916       case MAPI_TYPE_ULONG:
00917         kDebug().nospace() << "(tag="
00918                            << hex << mapi.tag
00919                            << ") MAPI long" <<  mapiname.toAscii().data()
00920                            << ":" << hex << mapi.value.toUInt();
00921         break;
00922       case MAPI_TYPE_BOOLEAN:
00923         kDebug().nospace() << "(tag="
00924                            << hex << mapi.tag
00925                            << ") MAPI boolean" <<  mapiname.toAscii().data()
00926                            << ":" << mapi.value.toBool();
00927         break;
00928       case MAPI_TYPE_TIME:
00929         kDebug().nospace() << "(tag="
00930                            << hex << mapi.tag
00931                            << ") MAPI time" <<  mapiname.toAscii().data()
00932                            << ":" << mapi.value.toString().toAscii().data();
00933         break;
00934       case MAPI_TYPE_USTRING:
00935       case MAPI_TYPE_STRING8:
00936         kDebug().nospace() << "(tag="
00937                            << hex << mapi.tag
00938                            << ") MAPI string" <<  mapiname.toAscii().data()
00939                            << ":size=" << mapi.value.toByteArray().size()
00940                            << mapi.value.toString();
00941         break;
00942       case MAPI_TYPE_BINARY:
00943         kDebug().nospace() << "(tag="
00944                            << hex << mapi.tag
00945                            << ") MAPI binary" <<  mapiname.toAscii().data()
00946                            << ":size=" << mapi.value.toByteArray().size();
00947         break;
00948       }
00949     }
00950     break;
00951     }
00952     // do not remove potential existing similar entry
00953     if ( ( it = props.constFind( key ) ) == props.constEnd() ) {
00954       p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
00955                              mapi.value, mapi.name.value );
00956       props[ p->key() ] = p;
00957     }
00958     //kDebug() << "stream:" << device_->pos();
00959   }
00960 
00961   if ( foundAttachment && attach ) {
00962     attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
00963     attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
00964     QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString();
00965     if ( !str.isEmpty() ) {
00966       attach->setDisplayName( str );
00967     }
00968     attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() );
00969     str = attach->property( MAPI_TAG_MIMETAG ).toString();
00970     if ( !str.isEmpty() ) {
00971       attach->setMimeTag( str );
00972     }
00973     attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() );
00974     if ( attach->name().isEmpty() ) {
00975       attach->setName( attach->fileName() );
00976     }
00977   }
00978 
00979   return true;
00980 }

KTNEF Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.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