summaryrefslogtreecommitdiffstats
path: root/ktnef/lib/ktnefwriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ktnef/lib/ktnefwriter.cpp')
-rw-r--r--ktnef/lib/ktnefwriter.cpp497
1 files changed, 497 insertions, 0 deletions
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*/ )
+{
+
+}