summaryrefslogtreecommitdiffstats
path: root/ktnef/lib
diff options
context:
space:
mode:
Diffstat (limited to 'ktnef/lib')
-rw-r--r--ktnef/lib/Makefile.am11
-rw-r--r--ktnef/lib/ktnefattach.cpp127
-rw-r--r--ktnef/lib/ktnefmessage.cpp82
-rw-r--r--ktnef/lib/ktnefparser.cpp883
-rw-r--r--ktnef/lib/ktnefproperty.cpp99
-rw-r--r--ktnef/lib/ktnefpropertyset.cpp155
-rw-r--r--ktnef/lib/ktnefwriter.cpp497
-rw-r--r--ktnef/lib/lzfu.cpp178
-rw-r--r--ktnef/lib/lzfu.h25
-rw-r--r--ktnef/lib/mapi.cpp222
-rw-r--r--ktnef/lib/mapi.h26
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