KTnef

ktnefwriter.cpp
Go to the documentation of this file.
1/*
2 ktnefwriter.cpp
3
4 SPDX-FileCopyrightText: 2002 Bo Thorsen <bo@sonofthor.dk>
5
6 This file is part of KTNEF, the KDE TNEF support library/program.
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10/**
11 * @file
12 * This file is part of the API for handling TNEF data and
13 * defines the KTNEFWriter class.
14 *
15 * @author Bo Thorsen
16 */
17
18#include "ktnefwriter.h"
19using namespace Qt::Literals::StringLiterals;
20
21#include "ktnefdefs.h"
22#include "ktnefproperty.h"
23#include "ktnefpropertyset.h"
24
25#include "ktnef_debug.h"
26
27#include <QByteArray>
28#include <QDataStream>
29#include <QDateTime>
30#include <QIODevice>
31#include <QList>
32
33#include <cassert>
34
35using namespace KTnef;
36
37/**
38 * KTNEFWriterPrivate class that helps to provide binary compatibility between releases.
39 * @internal
40 */
41//@cond PRIVATE
42class KTnef::KTNEFWriterPrivateData
43{
44public:
45 KTNEFWriterPrivateData()
46 : mFirstAttachNum(QDateTime::currentDateTimeUtc().toSecsSinceEpoch())
47 {
48 }
49
51 const quint16 mFirstAttachNum;
52};
53//@endcond
54
56 : d(new KTnef::KTNEFWriterPrivateData)
57{
58 // This is not something the user should fiddle with
59 // First set the TNEF version
60 QVariant v(0x00010000);
61 addProperty(attTNEFVERSION, atpDWORD, v);
62
63 // Now set the code page to something reasonable. TODO: Use the right one
64 QVariant v1((quint32)0x4e4);
65 QVariant v2((quint32)0x0);
66 QList<QVariant> list;
67 list << v1;
68 list << v2;
69 v = QVariant(list);
70 addProperty(attOEMCODEPAGE, atpBYTE, list);
71}
72
74
75void KTNEFWriter::addProperty(int tag, int type, const QVariant &value)
76{
77 d->properties.addProperty(tag, type, value);
78}
79
80//@cond IGNORE
81void addToChecksum(quint32 i, quint16 &checksum)
82{
83 checksum += i & 0xff;
84 checksum += (i >> 8) & 0xff;
85 checksum += (i >> 16) & 0xff;
86 checksum += (i >> 24) & 0xff;
87}
88
89void addToChecksum(QByteArray &cs, quint16 &checksum)
90{
91 int len = cs.length();
92 for (int i = 0; i < len; i++) {
93 checksum += (quint8)cs[i];
94 }
95}
96
97void writeCString(QDataStream &stream, QByteArray &str)
98{
99 stream.writeRawData(str.data(), str.length());
100 stream << (quint8)0;
101}
102
103quint32 mergeTagAndType(quint32 tag, quint32 type)
104{
105 return ((type & 0xffff) << 16) | (tag & 0xffff);
106}
107//@endcond
108
109/* This writes a TNEF property to the file.
110 *
111 * A TNEF property has a 1 byte type (LVL_MESSAGE or LVL_ATTACHMENT),
112 * a 4 byte type/tag, a 4 byte length, the data and finally the checksum.
113 *
114 * The checksum is a 16 byte int with all bytes in the data added.
115 */
116bool KTNEFWriter::writeProperty(QDataStream &stream, int &bytes, int tag) const
117{
118 QMap<int, KTNEFProperty *> &properties = d->properties.properties();
119 QMap<int, KTNEFProperty *>::Iterator it = properties.find(tag);
120
121 if (it == properties.end()) {
122 return false;
123 }
124
125 KTNEFProperty *property = *it;
126
127 quint32 i;
128 quint16 checksum = 0;
129 QList<QVariant> list;
130 QByteArray cs;
131 QByteArray cs2;
132 QDateTime dt;
133 QDate date;
134 QTime time;
135 switch (tag) {
136 case attMSGSTATUS:
137 // quint8
138 i = property->value().toUInt() & 0xff;
139 checksum = i;
140
141 stream << (quint8)LVL_MESSAGE;
142 stream << mergeTagAndType(tag, property->type());
143 stream << (quint32)1;
144 stream << (quint8)i;
145
146 bytes += 10;
147 break;
148
149 case attMSGPRIORITY:
150 case attREQUESTRES:
151 // quint16
152 i = property->value().toUInt() & 0xffff;
153 addToChecksum(i, checksum);
154
155 stream << (quint8)LVL_MESSAGE;
156 stream << mergeTagAndType(tag, property->type());
157 stream << (quint32)2;
158 stream << (quint16)i;
159
160 bytes += 11;
161 break;
162
163 case attTNEFVERSION:
164 // quint32
165 i = property->value().toUInt();
166 addToChecksum(i, checksum);
167
168 stream << (quint8)LVL_MESSAGE;
169 stream << mergeTagAndType(tag, property->type());
170 stream << (quint32)4;
171 stream << (quint32)i;
172
173 bytes += 13;
174 break;
175
176 case attOEMCODEPAGE:
177 // 2 quint32
178 list = property->value().toList();
179 assert(list.count() == 2);
180
181 stream << (quint8)LVL_MESSAGE;
182 stream << mergeTagAndType(tag, property->type());
183 stream << (quint32)8;
184
185 i = list[0].toInt();
186 addToChecksum(i, checksum);
187 stream << (quint32)i;
188 i = list[1].toInt();
189 addToChecksum(i, checksum);
190 stream << (quint32)i;
191
192 bytes += 17;
193 break;
194
195 case attMSGCLASS:
196 case attSUBJECT:
197 case attBODY:
198 case attMSGID:
199 // QCString
200 cs = property->value().toString().toLocal8Bit();
201 addToChecksum(cs, checksum);
202
203 stream << (quint8)LVL_MESSAGE;
204 stream << mergeTagAndType(tag, property->type());
205 stream << (quint32)cs.length() + 1;
206 writeCString(stream, cs);
207
208 bytes += 9 + cs.length() + 1;
209 break;
210
211 case attFROM:
212 // 2 QString encoded to a TRP structure
213 list = property->value().toList();
214 assert(list.count() == 2);
215
216 cs = list[0].toString().toLocal8Bit(); // Name
217 cs2 = QString("smtp:"_L1 + list[1].toString()).toLocal8Bit(); // Email address
218 i = 18 + cs.length() + cs2.length(); // 2 * sizof(TRP) + strings + 2x'\0'
219
220 stream << (quint8)LVL_MESSAGE;
221 stream << mergeTagAndType(tag, property->type());
222 stream << (quint32)i;
223
224 // The stream has to be aligned to 4 bytes for the strings
225 // TODO: Or does it? Looks like Outlook doesn't do this
226 // bytes += 17;
227 // Write the first TRP structure
228 stream << (quint16)4; // trpidOneOff
229 stream << (quint16)i; // totalsize
230 stream << (quint16)(cs.length() + 1); // sizeof name
231 stream << (quint16)(cs2.length() + 1); // sizeof address
232
233 // if ( bytes % 4 != 0 )
234 // Align the buffer
235
236 // Write the strings
237 writeCString(stream, cs);
238 writeCString(stream, cs2);
239
240 // Write the empty padding TRP structure (just zeroes)
241 stream << (quint32)0 << (quint32)0;
242
243 addToChecksum(4, checksum);
244 addToChecksum(i, checksum);
245 addToChecksum(cs.length() + 1, checksum);
246 addToChecksum(cs2.length() + 1, checksum);
247 addToChecksum(cs, checksum);
248 addToChecksum(cs2, checksum);
249
250 bytes += 10;
251 break;
252
253 case attDATESENT:
254 case attDATERECD:
255 case attDATEMODIFIED:
256 // QDateTime
257 dt = property->value().toDateTime();
258 time = dt.time();
259 date = dt.date();
260
261 stream << (quint8)LVL_MESSAGE;
262 stream << mergeTagAndType(tag, property->type());
263 stream << (quint32)14;
264
265 i = (quint16)date.year();
266 addToChecksum(i, checksum);
267 stream << (quint16)i;
268 i = (quint16)date.month();
269 addToChecksum(i, checksum);
270 stream << (quint16)i;
271 i = (quint16)date.day();
272 addToChecksum(i, checksum);
273 stream << (quint16)i;
274 i = (quint16)time.hour();
275 addToChecksum(i, checksum);
276 stream << (quint16)i;
277 i = (quint16)time.minute();
278 addToChecksum(i, checksum);
279 stream << (quint16)i;
280 i = (quint16)time.second();
281 addToChecksum(i, checksum);
282 stream << (quint16)i;
283 i = (quint16)date.dayOfWeek();
284 addToChecksum(i, checksum);
285 stream << (quint16)i;
286 break;
287 /*
288 case attMSGSTATUS:
289 {
290 quint8 c;
291 quint32 flag = 0;
292 if ( c & fmsRead ) flag |= MSGFLAG_READ;
293 if ( !( c & fmsModified ) ) flag |= MSGFLAG_UNMODIFIED;
294 if ( c & fmsSubmitted ) flag |= MSGFLAG_SUBMIT;
295 if ( c & fmsHasAttach ) flag |= MSGFLAG_HASATTACH;
296 if ( c & fmsLocal ) flag |= MSGFLAG_UNSENT;
297 d->stream_ >> c;
298
299 i = property->value().toUInt();
300 stream << (quint8)LVL_MESSAGE;
301 stream << (quint32)type;
302 stream << (quint32)2;
303 stream << (quint8)i;
304 addToChecksum( i, checksum );
305 // from reader: d->message_->addProperty( 0x0E07, MAPI_TYPE_ULONG, flag );
306 }
307 qCDebug(KTNEF_LOG) << "Message Status" << "(length=" << i2 << ")";
308 break;
309 */
310
311 default:
312 qCDebug(KTNEF_LOG) << "Unknown TNEF tag:" << tag;
313 return false;
314 }
315
316 stream << (quint16)checksum;
317 return true;
318}
319
321{
322 if (!file.open(QIODevice::WriteOnly)) {
323 return false;
324 }
325
326 QDataStream stream(&file);
327 return writeFile(stream);
328}
329
331{
333
334 // Start by writing the opening TNEF stuff
335 stream << TNEF_SIGNATURE;
336
337 // Store the PR_ATTACH_NUM value for the first attachment
338 // ( must be stored even if *no* attachments are stored )
339 stream << d->mFirstAttachNum;
340
341 // Now do some writing
342 bool ok = true;
343 int bytesWritten = 0;
344 ok &= writeProperty(stream, bytesWritten, attTNEFVERSION);
345 ok &= writeProperty(stream, bytesWritten, attOEMCODEPAGE);
346 ok &= writeProperty(stream, bytesWritten, attMSGCLASS);
347 ok &= writeProperty(stream, bytesWritten, attMSGPRIORITY);
348 ok &= writeProperty(stream, bytesWritten, attSUBJECT);
349 ok &= writeProperty(stream, bytesWritten, attDATESENT);
350 ok &= writeProperty(stream, bytesWritten, attDATESTART);
351 ok &= writeProperty(stream, bytesWritten, attDATEEND);
352 // ok &= writeProperty( stream, bytesWritten, attAIDOWNER );
353 ok &= writeProperty(stream, bytesWritten, attREQUESTRES);
354 ok &= writeProperty(stream, bytesWritten, attFROM);
355 ok &= writeProperty(stream, bytesWritten, attDATERECD);
356 ok &= writeProperty(stream, bytesWritten, attMSGSTATUS);
357 ok &= writeProperty(stream, bytesWritten, attBODY);
358 return ok;
359}
360
361void KTNEFWriter::setSender(const QString &name, const QString &email)
362{
363 assert(!name.isEmpty());
364 assert(!email.isEmpty());
365
366 QVariant v1(name);
367 QVariant v2(email);
368
369 const QList<QVariant> list = {v1, v2};
370
371 addProperty(attFROM, 0, list); // What's up with the 0 here ??
372}
373
375{
376 // Note that the MessageType list here is probably not long enough,
377 // more entries are most likely needed later
378
379 QVariant v;
380 switch (m) {
381 case Appointment:
382 v = QVariant("IPM.Appointment"_L1);
383 break;
384
385 case MeetingCancelled:
386 v = QVariant("IPM.Schedule.Meeting.Cancelled"_L1);
387 break;
388
389 case MeetingRequest:
390 v = QVariant("IPM.Schedule.Meeting.Request"_L1);
391 break;
392
393 case MeetingNo:
394 v = QVariant("IPM.Schedule.Meeting.Resp.Neg"_L1);
395 break;
396
397 case MeetingYes:
398 v = QVariant("IPM.Schedule.Meeting.Resp.Pos"_L1);
399 break;
400
401 case MeetingTent:
402 // Tent?
403 v = QVariant("IPM.Schedule.Meeting.Resp.Tent"_L1);
404 break;
405
406 default:
407 return;
408 }
409
410 addProperty(attMSGCLASS, atpWORD, v);
411}
412
416
420
421void KTNEFWriter::addAttendee(const QString &cn, Role r, PartStat p, bool rsvp, const QString &mailto)
422{
423 Q_UNUSED(cn)
424 Q_UNUSED(r)
425 Q_UNUSED(p)
426 Q_UNUSED(rsvp)
427 Q_UNUSED(mailto)
428}
429
430// I assume this is the same as the sender?
431// U also assume that this is like "Name <address>"
432void KTNEFWriter::setOrganizer(const QString &organizer)
433{
434 int i = organizer.indexOf(QLatin1Char('<'));
435
436 if (i == -1) {
437 return;
438 }
439
440 QString name = organizer.left(i).trimmed();
441
442 QString email = organizer.right(i + 1);
443 email = email.left(email.length() - 1).trimmed();
444
445 setSender(name, email);
446}
447
449{
450 QVariant v(dtStart);
451 addProperty(attDATESTART, atpDATE, v);
452}
453
455{
456 QVariant v(dtEnd);
457 addProperty(attDATEEND, atpDATE, v);
458}
459
460void KTNEFWriter::setLocation(const QString & /*location*/)
461{
462}
463
465{
466 QVariant v(uid);
467 addProperty(attMSGID, atpSTRING, v);
468}
469
470// Date sent
472{
473 QVariant v(dtStamp);
474 addProperty(attDATESENT, atpDATE, v);
475}
476
480
481// I hope this is the body
483{
484 QVariant v(body);
485 addProperty(attBODY, atpTEXT, v);
486}
487
489{
490 QVariant v(s);
491 addProperty(attSUBJECT, atpSTRING, v);
492}
493
494// TNEF encoding: Normal = 3, high = 2, low = 1
495// MAPI encoding: Normal = -1, high = 0, low = 1
497{
498 QVariant v((quint32)p);
499 addProperty(attMSGPRIORITY, atpSHORT, v);
500}
501
502void KTNEFWriter::setAlarm(const QString &description, AlarmAction action, const QDateTime &wakeBefore)
503{
504 Q_UNUSED(description)
505 Q_UNUSED(action)
506 Q_UNUSED(wakeBefore)
507}
Interface for setting MAPI properties and TNEF attributes.
Interface for setting MAPI properties.
void setPriority(Priority priority)
Sets the priority to priority.
AlarmAction
The different alarm actions.
Definition ktnefwriter.h:99
void setAlarm(const QString &description, AlarmAction action, const QDateTime &wakeBefore)
Sets the alarm.
void setOrganizer(const QString &organizer)
Sets the name of the organizer to organizer.
void clearAttendees()
Clears the attendees list.
KTNEFWriter()
Constructs a TNEF writer object.
Role
The different types of meeting roles.
Definition ktnefwriter.h:67
void setDtStamp(const QDateTime &dtStamp)
Sets the timestamp to dtStamp.
void setCategories(const QStringList &categories)
Sets the category list to categories.
MessageType
The different types of messages.
Definition ktnefwriter.h:44
@ MeetingTent
Tentative affirmative to a meeting request.
Definition ktnefwriter.h:50
@ MeetingNo
Negative response to a meeting request.
Definition ktnefwriter.h:48
@ MeetingRequest
Meeting request.
Definition ktnefwriter.h:47
@ Appointment
Appointment.
Definition ktnefwriter.h:45
@ MeetingYes
Affirmative response to a meeting request.
Definition ktnefwriter.h:49
@ MeetingCancelled
The meeting is cancelled.
Definition ktnefwriter.h:46
Priority
The different priorities.
Definition ktnefwriter.h:90
~KTNEFWriter()
Destroys the TNEF writer object.
bool writeFile(QIODevice &file) const
Writes the attachment to the QIODevice specified by file.
void setMethod(Method method)
Sets the Method to method.
void setLocation(const QString &location)
Sets the Location to location.
void addProperty(int tag, int type, const QVariant &value)
Adds a TNEF property.
bool writeProperty(QDataStream &stream, int &bytes, int tag) const
Writes a TNEF property to the QDataStream specified by stream.
void setSummary(const QString &summary)
Sets the summary to summary.
Method
The different types of message statuses.
Definition ktnefwriter.h:56
PartStat
The different types of participant statuses.
Definition ktnefwriter.h:77
void setDtEnd(const QDateTime &dtEnd)
Sets the Ending Date and Time to dtEnd.
void setDtStart(const QDateTime &dtStart)
Sets the Starting Date and Time to dtStart.
void setDescription(const QString &description)
Sets the description to description.
void setSender(const QString &name, const QString &email)
Sets the sender's name and email address.
void setMessageType(MessageType methodType)
Sets the MessageType to methodType.
void addAttendee(const QString &name, Role role, PartStat partstat, bool rsvp, const QString &email)
Adds a meeting participant (attendee).
void setUID(const QString &uid)
Sets the UID to uid.
This file is part of the API for handling TNEF data and provides some basic definitions for general u...
This file is part of the API for handling TNEF data and defines the KTNEFProperty class.
This file is part of the API for handling TNEF data and defines the KTNEFPropertySet class.
This file is part of the API for handling TNEF data and defines the KTNEFWriter class.
KGuiItem properties()
char * data()
qsizetype length() const const
void setByteOrder(ByteOrder bo)
int writeRawData(const char *s, int len)
int day() const const
int dayOfWeek() const const
int month() const const
int year() const const
QDate date() const const
QTime time() const const
virtual bool open(QIODeviceBase::OpenMode mode)
qsizetype count() const const
T value(qsizetype i) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString right(qsizetype n) const const
QByteArray toLocal8Bit() const const
QString trimmed() const const
int hour() const const
int minute() const const
int second() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:55:06 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.