diff options
Diffstat (limited to 'ktnef/lib')
-rw-r--r-- | ktnef/lib/Makefile.am | 11 | ||||
-rw-r--r-- | ktnef/lib/ktnefattach.cpp | 127 | ||||
-rw-r--r-- | ktnef/lib/ktnefmessage.cpp | 82 | ||||
-rw-r--r-- | ktnef/lib/ktnefparser.cpp | 883 | ||||
-rw-r--r-- | ktnef/lib/ktnefproperty.cpp | 99 | ||||
-rw-r--r-- | ktnef/lib/ktnefpropertyset.cpp | 155 | ||||
-rw-r--r-- | ktnef/lib/ktnefwriter.cpp | 497 | ||||
-rw-r--r-- | ktnef/lib/lzfu.cpp | 178 | ||||
-rw-r--r-- | ktnef/lib/lzfu.h | 25 | ||||
-rw-r--r-- | ktnef/lib/mapi.cpp | 222 | ||||
-rw-r--r-- | ktnef/lib/mapi.h | 26 |
11 files changed, 2305 insertions, 0 deletions
diff --git a/ktnef/lib/Makefile.am b/ktnef/lib/Makefile.am new file mode 100644 index 000000000..731e3a377 --- /dev/null +++ b/ktnef/lib/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/ktnef $(all_includes) + +lib_LTLIBRARIES = libktnef.la + +libktnef_la_SOURCES = ktnefparser.cpp ktnefproperty.cpp ktnefattach.cpp mapi.cpp \ + ktnefpropertyset.cpp ktnefmessage.cpp ktnefwriter.cpp lzfu.cpp +libktnef_la_LIBADD = $(LIB_KDECORE) $(LIB_KIO) +libktnef_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -no-undefined +libktnef_la_METASOURCES = AUTO + +noinst_HEADERS = mapi.h lzfu.h diff --git a/ktnef/lib/ktnefattach.cpp b/ktnef/lib/ktnefattach.cpp new file mode 100644 index 000000000..76b1aeef0 --- /dev/null +++ b/ktnef/lib/ktnefattach.cpp @@ -0,0 +1,127 @@ +/* + ktnefattach.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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/ktnefattach.h" +#include "ktnef/ktnefproperty.h" + +class KTNEFAttach::AttachPrivate +{ +public: + int state_; + int size_; + int offset_; + int displaysize_; + QString name_; + int index_; + QString filename_; + QString displayname_; + QString mimetag_; + QString extension_; +}; + +KTNEFAttach::KTNEFAttach() +{ + d = new AttachPrivate; + d->state_ = Unparsed; + d->offset_ = -1; + d->size_ = 0; + d->displaysize_ = 0; + d->index_ = -1; +} + +KTNEFAttach::~KTNEFAttach() +{ + delete d; +} + +void KTNEFAttach::setTitleParsed() +{ d->state_ |= TitleParsed; } + +void KTNEFAttach::setDataParsed() +{ d->state_ |= DataParsed; } + +void KTNEFAttach::unsetDataParser() +{ d->state_ = ( d->state_ & ~DataParsed ); } + +void KTNEFAttach::setInfoParsed() +{ d->state_ |= InfoParsed; } + +bool KTNEFAttach::titleParsed() const +{ return (d->state_ & TitleParsed); } + +bool KTNEFAttach::dataParsed() const +{ return (d->state_ & DataParsed); } + +bool KTNEFAttach::infoParsed() const +{ return (d->state_ & InfoParsed); } + +bool KTNEFAttach::checkState(int state) const +{ return (d->state_ & state); } + +int KTNEFAttach::offset() const +{ return d->offset_; } + +void KTNEFAttach::setOffset(int n) +{ setDataParsed(); d->offset_ = n; } + +int KTNEFAttach::size() const +{ return d->size_; } + +void KTNEFAttach::setSize(int s) +{ d->size_ = s; } + +int KTNEFAttach::displaySize() const +{ return d->displaysize_; } + +void KTNEFAttach::setDisplaySize(int s) +{ d->displaysize_ = s; } + +QString KTNEFAttach::name() const +{ return d->name_; } + +void KTNEFAttach::setName(const QString& str) +{ setTitleParsed(); d->name_ = str; } + +int KTNEFAttach::index() const +{ return d->index_; } + +void KTNEFAttach::setIndex(int i) +{ setInfoParsed(); d->index_ = i; } + +QString KTNEFAttach::fileName() const +{ return d->filename_; } + +void KTNEFAttach::setFileName(const QString& str) +{ d->filename_ = str; } + +QString KTNEFAttach::displayName() const +{ return d->displayname_; } + +void KTNEFAttach::setDisplayName(const QString& str) +{ d->displayname_ = str; } + +QString KTNEFAttach::mimeTag() const +{ return d->mimetag_; } + +void KTNEFAttach::setMimeTag(const QString& str) +{ d->mimetag_ = str; } + +QString KTNEFAttach::extension() const +{ return d->extension_; } + +void KTNEFAttach::setExtension(const QString& str) +{ d->extension_ = str; } diff --git a/ktnef/lib/ktnefmessage.cpp b/ktnef/lib/ktnefmessage.cpp new file mode 100644 index 000000000..d94efb6c4 --- /dev/null +++ b/ktnef/lib/ktnefmessage.cpp @@ -0,0 +1,82 @@ +/* + ktnefmessage.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 <qbuffer.h> + +class KTNEFMessage::MessagePrivate +{ +public: + MessagePrivate() + { + attachments_.setAutoDelete( true ); + } + + QPtrList<KTNEFAttach> attachments_; +}; + +KTNEFMessage::KTNEFMessage() +{ + d = new MessagePrivate; +} + +KTNEFMessage::~KTNEFMessage() +{ + delete d; +} + +const QPtrList<KTNEFAttach>& KTNEFMessage::attachmentList() const +{ + return d->attachments_; +} + +KTNEFAttach* KTNEFMessage::attachment( const QString& filename ) const +{ + QPtrListIterator<KTNEFAttach> it( d->attachments_ ); + for ( ; it.current(); ++it ) + if ( it.current()->name() == filename ) + return it.current(); + return 0; +} + +void KTNEFMessage::addAttachment( KTNEFAttach *attach ) +{ + d->attachments_.append( attach ); +} + +void KTNEFMessage::clearAttachments() +{ + d->attachments_.clear(); +} + +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 ) ) + lzfu_decompress( &input, &output ); + return QString( rtf ); + } +} diff --git a/ktnef/lib/ktnefparser.cpp b/ktnef/lib/ktnefparser.cpp new file mode 100644 index 000000000..0c9881400 --- /dev/null +++ b/ktnef/lib/ktnefparser.cpp @@ -0,0 +1,883 @@ +/* + ktnefparser.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 <config.h> +#endif /* HAVE_CONFIG_H */ + +#include "ktnef/ktnefparser.h" +#include "ktnef/ktnefattach.h" +#include "ktnef/ktnefproperty.h" +#include "ktnef/ktnefmessage.h" + +#include <qdatetime.h> +#include <qdatastream.h> +#include <qfile.h> +#include <qvariant.h> +#include <kdebug.h> +#include <kmimetype.h> +#include <ksavefile.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#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<int,KTNEFProperty*>& 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; + u = 0; + 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<QVariant> recipTable; + d->stream_ >> rows; + for ( uint i=0; i<rows; i++ ) + { + QMap<int,KTNEFProperty*> 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 ) ) { + 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<KTNEFAttach> it(d->message_->attachmentList()); + for (;it.current();++it) + if (!extractAttachmentTo(it.current(),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(); + delete d->message_; + d->message_ = new KTNEFMessage(); + 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<int,KTNEFProperty*>& props ) +{ + QString s, dn, addr, t; + QMap<int,KTNEFProperty*>::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<totalLen; i++ ) + stream >> 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<fullLen; i++ ) + stream >> 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<QVariant>(); + } + for ( int i=0; i<n; i++ ) + { + value.clear(); + switch(mapi.type & 0x0FFF) + { + case MAPI_TYPE_UINT16: + stream >> 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;i++) + { + value.clear(); + value.asString() = readMAPIString( stream ); + } + break; + case MAPI_TYPE_USTRING: + mapi.type = MAPI_TYPE_NONE; + break; + case MAPI_TYPE_OBJECT: + case MAPI_TYPE_BINARY: + if ( ISVECTOR( mapi ) ) + d = 1; + else + stream >> d; + for (uint i=0;i<d;i++) + { + value.clear(); + Q_UINT32 len; + stream >> 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<fullLen; i++ ) + stream >> 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<int,KTNEFProperty*>& props, KTNEFAttach *attach ) +{ + Q_UINT32 n; + MAPI_value mapi; + KTNEFProperty *p; + QMap<int,KTNEFProperty*>::ConstIterator it; + bool foundAttachment = false; + + // 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;i<n;i++) + { + if (d->stream_.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; + } + else if ( mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0 ) + { + foundAttachment = true; + int len = mapi.value.toByteArray().size(); + ALIGN( len, 4 ) + attach->setSize( len ); + attach->setOffset( d->device_->at() - len ); + attach->addAttribute( attATTACHDATA, atpBYTE, QString( "< size=%1 >" ).arg( len ), false ); + } + } + 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; + } + + if ( foundAttachment && attach ) + { + attach->setIndex( attach->property( MAPI_TAG_INDEX ).toUInt() ); + attach->setDisplaySize( attach->property( MAPI_TAG_SIZE ).toUInt() ); + QString str = attach->property( MAPI_TAG_DISPLAYNAME ).toString(); + if ( !str.isEmpty() ) + attach->setDisplayName( str ); + attach->setFileName( attach->property( MAPI_TAG_FILENAME ).toString() ); + str = attach->property( MAPI_TAG_MIMETAG ).toString(); + if ( !str.isEmpty() ) + attach->setMimeTag( str ); + attach->setExtension( attach->property( MAPI_TAG_EXTENSION ).toString() ); + if ( attach->name().isEmpty() ) + attach->setName( attach->fileName() ); + } + + return true; +} diff --git a/ktnef/lib/ktnefproperty.cpp b/ktnef/lib/ktnefproperty.cpp new file mode 100644 index 000000000..3845b8d88 --- /dev/null +++ b/ktnef/lib/ktnefproperty.cpp @@ -0,0 +1,99 @@ +/* + ktnefproperty.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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/ktnefproperty.h" +#include "mapi.h" +#include <qdatetime.h> +#include <ctype.h> + +KTNEFProperty::KTNEFProperty() +{ +} + +KTNEFProperty::KTNEFProperty( int key_, int type_, const QVariant& value_, const QVariant& name_ ) + : _key( key_ ), _type( type_ ), _value( value_ ), _name( name_ ) +{ +} + +KTNEFProperty::KTNEFProperty( const KTNEFProperty& p ) + : _key( p._key ), _type( p._type ), _value( p._value ), _name( p._name ) +{ +} + +QString KTNEFProperty::keyString() +{ + if ( _name.isValid() ) + { + if ( _name.type() == QVariant::String ) + return _name.asString(); + else + return mapiNamedTagString( _name.asUInt(), _key ); + } + else + return mapiTagString( _key ); +} + +QString KTNEFProperty::formatValue( const QVariant& value, bool beautify ) +{ + if ( value.type() == QVariant::ByteArray ) + { + // check the first bytes (up to 8) if they are + // printable characters + QByteArray arr = value.toByteArray(); + bool printable = true; + for ( int i=QMIN( arr.size(), 8 )-1; i>=0 && printable; i-- ) + printable = ( isprint( arr[ i ] ) != 0 ); + if ( !printable ) + { + QString s; + uint i; + uint txtCount = beautify ? QMIN( arr.size(), 32 ) : arr.size(); + for ( i=0; i < txtCount; ++i ) + { + s.append( QString().sprintf( "%02X", ( uchar )arr[ i ] ) ); + if( beautify ) + s.append( " " ); + } + if ( i < arr.size() ) + s.append( "... (size=" + QString::number( arr.size() ) + ")" ); + return s; + } + } + //else if ( value.type() == QVariant::DateTime ) + // return value.toDateTime().toString(); + return value.toString(); +} + +QString KTNEFProperty::valueString() +{ + return formatValue( _value ); +} + +int KTNEFProperty::key() const +{ return _key; } + +int KTNEFProperty::type() const +{ return _type; } + +QVariant KTNEFProperty::value() const +{ return _value; } + +QVariant KTNEFProperty::name() const +{ return _name; } + +bool KTNEFProperty::isVector() const +{ return ( _value.type() == QVariant::List ); } diff --git a/ktnef/lib/ktnefpropertyset.cpp b/ktnef/lib/ktnefpropertyset.cpp new file mode 100644 index 000000000..cf35362fe --- /dev/null +++ b/ktnef/lib/ktnefpropertyset.cpp @@ -0,0 +1,155 @@ +/* + ktnefpropertyset.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 <kdebug.h> + +KTNEFPropertySet::KTNEFPropertySet() +{ +} + +KTNEFPropertySet::~KTNEFPropertySet() +{ + clear( true ); +} + +void KTNEFPropertySet::addProperty( int key, int type, const QVariant& value, const QVariant& name, bool overwrite ) +{ + QMap<int,KTNEFProperty*>::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<int,KTNEFProperty*>::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<int,KTNEFProperty*>::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 ){ + s = ""; + for ( QValueList<QVariant>::ConstIterator lit = value.listBegin(); + lit != value.listEnd(); + ++lit ){ + if( !s.isEmpty() ) + s += ','; + s += KTNEFProperty::formatValue( *lit, false ); + } + }else{ + s = KTNEFProperty::formatValue( value, false ); + } + return upper ? s.upper() : s; + } + } + } + return fallback; +} + + +QMap<int,KTNEFProperty*>& KTNEFPropertySet::properties() +{ + return properties_; +} + +const QMap<int,KTNEFProperty*>& KTNEFPropertySet::properties() const +{ + return properties_; +} + +QVariant KTNEFPropertySet::property( int key ) const +{ + QMap<int,KTNEFProperty*>::ConstIterator it = properties_.find( key ); + if ( it == properties_.end() ) + return QVariant(); + else + return ( *it )->value(); +} + +void KTNEFPropertySet::clear( bool deleteAll ) +{ + if ( deleteAll ) + { + for ( QMap<int,KTNEFProperty*>::ConstIterator it=properties_.begin(); it!=properties_.end(); ++it ) + delete ( *it ); + for ( QMap<int,KTNEFProperty*>::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<int,KTNEFProperty*>::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<int,KTNEFProperty*>& KTNEFPropertySet::attributes() +{ + return attributes_; +} + +const QMap<int,KTNEFProperty*>& KTNEFPropertySet::attributes() const +{ + return attributes_; +} + +QVariant KTNEFPropertySet::attribute( int key ) const +{ + QMap<int,KTNEFProperty*>::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 new file mode 100644 index 000000000..ce35dbd20 --- /dev/null +++ b/ktnef/lib/ktnefwriter.cpp @@ -0,0 +1,497 @@ +/* + ktnefwriter.cpp + + Copyright (C) 2002 Bo Thorsen <bo@sonofthor.dk> + + 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 <config.h> +#endif /* HAVE_CONFIG_H */ + +#include "ktnef/ktnefwriter.h" +#include "ktnef/ktnefproperty.h" +#include "ktnef/ktnefpropertyset.h" + +#include <qfile.h> +#include <qdatetime.h> +#include <qdatastream.h> +#include <kdebug.h> +#include <assert.h> + +#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<QVariant> 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 ) { + int len = cs.length(); + for (int i=0; i<len; i++) + checksum += (Q_UINT8)cs[i]; +} + +void writeCString( QDataStream &stream, QCString &str ) { + stream.writeRawBytes( str.data(), str.length() ); + stream << (Q_UINT8)0; +} + +Q_UINT32 mergeTagAndType( Q_UINT32 tag, Q_UINT32 type ) { + return ( ( type & 0xffff ) << 16 ) | ( tag & 0xffff ); +} + +/* This writes a TNEF property to the file. + * + * A TNEF property has a 1 byte type (LVL_MESSAGE or LVL_ATTACHMENT), + * a 4 byte type/tag, a 4 byte length, the data and finally the checksum. + * + * The checksum is a 16 byte int with all bytes in the data added. + */ +bool KTNEFWriter::writeProperty( QDataStream &stream, int &bytes, int tag) { + QMap<int,KTNEFProperty*>& properties = mData->properties.properties(); + QMap<int,KTNEFProperty*>::Iterator it = properties.find( tag ); + + if ( it == properties.end() ) + return false; + + KTNEFProperty *property = *it; + + Q_UINT32 i; + Q_UINT16 checksum = 0; + QValueList<QVariant> list; + QString s; + QCString 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 ) ) + 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<QVariant> 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 <address>" +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*/ ) +{ + +} diff --git a/ktnef/lib/lzfu.cpp b/ktnef/lib/lzfu.cpp new file mode 100644 index 000000000..ab4fa36a0 --- /dev/null +++ b/ktnef/lib/lzfu.cpp @@ -0,0 +1,178 @@ +/* + lzfu.cpp + + Copyright (C) 2003 Michael Goffioul <kdeprint@swing.be> + + 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 "config.h" + +#include "lzfu.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +#include <sys/types.h> +#include <string.h> +#include <qiodevice.h> +#include <stdio.h> + +//#define DO_DEBUG + +#define LZFU_COMPRESSED 0x75465a4c +#define LZFU_UNCOMPRESSED 0x414c454d + +#define LZFU_INITDICT "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \ + "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscrip" \ + "t \\fdecor MS Sans SerifSymbolArialTimes Ne" \ + "w RomanCourier{\\colortbl\\red0\\green0\\blue0" \ + "\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab" \ + "\\tx" +#define LZFU_INITLENGTH 207 + +typedef struct _lzfuheader { + uint32_t cbSize; + uint32_t cbRawSize; + uint32_t dwMagic; + uint32_t dwCRC; +} lzfuheader; + +#define FLAG(f,n) (f>>n)&0x1 + +/*typedef struct _blockheader { + unsigned int offset:12; + unsigned int length:4; +} blockheader;*/ + +#define OFFSET(b) (b>>4)&0xFFF +#define LENGTH(b) ((b&0xF)+2) + +int lzfu_decompress(QIODevice *input, QIODevice *output) +{ + unsigned char window[4096]; + unsigned int wlength = 0, cursor = 0, ocursor = 0; + lzfuheader lzfuhdr; + //blockheader blkhdr; + uint16_t blkhdr; + char bFlags; + int nFlags; + + memcpy(window, LZFU_INITDICT, LZFU_INITLENGTH); + wlength = LZFU_INITLENGTH; + if (input->readBlock((char*)&lzfuhdr, sizeof(lzfuhdr)) != sizeof(lzfuhdr)) + { + fprintf(stderr, "unexpected eof, cannot read LZFU header\n"); + return -1; + } + cursor += sizeof( lzfuhdr ); +#ifdef DO_DEBUG + fprintf(stdout, "total size : %d\n", lzfuhdr.cbSize+4); + fprintf(stdout, "raw size : %d\n", lzfuhdr.cbRawSize); + fprintf(stdout, "compressed : %s\n", (lzfuhdr.dwMagic == LZFU_COMPRESSED ? "yes" : "no")); + fprintf(stdout, "CRC : %x\n", lzfuhdr.dwCRC); + fprintf(stdout, "\n"); +#endif + + while (cursor < lzfuhdr.cbSize+4 && ocursor < lzfuhdr.cbRawSize && !input->atEnd()) + { + if (input->readBlock(&bFlags, 1) != 1) + { + fprintf(stderr, "unexpected eof, cannot read chunk flag\n"); + return -1; + } + nFlags = 8; + cursor++; +#ifdef DO_DEBUG + fprintf(stdout, "Flags : "); + for (int i=nFlags-1; i>=0; i--) + fprintf(stdout, "%d", FLAG(bFlags, i)); + fprintf(stdout, "\n"); +#endif + for (int i=0; i<nFlags && ocursor<lzfuhdr.cbRawSize && cursor<lzfuhdr.cbSize+4; i++) + { + if (FLAG(bFlags, i)) + { + // compressed chunck + char c1, c2; + if (input->readBlock(&c1, 1) != 1 || input->readBlock(&c2, 1) != 1) + { + fprintf(stderr, "unexpected eof, cannot read block header\n"); + return -1; + } + blkhdr = c1; + blkhdr <<= 8; + blkhdr |= (0xFF&c2); + unsigned int offset = OFFSET(blkhdr), length = LENGTH(blkhdr); + cursor += 2; +#ifdef DO_DEBUG + fprintf( stdout, "block : offset=%.4d [%d], length=%.2d (0x%04X)\n", OFFSET( blkhdr ), wlength, LENGTH( blkhdr ), blkhdr ); +#endif + //if (offset >= wlength) + //{ + // break; + //} +#ifdef DO_DEBUG + fprintf( stdout, "block : " ); +#endif + for (unsigned int i=0; i<length; i++) + { + c1 = window[( offset+i ) % 4096]; + //if (wlength < 4096) + //{ + window[wlength] = c1; + wlength = ( wlength+1 ) % 4096; + //} +#ifdef DO_DEBUG + if ( c1 == '\n' ) + fprintf( stdout, "\nblock : " ); + else + fprintf( stdout, "%c", c1 ); +#endif + output->putch(c1); + ocursor++; + } +#ifdef DO_DEBUG + fprintf( stdout, "\n" ); +#endif + } + else + { + // uncompressed chunk (char) + signed char c = (char)input->getch(); + if (c == -1) + { + if (!input->atEnd()) + { + fprintf(stderr, "unexpected eof, cannot read character\n"); + return -1; + } + break; + } +#ifdef DO_DEBUG + fprintf( stdout, "char : %c\n", c ); +#endif + cursor++; + //if (wlength < 4096) + //{ + window[wlength] = c; + wlength = ( wlength+1 ) % 4096; + //} + output->putch(c); + ocursor++; + } + } + + } + + return 0; +} diff --git a/ktnef/lib/lzfu.h b/ktnef/lib/lzfu.h new file mode 100644 index 000000000..4f8365586 --- /dev/null +++ b/ktnef/lib/lzfu.h @@ -0,0 +1,25 @@ +/* + lzfu.h + + Copyright (C) 2003 Michael Goffioul <kdeprint@swing.be> + + 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 LZFU_H +#define LZFU_H + +class QIODevice; + +int lzfu_decompress( QIODevice *input, QIODevice *output ); + +#endif /* LZFU_H */ diff --git a/ktnef/lib/mapi.cpp b/ktnef/lib/mapi.cpp new file mode 100644 index 000000000..8122153ce --- /dev/null +++ b/ktnef/lib/mapi.cpp @@ -0,0 +1,222 @@ +/* + mapi.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 "mapi.h" +#include <qmap.h> +#include <klocale.h> + +static struct +{ + int tag; + const char *str; +} MAPI_TagStrings[] = +{ + { 0x0002, I18N_NOOP( "Alternate Recipient Allowed" ) }, + { 0x001A, I18N_NOOP( "Message Class" ) }, + { 0x0023, I18N_NOOP( "Originator Delivery Report Requested" ) }, + { 0x0024, I18N_NOOP( "Originator Return Address" ) }, + { 0x0026, I18N_NOOP( "Priority" ) }, + { 0x0029, I18N_NOOP( "Read Receipt Requested" ) }, + { 0x002B, I18N_NOOP( "Recipient Reassignment Prohibited" ) }, + { 0x002E, I18N_NOOP( "Original Sensitivity" ) }, + { 0x0031, I18N_NOOP( "Report Tag" ) }, + { 0x0036, I18N_NOOP( "Sensitivity" ) }, + { 0x0037, I18N_NOOP( "Subject" ) }, + { 0x0039, I18N_NOOP( "Client Submit Time" ) }, + { 0x003B, I18N_NOOP( "Sent Representing Search Key" ) }, + { 0x003D, I18N_NOOP( "Subject Prefix" ) }, + { 0x0041, I18N_NOOP( "Sent Representing Entry ID" ) }, + { 0x0042, I18N_NOOP( "Sent Representing Name" ) }, + { 0x0047, I18N_NOOP( "Message Submission ID" ) }, + { 0x004D, I18N_NOOP( "Original Author Name" ) }, + { 0x0062, I18N_NOOP( "Owner Appointment ID" ) }, + { 0x0063, I18N_NOOP( "Response Requested" ) }, + { 0x0064, I18N_NOOP( "Sent Representing Address Type" ) }, + { 0x0065, I18N_NOOP( "Sent Representing E-mail Address" ) }, + { 0x0070, I18N_NOOP( "Conversation Topic" ) }, + { 0x0071, I18N_NOOP( "Conversation Index" ) }, + { 0x007F, I18N_NOOP( "TNEF Correlation Key" ) }, + { 0x0C17, I18N_NOOP( "Reply Requested" ) }, + { 0x0C1A, I18N_NOOP( "Sender Name" ) }, + { 0x0C1D, I18N_NOOP( "Sender Search Key" ) }, + { 0x0C1E, I18N_NOOP( "Sender Address Type" ) }, + { 0x0C1F, I18N_NOOP( "Sender E-mail Address" ) }, + { 0x0E01, I18N_NOOP( "Delete After Submit" ) }, + { 0x0E02, I18N_NOOP( "Display Bcc" ) }, + { 0x0E03, I18N_NOOP( "Display Cc" ) }, + { 0x0E04, I18N_NOOP( "Display To" ) }, + { 0x0E06, I18N_NOOP( "Message Delivery Time" ) }, + { 0x0E07, I18N_NOOP( "Message Flags" ) }, + { 0x0E08, I18N_NOOP( "Message Size" ) }, + { 0x0E09, I18N_NOOP( "Parent Entry ID" ) }, + { 0x0E0A, I18N_NOOP( "Sent-Mail Entry ID" ) }, + { 0x0E12, I18N_NOOP( "Message Recipients" ) }, + { 0x0E14, I18N_NOOP( "Submit Flags" ) }, + { 0x0E1B, I18N_NOOP( "Has Attachment" ) }, + { 0x0E1D, I18N_NOOP( "Normalized Subject" ) }, + { 0x0E1F, I18N_NOOP( "RTF In Sync" ) }, + { 0x0E20, I18N_NOOP( "Attachment Size" ) }, + { 0x0E21, I18N_NOOP( "Attachment Number" ) }, + { 0x0FF4, I18N_NOOP( "Access" ) }, + { 0x0FF7, I18N_NOOP( "Access Level" ) }, + { 0x0FF8, I18N_NOOP( "Mapping Signature" ) }, + { 0x0FF9, I18N_NOOP( "Record Key" ) }, + { 0x0FFA, I18N_NOOP( "Store Record Key" ) }, + { 0x0FFB, I18N_NOOP( "Store Entry ID" ) }, + { 0x0FFE, I18N_NOOP( "Object Type" ) }, + { 0x0FFF, I18N_NOOP( "Entry ID" ) }, + { 0x1000, I18N_NOOP( "Message Body" ) }, + { 0x1006, I18N_NOOP( "RTF Sync Body CRC" ) }, + { 0x1007, I18N_NOOP( "RTF Sync Body Count" ) }, + { 0x1008, I18N_NOOP( "RTF Sync Body Tag" ) }, + { 0x1009, I18N_NOOP( "RTF Compressed" ) }, + { 0x1010, I18N_NOOP( "RTF Sync Prefix Count" ) }, + { 0x1011, I18N_NOOP( "RTF Sync Trailing Count" ) }, + { 0x1013, I18N_NOOP( "HTML Message Body" ) }, + { 0x1035, I18N_NOOP( "Message ID" ) }, + { 0x1042, I18N_NOOP( "Parent's Message ID" ) }, + { 0x1080, I18N_NOOP( "Action" ) }, + { 0x1081, I18N_NOOP( "Action Flag" ) }, + { 0x1082, I18N_NOOP( "Action Date" ) }, + { 0x3001, I18N_NOOP( "Display Name" ) }, + { 0x3007, I18N_NOOP( "Creation Time" ) }, + { 0x3008, I18N_NOOP( "Last Modification Time" ) }, + { 0x300B, I18N_NOOP( "Search Key" ) }, + { 0x340D, I18N_NOOP( "Store Support Mask" ) }, + { 0x3414, I18N_NOOP( "MDB Provider" ) }, + { 0x3701, I18N_NOOP( "Attachment Data" ) }, + { 0x3702, I18N_NOOP( "Attachment Encoding" ) }, + { 0x3703, I18N_NOOP( "Attachment Extension" ) }, + { 0x3705, I18N_NOOP( "Attachment Method" ) }, + { 0x3707, I18N_NOOP( "Attachment Long File Name" ) }, + { 0x370B, I18N_NOOP( "Attachment Rendering Position" ) }, + { 0x370E, I18N_NOOP( "Attachment Mime Tag" ) }, + { 0x3714, I18N_NOOP( "Attachment Flags" ) }, + { 0x3A00, I18N_NOOP( "Account" ) }, + { 0x3A05, I18N_NOOP( "Generation" ) }, + { 0x3A06, I18N_NOOP( "Given Name" ) }, + { 0x3A0A, I18N_NOOP( "Initials" ) }, + { 0x3A0B, I18N_NOOP( "Keyword" ) }, + { 0x3A0C, I18N_NOOP( "Language" ) }, + { 0x3A0D, I18N_NOOP( "Location" ) }, + { 0x3A11, I18N_NOOP( "Surname" ) }, + { 0x3A16, I18N_NOOP( "Company Name" ) }, + { 0x3A17, I18N_NOOP( "Title" ) }, + { 0x3A18, I18N_NOOP( "Department Name" ) }, + { 0x3A26, I18N_NOOP( "Country" ) }, + { 0x3A27, I18N_NOOP( "Locality" ) }, + { 0x3A28, I18N_NOOP( "State/Province" ) }, + { 0x3A44, I18N_NOOP( "Middle Name" ) }, + { 0x3A45, I18N_NOOP( "Display Name Prefix" ) }, + + /* Some TNEF attributes */ + { 0x0008, I18N_NOOP( "Owner Appointment ID" ) }, + { 0x0009, I18N_NOOP( "Response Requested" ) }, + { 0x8000, I18N_NOOP( "From" ) }, + { 0x8004, I18N_NOOP( "Subject" ) }, + { 0x8005, I18N_NOOP( "Date Sent" ) }, + { 0x8006, I18N_NOOP( "Date Received" ) }, + { 0x8007, I18N_NOOP( "Message Status" ) }, + { 0x8008, I18N_NOOP( "Message Class" ) }, + { 0x8009, I18N_NOOP( "Message ID" ) }, + { 0x800A, I18N_NOOP( "Parent ID" ) }, + { 0x800B, I18N_NOOP( "Conversation ID" ) }, + { 0x800C, I18N_NOOP( "Body" ) }, + { 0x800D, I18N_NOOP( "Priority" ) }, + { 0x800F, I18N_NOOP( "Attachment Data" ) }, + { 0x8010, I18N_NOOP( "Attachment Title" ) }, + { 0x8011, I18N_NOOP( "Attachment Meta File" ) }, + { 0x8012, I18N_NOOP( "Attachment Create Date" ) }, + { 0x8013, I18N_NOOP( "Attachment Modify Date" ) }, + { 0x8020, I18N_NOOP( "Date Modified" ) }, + { 0x9001, I18N_NOOP( "Attachment Transport File Name" ) }, + { 0x9002, I18N_NOOP( "Attachment Rendering Data" ) }, + { 0x9003, I18N_NOOP( "MAPI Properties" ) }, + { 0x9004, I18N_NOOP( "Recipients Table" ) }, + { 0x9005, I18N_NOOP( "Attachment MAPI Properties" ) }, + { 0x9006, I18N_NOOP( "TNEF Version" ) }, + { 0x9007, I18N_NOOP( "OEM Code Page" ) }, + + { 0, 0 } +}, +MAPI_NamedTagStrings[] = +{ + { 0x8005, I18N_NOOP( "Contact File Under" ) }, + { 0x8017, I18N_NOOP( "Contact Last Name And First Name" ) }, + { 0x8018, I18N_NOOP( "Contact Company And Full Name" ) }, + + { 0x8080, I18N_NOOP( "Contact EMail-1 Full" ) }, + { 0x8082, I18N_NOOP( "Contact EMail-1 Address Type" ) }, + { 0x8083, I18N_NOOP( "Contact EMail-1 Address" ) }, + { 0x8084, I18N_NOOP( "Contact EMail-1 Display Name" ) }, + { 0x8085, I18N_NOOP( "Contact EMail-1 Entry ID" ) }, + + { 0x8090, I18N_NOOP( "Contact EMail-2 Full" ) }, + { 0x8092, I18N_NOOP( "Contact EMail-2 Address Type" ) }, + { 0x8093, I18N_NOOP( "Contact EMail-2 Address" ) }, + { 0x8094, I18N_NOOP( "Contact EMail-2 Display Name" ) }, + { 0x8095, I18N_NOOP( "Contact EMail-2 Entry ID" ) }, + + { 0x8208, I18N_NOOP( "Appointment Location" ) }, + { 0x8208, I18N_NOOP( "Appointment Location" ) }, + { 0x820D, I18N_NOOP( "Appointment Start Date" ) }, + { 0x820E, I18N_NOOP( "Appointment End Date" ) }, + { 0x8213, I18N_NOOP( "Appointment Duration" ) }, + { 0x8218, I18N_NOOP( "Appointment Response Status" ) }, + { 0x8223, I18N_NOOP( "Appointment Is Recurring" ) }, + { 0x8231, I18N_NOOP( "Appointment Recurrence Type" ) }, + { 0x8232, I18N_NOOP( "Appointment Recurrence Pattern" ) }, + { 0x8502, I18N_NOOP( "Reminder Time" ) }, + { 0x8503, I18N_NOOP( "Reminder Set" ) }, + { 0x8516, I18N_NOOP( "Start Date" ) }, + { 0x8517, I18N_NOOP( "End Date" ) }, + { 0x8560, I18N_NOOP( "Reminder Next Time" ) }, + { 0, 0 } +}; +static QMap<int,QString> MAPI_TagMap; +static QMap<int,QString> MAPI_NamedTagMap; + +QString mapiTagString( int key ) +{ + if ( MAPI_TagMap.count() == 0 ) + { + for ( int i=0; MAPI_TagStrings[ i ].str; i++ ) + MAPI_TagMap[ MAPI_TagStrings[ i ].tag ] = i18n(MAPI_TagStrings[ i ].str); + } + QMap<int,QString>::ConstIterator it = MAPI_TagMap.find( key ); + if ( it == MAPI_TagMap.end() ) + return QString().sprintf( "0x%04X", key ); + else + return QString().sprintf( "0x%04X ________: ", key ) + *it; +} + +QString mapiNamedTagString( int key, int tag ) +{ + if ( MAPI_NamedTagMap.count() == 0 ) + { + for ( int i=0; MAPI_NamedTagStrings[ i ].str; i++ ) + MAPI_NamedTagMap[ MAPI_NamedTagStrings[ i ].tag ] = i18n(MAPI_NamedTagStrings[ i ].str); + } + QMap<int,QString>::ConstIterator it = MAPI_NamedTagMap.find( key ); + if ( it == MAPI_NamedTagMap.end() ) + if ( tag >= 0 ) + return QString().sprintf( "0x%04X [0x%04X]: ", tag, key ) + *it; + else + return QString().sprintf( "0x%04X ________:", key ) + *it; + else + return *it; +} diff --git a/ktnef/lib/mapi.h b/ktnef/lib/mapi.h new file mode 100644 index 000000000..fe4a37bf1 --- /dev/null +++ b/ktnef/lib/mapi.h @@ -0,0 +1,26 @@ +/* + mapi.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 MAPI_H +#define MAPI_H + +#include <qstring.h> + +QString mapiTagString( int key ); +QString mapiNamedTagString( int key, int tag = -1 ); + +#endif |