/* ktnefparser.cpp Copyright (C) 2002 Michael Goffioul 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 #endif /* HAVE_CONFIG_H */ #include "ktnef/ktnefparser.h" #include "ktnef/ktnefattach.h" #include "ktnef/ktnefproperty.h" #include "ktnef/ktnefmessage.h" #include #include #include #include #include #include #include #ifdef HAVE_INTTYPES_H #include #endif /* HAVE_INTTYPES_H */ #include "ktnef/ktnefdefs.h" typedef struct { TQ_UINT16 type; TQ_UINT16 tag; TQVariant value; struct { TQ_UINT32 type; TQVariant value; } name; } MAPI_value; void clearMAPIName( MAPI_value& mapi ); void clearMAPIValue(MAPI_value& mapi, bool clearName = true); TQString readMAPIString( TQDataStream& stream, bool isUnicode = false, bool align = true, int len = -1 ); TQ_UINT16 readMAPIValue(TQDataStream& stream, MAPI_value& mapi); TQDateTime readTNEFDate( TQDataStream& stream ); TQString readTNEFAddress( TQDataStream& stream ); TQByteArray readTNEFData( TQDataStream& stream, TQ_UINT32 len ); TQVariant readTNEFAttribute( TQDataStream& stream, TQ_UINT16 type, TQ_UINT32 len ); TQDateTime formatTime( TQ_UINT32 lowB, TQ_UINT32 highB ); TQString formatRecipient( const TQMap& props ); //------------------------------------------------------------------------------------ class KTNEFParser::ParserPrivate { public: ParserPrivate() { defaultdir_ = "/tmp/"; current_ = 0; deleteDevice_ = false; device_ = 0; message_ = new KTNEFMessage; } ~ParserPrivate() { delete message_; } TQDataStream stream_; TQIODevice *device_; bool deleteDevice_; TQString 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() { TQ_UINT32 i1, i2, off; TQ_UINT16 u, tag, type; TQVariant 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 = TQString( "< %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: { TQ_UINT8 c; TQ_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: { TQ_UINT32 rows; TQValueList recipTable; d->stream_ >> rows; for ( uint i=0; i 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() { TQ_UINT32 i; TQ_UINT16 tag, type, u; TQVariant value; TQString 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 = TQString( "< 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 = TQString( "< %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 = TQString( "< 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 TQString& dirname) { d->defaultdir_ = dirname; } bool KTNEFParser::parseDevice() { TQ_UINT16 u; TQ_UINT32 i; TQ_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( TQDataStream::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 TQString& filename) { KTNEFAttach *att = d->message_->attachment(filename); if (!att) return false; return extractAttachmentTo(att, d->defaultdir_); } bool KTNEFParser::extractAttachmentTo(KTNEFAttach *att, const TQString& dirname) { TQString filename = dirname + "/" + att->name(); if (!d->device_->isOpen()) return false; if (!d->device_->at(att->offset())) return false; KSaveFile saveFile( filename ); TQFile *outfile = saveFile.file(); if ( !outfile ) return false; TQ_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,TQMIN(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() { TQPtrListIterator it(d->message_->attachmentList()); for (;it.current();++it) if (!extractAttachmentTo(it.current(),d->defaultdir_)) return false; return true; } bool KTNEFParser::extractFileTo(const TQString& filename, const TQString& 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 TQString& filename) { deleteDevice(); delete d->message_; d->message_ = new KTNEFMessage(); d->device_ = TQT_TQIODEVICE(new TQFile( filename )); d->deleteDevice_ = true; return parseDevice(); } bool KTNEFParser::openDevice( TQIODevice *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(); TQByteArray buffer( TQMIN( 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 ); } TQDateTime formatTime( TQ_UINT32 lowB, TQ_UINT32 highB ) { TQDateTime 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; } TQString formatRecipient( const TQMap& props ) { TQString s, dn, addr, t; TQMap::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(); } TQDateTime readTNEFDate( TQDataStream& stream ) { // 14-bytes long TQ_UINT16 y, m, d, hh, mm, ss, dm; stream >> y >> m >> d >> hh >> mm >> ss >> dm; return TQDateTime( TQDate( y, m, d ), TQTime( hh, mm, ss ) ); } TQString readTNEFAddress( TQDataStream& stream ) { TQ_UINT16 totalLen, strLen, addrLen; TQString s; stream >> totalLen >> totalLen >> strLen >> addrLen; s.append( readMAPIString( stream, false, false, strLen ) ); s.append( " <" ); s.append( readMAPIString( stream, false, false, addrLen ) ); s.append( ">" ); TQ_UINT8 c; for ( int i=8+strLen+addrLen; i> c; return s; } TQByteArray readTNEFData( TQDataStream& stream, TQ_UINT32 len ) { TQByteArray array( len ); if ( len > 0 ) stream.readRawBytes( array.data(), len ); return array; } TQVariant readTNEFAttribute( TQDataStream& stream, TQ_UINT16 type, TQ_UINT32 len ) { switch ( type ) { case atpTEXT: case atpSTRING: return readMAPIString( stream, false, false, len ); case atpDATE: return readTNEFDate( stream ); default: return readTNEFData( stream, len ); } } TQString readMAPIString( TQDataStream& stream, bool isUnicode, bool align, int len_ ) { TQ_UINT32 len; char *buf = 0; if ( len_ == -1 ) stream >> len; else len = len_; TQ_UINT32 fullLen = len; if ( align ) ALIGN( fullLen, 4 ); buf = new char[ len ]; stream.readRawBytes( buf, len ); TQ_UINT8 c; for ( uint i=len; i> c; TQString res; if ( isUnicode ) res = TQString::fromUcs2( ( const unsigned short* )buf ); else res = TQString::fromLocal8Bit( buf ); delete [] buf; return res; } TQ_UINT16 readMAPIValue(TQDataStream& stream, MAPI_value& mapi) { TQ_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; TQVariant value; if ( ISVECTOR( mapi ) ) { stream >> n; mapi.value = TQValueList(); } for ( int i=0; i> 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: { TQ_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; for (uint i=0;i> d; for (uint i=0;i> len; value = TQByteArray( len ); if (len > 0) { int fullLen = len; ALIGN(fullLen, 4); stream.readRawBytes(value.asByteArray().data(), len); TQ_UINT8 c; for ( int i=len; i> c; } } break; default: kdDebug().form( "unsupported type=%x\n", mapi.type ); mapi.type = MAPI_TYPE_NONE; break; } if ( ISVECTOR( mapi ) ) mapi.value.asList().append( value ); else mapi.value = value; } return mapi.tag; } bool KTNEFParser::readMAPIProperties( TQMap& props, KTNEFAttach *attach ) { TQ_UINT32 n; MAPI_value mapi; KTNEFProperty *p; TQMap::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;istream_.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 ) { TQByteArray data = mapi.value.toByteArray(); int len = data.size(); ALIGN( len, 4 ); d->device_->at( d->device_->at()-len ); TQ_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, TQString( "< size=%1 >" ).arg( len ), false ); } } kdDebug().form( "MAPI data: size=%d\n", mapi.value.toByteArray().size() ); break; default: { TQString mapiname = ""; if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) { if ( mapi.name.type == 0 ) mapiname = TQString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() ); else mapiname = TQString( " [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: kdDebug().form( "(tag=%04x) MAPI unicode string%s: size=%d \"%s\"\n", mapi.tag, mapiname.ascii(), mapi.value.toByteArray().size(), mapi.value.toString().ascii() ); break; 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() ); TQString 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; }