File indexing completed on 2024-04-28 05:19:26
0001 /* 0002 ktnefparser.cpp 0003 0004 SPDX-FileCopyrightText: 2002 Michael Goffioul <kdeprint@swing.be> 0005 0006 This file is part of KTNEF, the KDE TNEF support library/program. 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 /** 0011 * @file 0012 * This file is part of the API for handling TNEF data and 0013 * defines the KTNEFParser class. 0014 * 0015 * @author Michael Goffioul 0016 */ 0017 0018 #include "ktnefparser.h" 0019 #include "ktnefattach.h" 0020 #include "ktnefdefs.h" 0021 #include "ktnefmessage.h" 0022 #include "ktnefproperty.h" 0023 0024 #include "ktnef_debug.h" 0025 #include <QMimeDatabase> 0026 #include <QMimeType> 0027 #include <QSaveFile> 0028 0029 #include <QDataStream> 0030 #include <QDateTime> 0031 #include <QDir> 0032 #include <QFile> 0033 #include <QFileInfo> 0034 #include <QList> 0035 #include <QStandardPaths> 0036 #include <QVariant> 0037 0038 using namespace KTnef; 0039 0040 //@cond PRIVATE 0041 typedef struct { 0042 quint16 type; 0043 quint16 tag; 0044 QVariant value; 0045 struct { 0046 quint32 type; 0047 QVariant value; 0048 } name; 0049 } MAPI_value; 0050 //@endcond 0051 0052 //@cond IGNORE 0053 void clearMAPIName(MAPI_value &mapi); 0054 void clearMAPIValue(MAPI_value &mapi, bool clearName = true); 0055 QString readMAPIString(QDataStream &stream, bool isUnicode = false, bool align = true, int len = -1); 0056 quint16 readMAPIValue(QDataStream &stream, MAPI_value &mapi); 0057 QDateTime readTNEFDate(QDataStream &stream); 0058 QString readTNEFAddress(QDataStream &stream); 0059 QByteArray readTNEFData(QDataStream &stream, quint32 len); 0060 QVariant readTNEFAttribute(QDataStream &stream, quint16 type, quint32 len); 0061 QDateTime formatTime(quint32 lowB, quint32 highB); 0062 QString formatRecipient(const QMap<int, KTnef::KTNEFProperty *> &props); 0063 //@endcond 0064 0065 //------------------------------------------------------------------------------ 0066 0067 /** 0068 * Private class that helps to provide binary compatibility between releases. 0069 * @internal 0070 */ 0071 //@cond PRIVATE 0072 class KTnef::KTNEFParser::ParserPrivate 0073 { 0074 public: 0075 ParserPrivate() 0076 : defaultdir_(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) 0077 , message_(new KTNEFMessage) 0078 { 0079 } 0080 ~ParserPrivate() 0081 { 0082 delete message_; 0083 } 0084 0085 bool decodeAttachment(); 0086 bool decodeMessage(); 0087 bool extractAttachmentTo(KTNEFAttach *att, const QString &dirname); 0088 void checkCurrent(int key); 0089 bool readMAPIProperties(QMap<int, KTNEFProperty *> &props, KTNEFAttach *attach = nullptr); 0090 bool parseDevice(); 0091 void deleteDevice(); 0092 0093 QString defaultdir_; 0094 QDataStream stream_; 0095 QIODevice *device_ = nullptr; 0096 KTNEFAttach *current_ = nullptr; 0097 KTNEFMessage *message_ = nullptr; 0098 bool deleteDevice_ = false; 0099 }; 0100 //@endcond 0101 0102 KTNEFParser::KTNEFParser() 0103 : d(new ParserPrivate) 0104 { 0105 } 0106 0107 KTNEFParser::~KTNEFParser() 0108 { 0109 d->deleteDevice(); 0110 } 0111 0112 KTNEFMessage *KTNEFParser::message() const 0113 { 0114 return d->message_; 0115 } 0116 0117 void KTNEFParser::ParserPrivate::deleteDevice() 0118 { 0119 if (deleteDevice_) { 0120 delete device_; 0121 } 0122 device_ = nullptr; 0123 deleteDevice_ = false; 0124 } 0125 0126 bool KTNEFParser::ParserPrivate::decodeMessage() 0127 { 0128 quint32 i1; 0129 quint32 i2; 0130 quint32 off; 0131 quint16 u; 0132 quint16 tag; 0133 quint16 type; 0134 QVariant value; 0135 0136 // read (type+name) 0137 stream_ >> i1; 0138 u = 0; 0139 tag = (i1 & 0x0000FFFF); 0140 type = ((i1 & 0xFFFF0000) >> 16); 0141 // read data length 0142 stream_ >> i2; 0143 // offset after reading the value 0144 off = device_->pos() + i2; 0145 switch (tag) { 0146 case attAIDOWNER: { 0147 uint tmp; 0148 stream_ >> tmp; 0149 value.setValue(tmp); 0150 message_->addProperty(0x0062, MAPI_TYPE_ULONG, value); 0151 qCDebug(KTNEF_LOG) << "Message Owner Appointment ID" 0152 << "(length=" << i2 << ")"; 0153 break; 0154 } 0155 case attREQUESTRES: 0156 stream_ >> u; 0157 message_->addProperty(0x0063, MAPI_TYPE_UINT16, u); 0158 value = (bool)u; 0159 qCDebug(KTNEF_LOG) << "Message Request Response" 0160 << "(length=" << i2 << ")"; 0161 break; 0162 case attDATERECD: 0163 value = readTNEFDate(stream_); 0164 message_->addProperty(0x0E06, MAPI_TYPE_TIME, value); 0165 qCDebug(KTNEF_LOG) << "Message Receive Date" 0166 << "(length=" << i2 << ")"; 0167 break; 0168 case attMSGCLASS: 0169 value = readMAPIString(stream_, false, false, i2); 0170 message_->addProperty(0x001A, MAPI_TYPE_STRING8, value); 0171 qCDebug(KTNEF_LOG) << "Message Class" 0172 << "(length=" << i2 << ")"; 0173 break; 0174 case attMSGPRIORITY: 0175 stream_ >> u; 0176 message_->addProperty(0x0026, MAPI_TYPE_ULONG, 2 - u); 0177 value = u; 0178 qCDebug(KTNEF_LOG) << "Message Priority" 0179 << "(length=" << i2 << ")"; 0180 break; 0181 case attMAPIPROPS: 0182 qCDebug(KTNEF_LOG) << "Message MAPI Properties" 0183 << "(length=" << i2 << ")"; 0184 { 0185 int nProps = message_->properties().count(); 0186 i2 += device_->pos(); 0187 readMAPIProperties(message_->properties(), nullptr); 0188 device_->seek(i2); 0189 qCDebug(KTNEF_LOG) << "Properties:" << message_->properties().count(); 0190 value = QStringLiteral("< %1 properties >").arg(message_->properties().count() - nProps); 0191 } 0192 break; 0193 case attTNEFVERSION: { 0194 uint tmp; 0195 stream_ >> tmp; 0196 value.setValue(tmp); 0197 qCDebug(KTNEF_LOG) << "Message TNEF Version" 0198 << "(length=" << i2 << ")"; 0199 } break; 0200 case attFROM: 0201 message_->addProperty(0x0024, MAPI_TYPE_STRING8, readTNEFAddress(stream_)); 0202 device_->seek(device_->pos() - i2); 0203 value = readTNEFData(stream_, i2); 0204 qCDebug(KTNEF_LOG) << "Message From" 0205 << "(length=" << i2 << ")"; 0206 break; 0207 case attSUBJECT: 0208 value = readMAPIString(stream_, false, false, i2); 0209 message_->addProperty(0x0037, MAPI_TYPE_STRING8, value); 0210 qCDebug(KTNEF_LOG) << "Message Subject" 0211 << "(length=" << i2 << ")"; 0212 break; 0213 case attDATESENT: 0214 value = readTNEFDate(stream_); 0215 message_->addProperty(0x0039, MAPI_TYPE_TIME, value); 0216 qCDebug(KTNEF_LOG) << "Message Date Sent" 0217 << "(length=" << i2 << ")"; 0218 break; 0219 case attMSGSTATUS: { 0220 quint8 c; 0221 quint32 flag = 0; 0222 stream_ >> c; 0223 if (c & fmsRead) { 0224 flag |= MSGFLAG_READ; 0225 } 0226 if (!(c & fmsModified)) { 0227 flag |= MSGFLAG_UNMODIFIED; 0228 } 0229 if (c & fmsSubmitted) { 0230 flag |= MSGFLAG_SUBMIT; 0231 } 0232 if (c & fmsHasAttach) { 0233 flag |= MSGFLAG_HASATTACH; 0234 } 0235 if (c & fmsLocal) { 0236 flag |= MSGFLAG_UNSENT; 0237 } 0238 message_->addProperty(0x0E07, MAPI_TYPE_ULONG, flag); 0239 value = c; 0240 } 0241 qCDebug(KTNEF_LOG) << "Message Status" 0242 << "(length=" << i2 << ")"; 0243 break; 0244 case attRECIPTABLE: { 0245 quint32 rows; 0246 QList<QVariant> recipTable; 0247 stream_ >> rows; 0248 if (rows > (INT_MAX / sizeof(QVariant))) { 0249 return false; 0250 } 0251 recipTable.reserve(rows); 0252 for (uint i = 0; i < rows; i++) { 0253 QMap<int, KTNEFProperty *> props; 0254 readMAPIProperties(props, nullptr); 0255 recipTable << formatRecipient(props); 0256 } 0257 message_->addProperty(0x0E12, MAPI_TYPE_STRING8, recipTable); 0258 device_->seek(device_->pos() - i2); 0259 value = readTNEFData(stream_, i2); 0260 } 0261 qCDebug(KTNEF_LOG) << "Message Recipient Table" 0262 << "(length=" << i2 << ")"; 0263 break; 0264 case attBODY: 0265 value = readMAPIString(stream_, false, false, i2); 0266 message_->addProperty(0x1000, MAPI_TYPE_STRING8, value); 0267 qCDebug(KTNEF_LOG) << "Message Body" 0268 << "(length=" << i2 << ")"; 0269 break; 0270 case attDATEMODIFIED: 0271 value = readTNEFDate(stream_); 0272 message_->addProperty(0x3008, MAPI_TYPE_TIME, value); 0273 qCDebug(KTNEF_LOG) << "Message Date Modified" 0274 << "(length=" << i2 << ")"; 0275 break; 0276 case attMSGID: 0277 value = readMAPIString(stream_, false, false, i2); 0278 message_->addProperty(0x300B, MAPI_TYPE_STRING8, value); 0279 qCDebug(KTNEF_LOG) << "Message ID" 0280 << "(length=" << i2 << ")"; 0281 break; 0282 case attOEMCODEPAGE: 0283 value = readTNEFData(stream_, i2); 0284 qCDebug(KTNEF_LOG) << "Message OEM Code Page" 0285 << "(length=" << i2 << ")"; 0286 break; 0287 default: 0288 value = readTNEFAttribute(stream_, type, i2); 0289 // qCDebug(KTNEF_LOG).form( "Message: type=%x, length=%d, check=%x\n", i1, i2, u ); 0290 break; 0291 } 0292 // skip data 0293 if (device_->pos() != off && !device_->seek(off)) { 0294 return false; 0295 } 0296 // get checksum 0297 stream_ >> u; 0298 // add TNEF attribute 0299 message_->addAttribute(tag, type, value, true); 0300 // qCDebug(KTNEF_LOG) << "stream:" << device_->pos(); 0301 return true; 0302 } 0303 0304 bool KTNEFParser::ParserPrivate::decodeAttachment() 0305 { 0306 quint32 i; 0307 quint16 tag; 0308 quint16 type; 0309 quint16 u; 0310 QVariant value; 0311 QString str; 0312 0313 stream_ >> i; // i <- attribute type & name 0314 tag = (i & 0x0000FFFF); 0315 type = ((i & 0xFFFF0000) >> 16); 0316 stream_ >> i; // i <- data length 0317 checkCurrent(tag); 0318 switch (tag) { 0319 case attATTACHTITLE: 0320 value = readMAPIString(stream_, false, false, i); 0321 current_->setName(value.toString()); 0322 qCDebug(KTNEF_LOG) << "Attachment Title:" << current_->name(); 0323 break; 0324 case attATTACHDATA: 0325 current_->setSize(i); 0326 current_->setOffset(device_->pos()); 0327 device_->seek(device_->pos() + i); 0328 value = QStringLiteral("< size=%1 >").arg(i); 0329 qCDebug(KTNEF_LOG) << "Attachment Data: size=" << i; 0330 break; 0331 case attATTACHMENT: // try to get attachment info 0332 i += device_->pos(); 0333 readMAPIProperties(current_->properties(), current_); 0334 device_->seek(i); 0335 current_->setIndex(current_->property(MAPI_TAG_INDEX).toUInt()); 0336 current_->setDisplaySize(current_->property(MAPI_TAG_SIZE).toUInt()); 0337 str = current_->property(MAPI_TAG_DISPLAYNAME).toString(); 0338 if (!str.isEmpty()) { 0339 current_->setDisplayName(str); 0340 } 0341 current_->setFileName(current_->property(MAPI_TAG_FILENAME).toString()); 0342 str = current_->property(MAPI_TAG_MIMETAG).toString(); 0343 if (!str.isEmpty()) { 0344 current_->setMimeTag(str); 0345 } 0346 current_->setExtension(current_->property(MAPI_TAG_EXTENSION).toString()); 0347 value = QStringLiteral("< %1 properties >").arg(current_->properties().count()); 0348 break; 0349 case attATTACHMODDATE: 0350 value = readTNEFDate(stream_); 0351 qCDebug(KTNEF_LOG) << "Attachment Modification Date:" << value.toString(); 0352 break; 0353 case attATTACHCREATEDATE: 0354 value = readTNEFDate(stream_); 0355 qCDebug(KTNEF_LOG) << "Attachment Creation Date:" << value.toString(); 0356 break; 0357 case attATTACHMETAFILE: 0358 qCDebug(KTNEF_LOG) << "Attachment Metafile: size=" << i; 0359 // value = QString( "< size=%1 >" ).arg( i ); 0360 // device_->seek( device_->pos()+i ); 0361 value = readTNEFData(stream_, i); 0362 break; 0363 default: 0364 value = readTNEFAttribute(stream_, type, i); 0365 qCDebug(KTNEF_LOG) << "Attachment unknown field: tag=" << Qt::hex << tag << ", length=" << Qt::dec << i; 0366 break; 0367 } 0368 stream_ >> u; // u <- checksum 0369 // add TNEF attribute 0370 current_->addAttribute(tag, type, value, true); 0371 // qCDebug(KTNEF_LOG) << "stream:" << device_->pos(); 0372 0373 return true; 0374 } 0375 0376 void KTNEFParser::setDefaultExtractDir(const QString &dirname) 0377 { 0378 d->defaultdir_ = dirname; 0379 } 0380 0381 bool KTNEFParser::ParserPrivate::parseDevice() 0382 { 0383 quint16 u; 0384 quint32 i; 0385 quint8 c; 0386 0387 message_->clearAttachments(); 0388 delete current_; 0389 current_ = nullptr; 0390 0391 if (!device_->isOpen()) { 0392 if (!device_->open(QIODevice::ReadOnly)) { 0393 qCDebug(KTNEF_LOG) << "Couldn't open device"; 0394 return false; 0395 } 0396 } 0397 if (!device_->isReadable()) { 0398 qCDebug(KTNEF_LOG) << "Device not readable"; 0399 return false; 0400 } 0401 0402 stream_.setDevice(device_); 0403 stream_.setByteOrder(QDataStream::LittleEndian); 0404 stream_ >> i; 0405 if (i == TNEF_SIGNATURE) { 0406 stream_ >> u; 0407 qCDebug(KTNEF_LOG).nospace() << "Attachment cross reference key: 0x" << Qt::hex << qSetFieldWidth(4) << qSetPadChar(QLatin1Char('0')) << u; 0408 // qCDebug(KTNEF_LOG) << "stream:" << device_->pos(); 0409 while (!stream_.atEnd()) { 0410 stream_ >> c; 0411 switch (c) { 0412 case LVL_MESSAGE: 0413 if (!decodeMessage()) { 0414 goto end; 0415 } 0416 break; 0417 case LVL_ATTACHMENT: 0418 if (!decodeAttachment()) { 0419 goto end; 0420 } 0421 break; 0422 default: 0423 qCDebug(KTNEF_LOG) << "Unknown Level:" << c << ", at offset" << device_->pos(); 0424 goto end; 0425 } 0426 } 0427 if (current_) { 0428 checkCurrent(attATTACHDATA); // this line has the effect to append the 0429 // attachment, if it has data. If not it does 0430 // nothing, and the attachment will be discarded 0431 delete current_; 0432 current_ = nullptr; 0433 } 0434 return true; 0435 } else { 0436 qCDebug(KTNEF_LOG) << "This is not a TNEF file"; 0437 end: 0438 device_->close(); 0439 return false; 0440 } 0441 } 0442 0443 bool KTNEFParser::extractFile(const QString &filename) const 0444 { 0445 KTNEFAttach *att = d->message_->attachment(filename); 0446 if (!att) { 0447 return false; 0448 } 0449 return d->extractAttachmentTo(att, d->defaultdir_); 0450 } 0451 0452 bool KTNEFParser::ParserPrivate::extractAttachmentTo(KTNEFAttach *att, const QString &dirname) 0453 { 0454 const QString destDir(QDir(dirname).absolutePath()); // get directory path without any "." or ".." 0455 0456 QString filename = destDir + QLatin1Char('/'); 0457 if (!att->fileName().isEmpty()) { 0458 filename += att->fileName(); 0459 } else { 0460 filename += att->name(); 0461 } 0462 if (filename.endsWith(QLatin1Char('/'))) { 0463 return false; 0464 } 0465 0466 if (!device_->isOpen()) { 0467 return false; 0468 } 0469 if (!device_->seek(att->offset())) { 0470 return false; 0471 } 0472 0473 const QFileInfo fi(filename); 0474 if (!fi.absoluteFilePath().startsWith(destDir)) { 0475 qWarning() << "Attempted extract into" << fi.absoluteFilePath() << "which is outside of the extraction root folder" << destDir << "." 0476 << "Changing export of contained files to extraction root folder."; 0477 filename = destDir + QLatin1Char('/') + fi.fileName(); 0478 } 0479 0480 QSaveFile outfile(filename); 0481 if (!outfile.open(QIODevice::WriteOnly)) { 0482 return false; 0483 } 0484 0485 quint32 len = att->size(); 0486 quint32 sz(16384); 0487 char *buf = new char[sz]; 0488 bool ok(true); 0489 while (ok && len > 0) { 0490 const int n = device_->read(buf, qMin(sz, len)); 0491 if (n < 0) { 0492 ok = false; 0493 } else { 0494 len -= n; 0495 if (outfile.write(buf, n) != n) { 0496 ok = false; 0497 } 0498 } 0499 } 0500 outfile.commit(); 0501 delete[] buf; 0502 0503 return ok; 0504 } 0505 0506 bool KTNEFParser::extractAll() 0507 { 0508 QList<KTNEFAttach *> l = d->message_->attachmentList(); 0509 QList<KTNEFAttach *>::const_iterator it = l.constBegin(); 0510 const QList<KTNEFAttach *>::const_iterator itEnd = l.constEnd(); 0511 for (; it != itEnd; ++it) { 0512 if (!d->extractAttachmentTo(*it, d->defaultdir_)) { 0513 return false; 0514 } 0515 } 0516 return true; 0517 } 0518 0519 bool KTNEFParser::extractFileTo(const QString &filename, const QString &dirname) const 0520 { 0521 qCDebug(KTNEF_LOG) << "Extracting attachment: filename=" << filename << ", dir=" << dirname; 0522 KTNEFAttach *att = d->message_->attachment(filename); 0523 if (!att) { 0524 return false; 0525 } 0526 return d->extractAttachmentTo(att, dirname); 0527 } 0528 0529 bool KTNEFParser::openFile(const QString &filename) const 0530 { 0531 d->deleteDevice(); 0532 delete d->message_; 0533 d->message_ = new KTNEFMessage(); 0534 auto file = new QFile(filename); 0535 d->device_ = file; 0536 d->deleteDevice_ = true; 0537 if (!file->exists()) { 0538 return false; 0539 } 0540 return d->parseDevice(); 0541 } 0542 0543 bool KTNEFParser::openDevice(QIODevice *device) 0544 { 0545 d->deleteDevice(); 0546 d->device_ = device; 0547 return d->parseDevice(); 0548 } 0549 0550 void KTNEFParser::ParserPrivate::checkCurrent(int key) 0551 { 0552 if (!current_) { 0553 current_ = new KTNEFAttach(); 0554 } else { 0555 if (current_->attributes().contains(key)) { 0556 if (current_->offset() >= 0) { 0557 if (current_->name().isEmpty()) { 0558 current_->setName(QStringLiteral("Unnamed")); 0559 } 0560 if (current_->mimeTag().isEmpty()) { 0561 // No mime type defined in the TNEF structure, 0562 // try to find it from the attachment filename 0563 // and/or content (using at most 32 bytes) 0564 QMimeType mimetype; 0565 QMimeDatabase db; 0566 if (!current_->fileName().isEmpty()) { 0567 mimetype = db.mimeTypeForFile(current_->fileName(), QMimeDatabase::MatchExtension); 0568 } 0569 if (!mimetype.isValid()) { 0570 return; // FIXME 0571 } 0572 if (mimetype.name() == QLatin1StringView("application/octet-stream") && current_->size() > 0) { 0573 qint64 oldOffset = device_->pos(); 0574 QByteArray buffer(qMin(32, current_->size()), '\0'); 0575 device_->seek(current_->offset()); 0576 device_->read(buffer.data(), buffer.size()); 0577 mimetype = db.mimeTypeForData(buffer); 0578 device_->seek(oldOffset); 0579 } 0580 current_->setMimeTag(mimetype.name()); 0581 } 0582 message_->addAttachment(current_); 0583 current_ = nullptr; 0584 } else { 0585 // invalid attachment, skip it 0586 delete current_; 0587 current_ = nullptr; 0588 } 0589 current_ = new KTNEFAttach(); 0590 } 0591 } 0592 } 0593 0594 //------------------------------------------------------------------------------ 0595 0596 //@cond IGNORE 0597 #define ALIGN(n, b) \ 0598 if (n & (b - 1)) { \ 0599 n = (n + b) & ~(b - 1); \ 0600 } 0601 #define ISVECTOR(m) (((m).type & 0xF000) == MAPI_TYPE_VECTOR) 0602 0603 void clearMAPIName(MAPI_value &mapi) 0604 { 0605 mapi.name.value.clear(); 0606 } 0607 0608 void clearMAPIValue(MAPI_value &mapi, bool clearName) 0609 { 0610 mapi.value.clear(); 0611 if (clearName) { 0612 clearMAPIName(mapi); 0613 } 0614 } 0615 0616 QDateTime formatTime(quint32 lowB, quint32 highB) 0617 { 0618 QDateTime dt; 0619 quint64 u64; 0620 u64 = highB; 0621 u64 <<= 32; 0622 u64 |= lowB; 0623 u64 -= 116444736000000000LL; 0624 u64 /= 10000000; 0625 if (u64 <= 0xffffffffU) { 0626 dt = QDateTime::fromSecsSinceEpoch((unsigned int)u64); 0627 } else { 0628 qCWarning(KTNEF_LOG).nospace() << "Invalid date: low byte=" << Qt::showbase << qSetFieldWidth(8) << qSetPadChar(QLatin1Char('0')) << lowB 0629 << ", high byte=" << highB; 0630 } 0631 return dt; 0632 } 0633 0634 QString formatRecipient(const QMap<int, KTnef::KTNEFProperty *> &props) 0635 { 0636 QString s; 0637 QString dn; 0638 QString addr; 0639 QString t; 0640 QMap<int, KTnef::KTNEFProperty *>::ConstIterator it; 0641 if ((it = props.find(0x3001)) != props.end()) { 0642 dn = (*it)->valueString(); 0643 } 0644 if ((it = props.find(0x3003)) != props.end()) { 0645 addr = (*it)->valueString(); 0646 } 0647 if ((it = props.find(0x0C15)) != props.end()) { 0648 switch ((*it)->value().toInt()) { 0649 case 0: 0650 t = QStringLiteral("From:"); 0651 break; 0652 case 1: 0653 t = QStringLiteral("To:"); 0654 break; 0655 case 2: 0656 t = QStringLiteral("Cc:"); 0657 break; 0658 case 3: 0659 t = QStringLiteral("Bcc:"); 0660 break; 0661 } 0662 } 0663 if (!t.isEmpty()) { 0664 s.append(t); 0665 } 0666 if (!dn.isEmpty()) { 0667 s.append(QLatin1Char(' ') + dn); 0668 } 0669 if (!addr.isEmpty() && addr != dn) { 0670 s.append(QLatin1StringView(" <") + addr + QLatin1Char('>')); 0671 } 0672 0673 return s.trimmed(); 0674 } 0675 0676 QDateTime readTNEFDate(QDataStream &stream) 0677 { 0678 // 14-bytes long 0679 quint16 y; 0680 quint16 m; 0681 quint16 d; 0682 quint16 hh; 0683 quint16 mm; 0684 quint16 ss; 0685 quint16 dm; 0686 stream >> y >> m >> d >> hh >> mm >> ss >> dm; 0687 return QDateTime(QDate(y, m, d), QTime(hh, mm, ss)); 0688 } 0689 0690 QString readTNEFAddress(QDataStream &stream) 0691 { 0692 quint16 totalLen; 0693 quint16 strLen; 0694 quint16 addrLen; 0695 QString s; 0696 stream >> totalLen >> totalLen >> strLen >> addrLen; 0697 s.append(readMAPIString(stream, false, false, strLen)); 0698 s.append(QLatin1StringView(" <")); 0699 s.append(readMAPIString(stream, false, false, addrLen)); 0700 s.append(QLatin1StringView(">")); 0701 quint8 c; 0702 for (int i = 8 + strLen + addrLen; i < totalLen; i++) { 0703 stream >> c; 0704 } 0705 return s; 0706 } 0707 0708 QByteArray readTNEFData(QDataStream &stream, quint32 len) 0709 { 0710 QByteArray array(len, '\0'); 0711 if (len > 0) { 0712 stream.readRawData(array.data(), len); 0713 } 0714 return array; 0715 } 0716 0717 QVariant readTNEFAttribute(QDataStream &stream, quint16 type, quint32 len) 0718 { 0719 switch (type) { 0720 case atpTEXT: 0721 case atpSTRING: 0722 return readMAPIString(stream, false, false, len); 0723 case atpDATE: 0724 return readTNEFDate(stream); 0725 default: 0726 return readTNEFData(stream, len); 0727 } 0728 } 0729 0730 QString readMAPIString(QDataStream &stream, bool isUnicode, bool align, int len_) 0731 { 0732 quint32 len; 0733 char *buf = nullptr; 0734 if (len_ == -1) { 0735 stream >> len; 0736 } else { 0737 len = len_; 0738 } 0739 if (len > INT_MAX) { 0740 return QString(); 0741 } 0742 0743 quint32 fullLen = len; 0744 if (align) { 0745 ALIGN(fullLen, 4) 0746 } 0747 buf = new char[len]; 0748 stream.readRawData(buf, len); 0749 quint8 c; 0750 for (uint i = len; i < fullLen; i++) { 0751 stream >> c; 0752 } 0753 QString res; 0754 if (isUnicode) { 0755 res = QString::fromUtf16((const char16_t *)buf); 0756 } else { 0757 res = QString::fromLatin1(buf); 0758 } 0759 delete[] buf; 0760 return res; 0761 } 0762 0763 quint16 readMAPIValue(QDataStream &stream, MAPI_value &mapi) 0764 { 0765 quint32 d; 0766 0767 clearMAPIValue(mapi); 0768 stream >> d; 0769 mapi.type = (d & 0x0000FFFF); 0770 mapi.tag = ((d & 0xFFFF0000) >> 16); 0771 if (mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE) { 0772 // skip GUID 0773 stream >> d >> d >> d >> d; 0774 // name type 0775 stream >> mapi.name.type; 0776 // name 0777 if (mapi.name.type == 0) { 0778 uint tmp; 0779 stream >> tmp; 0780 mapi.name.value.setValue(tmp); 0781 } else if (mapi.name.type == 1) { 0782 mapi.name.value.setValue(readMAPIString(stream, true)); 0783 } 0784 } 0785 0786 int n = 1; 0787 QVariant value; 0788 if (ISVECTOR(mapi)) { 0789 stream >> n; 0790 mapi.value = QList<QVariant>(); 0791 } 0792 for (int i = 0; i < n; i++) { 0793 value.clear(); 0794 switch (mapi.type & 0x0FFF) { 0795 case MAPI_TYPE_UINT16: 0796 stream >> d; 0797 value.setValue(d & 0x0000FFFF); 0798 break; 0799 case MAPI_TYPE_BOOLEAN: 0800 case MAPI_TYPE_ULONG: { 0801 uint tmp; 0802 stream >> tmp; 0803 value.setValue(tmp); 0804 } break; 0805 case MAPI_TYPE_FLOAT: 0806 // FIXME: Don't we have to set the value here 0807 stream >> d; 0808 break; 0809 case MAPI_TYPE_DOUBLE: { 0810 double tmp; 0811 stream >> tmp; 0812 value.setValue(tmp); 0813 } break; 0814 case MAPI_TYPE_TIME: { 0815 quint32 lowB; 0816 quint32 highB; 0817 stream >> lowB >> highB; 0818 value = formatTime(lowB, highB); 0819 } break; 0820 case MAPI_TYPE_USTRING: 0821 case MAPI_TYPE_STRING8: 0822 // in case of a vector'ed value, the number of elements 0823 // has already been read in the upper for-loop 0824 if (ISVECTOR(mapi)) { 0825 d = 1; 0826 } else { 0827 stream >> d; 0828 } 0829 for (uint j = 0; j < d; j++) { 0830 value.clear(); 0831 value.setValue(readMAPIString(stream, (mapi.type & 0x0FFF) == MAPI_TYPE_USTRING)); 0832 } 0833 break; 0834 case MAPI_TYPE_OBJECT: 0835 case MAPI_TYPE_BINARY: 0836 if (ISVECTOR(mapi)) { 0837 d = 1; 0838 } else { 0839 stream >> d; 0840 } 0841 for (uint i = 0; i < d && !stream.atEnd(); i++) { 0842 value.clear(); 0843 quint32 len; 0844 stream >> len; 0845 value = QByteArray(len, '\0'); 0846 if (len > 0 && len <= INT_MAX) { 0847 uint fullLen = len; 0848 ALIGN(fullLen, 4) 0849 stream.readRawData(value.toByteArray().data(), len); 0850 quint8 c; 0851 for (uint i = len; i < fullLen; i++) { 0852 stream >> c; 0853 } 0854 // FIXME: Shouldn't we do something with the value??? 0855 } 0856 } 0857 break; 0858 default: 0859 mapi.type = MAPI_TYPE_NONE; 0860 break; 0861 } 0862 if (ISVECTOR(mapi)) { 0863 QList<QVariant> lst = mapi.value.toList(); 0864 lst << value; 0865 mapi.value.setValue(lst); 0866 } else { 0867 mapi.value = value; 0868 } 0869 } 0870 return mapi.tag; 0871 } 0872 //@endcond 0873 0874 bool KTNEFParser::ParserPrivate::readMAPIProperties(QMap<int, KTNEFProperty *> &props, KTNEFAttach *attach) 0875 { 0876 quint32 n; 0877 MAPI_value mapi; 0878 KTNEFProperty *p; 0879 QMap<int, KTNEFProperty *>::ConstIterator it; 0880 bool foundAttachment = false; 0881 0882 // some initializations 0883 mapi.type = MAPI_TYPE_NONE; 0884 mapi.value.clear(); 0885 0886 // get number of properties 0887 stream_ >> n; 0888 qCDebug(KTNEF_LOG) << "MAPI Properties:" << n; 0889 for (uint i = 0; i < n; i++) { 0890 if (stream_.atEnd()) { 0891 clearMAPIValue(mapi); 0892 return false; 0893 } 0894 readMAPIValue(stream_, mapi); 0895 if (mapi.type == MAPI_TYPE_NONE) { 0896 qCDebug(KTNEF_LOG).nospace() << "MAPI unsupported: tag=" << Qt::hex << mapi.tag << ", type=" << mapi.type; 0897 clearMAPIValue(mapi); 0898 return false; 0899 } 0900 int key = mapi.tag; 0901 switch (mapi.tag) { 0902 case MAPI_TAG_DATA: { 0903 if (mapi.type == MAPI_TYPE_OBJECT && attach) { 0904 QByteArray data = mapi.value.toByteArray(); 0905 int len = data.size(); 0906 ALIGN(len, 4) 0907 device_->seek(device_->pos() - len); 0908 quint32 interface_ID; 0909 stream_ >> interface_ID; 0910 if (interface_ID == MAPI_IID_IMessage) { 0911 // embedded TNEF file 0912 attach->unsetDataParser(); 0913 attach->setOffset(device_->pos() + 12); 0914 attach->setSize(data.size() - 16); 0915 attach->setMimeTag(QStringLiteral("application/vnd.ms-tnef")); 0916 attach->setDisplayName(QStringLiteral("Embedded Message")); 0917 qCDebug(KTNEF_LOG) << "MAPI Embedded Message: size=" << data.size(); 0918 } 0919 device_->seek(device_->pos() + (len - 4)); 0920 break; 0921 } else if (mapi.type == MAPI_TYPE_BINARY && attach && attach->offset() < 0) { 0922 foundAttachment = true; 0923 int len = mapi.value.toByteArray().size(); 0924 ALIGN(len, 4) 0925 attach->setSize(len); 0926 attach->setOffset(device_->pos() - len); 0927 attach->addAttribute(attATTACHDATA, atpBYTE, QStringLiteral("< size=%1 >").arg(len), false); 0928 } 0929 } 0930 qCDebug(KTNEF_LOG) << "MAPI data: size=" << mapi.value.toByteArray().size(); 0931 break; 0932 default: { 0933 QString mapiname = QLatin1StringView(""); 0934 if (mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE) { 0935 if (mapi.name.type == 0) { 0936 mapiname = QString::asprintf(" [name = 0x%04x]", mapi.name.value.toUInt()); 0937 } else { 0938 mapiname = QStringLiteral(" [name = %1]").arg(mapi.name.value.toString()); 0939 } 0940 } 0941 switch (mapi.type & 0x0FFF) { 0942 case MAPI_TYPE_UINT16: 0943 qCDebug(KTNEF_LOG).nospace() << "(tag=" << Qt::hex << mapi.tag << ") MAPI short" << mapiname.toLatin1().data() << ":" << Qt::hex 0944 << mapi.value.toUInt(); 0945 break; 0946 case MAPI_TYPE_ULONG: 0947 qCDebug(KTNEF_LOG).nospace() << "(tag=" << Qt::hex << mapi.tag << ") MAPI long" << mapiname.toLatin1().data() << ":" << Qt::hex 0948 << mapi.value.toUInt(); 0949 break; 0950 case MAPI_TYPE_BOOLEAN: 0951 qCDebug(KTNEF_LOG).nospace() << "(tag=" << Qt::hex << mapi.tag << ") MAPI boolean" << mapiname.toLatin1().data() << ":" << mapi.value.toBool(); 0952 break; 0953 case MAPI_TYPE_TIME: 0954 qCDebug(KTNEF_LOG).nospace() << "(tag=" << Qt::hex << mapi.tag << ") MAPI time" << mapiname.toLatin1().data() << ":" 0955 << mapi.value.toString().toLatin1().data(); 0956 break; 0957 case MAPI_TYPE_USTRING: 0958 case MAPI_TYPE_STRING8: 0959 qCDebug(KTNEF_LOG).nospace() << "(tag=" << Qt::hex << mapi.tag << ") MAPI string" << mapiname.toLatin1().data() 0960 << ":size=" << mapi.value.toByteArray().size() << mapi.value.toString(); 0961 break; 0962 case MAPI_TYPE_BINARY: 0963 qCDebug(KTNEF_LOG).nospace() << "(tag=" << Qt::hex << mapi.tag << ") MAPI binary" << mapiname.toLatin1().data() 0964 << ":size=" << mapi.value.toByteArray().size(); 0965 break; 0966 } 0967 } break; 0968 } 0969 // do not remove potential existing similar entry 0970 if ((it = props.constFind(key)) == props.constEnd()) { 0971 p = new KTNEFProperty(key, (mapi.type & 0x0FFF), mapi.value, mapi.name.value); 0972 props[p->key()] = p; 0973 } 0974 // qCDebug(KTNEF_LOG) << "stream:" << device_->pos(); 0975 } 0976 0977 if (foundAttachment && attach) { 0978 attach->setIndex(attach->property(MAPI_TAG_INDEX).toUInt()); 0979 attach->setDisplaySize(attach->property(MAPI_TAG_SIZE).toUInt()); 0980 QString str = attach->property(MAPI_TAG_DISPLAYNAME).toString(); 0981 if (!str.isEmpty()) { 0982 attach->setDisplayName(str); 0983 } 0984 attach->setFileName(attach->property(MAPI_TAG_FILENAME).toString()); 0985 str = attach->property(MAPI_TAG_MIMETAG).toString(); 0986 if (!str.isEmpty()) { 0987 attach->setMimeTag(str); 0988 } 0989 attach->setExtension(attach->property(MAPI_TAG_EXTENSION).toString()); 0990 if (attach->name().isEmpty()) { 0991 attach->setName(attach->fileName()); 0992 } 0993 } 0994 0995 return true; 0996 }