• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.8.3 API Reference
  • 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_USTRING:
00783     case MAPI_TYPE_STRING8:
00784       // in case of a vector'ed value, the number of elements
00785       // has already been read in the upper for-loop
00786       if ( ISVECTOR( mapi ) ) {
00787         d = 1;
00788       } else {
00789         stream >> d;
00790       }
00791       for ( uint i=0; i<d; i++ ) {
00792         value.clear();
00793         value.setValue( readMAPIString( stream,( mapi.type & 0x0FFF ) == MAPI_TYPE_USTRING ) );
00794       }
00795       break;
00796     case MAPI_TYPE_OBJECT:
00797     case MAPI_TYPE_BINARY:
00798       if ( ISVECTOR( mapi ) ) {
00799         d = 1;
00800       } else {
00801         stream >> d;
00802       }
00803       for ( uint i=0; i<d; i++ ) {
00804         value.clear();
00805         quint32 len;
00806         stream >> len;
00807         value = QByteArray( len, '\0' );
00808         if ( len > 0 ) {
00809           int fullLen = len;
00810           ALIGN( fullLen, 4 );
00811           stream.readRawData( value.toByteArray().data(), len );
00812           quint8 c;
00813           for ( int i=len; i<fullLen; i++ ) {
00814             stream >> c;
00815           }
00816           // FIXME: Shouldn't we do something with the value???
00817         }
00818       }
00819       break;
00820     default:
00821       mapi.type = MAPI_TYPE_NONE;
00822       break;
00823     }
00824     if ( ISVECTOR( mapi ) ) {
00825       QList <QVariant> lst = mapi.value.toList();
00826       lst << value;
00827       mapi.value.setValue( lst );
00828     } else {
00829       mapi.value = value;
00830     }
00831   }
00832   return mapi.tag;
00833 }
00834 //@endcond
00835 
00836 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
00837                                                      KTNEFAttach *attach )
00838 {
00839   quint32       n;
00840   MAPI_value    mapi;
00841   KTNEFProperty *p;
00842   QMap<int,KTNEFProperty*>::ConstIterator it;
00843   bool foundAttachment = false;
00844 
00845   // some initializations
00846   mapi.type = MAPI_TYPE_NONE;
00847   mapi.value.clear();
00848 
00849   // get number of properties
00850   stream_ >> n;
00851   kDebug() << "MAPI Properties:" << n;
00852   for ( uint i=0; i<n; i++ ) {
00853     if ( stream_.atEnd() ) {
00854       clearMAPIValue( mapi );
00855       return false;
00856     }
00857     readMAPIValue( stream_, mapi );
00858     if ( mapi.type == MAPI_TYPE_NONE ) {
00859       kDebug().nospace() << "MAPI unsupported:         tag="
00860                          << hex << mapi.tag << ", type=" << mapi.type;
00861       clearMAPIValue( mapi );
00862       return false;
00863     }
00864     int key = mapi.tag;
00865     switch ( mapi.tag ) {
00866     case MAPI_TAG_DATA:
00867     {
00868       if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
00869         QByteArray data = mapi.value.toByteArray();
00870         int len = data.size();
00871         ALIGN( len, 4 );
00872         device_->seek( device_->pos()-len );
00873         quint32 interface_ID;
00874         stream_ >> interface_ID;
00875         if ( interface_ID == MAPI_IID_IMessage ) {
00876           // embedded TNEF file
00877           attach->unsetDataParser();
00878           attach->setOffset( device_->pos()+12 );
00879           attach->setSize( data.size()-16 );
00880           attach->setMimeTag( "application/vnd.ms-tnef" );
00881           attach->setDisplayName( "Embedded Message" );
00882           kDebug() << "MAPI Embedded Message: size=" << data.size();
00883         }
00884         device_->seek( device_->pos() + ( len-4 ) );
00885         break;
00886       } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) {
00887         foundAttachment = true;
00888         int len = mapi.value.toByteArray().size();
00889         ALIGN( len, 4 );
00890         attach->setSize( len );
00891         attach->setOffset( device_->pos() - len );
00892         attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
00893       }
00894     }
00895     kDebug() << "MAPI data: size=" << mapi.value.toByteArray().size();
00896     break;
00897     default:
00898     {
00899       QString mapiname = "";
00900       if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
00901         if ( mapi.name.type == 0 ) {
00902           mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
00903         } else {
00904           mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() );
00905         }
00906       }
00907       switch ( mapi.type & 0x0FFF ) {
00908       case MAPI_TYPE_UINT16:
00909         kDebug().nospace() << "(tag="
00910                            << hex << mapi.tag
00911                            << ") MAPI short" <<  mapiname.toAscii().data()
00912                            << ":" << hex << mapi.value.toUInt();
00913         break;
00914       case MAPI_TYPE_ULONG:
00915         kDebug().nospace() << "(tag="
00916                            << hex << mapi.tag
00917                            << ") MAPI long" <<  mapiname.toAscii().data()
00918                            << ":" << hex << mapi.value.toUInt();
00919         break;
00920       case MAPI_TYPE_BOOLEAN:
00921         kDebug().nospace() << "(tag="
00922                            << hex << mapi.tag
00923                            << ") MAPI boolean" <<  mapiname.toAscii().data()
00924                            << ":" << mapi.value.toBool();
00925         break;
00926       case MAPI_TYPE_TIME:
00927         kDebug().nospace() << "(tag="
00928                            << hex << mapi.tag
00929                            << ") MAPI time" <<  mapiname.toAscii().data()
00930                            << ":" << mapi.value.toString().toAscii().data();
00931         break;
00932       case MAPI_TYPE_USTRING:
00933       case MAPI_TYPE_STRING8:
00934         kDebug().nospace() << "(tag="
00935                            << hex << mapi.tag
00936                            << ") MAPI string" <<  mapiname.toAscii().data()
00937                            << ":size=" << mapi.value.toByteArray().size()
00938                            << mapi.value.toString();
00939         break;
00940       case MAPI_TYPE_BINARY:
00941         kDebug().nospace() << "(tag="
00942                            << hex << mapi.tag
00943                            << ") MAPI binary" <<  mapiname.toAscii().data()
00944                            << ":size=" << mapi.value.toByteArray().size();
00945         break;
00946       }
00947     }
00948     break;
00949     }
00950     // do not remove potential existing similar entry
00951     if ( ( it = props.constFind( key ) ) == props.constEnd() ) {
00952       p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
00953                              mapi.value, mapi.name.value );
00954       props[ p->key() ] = p;
00955     }
00956     //kDebug() << "stream:" << device_->pos();
00957   }
00958 
00959   if ( foundAttachment && attach ) {
00960     attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
00961     attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
00962     QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString();
00963     if ( !str.isEmpty() ) {
00964       attach->setDisplayName( str );
00965     }
00966     attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() );
00967     str = attach->property( MAPI_TAG_MIMETAG ).toString();
00968     if ( !str.isEmpty() ) {
00969       attach->setMimeTag( str );
00970     }
00971     attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() );
00972     if ( attach->name().isEmpty() ) {
00973       attach->setName( attach->fileName() );
00974     }
00975   }
00976 
00977   return true;
00978 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 22:20:37 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

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

kdepimlibs-4.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal