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

KTNEF Library

  • sources
  • kde-4.14
  • kdepimlibs
  • 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 + '/';
450  if ( !att->fileName().isEmpty()) {
451  filename += att->fileName();
452  } else {
453  filename += att->name();
454  }
455  if ( filename.endsWith( '/') ) {
456  return false;
457  }
458 
459  if ( !device_->isOpen() ) {
460  return false;
461  }
462  if ( !device_->seek( att->offset() ) ) {
463  return false;
464  }
465  KSaveFile outfile( filename );
466  if ( !outfile.open() ) {
467  return false;
468  }
469 
470  quint32 len = att->size(), sz( 16384 );
471  int n( 0 );
472  char *buf = new char[sz];
473  bool ok( true );
474  while ( ok && len > 0 ) {
475  n = device_->read( buf, qMin( sz, len ) );
476  if ( n < 0 ) {
477  ok = false;
478  } else {
479  len -= n;
480  if ( outfile.write( buf, n ) != n ) {
481  ok = false;
482  }
483  }
484  }
485  delete [] buf;
486 
487  return ok;
488 }
489 
490 bool KTNEFParser::extractAll()
491 {
492  QList<KTNEFAttach*> l = d->message_->attachmentList();
493  QList<KTNEFAttach*>::const_iterator it = l.constBegin();
494  for ( ; it != l.constEnd(); ++it ) {
495  if ( !d->extractAttachmentTo( *it, d->defaultdir_ ) ) {
496  return false;
497  }
498  }
499  return true;
500 }
501 
502 bool KTNEFParser::extractFileTo( const QString &filename,
503  const QString &dirname ) const
504 {
505  kDebug() << "Extracting attachment: filename="
506  << filename << ", dir=" << dirname;
507  KTNEFAttach *att = d->message_->attachment( filename );
508  if ( !att ) {
509  return false;
510  }
511  return d->extractAttachmentTo( att, dirname );
512 }
513 
514 bool KTNEFParser::openFile( const QString &filename ) const
515 {
516  d->deleteDevice();
517  delete d->message_;
518  d->message_ = new KTNEFMessage();
519  d->device_ = new QFile( filename );
520  d->deleteDevice_ = true;
521  return d->parseDevice();
522 }
523 
524 bool KTNEFParser::openDevice( QIODevice *device )
525 {
526  d->deleteDevice();
527  d->device_ = device;
528  return d->parseDevice();
529 }
530 
531 void KTNEFParser::ParserPrivate::checkCurrent( int key )
532 {
533  if ( !current_ ) {
534  current_ = new KTNEFAttach();
535  } else {
536  if ( current_->attributes().contains( key ) ) {
537  if ( current_->offset() >= 0 ) {
538  if ( current_->name().isEmpty() ) {
539  current_->setName( "Unnamed" );
540  }
541  if ( current_->mimeTag().isEmpty() ) {
542  // No mime type defined in the TNEF structure,
543  // try to find it from the attachment filename
544  // and/or content (using at most 32 bytes)
545  KMimeType::Ptr mimetype;
546  if ( !current_->fileName().isEmpty() ) {
547  mimetype = KMimeType::findByPath( current_->fileName(), 0, true );
548  }
549  if ( !mimetype ) {
550  return; // FIXME
551  }
552  if ( mimetype->name() == "application/octet-stream" &&
553  current_->size() > 0 ) {
554  int oldOffset = device_->pos();
555  QByteArray buffer( qMin( 32, current_->size() ), '\0' );
556  device_->seek( current_->offset() );
557  device_->read( buffer.data(), buffer.size() );
558  mimetype = KMimeType::findByContent( buffer );
559  device_->seek( oldOffset );
560  }
561  current_->setMimeTag( mimetype->name() );
562  }
563  message_->addAttachment( current_ );
564  current_ = 0;
565  } else {
566  // invalid attachment, skip it
567  delete current_;
568  current_ = 0;
569  }
570  current_ = new KTNEFAttach();
571  }
572  }
573 }
574 
575 //------------------------------------------------------------------------------
576 
577 //@cond IGNORE
578 #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); }
579 #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR )
580 
581 void clearMAPIName( MAPI_value &mapi )
582 {
583  mapi.name.value.clear();
584 }
585 
586 void clearMAPIValue( MAPI_value &mapi, bool clearName )
587 {
588  mapi.value.clear();
589  if ( clearName ) {
590  clearMAPIName( mapi );
591  }
592 }
593 
594 QDateTime formatTime( quint32 lowB, quint32 highB )
595 {
596  QDateTime dt;
597  quint64 u64;
598  u64 = highB;
599  u64 <<= 32;
600  u64 |= lowB;
601  u64 -= 116444736000000000LL;
602  u64 /= 10000000;
603  if ( u64 <= 0xffffffffU ) {
604  dt.setTime_t( ( unsigned int )u64 );
605  } else {
606  kWarning().nospace() << "Invalid date: low byte="
607  << showbase << qSetFieldWidth( 8 ) << qSetPadChar( '0' )
608  << lowB << ", high byte=" << highB;
609  dt.setTime_t( 0xffffffffU );
610  }
611  return dt;
612 }
613 
614 QString formatRecipient( const QMap<int,KTnef::KTNEFProperty*> &props )
615 {
616  QString s, dn, addr, t;
617  QMap<int,KTnef::KTNEFProperty*>::ConstIterator it;
618  if ( ( it = props.find( 0x3001 ) ) != props.end() ) {
619  dn = ( *it )->valueString();
620  }
621  if ( ( it = props.find( 0x3003 ) ) != props.end() ) {
622  addr = ( *it )->valueString();
623  }
624  if ( ( it = props.find( 0x0C15 ) ) != props.end() ) {
625  switch ( ( *it )->value().toInt() ) {
626  case 0:
627  t = "From:";
628  break;
629  case 1:
630  t = "To:";
631  break;
632  case 2:
633  t = "Cc:";
634  break;
635  case 3:
636  t = "Bcc:";
637  break;
638  }
639  }
640  if ( !t.isEmpty() ) {
641  s.append( t );
642  }
643  if ( !dn.isEmpty() ) {
644  s.append( ' ' + dn );
645  }
646  if ( !addr.isEmpty() && addr != dn ) {
647  s.append( " <" + addr + '>' );
648  }
649 
650  return s.trimmed();
651 }
652 
653 QDateTime readTNEFDate( QDataStream &stream )
654 {
655  // 14-bytes long
656  quint16 y, m, d, hh, mm, ss, dm;
657  stream >> y >> m >> d >> hh >> mm >> ss >> dm;
658  return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) );
659 }
660 
661 QString readTNEFAddress( QDataStream &stream )
662 {
663  quint16 totalLen, strLen, addrLen;
664  QString s;
665  stream >> totalLen >> totalLen >> strLen >> addrLen;
666  s.append( readMAPIString( stream, false, false, strLen ) );
667  s.append( " <" );
668  s.append( readMAPIString( stream, false, false, addrLen ) );
669  s.append( ">" );
670  quint8 c;
671  for ( int i=8+strLen+addrLen; i<totalLen; i++ ) {
672  stream >> c;
673  }
674  return s;
675 }
676 
677 QByteArray readTNEFData( QDataStream &stream, quint32 len )
678 {
679  QByteArray array( len, '\0' );
680  if ( len > 0 ) {
681  stream.readRawData( array.data(), len );
682  }
683  return array;
684 }
685 
686 QVariant readTNEFAttribute( QDataStream &stream, quint16 type, quint32 len )
687 {
688  switch ( type ) {
689  case atpTEXT:
690  case atpSTRING:
691  return readMAPIString( stream, false, false, len );
692  case atpDATE:
693  return readTNEFDate( stream );
694  default:
695  return readTNEFData( stream, len );
696  }
697 }
698 
699 QString readMAPIString( QDataStream &stream, bool isUnicode, bool align,
700  int len_ )
701 {
702  quint32 len;
703  char *buf = 0;
704  if ( len_ == -1 ) {
705  stream >> len;
706  } else {
707  len = len_;
708  }
709  quint32 fullLen = len;
710  if ( align ) {
711  ALIGN( fullLen, 4 );
712  }
713  buf = new char[ len ];
714  stream.readRawData( buf, len );
715  quint8 c;
716  for ( uint i=len; i<fullLen; i++ ) {
717  stream >> c;
718  }
719  QString res;
720  if ( isUnicode ) {
721  res = QString::fromUtf16( ( const unsigned short *)buf );
722  } else {
723  res = QString::fromLatin1( buf );
724  }
725  delete [] buf;
726  return res;
727 }
728 
729 quint16 readMAPIValue( QDataStream &stream, MAPI_value &mapi )
730 {
731  quint32 d;
732 
733  clearMAPIValue( mapi );
734  stream >> d;
735  mapi.type = ( d & 0x0000FFFF );
736  mapi.tag = ( ( d & 0xFFFF0000 ) >> 16 );
737  if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
738  // skip GUID
739  stream >> d >> d >> d >> d;
740  // name type
741  stream >> mapi.name.type;
742  // name
743  if ( mapi.name.type == 0 ) {
744  uint tmp;
745  stream >> tmp;
746  mapi.name.value.setValue( tmp );
747  } else if ( mapi.name.type == 1 ) {
748  mapi.name.value.setValue( readMAPIString( stream, true ) );
749  }
750  }
751 
752  int n = 1;
753  QVariant value;
754  if ( ISVECTOR( mapi ) ) {
755  stream >> n;
756  mapi.value = QList<QVariant>();
757  }
758  for ( int i=0; i<n; i++ ) {
759  value.clear();
760  switch( mapi.type & 0x0FFF ) {
761  case MAPI_TYPE_UINT16:
762  stream >> d;
763  value.setValue( d & 0x0000FFFF );
764  break;
765  case MAPI_TYPE_BOOLEAN:
766  case MAPI_TYPE_ULONG:
767  {
768  uint tmp;
769  stream >> tmp;
770  value.setValue( tmp );
771  }
772  break;
773  case MAPI_TYPE_FLOAT:
774  // FIXME: Don't we have to set the value here
775  stream >> d;
776  break;
777  case MAPI_TYPE_DOUBLE:
778  {
779  double tmp;
780  stream >> tmp;
781  value.setValue( tmp );
782  }
783  break;
784  case MAPI_TYPE_TIME:
785  {
786  quint32 lowB, highB;
787  stream >> lowB >> highB;
788  value = formatTime( lowB, highB );
789  }
790  break;
791  case MAPI_TYPE_USTRING:
792  case MAPI_TYPE_STRING8:
793  // in case of a vector'ed value, the number of elements
794  // has already been read in the upper for-loop
795  if ( ISVECTOR( mapi ) ) {
796  d = 1;
797  } else {
798  stream >> d;
799  }
800  for ( uint i=0; i<d; i++ ) {
801  value.clear();
802  value.setValue( readMAPIString( stream,( mapi.type & 0x0FFF ) == MAPI_TYPE_USTRING ) );
803  }
804  break;
805  case MAPI_TYPE_OBJECT:
806  case MAPI_TYPE_BINARY:
807  if ( ISVECTOR( mapi ) ) {
808  d = 1;
809  } else {
810  stream >> d;
811  }
812  for ( uint i=0; i<d; i++ ) {
813  value.clear();
814  quint32 len;
815  stream >> len;
816  value = QByteArray( len, '\0' );
817  if ( len > 0 ) {
818  int fullLen = len;
819  ALIGN( fullLen, 4 );
820  stream.readRawData( value.toByteArray().data(), len );
821  quint8 c;
822  for ( int i=len; i<fullLen; i++ ) {
823  stream >> c;
824  }
825  // FIXME: Shouldn't we do something with the value???
826  }
827  }
828  break;
829  default:
830  mapi.type = MAPI_TYPE_NONE;
831  break;
832  }
833  if ( ISVECTOR( mapi ) ) {
834  QList <QVariant> lst = mapi.value.toList();
835  lst << value;
836  mapi.value.setValue( lst );
837  } else {
838  mapi.value = value;
839  }
840  }
841  return mapi.tag;
842 }
843 //@endcond
844 
845 bool KTNEFParser::ParserPrivate::readMAPIProperties( QMap<int,KTNEFProperty*> & props,
846  KTNEFAttach *attach )
847 {
848  quint32 n;
849  MAPI_value mapi;
850  KTNEFProperty *p;
851  QMap<int,KTNEFProperty*>::ConstIterator it;
852  bool foundAttachment = false;
853 
854  // some initializations
855  mapi.type = MAPI_TYPE_NONE;
856  mapi.value.clear();
857 
858  // get number of properties
859  stream_ >> n;
860  kDebug() << "MAPI Properties:" << n;
861  for ( uint i=0; i<n; i++ ) {
862  if ( stream_.atEnd() ) {
863  clearMAPIValue( mapi );
864  return false;
865  }
866  readMAPIValue( stream_, mapi );
867  if ( mapi.type == MAPI_TYPE_NONE ) {
868  kDebug().nospace() << "MAPI unsupported: tag="
869  << hex << mapi.tag << ", type=" << mapi.type;
870  clearMAPIValue( mapi );
871  return false;
872  }
873  int key = mapi.tag;
874  switch ( mapi.tag ) {
875  case MAPI_TAG_DATA:
876  {
877  if ( mapi.type == MAPI_TYPE_OBJECT && attach ) {
878  QByteArray data = mapi.value.toByteArray();
879  int len = data.size();
880  ALIGN( len, 4 );
881  device_->seek( device_->pos()-len );
882  quint32 interface_ID;
883  stream_ >> interface_ID;
884  if ( interface_ID == MAPI_IID_IMessage ) {
885  // embedded TNEF file
886  attach->unsetDataParser();
887  attach->setOffset( device_->pos()+12 );
888  attach->setSize( data.size()-16 );
889  attach->setMimeTag( "application/vnd.ms-tnef" );
890  attach->setDisplayName( "Embedded Message" );
891  kDebug() << "MAPI Embedded Message: size=" << data.size();
892  }
893  device_->seek( device_->pos() + ( len-4 ) );
894  break;
895  } else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) {
896  foundAttachment = true;
897  int len = mapi.value.toByteArray().size();
898  ALIGN( len, 4 );
899  attach->setSize( len );
900  attach->setOffset( device_->pos() - len );
901  attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false );
902  }
903  }
904  kDebug() << "MAPI data: size=" << mapi.value.toByteArray().size();
905  break;
906  default:
907  {
908  QString mapiname = "";
909  if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) {
910  if ( mapi.name.type == 0 ) {
911  mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() );
912  } else {
913  mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() );
914  }
915  }
916  switch ( mapi.type & 0x0FFF ) {
917  case MAPI_TYPE_UINT16:
918  kDebug().nospace() << "(tag="
919  << hex << mapi.tag
920  << ") MAPI short" << mapiname.toLatin1().data()
921  << ":" << hex << mapi.value.toUInt();
922  break;
923  case MAPI_TYPE_ULONG:
924  kDebug().nospace() << "(tag="
925  << hex << mapi.tag
926  << ") MAPI long" << mapiname.toLatin1().data()
927  << ":" << hex << mapi.value.toUInt();
928  break;
929  case MAPI_TYPE_BOOLEAN:
930  kDebug().nospace() << "(tag="
931  << hex << mapi.tag
932  << ") MAPI boolean" << mapiname.toLatin1().data()
933  << ":" << mapi.value.toBool();
934  break;
935  case MAPI_TYPE_TIME:
936  kDebug().nospace() << "(tag="
937  << hex << mapi.tag
938  << ") MAPI time" << mapiname.toLatin1().data()
939  << ":" << mapi.value.toString().toLatin1().data();
940  break;
941  case MAPI_TYPE_USTRING:
942  case MAPI_TYPE_STRING8:
943  kDebug().nospace() << "(tag="
944  << hex << mapi.tag
945  << ") MAPI string" << mapiname.toLatin1().data()
946  << ":size=" << mapi.value.toByteArray().size()
947  << mapi.value.toString();
948  break;
949  case MAPI_TYPE_BINARY:
950  kDebug().nospace() << "(tag="
951  << hex << mapi.tag
952  << ") MAPI binary" << mapiname.toLatin1().data()
953  << ":size=" << mapi.value.toByteArray().size();
954  break;
955  }
956  }
957  break;
958  }
959  // do not remove potential existing similar entry
960  if ( ( it = props.constFind( key ) ) == props.constEnd() ) {
961  p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ),
962  mapi.value, mapi.name.value );
963  props[ p->key() ] = p;
964  }
965  //kDebug() << "stream:" << device_->pos();
966  }
967 
968  if ( foundAttachment && attach ) {
969  attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() );
970  attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() );
971  QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString();
972  if ( !str.isEmpty() ) {
973  attach->setDisplayName( str );
974  }
975  attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() );
976  str = attach->property( MAPI_TAG_MIMETAG ).toString();
977  if ( !str.isEmpty() ) {
978  attach->setMimeTag( str );
979  }
980  attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() );
981  if ( attach->name().isEmpty() ) {
982  attach->setName( attach->fileName() );
983  }
984  }
985 
986  return true;
987 }
QIODevice
QVariant::toByteArray
QByteArray toByteArray() const
KTnef::KTNEFAttach::setOffset
void setOffset(int offset)
Sets the offset value of this attachment to offset.
Definition: ktnefattach.cpp:116
KTnef::KTNEFPropertySet::property
QVariant property(int key) const
Returns the property associcated with the specified key.
Definition: ktnefpropertyset.cpp:135
QString::append
QString & append(QChar ch)
KTnef::KTNEFAttach::setDisplaySize
void setDisplaySize(int size)
Sets the display size of the attachment to size.
Definition: ktnefattach.cpp:137
KTnef::KTNEFParser::extractAll
bool extractAll()
Extracts all TNEF attachments into the default directory.
Definition: ktnefparser.cpp:490
QByteArray
QDataStream
KTnef::KTNEFProperty::key
int key() const
Returns the integer key of the property.
Definition: ktnefproperty.cpp:133
QMap< int, KTnef::KTNEFProperty * >
KTnef::KTNEFParser::~KTNEFParser
~KTNEFParser()
Destroys the TNEF parser object.
Definition: ktnefparser.cpp:121
KTnef::KTNEFParser::setDefaultExtractDir
void setDefaultExtractDir(const QString &dirname)
Sets the default extraction directory to dirname.
Definition: ktnefparser.cpp:375
QVariant::value
T value() const
QMap::constFind
const_iterator constFind(const Key &key) const
KTnef::KTNEFAttach::setExtension
void setExtension(const QString &str)
Sets the filename extension of this attachment to str.
Definition: ktnefattach.cpp:199
ktnefmessage.h
This file is part of the API for handling TNEF data and defines the KTNEFMessage class.
KTnef::KTNEFParser::extractFileTo
bool extractFileTo(const QString &filename, const QString &dirname) const
Extracts a TNEF attachment having filename filename into the directory dirname.
Definition: ktnefparser.cpp:502
QTime
QList::const_iterator
QFile
QDataStream::readRawData
int readRawData(char *s, int len)
KTnef::KTNEFParser::extractFile
bool extractFile(const QString &filename) const
Extracts a TNEF attachment having filename filename into the default directory.
Definition: ktnefparser.cpp:437
QList::value
T value(int i) const
KTnef::KTNEFMessage
Represents a TNEF message.
Definition: ktnefmessage.h:49
KTnef::KTNEFPropertySet::addAttribute
void addAttribute(int key, int type, const QVariant &value, bool overwrite=false)
Adds a TNEF attribute.
Definition: ktnefpropertyset.cpp:161
ktnefdefs.h
This file is part of the API for handling TNEF data and provides some basic definitions for general u...
QVariant::toUInt
uint toUInt(bool *ok) const
QString::fromUtf16
QString fromUtf16(const ushort *unicode, int size)
ktnefparser.h
This file is part of the API for handling TNEF data and defines the KTNEFParser class.
KTnef::KTNEFAttach::size
int size() const
Returns the size of the attachment.
Definition: ktnefattach.cpp:122
QString::isEmpty
bool isEmpty() const
QString::trimmed
QString trimmed() const
QMap::constEnd
const_iterator constEnd() const
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
QDate
QString
QList
KTnef::KTNEFAttach::fileName
QString fileName() const
Returns the filename of the attachment.
Definition: ktnefattach.cpp:164
QMap::end
iterator end()
KTnef::KTNEFAttach::setMimeTag
void setMimeTag(const QString &str)
Sets the MIME tag of this attachment to str.
Definition: ktnefattach.cpp:189
KTnef::KTNEFAttach::setFileName
void setFileName(const QString &str)
Sets the filename of this attachment to str.
Definition: ktnefattach.cpp:169
KTnef::KTNEFAttach::unsetDataParser
void unsetDataParser()
Unsets the DataParsed flag for this attachment.
Definition: ktnefattach.cpp:81
KTnef::KTNEFAttach
Represents a TNEF attachment.
Definition: ktnefattach.h:51
QVariant::clear
void clear()
QVariant::setValue
void setValue(const T &value)
KTnef::KTNEFParser::openDevice
bool openDevice(QIODevice *device)
Opens the QIODevice device for parsing.
Definition: ktnefparser.cpp:524
KTnef::KTNEFParser::openFile
bool openFile(const QString &filename) const
Opens the filename for parsing.
Definition: ktnefparser.cpp:514
KTnef::KTNEFAttach::setName
void setName(const QString &str)
Sets the name of this attachment to str.
Definition: ktnefattach.cpp:147
QString::toLatin1
QByteArray toLatin1() const
QTest::toString
char * toString(const T &value)
ktnefproperty.h
This file is part of the API for handling TNEF data and defines the KTNEFProperty class...
QString::sprintf
QString & sprintf(const char *cformat,...)
KTnef::KTNEFAttach::setSize
void setSize(int size)
Sets the size of the attachment to size.
Definition: ktnefattach.cpp:127
QByteArray::data
char * data()
QString::fromLatin1
QString fromLatin1(const char *str, int size)
KTnef::KTNEFAttach::offset
int offset() const
Returns the offset value of the attachment.
Definition: ktnefattach.cpp:111
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
KTnef::KTNEFParser::KTNEFParser
KTNEFParser()
Constructs a TNEF parser object.
Definition: ktnefparser.cpp:116
QByteArray::size
int size() const
ktnefattach.h
This file is part of the API for handling TNEF data and defines the KTNEFAttach class.
KTnef::KTNEFAttach::setIndex
void setIndex(int indx)
Sets the index of this attachment to indx.
Definition: ktnefattach.cpp:158
KTnef::KTNEFParser::message
KTNEFMessage * message() const
Returns the KTNEFMessage used in the parsing process.
Definition: ktnefparser.cpp:127
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QVariant::toString
QString toString() const
KTnef::KTNEFAttach::name
QString name() const
Returns the name of the attachment.
Definition: ktnefattach.cpp:142
KTnef::KTNEFProperty
Interface for setting MAPI properties.
Definition: ktnefproperty.h:44
QMap::find
iterator find(const Key &key)
KTnef::KTNEFAttach::setDisplayName
void setDisplayName(const QString &str)
Sets the display name of this attachment to str.
Definition: ktnefattach.cpp:179
QDateTime
QDateTime::setTime_t
void setTime_t(uint seconds)
QMap::value
const T value(const Key &key) const
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:43 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KTNEF Library

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

kdepimlibs API Reference

Skip menu "kdepimlibs 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

Search



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