4
Copyright (C) 2002 Bo Thorsen <bo@sonofthor.dk>
6
This file is part of KTNEF, the KDE TNEF support library/program.
8
This library is free software; you can redistribute it and/or
9
modify it under the terms of the GNU Library General Public
10
License as published by the Free Software Foundation; either
11
version 2 of the License, or (at your option) any later version.
13
This library is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
Library General Public License for more details.
18
You should have received a copy of the GNU Library General Public License
19
along with this library; see the file COPYING.LIB. If not, write to
20
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
Boston, MA 02110-1301, USA.
26
#endif /* HAVE_CONFIG_H */
32
#include <QDataStream>
38
#include "ktnef/ktnefwriter.h"
39
#include "ktnef/ktnefproperty.h"
40
#include "ktnef/ktnefpropertyset.h"
41
#include "ktnef/ktnefdefs.h"
43
using namespace KTnef;
45
class KTNEFWriter::PrivateData {
47
PrivateData() { mFirstAttachNum = QDateTime::currentDateTime().toTime_t(); }
49
KTNEFPropertySet properties;
50
quint16 mFirstAttachNum;
54
KTNEFWriter::KTNEFWriter() {
55
mData = new PrivateData;
57
// This is not something the user should fiddle with
58
// First set the TNEF version
59
QVariant v(0x00010000);
60
addProperty( attTNEFVERSION, atpDWORD, v );
62
// Now set the code page to something reasonable. TODO: Use the right one
63
QVariant v1( (quint32)0x4e4 );
64
QVariant v2( (quint32)0x0 );
69
addProperty( attOEMCODEPAGE, atpBYTE, list );
72
KTNEFWriter::~KTNEFWriter() {
77
void KTNEFWriter::addProperty( int tag, int type, const QVariant& value ) {
78
mData->properties.addProperty( tag, type, value );
82
void addToChecksum( quint32 i, quint16 &checksum ) {
84
checksum += (i >> 8) & 0xff;
85
checksum += (i >> 16) & 0xff;
86
checksum += (i >> 24) & 0xff;
89
void addToChecksum( QByteArray &cs, quint16 &checksum ) {
90
int len = cs.length();
91
for (int i=0; i<len; i++)
92
checksum += (quint8)cs[i];
95
void writeCString( QDataStream &stream, QByteArray &str ) {
96
stream.writeRawData( str.data(), str.length() );
100
quint32 mergeTagAndType( quint32 tag, quint32 type ) {
101
return ( ( type & 0xffff ) << 16 ) | ( tag & 0xffff );
104
/* This writes a TNEF property to the file.
106
* A TNEF property has a 1 byte type (LVL_MESSAGE or LVL_ATTACHMENT),
107
* a 4 byte type/tag, a 4 byte length, the data and finally the checksum.
109
* The checksum is a 16 byte int with all bytes in the data added.
111
bool KTNEFWriter::writeProperty( QDataStream &stream, int &bytes, int tag) {
112
QMap<int,KTNEFProperty*>& properties = mData->properties.properties();
113
QMap<int,KTNEFProperty*>::Iterator it = properties.find( tag );
115
if ( it == properties.end() )
118
KTNEFProperty *property = *it;
121
quint16 checksum = 0;
122
QList<QVariant> list;
131
i = property->value().toUInt() & 0xff;
134
stream << (quint8)LVL_MESSAGE;
135
stream << mergeTagAndType( tag, property->type() );
136
stream << (quint32)1;
145
i = property->value().toUInt() & 0xffff;
146
addToChecksum( i, checksum );
148
stream << (quint8)LVL_MESSAGE;
149
stream << mergeTagAndType( tag, property->type() );
150
stream << (quint32)2;
151
stream << (quint16)i;
158
i = property->value().toUInt();
159
addToChecksum( i, checksum );
161
stream << (quint8)LVL_MESSAGE;
162
stream << mergeTagAndType( tag, property->type() );
163
stream << (quint32)4;
164
stream << (quint32)i;
171
list = property->value().toList();
172
assert( list.count() == 2 );
174
stream << (quint8)LVL_MESSAGE;
175
stream << mergeTagAndType( tag, property->type() );
176
stream << (quint32)8;
179
addToChecksum( i, checksum );
180
stream << (quint32)i;
182
addToChecksum( i, checksum );
183
stream << (quint32)i;
193
cs = property->value().toString().toLocal8Bit();
194
addToChecksum( cs, checksum );
196
stream << (quint8)LVL_MESSAGE;
197
stream << mergeTagAndType( tag, property->type() );
198
stream << (quint32)cs.length()+1;
199
writeCString( stream, cs );
201
bytes += 9 + cs.length()+1;
205
// 2 QString encoded to a TRP structure
206
list = property->value().toList();
207
assert( list.count() == 2 );
209
cs = list[0].toString().toLocal8Bit(); // Name
210
cs2 = (QString("smtp:") + list[1].toString()).toLocal8Bit(); // Email address
211
i = 18 + cs.length() + cs2.length(); // 2 * sizof(TRP) + strings + 2x'\0'
213
stream << (quint8)LVL_MESSAGE;
214
stream << mergeTagAndType( tag, property->type() );
215
stream << (quint32)i;
217
// The stream has to be aligned to 4 bytes for the strings
218
// TODO: Or does it? Looks like Outlook doesn't do this
220
// Write the first TRP structure
221
stream << (quint16)4; // trpidOneOff
222
stream << (quint16)i; // totalsize
223
stream << (quint16)(cs.length()+1); // sizeof name
224
stream << (quint16)(cs2.length()+1); // sizeof address
226
// if ( bytes % 4 != 0 )
230
writeCString( stream, cs );
231
writeCString( stream, cs2 );
233
// Write the empty padding TRP structure (just zeroes)
234
stream << (quint32)0 << (quint32)0;
236
addToChecksum( 4, checksum );
237
addToChecksum( i, checksum );
238
addToChecksum( cs.length()+1, checksum );
239
addToChecksum( cs2.length()+1, checksum );
240
addToChecksum( cs, checksum );
241
addToChecksum( cs2, checksum );
248
case attDATEMODIFIED:
250
dt = property->value().toDateTime();
254
stream << (quint8)LVL_MESSAGE;
255
stream << mergeTagAndType( tag, property->type() );
256
stream << (quint32)14;
258
i = (quint16)date.year();
259
addToChecksum( i, checksum );
260
stream << (quint16)i;
261
i = (quint16)date.month();
262
addToChecksum( i, checksum );
263
stream << (quint16)i;
264
i = (quint16)date.day();
265
addToChecksum( i, checksum );
266
stream << (quint16)i;
267
i = (quint16)time.hour();
268
addToChecksum( i, checksum );
269
stream << (quint16)i;
270
i = (quint16)time.minute();
271
addToChecksum( i, checksum );
272
stream << (quint16)i;
273
i = (quint16)time.second();
274
addToChecksum( i, checksum );
275
stream << (quint16)i;
276
i = (quint16)date.dayOfWeek();
277
addToChecksum( i, checksum );
278
stream << (quint16)i;
285
if ( c & fmsRead ) flag |= MSGFLAG_READ;
286
if ( !( c & fmsModified ) ) flag |= MSGFLAG_UNMODIFIED;
287
if ( c & fmsSubmitted ) flag |= MSGFLAG_SUBMIT;
288
if ( c & fmsHasAttach ) flag |= MSGFLAG_HASATTACH;
289
if ( c & fmsLocal ) flag |= MSGFLAG_UNSENT;
292
i = property->value().toUInt();
293
stream << (quint8)LVL_MESSAGE;
294
stream << (quint32)type;
295
stream << (quint32)2;
297
addToChecksum( i, checksum );
298
// from reader: d->message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
300
kDebug() << "Message Status" << " (length=" << i2 << ")" << endl;
305
kDebug() << "Unknown TNEF tag: " << tag << endl;
309
stream << (quint16)checksum;
314
bool KTNEFWriter::writeFile( QIODevice &file ) {
315
if ( !file.open( QIODevice::WriteOnly ) )
318
QDataStream stream( &file );
319
return writeFile( stream );
323
bool KTNEFWriter::writeFile( QDataStream &stream ) {
324
stream.setByteOrder( QDataStream::LittleEndian );
326
// Start by writing the opening TNEF stuff
327
stream << TNEF_SIGNATURE;
329
// Store the PR_ATTACH_NUM value for the first attachment
330
// ( must be stored even if *no* attachments are stored )
331
stream << mData->mFirstAttachNum;
333
// Now do some writing
335
int bytesWritten = 0;
336
ok &= writeProperty( stream, bytesWritten, attTNEFVERSION );
337
ok &= writeProperty( stream, bytesWritten, attOEMCODEPAGE );
338
ok &= writeProperty( stream, bytesWritten, attMSGCLASS );
339
ok &= writeProperty( stream, bytesWritten, attMSGPRIORITY );
340
ok &= writeProperty( stream, bytesWritten, attSUBJECT );
341
ok &= writeProperty( stream, bytesWritten, attDATESENT );
342
ok &= writeProperty( stream, bytesWritten, attDATESTART );
343
ok &= writeProperty( stream, bytesWritten, attDATEEND );
344
// ok &= writeProperty( stream, bytesWritten, attAIDOWNER );
345
ok &= writeProperty( stream, bytesWritten, attREQUESTRES );
346
ok &= writeProperty( stream, bytesWritten, attFROM );
347
ok &= writeProperty( stream, bytesWritten, attDATERECD );
348
ok &= writeProperty( stream, bytesWritten, attMSGSTATUS );
349
ok &= writeProperty( stream, bytesWritten, attBODY );
354
void KTNEFWriter::setSender(const QString &name, const QString &email) {
355
assert( !name.isEmpty() );
356
assert( !email.isEmpty() );
359
QVariant v2( email );
361
QList<QVariant> list;
366
addProperty( attFROM, 0, list ); // What's up with the 0 here ??
369
void KTNEFWriter::setMessageType(MessageType m) {
370
// Note that the MessageType list here is probably not long enough,
371
// more entries are most likely needed later
376
v = QVariant( QString( "IPM.Appointment" ) );
379
case MeetingCancelled:
380
v = QVariant( QString( "IPM.Schedule.Meeting.Cancelled" ) );
384
v = QVariant( QString( "IPM.Schedule.Meeting.Request" ) );
388
v = QVariant( QString( "IPM.Schedule.Meeting.Resp.Neg" ) );
392
v = QVariant( QString( "IPM.Schedule.Meeting.Resp.Pos" ) );
397
v = QVariant( QString( "IPM.Schedule.Meeting.Resp.Tent" ) );
404
addProperty( attMSGCLASS, atpWORD, v );
408
void KTNEFWriter::setMethod( Method )
414
void KTNEFWriter::clearAttendees()
420
void KTNEFWriter::addAttendee( const QString& /*cn*/, Role /*r*/, PartStat /*p*/,
421
bool /*rsvp*/, const QString& /*mailto*/ )
427
// I assume this is the same as the sender?
428
// U also assume that this is like "Name <address>"
429
void KTNEFWriter::setOrganizer( const QString& organizer ) {
430
int i = organizer.indexOf( '<' );
435
QString name = organizer.left( i );
438
QString email = organizer.right( i+1 );
439
email = email.left( email.length()-1 );
442
setSender( name, email );
446
void KTNEFWriter::setDtStart( const QDateTime& dtStart ) {
447
QVariant v( dtStart );
448
addProperty( attDATESTART, atpDATE, v );
452
void KTNEFWriter::setDtEnd( const QDateTime& dtEnd ) {
454
addProperty( attDATEEND, atpDATE, v );
458
void KTNEFWriter::setLocation( const QString& /*location*/ )
464
void KTNEFWriter::setUID( const QString& uid ) {
466
addProperty( attMSGID, atpSTRING, v );
471
void KTNEFWriter::setDtStamp( const QDateTime& dtStamp ) {
472
QVariant v( dtStamp );
473
addProperty( attDATESENT, atpDATE, v );
477
void KTNEFWriter::setCategories( const QStringList& )
483
// I hope this is the body
484
void KTNEFWriter::setDescription( const QString &body ) {
486
addProperty( attBODY, atpTEXT, v );
490
void KTNEFWriter::setSummary( const QString &s ) {
492
addProperty( attSUBJECT, atpSTRING, v );
495
// TNEF encoding: Normal = 3, high = 2, low = 1
496
// MAPI encoding: Normal = -1, high = 0, low = 1
497
void KTNEFWriter::setPriority( Priority p ) {
498
QVariant v( (quint32)p );
499
addProperty( attMSGPRIORITY, atpSHORT, v );
503
void KTNEFWriter::setAlarm( const QString& /*description*/, AlarmAction /*action*/,
504
const QDateTime& /*wakeBefore*/ )