• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.10.5 API Reference
  • KDE Home
  • Contact Us
 

KTNEF Library

  • ktnef
ktnefparser.cpp
Go to the documentation of this file.
1 /*
2  ktnefparser.cpp
3 
4  Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be>
5 
6  This file is part of KTNEF, the KDE TNEF support library/program.
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Library General Public License for more details.
17 
18  You should have received a copy of the GNU Library General Public License
19  along with this library; see the file COPYING.LIB. If not, write to
20  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  Boston, MA 02110-1301, USA.
22  */
31 #include "ktnefparser.h"
32 #include "ktnefattach.h"
33 #include "ktnefproperty.h"
34 #include "ktnefmessage.h"
35 #include "ktnefdefs.h"
36 
37 #include <kdebug.h>
38 #include <kmimetype.h>
39 #include <ksavefile.h>
40 
41 #include <QtCore/QDateTime>
42 #include <QtCore/QDataStream>
43 #include <QtCore/QFile>
44 #include <QtCore/QVariant>
45 #include <QtCore/QList>
46 
47 using namespace KTnef;
48 
49 //@cond PRIVATE
50 typedef struct {
51  quint16 type;
52  quint16 tag;
53  QVariant value;
54  struct {
55  quint32 type;
56  QVariant value;
57  } name;
58 } MAPI_value;
59 //@endcond
60 
61 //@cond IGNORE
62 void clearMAPIName( MAPI_value &mapi );
63 void clearMAPIValue( MAPI_value &mapi, bool clearName = true );
64 QString readMAPIString( QDataStream &stream, bool isUnicode = false,
65  bool align = true, int len = -1 );
66 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi );
67 QDateTime readTNEFDate( QDataStream &stream );
68 QString readTNEFAddress( QDataStream &stream );
69 QByteArray readTNEFData( QDataStream &stream, quint32 len );
70 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len );
71 QDateTime formatTime( quint32 lowB, quint32 highB );
72 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props );
73 //@endcond
74 
75 //------------------------------------------------------------------------------
76 
81 //@cond PRIVATE
82 class KTnef::KTNEFParser::ParserPrivate
83 {
84  public:
85  ParserPrivate()
86  {
87  defaultdir_ = "/tmp/";
88  current_ = 0;
89  deleteDevice_ = false;
90  device_ = 0;
91  message_ = new KTNEFMessage;
92  }
93  ~ParserPrivate()
94  {
95  delete message_;
96  }
97 
98  bool decodeAttachment();
99  bool decodeMessage();
100  bool extractAttachmentTo( KTNEFAttach *att, const QString &dirname );
101  void checkCurrent( int key );
102  bool readMAPIProperties( QMap<int,KTNEFProperty*>& props,
103  KTNEFAttach *attach = 0 );
104  bool parseDevice();
105  void deleteDevice();
106 
107  QDataStream stream_;
108  QIODevice *device_;
109  bool deleteDevice_;
110  QString defaultdir_;
111  KTNEFAttach *current_;
112  KTNEFMessage *message_;
113 };
114 //@endcond
115 
116 KTNEFParser::KTNEFParser()
117  : d( new ParserPrivate )
118 {
119 }
120 
121 KTNEFParser::~KTNEFParser()
122 {
123  d->deleteDevice();
124  delete d;
125 }
126 
127 KTNEFMessage *KTNEFParser::message() const
128 {
129  return d->message_;
130 }
131 
132 void KTNEFParser::ParserPrivate::deleteDevice()
133 {
134  if ( deleteDevice_ ) {
135  delete device_;
136  }
137  device_ = 0;
138  deleteDevice_ = false;
139 }
140 
141 bool KTNEFParser::ParserPrivate::decodeMessage()
142 {
143  quint32 i1, i2, off;
144  quint16 u, tag, type;
145  QVariant value;
146 
147  // read (type+name)
148  stream_ >> i1;
149  u = 0;
150  tag = ( i1 & 0x0000FFFF );
151  type = ( ( i1 & 0xFFFF0000 ) >> 16 );
152  // read data length
153  stream_ >> i2;
154  // offset after reading the value
155  off = device_->pos() + i2;
156  switch ( tag ) {
157  case attAIDOWNER:
158  {
159  uint tmp;
160  stream_ >> tmp;
161  value.setValue( tmp );
162  message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value );
163  kDebug() << "Message Owner Appointment ID" << "(length=" << i2 << ")";
164  break;
165  }
166  case attREQUESTRES:
167  stream_ >> u;
168  message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u );
169  value = ( bool )u;
170  kDebug() << "Message Request Response" << "(length=" << i2 << ")";
171  break;
172  case attDATERECD:
173  value = readTNEFDate( stream_ );
174  message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value );
175  kDebug() << "Message Receive Date" << "(length=" << i2 << ")";
176  break;
177  case attMSGCLASS:
178  value = readMAPIString( stream_, false, false, i2 );
179  message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value );
180  kDebug() << "Message Class" << "(length=" << i2 << ")";
181  break;
182  case attMSGPRIORITY:
183  stream_ >> u;
184  message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u );
185  value = u;
186  kDebug() << "Message Priority" << "(length=" << i2 << ")";
187  break;
188  case attMAPIPROPS:
189  kDebug() << "Message MAPI Properties" << "(length=" << i2 << ")";
190  {
191  int nProps = message_->properties().count();
192  i2 += device_->pos();
193  readMAPIProperties( message_->properties(), 0 );
194  device_->seek( i2 );
195  kDebug() << "Properties:" << message_->properties().count();
196  value = QString( "< %1 properties >" ).
197  arg( message_->properties().count() - nProps );
198  }
199  break;
200  case attTNEFVERSION:
201  {
202  uint tmp;
203  stream_ >> tmp;
204  value.setValue( tmp );
205  kDebug() << "Message TNEF Version" << "(length=" << i2 << ")";
206  }
207  break;
208  case attFROM:
209  message_->addProperty( 0x0024, MAPI_TYPE_STRING8, readTNEFAddress( stream_ ) );
210  device_->seek( device_->pos() - i2 );
211  value = readTNEFData( stream_, i2 );
212  kDebug() << "Message From" << "(length=" << i2 << ")";
213  break;
214  case attSUBJECT:
215  value = readMAPIString( stream_, false, false, i2 );
216  message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value );
217  kDebug() << "Message Subject" << "(length=" << i2 << ")";
218  break;
219  case attDATESENT:
220  value = readTNEFDate( stream_ );
221  message_->addProperty( 0x0039, MAPI_TYPE_TIME, value );
222  kDebug() << "Message Date Sent" << "(length=" << i2 << ")";
223  break;
224  case attMSGSTATUS:
225  {
226  quint8 c;
227  quint32 flag = 0;
228  stream_ >> c;
229  if ( c & fmsRead ) {
230  flag |= MSGFLAG_READ;
231  }
232  if ( !( c & fmsModified ) ) {
233  flag |= MSGFLAG_UNMODIFIED;
234  }
235  if ( c & fmsSubmitted ) {
236  flag |= MSGFLAG_SUBMIT;
237  }
238  if ( c & fmsHasAttach ) {
239  flag |= MSGFLAG_HASATTACH;
240  }
241  if ( c & fmsLocal ) {
242  flag |= MSGFLAG_UNSENT;
243  }
244  message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
245  value = c;
246  }
247  kDebug() << "Message Status" << "(length=" << i2 << ")";
248  break;
249  case attRECIPTABLE:
250  {
251  quint32 rows;
252  QList<QVariant> recipTable;
253  stream_ >> rows;
254  for ( uint i=0; i<rows; i++ ) {
255  QMap<int,KTNEFProperty*> props;
256  readMAPIProperties( props, 0 );
257  recipTable << formatRecipient( props );
258  }
259  message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable );
260  device_->seek( device_->pos() - i2 );
261  value = readTNEFData( stream_, i2 );
262  }
263  kDebug() << "Message Recipient Table" << "(length=" << i2 << ")";
264  break;
265  case attBODY:
266  value = readMAPIString( stream_, false, false, i2 );
267  message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value );
268  kDebug() << "Message Body" << "(length=" << i2 << ")";
269  break;
270  case attDATEMODIFIED:
271  value = readTNEFDate( stream_ );
272  message_->addProperty( 0x3008, MAPI_TYPE_TIME, value );
273  kDebug() << "Message Date Modified" << "(length=" << i2 << ")";
274  break;
275  case attMSGID:
276  value = readMAPIString( stream_, false, false, i2 );
277  message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value );
278  kDebug() << "Message ID" << "(length=" << i2 << ")";
279  break;
280  case attOEMCODEPAGE:
281  value = readTNEFData( stream_, i2 );
282  kDebug() << "Message OEM Code Page" << "(length=" << i2 << ")";
283  break;
284  default:
285  value = readTNEFAttribute( stream_, type, i2 );
286  //kDebug().form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u );
287  break;
288  }
289  // skip data
290  if ( device_->pos() != off && !device_->seek( off ) ) {
291  return false;
292  }
293  // get checksum
294  stream_ >> u;
295  // add TNEF attribute
296  message_->addAttribute( tag, type, value, true );
297  //kDebug() << "stream:" << device_->pos();
298  return true;
299 }
300 
301 bool KTNEFParser::ParserPrivate::decodeAttachment()
302 {
303  quint32 i;
304  quint16 tag, type, u;
305  QVariant value;
306  QString str;
307 
308  stream_ >> i; // i <- attribute type & name
309  tag = ( i & 0x0000FFFF );
310  type = ( ( i & 0xFFFF0000 ) >> 16 );
311  stream_ >> i; // i <- data length
312  checkCurrent( tag );
313  switch ( tag ) {
314  case attATTACHTITLE:
315  value = readMAPIString( stream_, false, false, i );
316  current_->setName( value.toString() );
317  kDebug() << "Attachment Title:" << current_->name();
318  break;
319  case attATTACHDATA:
320  current_->setSize( i );
321  current_->setOffset( device_->pos() );
322  device_->seek( device_->pos() + i );
323  value = QString( "< size=%1 >" ).arg( i );
324  kDebug() << "Attachment Data: size=" << i;
325  break;
326  case attATTACHMENT: // try to get attachment info
327  i += device_->pos();
328  readMAPIProperties( current_->properties(), current_ );
329  device_->seek( i );
330  current_->setIndex( current_->property( MAPI_TAG_INDEX ).toUInt() );
331  current_->setDisplaySize( current_->property( MAPI_TAG_SIZE ).toUInt() );
332  str = current_->property( MAPI_TAG_DISPLAYNAME ).toString();
333  if ( !str.isEmpty() ) {
334  current_->setDisplayName( str );
335  }
336  current_->setFileName( current_->property( MAPI_TAG_FILENAME ).
337  toString() );
338  str = current_->property( MAPI_TAG_MIMETAG ).toString();
339  if ( !str.isEmpty() ) {
340  current_->setMimeTag( str );
341  }
342  current_->setExtension( current_->property( MAPI_TAG_EXTENSION ).
343  toString() );
344  value = QString( "< %1 properties >" ).
345  arg( current_->properties().count() );
346  break;
347  case attATTACHMODDATE:
348  value = readTNEFDate( stream_ );
349  kDebug() << "Attachment Modification Date:" << value.toString();
350  break;
351  case attATTACHCREATEDATE:
352  value = readTNEFDate( stream_ );
353  kDebug() << "Attachment Creation Date:" << value.toString();
354  break;
355  case attATTACHMETAFILE:
356  kDebug() << "Attachment Metafile: size=" << i;
357  //value = QString( "< size=%1 >" ).arg( i );
358  //device_->seek( device_->pos()+i );
359  value = readTNEFData( stream_, i );
360  break;
361  default:
362  value = readTNEFAttribute( stream_, type, i );
363  kDebug() << "Attachment unknown field: tag="
364  << hex << tag << ", length=" << dec << i;
365  break;
366  }
367  stream_ >> u; // u <- checksum
368  // add TNEF attribute
369  current_->addAttribute( tag, type, value, true );
370  //kDebug() << "stream:" << device_->pos();
371 
372  return true;
373 }
374 
375 void KTNEFParser::setDefaultExtractDir( const QString &dirname )
376 {
377  d->defaultdir_ = dirname;
378 }
379 
380 bool KTNEFParser::ParserPrivate::parseDevice()
381 {
382  quint16 u;
383  quint32 i;
384  quint8 c;
385 
386  message_->clearAttachments();
387  delete current_;
388  current_ = 0;
389 
390  if ( !device_->open( QIODevice::ReadOnly ) ) {
391  kDebug() << "Couldn't open device";
392  return false;
393  }
394 
395  stream_.setDevice( device_ );
396  stream_.setByteOrder( QDataStream::LittleEndian );
397  stream_ >> i;
398  if ( i == TNEF_SIGNATURE ) {
399  stream_ >> u;
400  kDebug().nospace() << "Attachment cross reference key: 0x"
401  << hex << qSetFieldWidth( 4 ) << qSetPadChar( '0' ) << u;
402  //kDebug() << "stream:" << device_->pos();
403  while ( !stream_.atEnd() ) {
404  stream_ >> c;
405  switch( c ) {
406  case LVL_MESSAGE:
407  if ( !decodeMessage() ) {
408  goto end;
409  }
410  break;
411  case LVL_ATTACHMENT:
412  if ( !decodeAttachment() ) {
413  goto end;
414  }
415  break;
416  default:
417  kDebug() << "Unknown Level:" << c << ", at offset" << device_->pos();
418  goto end;
419  }
420  }
421  if ( current_ ) {
422  checkCurrent( attATTACHDATA ); // this line has the effect to append the
423  // attachment, if it has data. If not it does
424  // nothing, and the attachment will be discarded
425  delete current_;
426  current_ = 0;
427  }
428  return true;
429  } else {
430  kDebug() << "This is not a TNEF file";
431  end:
432  device_->close();
433  return false;
434  }
435 }
436 
437 bool KTNEFParser::extractFile( const QString &filename ) const
438 {
439  KTNEFAttach *att = d->message_->attachment( filename );
440  if ( !att ) {
441  return false;
442  }
443  return d->extractAttachmentTo( att, d->defaultdir_ );
444 }
445 
446 bool KTNEFParser::ParserPrivate::extractAttachmentTo( KTNEFAttach *att,
447  const QString &dirname )
448 {
449  QString filename = dirname + '/' + att->name();
450  if ( !device_->isOpen() ) {
451  return false;
452  }
453  if ( !device_->seek( att->offset() ) ) {
454  return false;
455  }
456  KSaveFile outfile( filename );
457  if ( !outfile.open() ) {
458  return false;
459  }
460 
461  quint32 len = att->size(), sz( 16384 );
462  int n( 0 );
463  char *buf = new char[sz];
464  bool ok( true );
465  while ( ok && len > 0 ) {
466  n = device_->read( buf, qMin( sz, len ) );
467  if ( n < 0 ) {
468  ok = false;
469  } else {
470  len -= n;
471  if ( outfile.write( buf, n ) != n ) {
472  ok = false;
473  }
474  }
475  }
476  delete [] buf;
477 
478  return ok;
479 }
480 
481 bool KTNEFParser::extractAll()
482 {
483  QList<KTNEFAttach*> l = d->message_->attachmentList();
484  QList<KTNEFAttach*>::const_iterator it = l.constBegin();
485  for ( ; it != l.constEnd(); ++it ) {
486  if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
487  return false;
488  }
489  }
490  return true;
491 }
492 
493 bool KTNEFParser::extractFileTo( const QString &filename,
494  const QString &dirname ) const
495 {
496  kDebug() << "Extracting attachment: filename="
497  << filename << ", dir=" << dirname;
498  KTNEFAttach *att = d->message_->attachment( filename );
499  if ( !att ) {
500  return false;
501  }
502  return d->extractAttachmentTo( att, dirname );
503 }
504 
505 bool KTNEFParser::openFile( const QString &filename ) const
506 {
507  d->deleteDevice();
508  delete d->message_;
509  d->message_ = new KTNEFMessage();
510  d->device_ = new QFile( filename );
511  d->deleteDevice_ = true;
512  return d->parseDevice();
513 }
514 
515 bool KTNEFParser::openDevice( QIODevice *device )
516 {
517  d->deleteDevice();
518  d->device_ = device;
519  return d->parseDevice();
520 }
521 
522 void KTNEFParser::ParserPrivate::checkCurrent( int key )
523 {
524  if ( !current_ ) {
525  current_ = new KTNEFAttach();
526  } else {
527  if ( current_->attributes().contains( key ) ) {
528  if ( current_->offset() >= 0 ) {
529  if ( current_->name().isEmpty() ) {
530  current_->setName( "Unnamed" );
531  }
532  if ( current_->mimeTag().isEmpty() ) {
533  // No mime type defined in the TNEF structure,
534  // try to find it from the attachment filename
535  // and/or content (using at most 32 bytes)
536  KMimeType::Ptr mimetype;
537  if ( !current_->fileName().isEmpty() ) {
538  mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
539  }
540  if ( !mimetype ) {
541  return; // FIXME
542  }
543  if ( mimetype->name() == "application/octet-stream" &&
544  current_->size() > 0 ) {
545  int oldOffset = device_->pos();
546  QByteArray buffer( qMin( 32, current_->size() ), '\0' );
547  device_->seek( current_->offset() );
548  device_->read( buffer.data(), buffer.size() );
549  mimetype = KMimeType::findByContent( buffer );
550  device_->seek( oldOffset );
551  }
552  current_->setMimeTag( mimetype->name() );
553  }
554  message_->addAttachment( current_ );
555  current_ = 0;
556  } else {
557  // invalid attachment, skip it
558  delete current_;
559  current_ = 0;
560  }
561  current_ = new KTNEFAttach();
562  }
563  }
564 }
565 
566 //------------------------------------------------------------------------------
567 
568 //@cond IGNORE
569 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); }
570 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR )
571 
572 void clearMAPIName( MAPI_value &mapi )
573 {
574  mapi.name.value.clear();
575 }
576 
577 void clearMAPIValue( MAPI_value &mapi, bool clearName )
578 {
579  mapi.value.clear();
580  if ( clearName ) {
581  clearMAPIName( mapi );
582  }
583 }
584 
585 QDateTime formatTime( quint32 lowB, quint32 highB )
586 {
587  QDateTime dt;
588  quint64 u64;
589  u64 = highB;
590  u64 <<= 32;
591  u64 |= lowB;
592  u64 -= 116444736000000000LL;
593  u64 /= 10000000;
594  if ( u64 <= 0xffffffffU ) {
595  dt.setTime_t( ( unsigned int )u64 );
596  } else {
597  kWarning().nospace() << "Invalid date: low byte="
598  << showbase << qSetFieldWidth( 8 ) << qSetPadChar( '0' )
599  << lowB << ", high byte=" << highB;
600  dt.setTime_t( 0xffffffffU );
601  }
602  return dt;
603 }
604 
605 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props )
606 {
607  QString s, dn, addr, t;
608  QMap<int,KTnef::KTNEFProperty*>::ConstIterator it;
609  if ( ( it = props.find( 0x3001 ) ) != props.end() ) {
610  dn = ( *it )->valueString();
611  }
612  if ( ( it = props.find( 0x3003 ) ) != props.end() ) {
613  addr = ( *it )->valueString();
614  }
615  if ( ( it = props.find( 0x0C15 ) ) != props.end() ) {
616  switch ( ( *it )->value().toInt() ) {
617  case 0:
618  t = "From:";
619  break;
620  case 1:
621  t = "To:";
622  break;
623  case 2:
624  t = "Cc:";
625  break;
626  case 3:
627  t = "Bcc:";
628  break;
629  }
630  }
631  if ( !t.isEmpty() ) {
632  s.append( t );
633  }
634  if ( !dn.isEmpty() ) {
635  s.append( ' ' + dn );
636  }
637  if ( !addr.isEmpty() && addr != dn ) {
638  s.append( " <" + addr + '>' );
639  }
640 
641  return s.trimmed();
642 }
643 
644 QDateTime readTNEFDate( QDataStream &stream )
645 {
646  // 14-bytes long
647  quint16 y, m, d, hh, mm, ss, dm;
648  stream >> y >> m >> d >> hh >> mm >> ss >> dm;
649  return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) );
650 }
651 
652 QString readTNEFAddress( QDataStream &stream )
653 {
654  quint16 totalLen, strLen, addrLen;
655  QString s;
656  stream >> totalLen >> totalLen >> strLen >> addrLen;
657  s.append( readMAPIString( stream, false, false, strLen ) );
658  s.append( " <" );
659  s.append( readMAPIString( stream, false, false, addrLen ) );
660  s.append( ">" );
661  quint8 c;
662  for ( int i=8+strLen+addrLen; i<totalLen; i++ ) {
663  stream >> c;
664  }
665  return s;
666 }
667 
668 QByteArray readTNEFData( QDataStream &stream, quint32 len )
669 {
670  QByteArray array( len, '\0' );
671  if ( len > 0 ) {
672  stream.readRawData( array.data(), len );
673  }
674  return array;
675 }
676 
677 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len )
678 {
679  switch ( type ) {
680  case atpTEXT:
681  case atpSTRING:
682  return readMAPIString( stream, false, false, len );
683  case atpDATE:
684  return readTNEFDate( stream );
685  default:
686  return readTNEFData( stream, len );
687  }
688 }
689 
690 QString readMAPIString( QDataStream &stream, bool isUnicode, bool align,
691  int len_ )
692 {
693  quint32 len;
694  char *buf = 0;
695  if ( len_ == -1 ) {
696  stream >> len;
697  } else {
698  len = len_;
699  }
700  quint32 fullLen = len;
701  if ( align ) {
702  ALIGN( fullLen, 4 );
703  }
704  buf = new char[ len ];
705  stream.readRawData( buf, len );
706  quint8 c;
707  for ( uint i=len; i<fullLen; i++ ) {
708  stream >> c;
709  }
710  QString res;
711  if ( isUnicode ) {
712  res = QString::fromUtf16( ( const unsigned short *)buf );
713  } else {
714  res = QString::fromLocal8Bit( buf );
715  }
716  delete [] buf;
717  return res;
718 }
719 
720 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi )
721 {
722  quint32 d;
723 
724  clearMAPIValue( mapi );
725  stream >> d;
726  mapi.type = ( d & 0x0000FFFF );
727  mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 );
728  if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
729  // skip GUID
730  stream >> d >> d >> d >> d;
731  // name type
732  stream >> mapi.name.type;
733  // name
734  if ( mapi.name.type == 0 ) {
735  uint tmp;
736  stream >> tmp;
737  mapi.name.value.setValue( tmp );
738  } else if ( mapi.name.type == 1 ) {
739  mapi.name.value.setValue( readMAPIString( stream, true ) );
740  }
741  }
742 
743  int n = 1;
744  QVariant value;
745  if ( ISVECTOR( mapi ) ) {
746  stream >> n;
747  mapi.value = QList<QVariant>();
748  }
749  for ( int i=0; i<n; i++ ) {
750  value.clear();
751  switch( mapi.type & 0x0FFF ) {
752  case MAPI_TYPE_UINT16:
753  stream >> d;
754  value.setValue( d & 0x0000FFFF );
755  break;
756  case MAPI_TYPE_BOOLEAN:
757  case MAPI_TYPE_ULONG:
758  {
759  uint tmp;
760  stream >> tmp;
761  value.setValue( tmp );
762  }
763  break;
764  case MAPI_TYPE_FLOAT:
765  // FIXME: Don't we have to set the value here
766  stream >> d;
767  break;
768  case MAPI_TYPE_DOUBLE:
769  {
770  double tmp;
771  stream >> tmp;
772  value.setValue( tmp );
773  }
774  break;
775  case MAPI_TYPE_TIME:
776  {
777  quint32 lowB, highB;
778  stream >> lowB >> highB;
779  value = formatTime( lowB, highB );
780  }
781  break;
782  case MAPI_TYPE_USTRING:
783  case MAPI_TYPE_STRING8:
784  // in case of a vector'ed value, the number of elements
785  // has already been read in the upper for-loop
786  if ( ISVECTOR( mapi ) ) {
787  d = 1;
788  } else {
789  stream >> d;
790  }
791  for ( uint i=0; i<d; i++ ) {
792  value.clear();
793  value.setValue( readMAPIString( stream,( mapi.type & 0x0FFF ) == MAPI_TYPE_USTRING ) );
794  }
795  break;
796  case MAPI_TYPE_OBJECT:
797  case MAPI_TYPE_BINARY:
798  if ( ISVECTOR( mapi ) ) {
799  d = 1;
800  } else {
801  stream >> d;
802  }
803  for ( uint i=0; i<d; i++ ) {
804  value.clear();
805  quint32 len;
806  stream >> len;
807  value = QByteArray( len, '\0' );
808  if ( len > 0 ) {
809  int fullLen = len;
810  ALIGN( fullLen, 4 );
811  stream.readRawData( value.toByteArray().data(), len );
812  quint8 c;
813  for ( int i=len; i<fullLen; i++ ) {
814  stream >> c;
815  }
816  // FIXME: Shouldn't we do something with the value???
817  }
818  }
819  break;
820  default:
821  mapi.type = MAPI_TYPE_NONE;
822  break;
823  }
824  if ( ISVECTOR( mapi ) ) {
825  QList <QVariant> lst = mapi.value.toList();
826  lst << value;
827  mapi.value.setValue( lst );
828  } else {
829  mapi.value = value;
830  }
831  }
832  return mapi.tag;
833 }
834 //@endcond
835 
836 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
837  KTNEFAttach *attach )
838 {
839  quint32 n;
840  MAPI_value mapi;
841  KTNEFProperty *p;
842  QMap<int,KTNEFProperty*>::ConstIterator it;
843  bool foundAttachment = false;
844 
845  // some initializations
846  mapi.type = MAPI_TYPE_NONE;
847  mapi.value.clear();
848 
849  // get number of properties
850  stream_ >> n;
851  kDebug() << "MAPI Properties:" << n;
852  for ( uint i=0; i<n; i++ ) {
853  if ( stream_.atEnd() ) {
854  clearMAPIValue( mapi );
855  return false;
856  }
857  readMAPIValue( stream_, mapi );
858  if ( mapi.type == MAPI_TYPE_NONE ) {
859  kDebug().nospace() << "MAPI unsupported: tag="
860  << hex << mapi.tag << ", type=" << mapi.type;
861  clearMAPIValue( mapi );
862  return false;
863  }
864  int key = mapi.tag;
865  switch ( mapi.tag ) {
866  case MAPI_TAG_DATA:
867  {
868  if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
869  QByteArray data = mapi.value.toByteArray();
870  int len = data.size();
871  ALIGN( len, 4 );
872  device_->seek( device_->pos()-len );
873  quint32 interface_ID;
874  stream_ >> interface_ID;
875  if ( interface_ID == MAPI_IID_IMessage ) {
876  // embedded TNEF file
877  attach->unsetDataParser();
878  attach->setOffset( device_->pos()+12 );
879  attach->setSize( data.size()-16 );
880  attach->setMimeTag( "application/vnd.ms-tnef" );
881  attach->setDisplayName( "Embedded Message" );
882  kDebug() << "MAPI Embedded Message: size=" << data.size();
883  }
884  device_->seek( device_->pos() + ( len-4 ) );
885  break;
886  } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) {
887  foundAttachment = true;
888  int len = mapi.value.toByteArray().size();
889  ALIGN( len, 4 );
890  attach->setSize( len );
891  attach->setOffset( device_->pos() - len );
892  attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
893  }
894  }
895  kDebug() << "MAPI data: size=" << mapi.value.toByteArray().size();
896  break;
897  default:
898  {
899  QString mapiname = "";
900  if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
901  if ( mapi.name.type == 0 ) {
902  mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
903  } else {
904  mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() );
905  }
906  }
907  switch ( mapi.type & 0x0FFF ) {
908  case MAPI_TYPE_UINT16:
909  kDebug().nospace() << "(tag="
910  << hex << mapi.tag
911  << ") MAPI short" << mapiname.toLatin1().data()
912  << ":" << hex << mapi.value.toUInt();
913  break;
914  case MAPI_TYPE_ULONG:
915  kDebug().nospace() << "(tag="
916  << hex << mapi.tag
917  << ") MAPI long" << mapiname.toLatin1().data()
918  << ":" << hex << mapi.value.toUInt();
919  break;
920  case MAPI_TYPE_BOOLEAN:
921  kDebug().nospace() << "(tag="
922  << hex << mapi.tag
923  << ") MAPI boolean" << mapiname.toLatin1().data()
924  << ":" << mapi.value.toBool();
925  break;
926  case MAPI_TYPE_TIME:
927  kDebug().nospace() << "(tag="
928  << hex << mapi.tag
929  << ") MAPI time" << mapiname.toLatin1().data()
930  << ":" << mapi.value.toString().toLatin1().data();
931  break;
932  case MAPI_TYPE_USTRING:
933  case MAPI_TYPE_STRING8:
934  kDebug().nospace() << "(tag="
935  << hex << mapi.tag
936  << ") MAPI string" << mapiname.toLatin1().data()
937  << ":size=" << mapi.value.toByteArray().size()
938  << mapi.value.toString();
939  break;
940  case MAPI_TYPE_BINARY:
941  kDebug().nospace() << "(tag="
942  << hex << mapi.tag
943  << ") MAPI binary" << mapiname.toLatin1().data()
944  << ":size=" << mapi.value.toByteArray().size();
945  break;
946  }
947  }
948  break;
949  }
950  // do not remove potential existing similar entry
951  if ( ( it = props.constFind( key ) ) == props.constEnd() ) {
952  p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
953  mapi.value, mapi.name.value );
954  props[ p->key() ] = p;
955  }
956  //kDebug() << "stream:" << device_->pos();
957  }
958 
959  if ( foundAttachment && attach ) {
960  attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
961  attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
962  QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString();
963  if ( !str.isEmpty() ) {
964  attach->setDisplayName( str );
965  }
966  attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() );
967  str = attach->property( MAPI_TAG_MIMETAG ).toString();
968  if ( !str.isEmpty() ) {
969  attach->setMimeTag( str );
970  }
971  attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() );
972  if ( attach->name().isEmpty() ) {
973  attach->setName( attach->fileName() );
974  }
975  }
976 
977  return true;
978 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:29:58 by doxygen 1.8.3.1 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.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • 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