diff --git a/ktnef/ktnef/ktnefmessage.h b/ktnef/ktnef/ktnefmessage.h index efdc236a8..e1fb6668b 100644 --- a/ktnef/ktnef/ktnefmessage.h +++ b/ktnef/ktnef/ktnefmessage.h @@ -1,44 +1,44 @@ /* ktnefmessage.h Copyright (C) 2002 Michael Goffioul This file is part of KTNEF, the KDE TNEF support library/program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef KTNEFMESSAGE_H #define KTNEFMESSAGE_H #include -#include +#include #include class KTNEFAttach; class KDE_EXPORT KTNEFMessage : public KTNEFPropertySet { public: KTNEFMessage(); ~KTNEFMessage(); - const QPtrList& attachmentList() const; + const QList& attachmentList() const; KTNEFAttach* attachment( const QString& filename ) const; void addAttachment( KTNEFAttach* attach ); void clearAttachments(); QString rtfString(); private: class MessagePrivate; MessagePrivate *d; }; #endif /* KTNEFMESSAGE_H */ diff --git a/ktnef/ktnef/ktnefparser.h b/ktnef/ktnef/ktnefparser.h index 3696cd17c..b30be8e54 100644 --- a/ktnef/ktnef/ktnefparser.h +++ b/ktnef/ktnef/ktnefparser.h @@ -1,58 +1,58 @@ /* ktnefparser.h Copyright (C) 2002 Michael Goffioul This file is part of KTNEF, the KDE TNEF support library/program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef KTNEFPARSER_H #define KTNEFPARSER_H -#include #include #include +#include #include class KTNEFAttach; class KTNEFMessage; class KTNEFProperty; class KDE_EXPORT KTNEFParser { public: KTNEFParser(); ~KTNEFParser(); bool openFile(const QString& filename); bool openDevice( QIODevice *device ); bool extractFile(const QString& filename); bool extractFileTo(const QString& filename, const QString& dirname); bool extractAll(); void setDefaultExtractDir(const QString& dirname); KTNEFMessage* message() const; private: bool decodeAttachment(); bool decodeMessage(); bool extractAttachmentTo(KTNEFAttach *att, const QString& dirname); bool parseDevice(); void checkCurrent(int state); bool readMAPIProperties(QMap& pros, KTNEFAttach *attach = 0); void deleteDevice(); private: class ParserPrivate; ParserPrivate *d; }; #endif diff --git a/ktnef/lib/ktnefmessage.cpp b/ktnef/lib/ktnefmessage.cpp index d94efb6c4..38fded2e9 100644 --- a/ktnef/lib/ktnefmessage.cpp +++ b/ktnef/lib/ktnefmessage.cpp @@ -1,82 +1,94 @@ /* ktnefmessage.cpp Copyright (C) 2002 Michael Goffioul This file is part of KTNEF, the KDE TNEF support library/program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "ktnef/ktnefmessage.h" #include "ktnef/ktnefattach.h" #include "lzfu.h" #include +#include class KTNEFMessage::MessagePrivate { public: - MessagePrivate() - { - attachments_.setAutoDelete( true ); - } + MessagePrivate() {} + ~MessagePrivate(); + + void clearAttachments(); - QPtrList attachments_; + QList attachments_; }; +KTNEFMessage::MessagePrivate::~MessagePrivate() +{ + clearAttachments(); +} + +void KTNEFMessage::MessagePrivate::clearAttachments() +{ + while ( !attachments_.isEmpty() ) + delete attachments_.takeFirst(); +} + KTNEFMessage::KTNEFMessage() { d = new MessagePrivate; } KTNEFMessage::~KTNEFMessage() { delete d; } -const QPtrList& KTNEFMessage::attachmentList() const +const QList& KTNEFMessage::attachmentList() const { return d->attachments_; } KTNEFAttach* KTNEFMessage::attachment( const QString& filename ) const { - QPtrListIterator it( d->attachments_ ); - for ( ; it.current(); ++it ) - if ( it.current()->name() == filename ) - return it.current(); + QList::const_iterator it = d->attachments_.begin(); + for ( ; it != d->attachments_.end(); ++it ) + if ( (*it)->name() == filename ) + return *it; return 0; } void KTNEFMessage::addAttachment( KTNEFAttach *attach ) { d->attachments_.append( attach ); } void KTNEFMessage::clearAttachments() { - d->attachments_.clear(); + d->clearAttachments(); } QString KTNEFMessage::rtfString() { QVariant prop = property( 0x1009 ); if ( prop.isNull() || prop.type() != QVariant::ByteArray) return QString::null; else { QByteArray rtf; - QBuffer input( prop.asByteArray() ), output( rtf ); - if ( input.open( IO_ReadOnly ) && output.open( IO_WriteOnly ) ) + QBuffer input( &prop.asByteArray() ), output( &rtf ); + if ( input.open( QIODevice::ReadOnly ) && output.open( QIODevice::WriteOnly ) ) lzfu_decompress( &input, &output ); return QString( rtf ); } } diff --git a/ktnef/lib/ktnefparser.cpp b/ktnef/lib/ktnefparser.cpp index d282a0887..e01adf50c 100644 --- a/ktnef/lib/ktnefparser.cpp +++ b/ktnef/lib/ktnefparser.cpp @@ -1,853 +1,855 @@ /* ktnefparser.cpp Copyright (C) 2002 Michael Goffioul This file is part of KTNEF, the KDE TNEF support library/program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include "ktnef/ktnefparser.h" #include "ktnef/ktnefattach.h" #include "ktnef/ktnefproperty.h" #include "ktnef/ktnefmessage.h" #include #include #include #include +#include #include #include #include #ifdef HAVE_INTTYPES_H #include #endif /* HAVE_INTTYPES_H */ #include "ktnef/ktnefdefs.h" typedef struct { Q_UINT16 type; Q_UINT16 tag; QVariant value; struct { Q_UINT32 type; QVariant value; } name; } MAPI_value; void clearMAPIName( MAPI_value& mapi ); void clearMAPIValue(MAPI_value& mapi, bool clearName = true); QString readMAPIString( QDataStream& stream, bool isUnicode = false, bool align = true, int len = -1 ); Q_UINT16 readMAPIValue(QDataStream& stream, MAPI_value& mapi); QDateTime readTNEFDate( QDataStream& stream ); QString readTNEFAddress( QDataStream& stream ); QByteArray readTNEFData( QDataStream& stream, Q_UINT32 len ); QVariant readTNEFAttribute( QDataStream& stream, Q_UINT16 type, Q_UINT32 len ); QDateTime formatTime( Q_UINT32 lowB, Q_UINT32 highB ); QString formatRecipient( const QMap& props ); //------------------------------------------------------------------------------------ class KTNEFParser::ParserPrivate { public: ParserPrivate() { defaultdir_ = "/tmp/"; current_ = 0; deleteDevice_ = false; device_ = 0; message_ = new KTNEFMessage; } ~ParserPrivate() { delete message_; } QDataStream stream_; QIODevice *device_; bool deleteDevice_; QString defaultdir_; KTNEFAttach *current_; KTNEFMessage *message_; }; KTNEFParser::KTNEFParser() { d = new ParserPrivate; } KTNEFParser::~KTNEFParser() { deleteDevice(); delete d; } KTNEFMessage* KTNEFParser::message() const { return d->message_; } void KTNEFParser::deleteDevice() { if ( d->deleteDevice_ ) delete d->device_; d->device_ = 0; d->deleteDevice_ = false; } bool KTNEFParser::decodeMessage() { Q_UINT32 i1, i2, off; Q_UINT16 u, tag, type; QVariant value; // read (type+name) d->stream_ >> i1; tag = ( i1 & 0x0000FFFF ); type = ( ( i1 & 0xFFFF0000 ) >> 16 ); // read data length d->stream_ >> i2; // offset after reading the value off = d->device_->at() + i2; switch ( tag ) { case attAIDOWNER: d->stream_ >> value.asUInt(); d->message_->addProperty( 0x0062, MAPI_TYPE_ULONG, value ); kdDebug() << "Message Owner Appointment ID" << " (length=" << i2 << ")" << endl; break; case attREQUESTRES: d->stream_ >> u; d->message_->addProperty( 0x0063, MAPI_TYPE_UINT16, u ); value = ( bool )u; kdDebug() << "Message Request Response" << " (length=" << i2 << ")" << endl; break; case attDATERECD: value = readTNEFDate( d->stream_ ); d->message_->addProperty( 0x0E06, MAPI_TYPE_TIME, value ); kdDebug() << "Message Receive Date" << " (length=" << i2 << ")" << endl; break; case attMSGCLASS: value = readMAPIString( d->stream_, false, false, i2 ); d->message_->addProperty( 0x001A, MAPI_TYPE_STRING8, value ); kdDebug() << "Message Class" << " (length=" << i2 << ")" << endl; break; case attMSGPRIORITY: d->stream_ >> u; d->message_->addProperty( 0x0026, MAPI_TYPE_ULONG, 2-u ); value = u; kdDebug() << "Message Priority" << " (length=" << i2 << ")" << endl; break; case attMAPIPROPS: kdDebug() << "Message MAPI Properties" << " (length=" << i2 << ")" << endl; { int nProps = d->message_->properties().count(); i2 += d->device_->at(); readMAPIProperties( d->message_->properties(), 0 ); d->device_->at( i2 ); kdDebug() << "Properties: " << d->message_->properties().count() << endl; value = QString( "< %1 properties >" ).arg( d->message_->properties().count() - nProps ); } break; case attTNEFVERSION: d->stream_ >> value.asUInt(); kdDebug() << "Message TNEF Version" << " (length=" << i2 << ")" << endl; break; case attFROM: d->message_->addProperty( 0x0024, MAPI_TYPE_STRING8, readTNEFAddress( d->stream_ ) ); d->device_->at( d->device_->at() - i2 ); value = readTNEFData( d->stream_, i2 ); kdDebug() << "Message From" << " (length=" << i2 << ")" << endl; break; case attSUBJECT: value = readMAPIString( d->stream_, false, false, i2 ); d->message_->addProperty( 0x0037, MAPI_TYPE_STRING8, value ); kdDebug() << "Message Subject" << " (length=" << i2 << ")" << endl; break; case attDATESENT: value = readTNEFDate( d->stream_ ); d->message_->addProperty( 0x0039, MAPI_TYPE_TIME, value ); kdDebug() << "Message Date Sent" << " (length=" << i2 << ")" << endl; break; case attMSGSTATUS: { Q_UINT8 c; Q_UINT32 flag = 0; d->stream_ >> c; if ( c & fmsRead ) flag |= MSGFLAG_READ; if ( !( c & fmsModified ) ) flag |= MSGFLAG_UNMODIFIED; if ( c & fmsSubmitted ) flag |= MSGFLAG_SUBMIT; if ( c & fmsHasAttach ) flag |= MSGFLAG_HASATTACH; if ( c & fmsLocal ) flag |= MSGFLAG_UNSENT; d->message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag ); value = c; } kdDebug() << "Message Status" << " (length=" << i2 << ")" << endl; break; case attRECIPTABLE: { Q_UINT32 rows; - QValueList recipTable; + QList recipTable; d->stream_ >> rows; for ( uint i=0; i props; readMAPIProperties( props, 0 ); recipTable << formatRecipient( props ); } d->message_->addProperty( 0x0E12, MAPI_TYPE_STRING8, recipTable ); d->device_->at( d->device_->at() - i2 ); value = readTNEFData( d->stream_, i2 ); } kdDebug() << "Message Recipient Table" << " (length=" << i2 << ")" << endl; break; case attBODY: value = readMAPIString( d->stream_, false, false, i2 ); d->message_->addProperty( 0x1000, MAPI_TYPE_STRING8, value ); kdDebug() << "Message Body" << " (length=" << i2 << ")" << endl; break; case attDATEMODIFIED: value = readTNEFDate( d->stream_ ); d->message_->addProperty( 0x3008, MAPI_TYPE_TIME, value ); kdDebug() << "Message Date Modified" << " (length=" << i2 << ")" << endl; break; case attMSGID: value = readMAPIString( d->stream_, false, false, i2 ); d->message_->addProperty( 0x300B, MAPI_TYPE_STRING8, value ); kdDebug() << "Message ID" << " (length=" << i2 << ")" << endl; break; case attOEMCODEPAGE: value = readTNEFData( d->stream_, i2 ); kdDebug() << "Message OEM Code Page" << " (length=" << i2 << ")" << endl; break; default: value = readTNEFAttribute( d->stream_, type, i2 ); kdDebug().form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u ); break; } // skip data if ( d->device_->at() != off && !d->device_->at( off ) ) return false; // get checksum d->stream_ >> u; // add TNEF attribute d->message_->addAttribute( tag, type, value, true ); //kdDebug() << "stream: " << d->device_->at() << endl; return true; } bool KTNEFParser::decodeAttachment() { Q_UINT32 i; Q_UINT16 tag, type, u; QVariant value; QString str; d->stream_ >> i; // i <- attribute type & name tag = ( i & 0x0000FFFF ); type = ( ( i & 0xFFFF0000 ) >> 16 ); d->stream_ >> i; // i <- data length checkCurrent( tag ); switch (tag) { case attATTACHTITLE: value = readMAPIString( d->stream_, false, false, i ); d->current_->setName( value.toString() ); kdDebug() << "Attachment Title: " << d->current_->name() << endl; break; case attATTACHDATA: d->current_->setSize( i ); d->current_->setOffset( d->device_->at() ); d->device_->at( d->device_->at() + i ); value = QString( "< size=%1 >" ).arg( i ); kdDebug() << "Attachment Data: size=" << i << endl; break; case attATTACHMENT: // try to get attachment info i += d->device_->at(); readMAPIProperties( d->current_->properties(), d->current_ ); d->device_->at( i ); d->current_->setIndex( d->current_->property( MAPI_TAG_INDEX ).toUInt() ); d->current_->setDisplaySize( d->current_->property( MAPI_TAG_SIZE ).toUInt() ); str = d->current_->property( MAPI_TAG_DISPLAYNAME ).toString(); if ( !str.isEmpty() ) d->current_->setDisplayName( str ); d->current_->setFileName( d->current_->property( MAPI_TAG_FILENAME ).toString() ); str = d->current_->property( MAPI_TAG_MIMETAG ).toString(); if ( !str.isEmpty() ) d->current_->setMimeTag( str ); d->current_->setExtension( d->current_->property( MAPI_TAG_EXTENSION ).toString() ); value = QString( "< %1 properties >" ).arg( d->current_->properties().count() ); break; case attATTACHMODDATE: value = readTNEFDate( d->stream_ ); kdDebug() << "Attachment Modification Date: " << value.toString() << endl; break; case attATTACHCREATEDATE: value = readTNEFDate( d->stream_ ); kdDebug() << "Attachment Creation Date: " << value.toString() << endl; break; case attATTACHMETAFILE: kdDebug() << "Attachment Metafile: size=" << i << endl; //value = QString( "< size=%1 >" ).arg( i ); //d->device_->at( d->device_->at()+i ); value = readTNEFData( d->stream_, i ); break; default: value = readTNEFAttribute( d->stream_, type, i ); kdDebug().form( "Attachment unknown field: tag=%x, length=%d\n", tag, i); break; } d->stream_ >> u; // u <- checksum // add TNEF attribute d->current_->addAttribute( tag, type, value, true ); //kdDebug() << "stream: " << d->device_->at() << endl; return true; } void KTNEFParser::setDefaultExtractDir(const QString& dirname) { d->defaultdir_ = dirname; } bool KTNEFParser::parseDevice() { Q_UINT16 u; Q_UINT32 i; Q_UINT8 c; d->message_->clearAttachments(); if (d->current_) { delete d->current_; d->current_ = 0; } - if ( !d->device_->open( IO_ReadOnly ) ) { + if ( !d->device_->open( QIODevice::ReadOnly ) ) { kdDebug() << "Couldn't open device" << endl; return false; } d->stream_.setDevice( d->device_ ); d->stream_.setByteOrder( QDataStream::LittleEndian ); d->stream_ >> i; if (i == TNEF_SIGNATURE) { d->stream_ >> u; kdDebug().form( "Attachment cross reference key: 0x%04x\n",u ); //kdDebug() << "stream: " << d->device_->at() << endl; while (!d->stream_.eof()) { d->stream_ >> c; switch (c) { case LVL_MESSAGE: if (!decodeMessage()) goto end; break; case LVL_ATTACHMENT: if (!decodeAttachment()) goto end; break; default: kdDebug() << "Unknown Level: " << c << ", at offset " << d->device_->at() << endl; goto end; } } if (d->current_) { checkCurrent(attATTACHDATA); // this line has the effect to append the // attachment, if it has data. If not it does // nothing, and the attachment will be discarded delete d->current_; d->current_ = 0; } return true; } else { kdDebug() << "This is not a TNEF file" << endl; end: d->device_->close(); return false; } } bool KTNEFParser::extractFile(const QString& filename) { KTNEFAttach *att = d->message_->attachment(filename); if (!att) return false; return extractAttachmentTo(att, d->defaultdir_); } bool KTNEFParser::extractAttachmentTo(KTNEFAttach *att, const QString& dirname) { QString filename = dirname + "/" + att->name(); if (!d->device_->isOpen()) return false; if (!d->device_->at(att->offset())) return false; KSaveFile saveFile( filename ); QFile *outfile = saveFile.file(); if ( !outfile ) return false; Q_UINT32 len = att->size(), sz(16384); int n(0); char *buf = new char[sz]; bool ok(true); while (ok && len > 0) { n = d->device_->readBlock(buf,QMIN(sz,len)); if (n < 0) ok = false; else { len -= n; if (outfile->writeBlock(buf,n) != n) ok = false; } } delete [] buf; return ok; } bool KTNEFParser::extractAll() { - QPtrListIterator it(d->message_->attachmentList()); - for (;it.current();++it) - if (!extractAttachmentTo(it.current(),d->defaultdir_)) return false; + QList l = d->message_->attachmentList(); + QList::const_iterator it = l.begin(); + for ( ; it != l.end(); ++it ) + if (!extractAttachmentTo(*it,d->defaultdir_)) return false; return true; } bool KTNEFParser::extractFileTo(const QString& filename, const QString& dirname) { kdDebug() << "Extracting attachment: filename=" << filename << ", dir=" << dirname << endl; KTNEFAttach *att = d->message_->attachment(filename); if (!att) return false; return extractAttachmentTo(att, dirname); } bool KTNEFParser::openFile(const QString& filename) { deleteDevice(); d->device_ = new QFile( filename ); d->deleteDevice_ = true; return parseDevice(); } bool KTNEFParser::openDevice( QIODevice *device ) { deleteDevice(); d->device_ = device; return parseDevice(); } void KTNEFParser::checkCurrent( int key ) { if ( !d->current_ ) d->current_ = new KTNEFAttach(); else { if ( d->current_->attributes().contains( key ) ) { if (d->current_->offset() >= 0 ) { if (d->current_->name().isEmpty()) d->current_->setName("Unnamed"); if ( d->current_->mimeTag().isEmpty() ) { // No mime type defined in the TNEF structure, // try to find it from the attachment filename // and/or content (using at most 32 bytes) KMimeType::Ptr mimetype; if ( !d->current_->fileName().isEmpty() ) mimetype = KMimeType::findByPath( d->current_->fileName(), 0, true ); if (!mimetype) return; // FIXME if ( mimetype->name() == "application/octet-stream" && d->current_->size() > 0 ) { int oldOffset = d->device_->at(); QByteArray buffer( QMIN( 32, d->current_->size() ) ); d->device_->at( d->current_->offset() ); d->device_->readBlock( buffer.data(), buffer.size() ); mimetype = KMimeType::findByContent( buffer ); d->device_->at( oldOffset ); } d->current_->setMimeTag( mimetype->name() ); } d->message_->addAttachment( d->current_ ); d->current_ = 0; } else { // invalid attachment, skip it delete d->current_; d->current_ = 0; } d->current_ = new KTNEFAttach(); } } } //---------------------------------------------------------------------------------------- #define ALIGN( n, b ) if ( n & ( b-1 ) ) { n = ( n + b ) & ~( b-1 ); } #define ISVECTOR( m ) ( ( ( m ).type & 0xF000 ) == MAPI_TYPE_VECTOR ) void clearMAPIName( MAPI_value& mapi ) { mapi.name.value.clear(); } void clearMAPIValue(MAPI_value& mapi, bool clearName) { mapi.value.clear(); if ( clearName ) clearMAPIName( mapi ); } QDateTime formatTime( Q_UINT32 lowB, Q_UINT32 highB ) { QDateTime dt; #if ( SIZEOF_UINT64_T == 8 ) uint64_t u64; #elif ( SIZEOF_UNSIGNED_LONG_LONG == 8 ) unsigned long long u64; #elif ( SIZEOF_UNSIGNED_LONG == 8 ) unsigned long u64; #else kdWarning() << "Unable to perform date conversion on this system, no 64-bits integer found" << endl; dt.setTime_t( 0xffffffffU ); return dt; #endif u64 = highB; u64 <<= 32; u64 |= lowB; u64 -= 116444736000000000LL; u64 /= 10000000; if ( u64 <= 0xffffffffU ) dt.setTime_t( ( unsigned int )u64 ); else { kdWarning().form( "Invalid date: low byte=0x%08X, high byte=0x%08X\n", lowB, highB ); dt.setTime_t( 0xffffffffU ); } return dt; } QString formatRecipient( const QMap& props ) { QString s, dn, addr, t; QMap::ConstIterator it; if ( ( it = props.find( 0x3001 ) ) != props.end() ) dn = ( *it )->valueString(); if ( ( it = props.find( 0x3003 ) ) != props.end() ) addr = ( *it )->valueString(); if ( ( it = props.find( 0x0C15 ) ) != props.end() ) switch ( ( *it )->value().toInt() ) { case 0: t = "From:"; break; case 1: t = "To:"; break; case 2: t = "Cc:"; break; case 3: t = "Bcc:"; break; } if ( !t.isEmpty() ) s.append( t ); if ( !dn.isEmpty() ) s.append( " " + dn ); if ( !addr.isEmpty() && addr != dn ) s.append( " <" + addr + ">" ); return s.stripWhiteSpace(); } QDateTime readTNEFDate( QDataStream& stream ) { // 14-bytes long Q_UINT16 y, m, d, hh, mm, ss, dm; stream >> y >> m >> d >> hh >> mm >> ss >> dm; return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) ); } QString readTNEFAddress( QDataStream& stream ) { Q_UINT16 totalLen, strLen, addrLen; QString s; stream >> totalLen >> totalLen >> strLen >> addrLen; s.append( readMAPIString( stream, false, false, strLen ) ); s.append( " <" ); s.append( readMAPIString( stream, false, false, addrLen ) ); s.append( ">" ); Q_UINT8 c; for ( int i=8+strLen+addrLen; i> c; return s; } QByteArray readTNEFData( QDataStream& stream, Q_UINT32 len ) { QByteArray array( len ); if ( len > 0 ) stream.readRawBytes( array.data(), len ); return array; } QVariant readTNEFAttribute( QDataStream& stream, Q_UINT16 type, Q_UINT32 len ) { switch ( type ) { case atpTEXT: case atpSTRING: return readMAPIString( stream, false, false, len ); case atpDATE: return readTNEFDate( stream ); default: return readTNEFData( stream, len ); } } QString readMAPIString( QDataStream& stream, bool isUnicode, bool align, int len_ ) { Q_UINT32 len; char *buf = 0; if ( len_ == -1 ) stream >> len; else len = len_; Q_UINT32 fullLen = len; if ( align ) ALIGN( fullLen, 4 ); buf = new char[ len ]; stream.readRawBytes( buf, len ); Q_UINT8 c; for ( uint i=len; i> c; QString res; if ( isUnicode ) res = QString::fromUcs2( ( const unsigned short* )buf ); else res = QString::fromLocal8Bit( buf ); delete [] buf; return res; } Q_UINT16 readMAPIValue(QDataStream& stream, MAPI_value& mapi) { Q_UINT32 d; clearMAPIValue(mapi); stream >> d; mapi.type = (d & 0x0000FFFF); mapi.tag = ((d & 0xFFFF0000) >> 16); if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) { // skip GUID stream >> d >> d >> d >> d; // name type stream >> mapi.name.type; // name if ( mapi.name.type == 0 ) stream >> mapi.name.value.asUInt(); else if ( mapi.name.type == 1 ) mapi.name.value.asString() = readMAPIString( stream, true ); } int n = 1; QVariant value; if ( ISVECTOR( mapi ) ) { stream >> n; - mapi.value = QValueList(); + mapi.value = QList(); } for ( int i=0; i> d; value.asUInt() = ( d & 0x0000FFFF ); break; case MAPI_TYPE_BOOLEAN: case MAPI_TYPE_ULONG: stream >> value.asUInt(); break; case MAPI_TYPE_FLOAT: stream >> d; break; case MAPI_TYPE_DOUBLE: stream >> value.asDouble(); break; case MAPI_TYPE_TIME: { Q_UINT32 lowB, highB; stream >> lowB >> highB; value = formatTime( lowB, highB ); } break; case MAPI_TYPE_STRING8: // in case of a vector'ed value, the number of elements // has already been read in the upper for-loop if ( ISVECTOR( mapi ) ) d = 1; else stream >> d; for (uint i=0;i> d; for (uint i=0;i> len; value = QByteArray( len ); if (len > 0) { int fullLen = len; ALIGN(fullLen, 4); stream.readRawBytes(value.asByteArray().data(), len); Q_UINT8 c; for ( int i=len; i> c; } } break; default: mapi.type = MAPI_TYPE_NONE; break; } if ( ISVECTOR( mapi ) ) mapi.value.asList().append( value ); else mapi.value = value; } return mapi.tag; } bool KTNEFParser::readMAPIProperties( QMap& props, KTNEFAttach *attach ) { Q_UINT32 n; MAPI_value mapi; KTNEFProperty *p; QMap::ConstIterator it; // some initializations mapi.type = MAPI_TYPE_NONE; mapi.value.clear(); // get number of properties d->stream_ >> n; kdDebug() << "MAPI Properties: " << n << endl; for (uint i=0;istream_.eof()) { clearMAPIValue(mapi); return false; } readMAPIValue(d->stream_, mapi); if (mapi.type == MAPI_TYPE_NONE) { kdDebug().form( "MAPI unsupported: tag=%x, type=%x\n", mapi.tag, mapi.type ); clearMAPIValue(mapi); return false; } int key = mapi.tag; switch (mapi.tag) { case MAPI_TAG_DATA: { if ( mapi.type == MAPI_TYPE_OBJECT && attach ) { QByteArray data = mapi.value.toByteArray(); int len = data.size(); ALIGN( len, 4 ); d->device_->at( d->device_->at()-len ); Q_UINT32 interface_ID; d->stream_ >> interface_ID; if ( interface_ID == MAPI_IID_IMessage ) { // embedded TNEF file attach->unsetDataParser(); attach->setOffset( d->device_->at()+12 ); attach->setSize( data.size()-16 ); attach->setMimeTag( "application/ms-tnef" ); attach->setDisplayName( "Embedded Message" ); kdDebug() << "MAPI Embedded Message: size=" << data.size() << endl; } d->device_->at( d->device_->at() + ( len-4 ) ); break; } } kdDebug().form( "MAPI data: size=%d\n", mapi.value.toByteArray().size() ); break; default: { QString mapiname = ""; if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) { if ( mapi.name.type == 0 ) mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() ); else mapiname = QString( " [name = %1]" ).arg( mapi.name.value.toString() ); } switch ( mapi.type & 0x0FFF ) { case MAPI_TYPE_UINT16: kdDebug().form( "(tag=%04x) MAPI short%s: 0x%x\n", mapi.tag, mapiname.ascii(), mapi.value.toUInt() ); break; case MAPI_TYPE_ULONG: kdDebug().form( "(tag=%04x) MAPI long%s: 0x%x\n", mapi.tag, mapiname.ascii(), mapi.value.toUInt() ); break; case MAPI_TYPE_BOOLEAN: kdDebug().form( "(tag=%04x) MAPI boolean%s: %s\n", mapi.tag, mapiname.ascii(), ( mapi.value.toBool() ? "true" : "false" ) ); break; case MAPI_TYPE_TIME: kdDebug().form( "(tag=%04x) MAPI time%s: %s\n", mapi.tag, mapiname.ascii(), mapi.value.toString().ascii() ); break; case MAPI_TYPE_USTRING: case MAPI_TYPE_STRING8: kdDebug().form( "(tag=%04x) MAPI string%s: size=%d \"%s\"\n", mapi.tag, mapiname.ascii(), mapi.value.toByteArray().size(), mapi.value.toString().ascii() ); break; case MAPI_TYPE_BINARY: kdDebug().form( "(tag=%04x) MAPI binary%s: size=%d\n", mapi.tag, mapiname.ascii(), mapi.value.toByteArray().size() ); break; } } break; } // do not remove potential existing similar entry if ( ( it = props.find( key ) ) == props.end() ) { p = new KTNEFProperty( key, ( mapi.type & 0x0FFF ), mapi.value, mapi.name.value ); props[ p->key() ] = p; } //kdDebug() << "stream: " << d->device_->at() << endl; } return true; } diff --git a/ktnef/lib/ktnefpropertyset.cpp b/ktnef/lib/ktnefpropertyset.cpp index cf35362fe..651d4e259 100644 --- a/ktnef/lib/ktnefpropertyset.cpp +++ b/ktnef/lib/ktnefpropertyset.cpp @@ -1,155 +1,159 @@ /* ktnefpropertyset.cpp Copyright (C) 2002 Michael Goffioul This file is part of KTNEF, the KDE TNEF support library/program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "ktnef/ktnefpropertyset.h" #include "ktnef/ktnefproperty.h" + +#include + #include KTNEFPropertySet::KTNEFPropertySet() { } KTNEFPropertySet::~KTNEFPropertySet() { clear( true ); } void KTNEFPropertySet::addProperty( int key, int type, const QVariant& value, const QVariant& name, bool overwrite ) { QMap::ConstIterator it = properties_.find( key ); if ( it != properties_.end() ) { if ( overwrite ) delete ( *it ); else return; } KTNEFProperty *p = new KTNEFProperty( key, type, value, name ); properties_[ p->key() ] = p; } QString KTNEFPropertySet::findProp(int key, const QString& fallback, bool upper) { QMap::Iterator it = properties_.find( key ); if( properties_.end() != it ) return upper ? KTNEFProperty::formatValue( (*it)->value(), false ).upper() : KTNEFProperty::formatValue( (*it)->value(), false ); else return fallback; } QString KTNEFPropertySet::findNamedProp(const QString& name, const QString& fallback, bool upper) { for ( QMap::Iterator it = properties_.begin(); it != properties_.end(); ++it ){ if ( (*it)->name().isValid() ){ QString s; if ( (*it)->name().type() == QVariant::String ) s = (*it)->name().asString(); else s = QString().sprintf( "0X%04X", (*it)->name().asUInt() ); - + if( s.upper() == name.upper() ){ QVariant value = ( *it )->value(); if( value.type() == QVariant::List ){ + QList l = value.toList(); s = ""; - for ( QValueList::ConstIterator lit = value.listBegin(); - lit != value.listEnd(); + for ( QList::ConstIterator lit = l.begin(); + lit != l.end(); ++lit ){ if( !s.isEmpty() ) s += ','; s += KTNEFProperty::formatValue( *lit, false ); } }else{ s = KTNEFProperty::formatValue( value, false ); } return upper ? s.upper() : s; } } } return fallback; } QMap& KTNEFPropertySet::properties() { return properties_; } const QMap& KTNEFPropertySet::properties() const { return properties_; } QVariant KTNEFPropertySet::property( int key ) const { QMap::ConstIterator it = properties_.find( key ); if ( it == properties_.end() ) return QVariant(); else return ( *it )->value(); } void KTNEFPropertySet::clear( bool deleteAll ) { if ( deleteAll ) { for ( QMap::ConstIterator it=properties_.begin(); it!=properties_.end(); ++it ) delete ( *it ); for ( QMap::ConstIterator it=attributes_.begin(); it!=attributes_.end(); ++it ) delete ( *it ); } properties_.clear(); attributes_.clear(); } void KTNEFPropertySet::addAttribute( int key, int type, const QVariant& value, bool overwrite ) { QMap::ConstIterator it = attributes_.find( key ); if ( it != attributes_.end() ) { if ( overwrite ) delete ( *it ); else return; } KTNEFProperty *p = new KTNEFProperty( key, type, value, QVariant() ); attributes_[ p->key() ] = p; } QMap& KTNEFPropertySet::attributes() { return attributes_; } const QMap& KTNEFPropertySet::attributes() const { return attributes_; } QVariant KTNEFPropertySet::attribute( int key ) const { QMap::ConstIterator it = attributes_.find( key ); if ( it == attributes_.end() ) return QVariant(); else return ( *it )->value(); } diff --git a/ktnef/lib/ktnefwriter.cpp b/ktnef/lib/ktnefwriter.cpp index ce35dbd20..e25effa3b 100644 --- a/ktnef/lib/ktnefwriter.cpp +++ b/ktnef/lib/ktnefwriter.cpp @@ -1,497 +1,500 @@ /* ktnefwriter.cpp Copyright (C) 2002 Bo Thorsen This file is part of KTNEF, the KDE TNEF support library/program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include "ktnef/ktnefwriter.h" #include "ktnef/ktnefproperty.h" #include "ktnef/ktnefpropertyset.h" #include #include #include +//Added by qt3to4: +#include +#include #include #include #include "ktnef/ktnefdefs.h" class KTNEFWriter::PrivateData { public: PrivateData() { mFirstAttachNum = QDateTime::currentDateTime().toTime_t(); } KTNEFPropertySet properties; Q_UINT16 mFirstAttachNum; }; KTNEFWriter::KTNEFWriter() { mData = new PrivateData; // This is not something the user should fiddle with // First set the TNEF version QVariant v(0x00010000); addProperty( attTNEFVERSION, atpDWORD, v ); // Now set the code page to something reasonable. TODO: Use the right one QVariant v1( (Q_UINT32)0x4e4 ); QVariant v2( (Q_UINT32)0x0 ); - QValueList list; + QList list; list << v1; list << v2; v = QVariant( list ); addProperty( attOEMCODEPAGE, atpBYTE, list ); } KTNEFWriter::~KTNEFWriter() { delete mData; } void KTNEFWriter::addProperty( int tag, int type, const QVariant& value ) { mData->properties.addProperty( tag, type, value ); } void addToChecksum( Q_UINT32 i, Q_UINT16 &checksum ) { checksum += i & 0xff; checksum += (i >> 8) & 0xff; checksum += (i >> 16) & 0xff; checksum += (i >> 24) & 0xff; } -void addToChecksum( QCString &cs, Q_UINT16 &checksum ) { +void addToChecksum( Q3CString &cs, Q_UINT16 &checksum ) { int len = cs.length(); for (int i=0; i& properties = mData->properties.properties(); QMap::Iterator it = properties.find( tag ); if ( it == properties.end() ) return false; KTNEFProperty *property = *it; Q_UINT32 i; Q_UINT16 checksum = 0; - QValueList list; + QList list; QString s; - QCString cs, cs2; + Q3CString cs, cs2; QDateTime dt; QDate date; QTime time; switch( tag ) { case attMSGSTATUS: // Q_UINT8 i = property->value().toUInt() & 0xff; checksum = i; stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)1; stream << (Q_UINT8)i; bytes += 10; break; case attMSGPRIORITY: case attREQUESTRES: // Q_UINT16 i = property->value().toUInt() & 0xffff; addToChecksum( i, checksum ); stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)2; stream << (Q_UINT16)i; bytes += 11; break; case attTNEFVERSION: // Q_UINT32 i = property->value().toUInt(); addToChecksum( i, checksum ); stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)4; stream << (Q_UINT32)i; bytes += 13; break; case attOEMCODEPAGE: // 2 Q_UINT32 list = property->value().toList(); assert( list.count() == 2 ); stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)8; i = list[0].toInt(); addToChecksum( i, checksum ); stream << (Q_UINT32)i; i = list[1].toInt(); addToChecksum( i, checksum ); stream << (Q_UINT32)i; bytes += 17; break; case attMSGCLASS: case attSUBJECT: case attBODY: case attMSGID: // QCString cs = property->value().toString().local8Bit(); addToChecksum( cs, checksum ); stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)cs.length()+1; writeCString( stream, cs ); bytes += 9 + cs.length()+1; break; case attFROM: // 2 QString encoded to a TRP structure list = property->value().toList(); assert( list.count() == 2 ); cs = list[0].toString().local8Bit(); // Name cs2 = (QString("smtp:") + list[1].toString()).local8Bit(); // Email address i = 18 + cs.length() + cs2.length(); // 2 * sizof(TRP) + strings + 2x'\0' stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)i; // The stream has to be aligned to 4 bytes for the strings // TODO: Or does it? Looks like Outlook doesn't do this // bytes += 17; // Write the first TRP structure stream << (Q_UINT16)4; // trpidOneOff stream << (Q_UINT16)i; // totalsize stream << (Q_UINT16)(cs.length()+1); // sizeof name stream << (Q_UINT16)(cs2.length()+1); // sizeof address // if ( bytes % 4 != 0 ) // Align the buffer // Write the strings writeCString( stream, cs ); writeCString( stream, cs2 ); // Write the empty padding TRP structure (just zeroes) stream << (Q_UINT32)0 << (Q_UINT32)0; addToChecksum( 4, checksum ); addToChecksum( i, checksum ); addToChecksum( cs.length()+1, checksum ); addToChecksum( cs2.length()+1, checksum ); addToChecksum( cs, checksum ); addToChecksum( cs2, checksum ); bytes += 10; break; case attDATESENT: case attDATERECD: case attDATEMODIFIED: // QDateTime dt = property->value().toDateTime(); time = dt.time(); date = dt.date(); stream << (Q_UINT8)LVL_MESSAGE; stream << mergeTagAndType( tag, property->type() ); stream << (Q_UINT32)14; i = (Q_UINT16)date.year(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; i = (Q_UINT16)date.month(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; i = (Q_UINT16)date.day(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; i = (Q_UINT16)time.hour(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; i = (Q_UINT16)time.minute(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; i = (Q_UINT16)time.second(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; i = (Q_UINT16)date.dayOfWeek(); addToChecksum( i, checksum ); stream << (Q_UINT16)i; break; /* case attMSGSTATUS: { Q_UINT8 c; Q_UINT32 flag = 0; if ( c & fmsRead ) flag |= MSGFLAG_READ; if ( !( c & fmsModified ) ) flag |= MSGFLAG_UNMODIFIED; if ( c & fmsSubmitted ) flag |= MSGFLAG_SUBMIT; if ( c & fmsHasAttach ) flag |= MSGFLAG_HASATTACH; if ( c & fmsLocal ) flag |= MSGFLAG_UNSENT; d->stream_ >> c; i = property->value().toUInt(); stream << (Q_UINT8)LVL_MESSAGE; stream << (Q_UINT32)type; stream << (Q_UINT32)2; stream << (Q_UINT8)i; addToChecksum( i, checksum ); // from reader: d->message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag ); } kdDebug() << "Message Status" << " (length=" << i2 << ")" << endl; break; */ default: kdDebug() << "Unknown TNEF tag: " << tag << endl; return false; } stream << (Q_UINT16)checksum; return true; } bool KTNEFWriter::writeFile( QIODevice &file ) { - if ( !file.open( IO_WriteOnly ) ) + if ( !file.open( QIODevice::WriteOnly ) ) return false; QDataStream stream( &file ); return writeFile( stream ); } bool KTNEFWriter::writeFile( QDataStream &stream ) { stream.setByteOrder( QDataStream::LittleEndian ); // Start by writing the opening TNEF stuff stream << TNEF_SIGNATURE; // Store the PR_ATTACH_NUM value for the first attachment // ( must be stored even if *no* attachments are stored ) stream << mData->mFirstAttachNum; // Now do some writing bool ok = true; int bytesWritten = 0; ok &= writeProperty( stream, bytesWritten, attTNEFVERSION ); ok &= writeProperty( stream, bytesWritten, attOEMCODEPAGE ); ok &= writeProperty( stream, bytesWritten, attMSGCLASS ); ok &= writeProperty( stream, bytesWritten, attMSGPRIORITY ); ok &= writeProperty( stream, bytesWritten, attSUBJECT ); ok &= writeProperty( stream, bytesWritten, attDATESENT ); ok &= writeProperty( stream, bytesWritten, attDATESTART ); ok &= writeProperty( stream, bytesWritten, attDATEEND ); // ok &= writeProperty( stream, bytesWritten, attAIDOWNER ); ok &= writeProperty( stream, bytesWritten, attREQUESTRES ); ok &= writeProperty( stream, bytesWritten, attFROM ); ok &= writeProperty( stream, bytesWritten, attDATERECD ); ok &= writeProperty( stream, bytesWritten, attMSGSTATUS ); ok &= writeProperty( stream, bytesWritten, attBODY ); return ok; } void KTNEFWriter::setSender(const QString &name, const QString &email) { assert( !name.isEmpty() ); assert( !email.isEmpty() ); QVariant v1( name ); QVariant v2( email ); - QValueList list; + QList list; list << v1; list << v2; QVariant v( list ); addProperty( attFROM, 0, list ); // What's up with the 0 here ?? } void KTNEFWriter::setMessageType(MessageType m) { // Note that the MessageType list here is probably not long enough, // more entries are most likely needed later QVariant v; switch( m ) { case Appointment: v = QVariant( QString( "IPM.Appointment" ) ); break; case MeetingCancelled: v = QVariant( QString( "IPM.Schedule.Meeting.Cancelled" ) ); break; case MeetingRequest: v = QVariant( QString( "IPM.Schedule.Meeting.Request" ) ); break; case MeetingNo: v = QVariant( QString( "IPM.Schedule.Meeting.Resp.Neg" ) ); break; case MeetingYes: v = QVariant( QString( "IPM.Schedule.Meeting.Resp.Pos" ) ); break; case MeetingTent: // Tent? v = QVariant( QString( "IPM.Schedule.Meeting.Resp.Tent" ) ); break; default: return; } addProperty( attMSGCLASS, atpWORD, v ); } void KTNEFWriter::setMethod( Method ) { } void KTNEFWriter::clearAttendees() { } void KTNEFWriter::addAttendee( const QString& /*cn*/, Role /*r*/, PartStat /*p*/, bool /*rsvp*/, const QString& /*mailto*/ ) { } // I assume this is the same as the sender? // U also assume that this is like "Name
" void KTNEFWriter::setOrganizer( const QString& organizer ) { int i = organizer.find( '<' ); if ( i == -1 ) return; QString name = organizer.left( i ); name.stripWhiteSpace(); QString email = organizer.right( i+1 ); email = email.left( email.length()-1 ); email.stripWhiteSpace(); setSender( name, email ); } void KTNEFWriter::setDtStart( const QDateTime& dtStart ) { QVariant v( dtStart ); addProperty( attDATESTART, atpDATE, v ); } void KTNEFWriter::setDtEnd( const QDateTime& dtEnd ) { QVariant v( dtEnd ); addProperty( attDATEEND, atpDATE, v ); } void KTNEFWriter::setLocation( const QString& /*location*/ ) { } void KTNEFWriter::setUID( const QString& uid ) { QVariant v( uid ); addProperty( attMSGID, atpSTRING, v ); } // Date sent void KTNEFWriter::setDtStamp( const QDateTime& dtStamp ) { QVariant v( dtStamp ); addProperty( attDATESENT, atpDATE, v ); } void KTNEFWriter::setCategories( const QStringList& ) { } // I hope this is the body void KTNEFWriter::setDescription( const QString &body ) { QVariant v( body ); addProperty( attBODY, atpTEXT, v ); } void KTNEFWriter::setSummary( const QString &s ) { QVariant v( s ); addProperty( attSUBJECT, atpSTRING, v ); } // TNEF encoding: Normal = 3, high = 2, low = 1 // MAPI encoding: Normal = -1, high = 0, low = 1 void KTNEFWriter::setPriority( Priority p ) { QVariant v( (Q_UINT32)p ); addProperty( attMSGPRIORITY, atpSHORT, v ); } void KTNEFWriter::setAlarm( const QString& /*description*/, AlarmAction /*action*/, const QDateTime& /*wakeBefore*/ ) { }