diff options
Diffstat (limited to 'ktnef')
55 files changed, 6363 insertions, 0 deletions
diff --git a/ktnef/AUTHORS b/ktnef/AUTHORS new file mode 100644 index 000000000..dfb1b0df6 --- /dev/null +++ b/ktnef/AUTHORS @@ -0,0 +1,2 @@ +Michael Goffioul <kdeprint@swing.be> + - Main developer diff --git a/ktnef/Makefile.am b/ktnef/Makefile.am new file mode 100644 index 000000000..e181c0636 --- /dev/null +++ b/ktnef/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = ktnef lib gui tests + +messages: rc.cpp + $(EXTRACTRC) gui/*.rc gui/*.ui >> rc.cpp + $(XGETTEXT) rc.cpp gui/*.cpp lib/*.cpp -o $(podir)/ktnef.pot diff --git a/ktnef/configure.in.in b/ktnef/configure.in.in new file mode 100644 index 000000000..1f4b03854 --- /dev/null +++ b/ktnef/configure.in.in @@ -0,0 +1,3 @@ +AC_CHECK_SIZEOF(unsigned long long) +AC_CHECK_SIZEOF(unsigned long) +AC_CHECK_SIZEOF(uint64_t) diff --git a/ktnef/gui/Makefile.am b/ktnef/gui/Makefile.am new file mode 100644 index 000000000..1a9674610 --- /dev/null +++ b/ktnef/gui/Makefile.am @@ -0,0 +1,26 @@ +INCLUDES = -I$(top_srcdir)/ktnef $(all_includes) + +SUBDIRS = pics + +bin_PROGRAMS = ktnef + +ktnef_LDFLAGS = $(KDE_RPATH) $(all_libraries) +ktnef_LDADD = $(LIB_KFILE) $(top_builddir)/ktnef/lib/libktnef.la +ktnef_SOURCES = ktnefmain.cpp ktnefview.cpp attachpropertydialogbase.ui \ + attachpropertydialog.cpp messagepropertydialog.cpp \ + qwmf.cpp main.cpp +ktnef_METASOURCES = AUTO + +noinst_HEADERS = ktnefmain.h ktnefview.h attachpropertydialog.h \ + qwmf.h wmfstruct.h metafuncs.h + +rcdir = $(kde_datadir)/ktnef +rc_DATA = ktnefui.rc + +#messages: rc.cpp +# $(XGETTEXT) -C -ki18n -x $(includedir)/kde.pot $(ktnef_SOURCES) && mv messages.po ../po/ktnef.pot + +xdg_apps_DATA = ktnef.desktop + +mime_DATA = ms-tnef.desktop +mimedir = $(kde_mimedir)/application diff --git a/ktnef/gui/attachpropertydialog.cpp b/ktnef/gui/attachpropertydialog.cpp new file mode 100644 index 000000000..8675b6baa --- /dev/null +++ b/ktnef/gui/attachpropertydialog.cpp @@ -0,0 +1,187 @@ +/* + attachpropertydialog.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "attachpropertydialog.h" +#include <ktnef/ktnefattach.h> +#include <ktnef/ktnefproperty.h> +#include <ktnef/ktnefpropertyset.h> +#include <ktnef/ktnefdefs.h> +#include "qwmf.h" + +#include <qlabel.h> +#include <klistview.h> +#include <kmimetype.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kfiledialog.h> +#include <qbuffer.h> +#include <qdatastream.h> +#include <qpicture.h> + +AttachPropertyDialog::AttachPropertyDialog(QWidget *parent, const char *name) + : AttachPropertyDialogBase(parent, name, true) +{ +} + +AttachPropertyDialog::~AttachPropertyDialog() +{ +} + +void AttachPropertyDialog::setAttachment(KTNEFAttach *attach) +{ + QString s = (attach->fileName().isEmpty() ? attach->name() : attach->fileName()); + filename_->setText("<b>"+s+"</b>"); + setCaption(s); + display_->setText(attach->displayName()); + mime_->setText(attach->mimeTag()); + s.setNum(attach->size()); + s.append(" bytes"); + size_->setText(s); + KMimeType::Ptr mimetype = KMimeType::mimeType(attach->mimeTag()); + QPixmap pix = loadRenderingPixmap( attach, colorGroup().background() ); + if ( !pix.isNull() ) + icon_->setPixmap( pix ); + else + icon_->setPixmap(mimetype->pixmap(KIcon::Small)); + description_->setText(mimetype->comment()); + s.setNum(attach->index()); + index_->setText(s); + + formatPropertySet( attach, properties_ ); + m_attach = attach; +} + +void AttachPropertyDialog::saveClicked() +{ + saveProperty( properties_, m_attach, this ); +} + +void formatProperties( const QMap<int,KTNEFProperty*>& props, QListView *lv, QListViewItem *item, const QString& prefix ) +{ + for ( QMap<int,KTNEFProperty*>::ConstIterator it=props.begin(); it!=props.end(); ++it ) + { + QListViewItem *newItem = 0; + if ( lv ) + newItem = new QListViewItem( lv, ( *it )->keyString() ); + else if ( item ) + newItem = new QListViewItem( item, ( *it )->keyString() ); + else + { + kdWarning() << "formatProperties() called with no listview and no item" << endl; + return; + } + + QVariant value = ( *it )->value(); + if ( value.type() == QVariant::List ) + { + newItem->setOpen( true ); + newItem->setText( 0, newItem->text( 0 ) + " [" + QString::number( value.asList().count() ) + "]" ); + int i = 0; + for ( QValueList<QVariant>::ConstIterator lit=value.listBegin(); lit!=value.listEnd(); ++lit, i++ ) + new QListViewItem( newItem, "[" + QString::number( i ) + "]", KTNEFProperty::formatValue( *lit ) ); + } + else if ( value.type() == QVariant::DateTime ) + newItem->setText( 1, value.asDateTime().toString() ); + else + { + newItem->setText( 1, ( *it )->valueString() ); + newItem->setText( 2, prefix + "_" + QString::number( it.key() ) ); + } + } +} + +void formatPropertySet( KTNEFPropertySet *pSet, QListView *lv ) +{ + formatProperties( pSet->properties(), lv, 0, "prop" ); + QListViewItem *item = new QListViewItem( lv, i18n( "TNEF Attributes" ) ); + item->setOpen( true ); + formatProperties( pSet->attributes(), 0, item, "attr" ); +} + +void saveProperty( QListView *lv, KTNEFPropertySet *pSet, QWidget *parent ) +{ + QListViewItem *item = lv->selectedItem(); + if ( !item ) + KMessageBox::error( parent, i18n( "Select an item." ) ); + else if ( item->text( 2 ).isEmpty() ) + KMessageBox::error( parent, i18n( "The selected item cannot be saved." ) ); + else + { + QString tag = item->text( 2 ); + int key = tag.mid( 5 ).toInt(); + QVariant prop = ( tag.startsWith( "attr_" ) ? pSet->attribute( key ) : pSet->property( key ) ); + QString filename = KFileDialog::getSaveFileName( tag, QString::null, parent ); + if ( !filename.isEmpty() ) + { + QFile f( filename ); + if ( f.open( IO_WriteOnly ) ) + { + switch ( prop.type() ) + { + case QVariant::ByteArray: + f.writeBlock( prop.asByteArray().data(), prop.asByteArray().size() ); + break; + default: + { + QTextStream t( &f ); + t << prop.toString(); + break; + } + } + f.close(); + } + else + KMessageBox::error( parent, i18n( "Unable to open file for writing, check file permissions." ) ); + } + } +} + +QPixmap loadRenderingPixmap( KTNEFPropertySet *pSet, const QColor& bgColor ) +{ + QPixmap pix; + QVariant rendData = pSet->attribute( attATTACHRENDDATA ), wmf = pSet->attribute( attATTACHMETAFILE ); + if ( !rendData.isNull() && !wmf.isNull() ) + { + // Get rendering size + QBuffer rendBuffer( rendData.asByteArray() ); + rendBuffer.open( IO_ReadOnly ); + QDataStream rendStream( &rendBuffer ); + rendStream.setByteOrder( QDataStream::LittleEndian ); + Q_UINT16 type, w, h; + rendStream >> type >> w >> w; // read type and skip 4 bytes + rendStream >> w >> h; + rendBuffer.close(); + + if ( type == 1 && w > 0 && h > 0 ) + { + // Load WMF data + QWinMetaFile wmfLoader; + QBuffer wmfBuffer( wmf.asByteArray() ); + wmfBuffer.open( IO_ReadOnly ); + wmfLoader.setBbox( QRect( 0, 0, w, h ) ); + if ( wmfLoader.load( wmfBuffer ) ) + { + pix.resize( w, h ); + pix.fill( bgColor ); + wmfLoader.paint( &pix ); + } + wmfBuffer.close(); + } + } + return pix; +} diff --git a/ktnef/gui/attachpropertydialog.h b/ktnef/gui/attachpropertydialog.h new file mode 100644 index 000000000..a731572eb --- /dev/null +++ b/ktnef/gui/attachpropertydialog.h @@ -0,0 +1,51 @@ +/* + attachpropertydialog.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef ATTACHPROPERTYDLG_H +#define ATTACHPROPERTYDLG_H + +#include "attachpropertydialogbase.h" +#include <qmap.h> +#include <qpixmap.h> + +class KTNEFAttach; +class KTNEFProperty; +class KTNEFPropertySet; +class QListView; +class QListViewItem; + +class AttachPropertyDialog : public AttachPropertyDialogBase +{ +public: + AttachPropertyDialog(QWidget *parent = 0, const char *name = 0); + ~AttachPropertyDialog(); + + void setAttachment(KTNEFAttach *attach); + +protected slots: + void saveClicked(); + +private: + KTNEFAttach *m_attach; +}; + +void formatProperties( const QMap<int,KTNEFProperty*>&, QListView*, QListViewItem*, const QString& = "prop" ); +void formatPropertySet( KTNEFPropertySet*, QListView* ); +void saveProperty( QListView*, KTNEFPropertySet*, QWidget* ); +QPixmap loadRenderingPixmap( KTNEFPropertySet*, const QColor& ); + +#endif diff --git a/ktnef/gui/attachpropertydialogbase.ui b/ktnef/gui/attachpropertydialogbase.ui new file mode 100644 index 000000000..0d68c3ac4 --- /dev/null +++ b/ktnef/gui/attachpropertydialogbase.ui @@ -0,0 +1,281 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>AttachPropertyDialogBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>AttachPropertyDialogBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>376</width> + <height>426</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>display_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>description_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="3" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>mime_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="4" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>size_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="5" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>index_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="Line" row="6" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>Line2</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>displayLabel_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Comment:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>descriptionLabel_</cstring> + </property> + <property name="text"> + <string>Description:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>mimeLabel_</cstring> + </property> + <property name="text"> + <string>Mime type:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>sizeLabel_</cstring> + </property> + <property name="text"> + <string>File size:</string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>indexLabel_</cstring> + </property> + <property name="text"> + <string>Index:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>filename_</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>icon_</cstring> + </property> + <property name="minimumSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </widget> + <widget class="KListView" row="7" column="0" rowspan="1" colspan="3"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Value</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>properties_</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + </widget> + <widget class="QLayoutWidget" row="8" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>Spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>150</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>save_</cstring> + </property> + <property name="text"> + <string>&Save</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>close_</cstring> + </property> + <property name="text"> + <string>&Close</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>close_</sender> + <signal>clicked()</signal> + <receiver>AttachPropertyDialogBase</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>save_</sender> + <signal>clicked()</signal> + <receiver>AttachPropertyDialogBase</receiver> + <slot>saveClicked()</slot> + </connection> +</connections> +<slots> + <slot access="protected">saveClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/ktnef/gui/ktnef.desktop b/ktnef/gui/ktnef.desktop new file mode 100644 index 000000000..b2d75f8de --- /dev/null +++ b/ktnef/gui/ktnef.desktop @@ -0,0 +1,109 @@ +[Desktop Entry] +Name=KTnef +Name[sv]=Ktnef +GenericName=TNEF File Viewer +GenericName[af]=TNEF lêer leser +GenericName[bg]=Преглед на файлове TNEF +GenericName[br]=Gweler restr TNEF +GenericName[ca]=Visor de fitxers TNEF +GenericName[cs]=ProhlÞeÄ TNEF souborů +GenericName[cy]=Syllwr Ffeil TNEF +GenericName[da]=TNEF Fil-fremviser +GenericName[de]=TNEF-Dateibetrachter +GenericName[el]=Î ÏοβολÎας αÏχείων TNEF +GenericName[eo]=TNEF-dosierorigardilo +GenericName[es]=Visor de archivos TNEF +GenericName[et]=TNEF-failide näitaja +GenericName[eu]=TNEF fitxategi ikustailea +GenericName[fa]=مشاهده‌گر پروندۀ TNEF +GenericName[fi]=TNEF-tiedostonäytin +GenericName[fr]=Afficheur de fichiers TNEF +GenericName[fy]=TNEF-triemwerjefte +GenericName[ga]=Amharcán Comhad TNEF +GenericName[gl]=Visor de Ficheiros TNEF +GenericName[hu]=TNEF-fájlnézegetÅ‘ +GenericName[is]=TNEF skráarskoðari +GenericName[it]=Visualizzatore file TNEF +GenericName[ja]=TNEF ファイルビュー +GenericName[ka]=TNEF ფáƒáƒ˜áƒšáƒ”ბის დáƒáƒ›áƒ—ვáƒáƒšáƒ˜áƒ”რებელი +GenericName[kk]=TNEF файлды қарау құралы +GenericName[km]=កម្មវិធី​មើល​ឯកសារ TNEF +GenericName[lt]=TNEF bylų žiÅ«ryklÄ— +GenericName[mk]=Прегледувач на TNEF-датотеки +GenericName[ms]=Pemapar Fail TNEF +GenericName[nb]=TNEF-filvisning +GenericName[nds]=TNEF-Dateikieker +GenericName[ne]=TNEF फाइल दरà¥à¤¶à¤• +GenericName[nl]=TNEF-bestandsweergave +GenericName[nn]=TNEF filvisar +GenericName[pl]=PrzeglÄ…darka plików TNEF +GenericName[pt]=Visualizador de Ficheiros TNEF +GenericName[pt_BR]=Visualizador de Arquivos TNEF +GenericName[ru]=ПроÑмотр файлов TNEF +GenericName[sk]=PrehliadaÄ súborov TNEF +GenericName[sl]=Pregledovalnik datotek TNEF +GenericName[sr]=Приказивач TNEF фајлова +GenericName[sr@Latn]=PrikazivaÄ TNEF fajlova +GenericName[sv]=TNEF-filvisning +GenericName[ta]=TNEF கோபà¯à®ªà¯ காடà¯à®šà®¿à®¯à®¾à®³à®©à¯ +GenericName[tr]=TNEF Dosya Göstericisi +GenericName[uk]=ПереглÑдач файлів TNEF +GenericName[zh_CN]=TNEF 文件查看器 +GenericName[zh_TW]=TNEF 檔案檢視器 +Exec=ktnef %i %m -caption "%c" +Icon=ktnef +Type=Application +DocPath=ktnef/index.html +Comment=A viewer/extractor for TNEF files +Comment[af]='n Leeser en data onttrekker vir TNEF lêers +Comment[bg]=Програма за преглед и извличане на файлове TNEF +Comment[bs]=Preglednik/ekstraktor za TNEF datoteke +Comment[ca]=Un visor/extractor per a fitxers TNEF +Comment[cs]=ProhlÞeÄ TNEF souborů +Comment[cy]=Gwelydd/echdynnydd i ffeiliau TNEF +Comment[da]=En fremviser/udpakker for TNEF-filer +Comment[de]=Betrachten und Extrahieren von TNEF-Dateien +Comment[el]=Ένας Ï€ÏοβολÎας/εξαγωγÎας για αÏχεία TNEF +Comment[es]=A visor/extractor para archivos TNEF +Comment[et]=TNEF-failide näitaja/ekstraktija +Comment[eu]=TNEF fitxategien ikustaile/erauzlea +Comment[fa]=یک مشاهده‌گر/استخراج‌گر برای پرونده‌های TNEF +Comment[fi]=Näytin/purkaja TNEF-tiedostoille +Comment[fr]=Pour afficher / extraire des fichiers TNEF +Comment[fy]=In werjefteprogramma/útpakker foar TNEF-triemmen +Comment[gl]=Un visor/extractor para ficheiros TNEF +Comment[hi]=टीà¤à¤¨à¤ˆà¤à¤« फ़ाइलों के लिठà¤à¤• पà¥à¤°à¤¦à¤°à¥à¤¶à¤•/à¤à¤•à¥à¤¸à¤Ÿà¥à¤°à¥‡à¤•à¥à¤Ÿà¤° +Comment[hr]=PrikazivaÄ/izdvajaÄ za TNEF datoteke +Comment[hu]=NézegetÅ‘program TNEF-fájlokhoz +Comment[is]=Sýnir/lesari fyrir TNEF skrár +Comment[it]=Un visualizzatore/estrattore di file TNEF +Comment[ja]=TNEF ファイルã®ãŸã‚ã®ãƒ“ューア/展開ツール +Comment[ka]= TNEF ფáƒáƒ˜áƒšáƒ”ბის ექსტრáƒáƒ¥áƒ¢áƒáƒ ი/დáƒáƒ›áƒ—ვáƒáƒšáƒ˜áƒ”რებელი +Comment[kk]=TNEF файдарды қарау/тарқату +Comment[km]=កម្មវិធី​មើល និង​ស្រង់​ឯកសារ TNEF +Comment[lt]=TNEF bylų žiÅ«ryklÄ— - iÅ¡pakuotojas +Comment[ms]=Pemapar/pengekstrak untuk fail TNEF +Comment[nb]=En utpakker/fremviser for TNEF-filer +Comment[nds]=TNEF-Dateien ankieken un Delen ruttrecken +Comment[ne]=TNEF फाइलका लागि दरà¥à¤¶à¤•/निषà¥à¤•à¤¾à¤¸à¤• +Comment[nl]=Een weergaveprogramma/uitpakker voor TNEF-bestanden +Comment[nn]=Ein framvisar/utpakkar for TNEF-filer +Comment[pl]=PrzeglÄ…darka/program wyciÄ…gajÄ…cy zawartość plików TNEF +Comment[pt]=Um visualizador/extractor de ficheiros TNEF +Comment[pt_BR]=Um visualizador/extrator para arquivos TNEF +Comment[ro]=Un vizualizor/extractor pentru fiÅŸiere TNEF +Comment[ru]=ПроÑмотр и раÑпаковка файлов TNEF +Comment[sk]=PrehliadaÄ/extraktor pre TNEF súbory +Comment[sl]=Pregledovalnik/izvleÄevalnik za datoteke TNEF +Comment[sr]=Приказивач/издвајач за TNEF фајлове +Comment[sr@Latn]=PrikazivaÄ/izdvajaÄ za TNEF fajlove +Comment[sv]=Visning och uppackning av TNEF-filer +Comment[ta]=TNEF கோபà¯à®ªà¯à®•à®³à¯à®•à¯à®•à®¾à®© காடà¯à®šà®¿à®¯à®•à®®à¯/பிரிதà¯à®¤à¯†à®Ÿà¯à®ªà¯à®ªà®¾à®³à®°à¯ +Comment[tg]=Ðамоиш ва боз кардани файлҳои TNEF +Comment[tr]=TNEF dosyaları için gösterici/çıkarıcı +Comment[uk]=ПереглÑдач/розпаковувач файлів TNEF +Comment[zh_CN]=TNEF 文件的查看/æå–器 +Comment[zh_TW]=TNEF 檔案檢視器 +Terminal=false +MimeType=application/ms-tnef; +Categories=Qt;KDE;X-KDE-Utilities-PIM;Office;Network;Email; diff --git a/ktnef/gui/ktnefmain.cpp b/ktnef/gui/ktnefmain.cpp new file mode 100644 index 000000000..a31d40161 --- /dev/null +++ b/ktnef/gui/ktnefmain.cpp @@ -0,0 +1,409 @@ +/* + ktnefmain.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include <kdebug.h> +#include "ktnefmain.h" +#include <ktnef/ktnefparser.h> +#include "ktnefview.h" +#include <ktnef/ktnefattach.h> +#include <ktnef/ktnefproperty.h> +#include <ktnef/ktnefmessage.h> +#include "attachpropertydialog.h" +#include "messagepropertydialog.h" + +#include <qpopupmenu.h> +#include <klistview.h> +#include <klocale.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <qpixmap.h> +#include <kstdaccel.h> +#include <qmessagebox.h> +#include <kfiledialog.h> +#include <qdir.h> +#include <kprocess.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kaction.h> +#include <kstdaction.h> +#include <kdialogbase.h> +#include <ktempfile.h> +#include <kkeydialog.h> + +#ifdef KDE_NO_COMPAT +#undef KDE_NO_COMPAT +#endif + +#include <krun.h> +#include <kopenwith.h> +#include <kedittoolbar.h> +#include <kstatusbar.h> +#include <kurldrag.h> + + +#define NOT_IMPLEMENTED QMessageBox::information(this, "ktnef", "Not implemented yet", QMessageBox::Ok|QMessageBox::Default, 0) + +KTNEFMain::KTNEFMain(QWidget *parent, const char *name) + : KMainWindow(parent, name) +{ + setupActions(); + setupStatusbar(); + + setupTNEF(); + + KGlobal::config()->setGroup("Settings"); + defaultdir_ = KGlobal::config()->readPathEntry("defaultdir", "/tmp/"); + lastdir_ = defaultdir_; + + // create personale temo extract dir + KStandardDirs::makeDir(KGlobal::dirs()->localkdedir() + "/share/apps/ktnef/tmp"); + + resize(430,350); + setAutoSaveSettings( "MainWindow" ); +} + +KTNEFMain::~KTNEFMain() +{ + delete parser_; + cleanup(); +} + +void KTNEFMain::setupActions() +{ + // File menu + KStdAction::open(this, SLOT(openFile()), actionCollection()); + KStdAction::quit(kapp, SLOT(quit()), actionCollection()); + + // Action menu + new KAction(i18n("View"), QString("viewmag"), 0, this, SLOT(viewFile()), actionCollection(), "view_file"); + new KAction(i18n("View With..."), QString("package_applications"), 0, this, SLOT(viewFileAs()), actionCollection(), "view_file_as"); + new KAction(i18n("Extract"), 0, this, SLOT(extractFile()), actionCollection(), "extract_file"); + new KAction(i18n("Extract To..."), QString("ktnef_extract_to"), 0, this, SLOT(extractFileTo()), actionCollection(), "extract_file_to"); + new KAction(i18n("Extract All To..."), QString("ktnef_extract_all_to"), 0, this, SLOT(extractAllFiles()), actionCollection(), "extract_all_files"); + new KAction( i18n( "Message Properties" ), "help", 0, this, SLOT( slotShowMessageProperties() ), actionCollection(), "msg_properties" ); + new KAction(i18n("Properties"), QString("contents"), 0, this, SLOT(propertiesFile()), actionCollection(), "properties_file"); + new KAction( i18n( "Show Message Text" ), "mail_generic", 0, this, SLOT( slotShowMessageText() ), actionCollection(), "msg_text" ); + new KAction( i18n( "Save Message Text As..." ), "filesave", 0, this, SLOT( slotSaveMessageText() ), actionCollection(), "msg_save" ); + actionCollection()->action("view_file")->setEnabled(false); + actionCollection()->action("view_file_as")->setEnabled(false); + actionCollection()->action("extract_file")->setEnabled(false); + actionCollection()->action("extract_file_to")->setEnabled(false); + actionCollection()->action("extract_all_files")->setEnabled(false); + actionCollection()->action("properties_file")->setEnabled(false); + + // Option menu + new KAction(i18n("Default Folder..."), QString("folder_open"), 0, this, SLOT(optionDefaultDir()), actionCollection(), "options_default_dir"); + + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); + KStdAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection()); + KStdAction::keyBindings( this, SLOT( slotConfigureKeys() ), actionCollection() ); + + createGUI(); +} + +void KTNEFMain::slotConfigureKeys() +{ + KKeyDialog::configure( actionCollection(), this ); +} + + +void KTNEFMain::setupStatusbar() +{ + statusBar()->insertItem(i18n("100 attachments found"), 0); + statusBar()->changeItem(i18n("No file loaded"), 0); +} + +void KTNEFMain::setupTNEF() +{ + view_ = new KTNEFView(this); + view_->setAllColumnsShowFocus( true ); + parser_ = new KTNEFParser; + + setCentralWidget(view_); + connect(view_, SIGNAL(selectionChanged()), SLOT(viewSelectionChanged())); + connect(view_, SIGNAL(rightButtonPressed(QListViewItem*,const QPoint&,int)), SLOT(viewRightButtonPressed(QListViewItem*,const QPoint&,int))); + connect(view_, SIGNAL(doubleClicked(QListViewItem*)), SLOT(viewDoubleClicked(QListViewItem*))); + connect(view_, SIGNAL(dragRequested(const QValueList<KTNEFAttach*>&)), SLOT(viewDragRequested(const QValueList<KTNEFAttach*>&))); +} + +void KTNEFMain::loadFile(const QString& filename) +{ + filename_ = filename; + setCaption(filename_); + if (!parser_->openFile(filename)) + { + QMessageBox::critical(this, i18n("Error"), i18n("Unable to open file."), QMessageBox::Ok|QMessageBox::Default, 0); + view_->setAttachments(0); + enableExtractAll(false); + } + else + { + QPtrList<KTNEFAttach> list = parser_->message()->attachmentList(); + QString msg; + msg = i18n( "%n attachment found", "%n attachments found", list.count() ); + statusBar()->changeItem(msg, 0); + view_->setAttachments(&list); + enableExtractAll((list.count() > 0)); + enableSingleAction(false); + } +} + +void KTNEFMain::openFile() +{ + QString filename = KFileDialog::getOpenFileName(0,0,this,0); + if (!filename.isEmpty()) loadFile(filename); +} + +void KTNEFMain::viewFile() +{ + KTNEFAttach *attach = view_->getSelection()->first(); + KURL url("file:"+extractTemp(attach)); + QString mimename(attach->mimeTag()); + + if (mimename.isEmpty() || mimename == "application/octet-stream") + { + kdDebug() << "No mime type found in attachment object, trying to guess..." << endl; + mimename = KMimeType::findByURL(url, 0, true)->name(); + kdDebug() << "Detected mime type: " << mimename << endl; + } + else + kdDebug() << "Mime type from attachment object: " << mimename << endl; + + KRun::runURL(url, mimename); +} + +QString KTNEFMain::extractTemp(KTNEFAttach *att) +{ + QString dir = KGlobal::dirs()->localkdedir() + "/share/apps/ktnef/tmp/"; + parser_->extractFileTo(att->name(), dir); + dir.append(att->name()); + return dir; +} + +void KTNEFMain::viewFileAs() +{ + KURL::List list; + list.append(KURL::fromPathOrURL( extractTemp(view_->getSelection()->first()) )); + + KRun::displayOpenWithDialog(list); +} + +void KTNEFMain::extractFile() +{ + extractTo(defaultdir_); +} + +void KTNEFMain::extractFileTo() +{ + QString dir = KFileDialog::getExistingDirectory(lastdir_, this); + if (!dir.isEmpty()) + { + extractTo(dir); + lastdir_ = dir; + } +} + +void KTNEFMain::extractAllFiles() +{ + QString dir = KFileDialog::getExistingDirectory(lastdir_, this); + if (!dir.isEmpty()) + { + lastdir_ = dir; + dir.append("/"); + QPtrList<KTNEFAttach> list = parser_->message()->attachmentList(); + QPtrListIterator<KTNEFAttach> it(list); + for (;it.current();++it) + if (!parser_->extractFileTo(it.current()->name(), dir)) + { + QString msg = i18n( "Unable to extract file \"%1\"" ).arg( it.current()->name() ); + QMessageBox::critical(this,i18n("Error"),msg,QMessageBox::Ok|QMessageBox::Default,0); + return; + } + } +} + +void KTNEFMain::propertiesFile() +{ + KTNEFAttach *attach = view_->getSelection()->first(); + AttachPropertyDialog dlg(this); + dlg.setAttachment(attach); + dlg.exec(); +} + +void KTNEFMain::optionDefaultDir() +{ + QString dirname = KFileDialog::getExistingDirectory(defaultdir_, this); + if (!dirname.isEmpty()) + { + defaultdir_ = dirname; + KGlobal::config()->setGroup("Settings"); + KGlobal::config()->writePathEntry("defaultdir",defaultdir_); + } +} + +void KTNEFMain::viewSelectionChanged() +{ + QPtrList<KTNEFAttach> *list = view_->getSelection(); + bool on1 = (list->count() == 1u), on2 = (list->count() > 0u); + actionCollection()->action("view_file")->setEnabled(on1); + actionCollection()->action("view_file_as")->setEnabled(on1); + actionCollection()->action("properties_file")->setEnabled(on1); + + actionCollection()->action("extract_file")->setEnabled(on2); + actionCollection()->action("extract_file_to")->setEnabled(on2); +} + +void KTNEFMain::enableExtractAll(bool on) +{ + if (!on) enableSingleAction(false); + actionCollection()->action("extract_all_files")->setEnabled(on); +} + +void KTNEFMain::enableSingleAction(bool on) +{ + actionCollection()->action("extract_file")->setEnabled(on); + actionCollection()->action("extract_file_to")->setEnabled(on); + actionCollection()->action("view_file")->setEnabled(on); + actionCollection()->action("view_file_as")->setEnabled(on); + actionCollection()->action("properties_file")->setEnabled(on); +} + +void KTNEFMain::cleanup() +{ + QDir d(KGlobal::dirs()->localkdedir() + "/share/apps/ktnef/tmp/"); + const QFileInfoList *list = d.entryInfoList(QDir::Files|QDir::Hidden,QDir::Unsorted); + QFileInfoListIterator it(*list); + for (;it.current();++it) + d.remove(it.current()->absFilePath()); +} + +void KTNEFMain::extractTo(const QString& dirname) +{ + QString dir = dirname; + if (dir.right(1) != "/") dir.append("/"); + QPtrList<KTNEFAttach> *list = view_->getSelection(); + QPtrListIterator<KTNEFAttach> it(*list); + for (;it.current();++it) + if (!parser_->extractFileTo(it.current()->name(), dir)) + { + QString msg = i18n("Unable to extract file \"%1\"").arg( it.current()->name() ); + QMessageBox::critical(this,i18n("Error"),msg,QMessageBox::Ok|QMessageBox::Default,0); + return; + } +} + +/* This breaks the saveMainWindowSettings stuff.... + void KTNEFMain::closeEvent(QCloseEvent *e) +{ + e->accept(); +}*/ + +void KTNEFMain::viewRightButtonPressed(QListViewItem*, const QPoint& p, int) +{ + QPtrList<KTNEFAttach> *list = view_->getSelection(); + QPopupMenu m; + if (list->count() > 0u) + { + if (list->count() == 1u) + { + m.insertItem(SmallIcon("viewmag"), i18n("View"), this, SLOT(viewFile())); + m.insertItem(SmallIcon("package_applications"), i18n("View With..."), this, SLOT(viewFileAs())); + m.insertSeparator(); + } + m.insertItem(i18n("Extract"), this, SLOT(extractFile())); + m.insertItem(SmallIcon("ktnef_extract_to"), i18n("Extract To..."), this, SLOT(extractFileTo())); + if (list->count() == 1u) + { + m.insertSeparator(); + m.insertItem(SmallIcon("contents"), i18n("Properties"), this, SLOT(propertiesFile())); + } + } + else if ( list->count() == 0 ) + actionCollection()->action( "msg_properties" )->plug( &m ); + m.exec( p ); +} + +void KTNEFMain::viewDoubleClicked(QListViewItem *item) +{ + if (item && item->isSelected()) + viewFile(); +} + +void KTNEFMain::viewDragRequested( const QValueList<KTNEFAttach*>& list ) +{ + KURL::List urlList; + for ( QValueList<KTNEFAttach*>::ConstIterator it=list.constBegin(); it!=list.constEnd(); ++it ) + urlList << KURL( extractTemp( *it ) ); + if ( !list.isEmpty() ) + { + KURLDrag *urlDrag = new KURLDrag( urlList, this ); + urlDrag->dragCopy(); + } +} + +void KTNEFMain::slotEditToolbars() +{ + saveMainWindowSettings( KGlobal::config(), QString::fromLatin1("MainWindow") ); + KEditToolbar dlg(actionCollection()); + connect(&dlg, SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() )); + dlg.exec(); +} + +void KTNEFMain::slotNewToolbarConfig() +{ + createGUI(); + applyMainWindowSettings( KGlobal::config(), QString::fromLatin1("MainWindow") ); +} + +void KTNEFMain::slotShowMessageProperties() +{ + MessagePropertyDialog dlg( this, parser_->message() ); + dlg.exec(); +} + +void KTNEFMain::slotShowMessageText() +{ + QString rtf = parser_->message()->rtfString(); + qDebug( "%s", rtf.latin1() ); + KTempFile tmpFile( KGlobal::dirs()->localkdedir() + "/share/apps/ktnef/tmp/", "rtf"); + *( tmpFile.textStream() ) << rtf; + tmpFile.close(); + + KRun::runURL( KURL::fromPathOrURL( tmpFile.name() ), "text/rtf", true ); +} + +void KTNEFMain::slotSaveMessageText() +{ + QString rtf = parser_->message()->rtfString(); + QString filename = KFileDialog::getSaveFileName( QString::null, QString::null, this ); + if ( !filename.isEmpty() ) + { + QFile f( filename ); + if ( f.open( IO_WriteOnly ) ) + { + QTextStream t( &f ); + t << rtf; + } + else + QMessageBox::critical( this, i18n( "Error" ), + i18n( "Unable to open file for writing, check file permissions." ), + QMessageBox::Ok|QMessageBox::Default, 0); + } +} + +#include "ktnefmain.moc" diff --git a/ktnef/gui/ktnefmain.h b/ktnef/gui/ktnefmain.h new file mode 100644 index 000000000..30f5a171f --- /dev/null +++ b/ktnef/gui/ktnefmain.h @@ -0,0 +1,81 @@ +/* + ktnefmain.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFMAIN_H +#define KTNEFMAIN_H + +#include <kmainwindow.h> +#include <qstring.h> +#include <qlistview.h> + +class KTNEFView; +class KTNEFParser; +class KTNEFAttach; + +class KTNEFMain : public KMainWindow +{ + Q_OBJECT + +public: + KTNEFMain(QWidget *parent = 0, const char *name = 0); + ~KTNEFMain(); + + void loadFile(const QString& filename); + +protected slots: + void openFile(); + void viewFile(); + void viewFileAs(); + void extractFile(); + void extractFileTo(); + void propertiesFile(); + void optionDefaultDir(); + void extractAllFiles(); + void slotEditToolbars(); + void slotNewToolbarConfig(); + void slotShowMessageProperties(); + void slotShowMessageText(); + void slotSaveMessageText(); + + void viewSelectionChanged(); + void viewRightButtonPressed(QListViewItem *item, const QPoint& p, int c); + void viewDoubleClicked(QListViewItem*); + void viewDragRequested( const QValueList<KTNEFAttach*>& list ); + void slotConfigureKeys(); +//protected: +// void closeEvent(QCloseEvent *e); + +private: + void setupStatusbar(); + void setupActions(); + void setupTNEF(); + void enableExtractAll(bool on = true); + void enableSingleAction(bool on = true); + void cleanup(); + + void extractTo(const QString& dirname); + QString extractTemp(KTNEFAttach *att); + +private: + KTNEFView *view_; + KTNEFParser *parser_; + QString filename_; + QString defaultdir_; + QString lastdir_; +}; + +#endif diff --git a/ktnef/gui/ktnefui.rc b/ktnef/gui/ktnefui.rc new file mode 100644 index 000000000..c08cf1366 --- /dev/null +++ b/ktnef/gui/ktnefui.rc @@ -0,0 +1,36 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kups" version="2"> +<MenuBar> + <Menu name="action"><text>&Action</text> + <Action name="view_file" /> + <Action name="view_file_as" /> + <Separator/> + <Action name="extract_file" /> + <Action name="extract_file_to" /> + <Separator/> + <Action name="extract_all_files" /> + <Separator/> + <Action name="properties_file" /> + <Separator/> + <Action name="msg_properties" /> + <Action name="msg_text" /> + <Action name="msg_save" /> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Action name="options_default_dir" append="save_merge"/> + </Menu> +</MenuBar> +<ToolBar name="mainToolBar" noMerge="1"> + <text>Main Toolbar</text> + <Action name="file_open" /> + <Separator/> + <Action name="view_file" /> + <Action name="properties_file" /> + <Separator/> + <Action name="extract_file_to" /> + <Action name="extract_all_files" /> + <Separator/> + <Action name="msg_properties" /> + <Action name="msg_text" /> +</ToolBar> +</kpartgui> diff --git a/ktnef/gui/ktnefview.cpp b/ktnef/gui/ktnefview.cpp new file mode 100644 index 000000000..abee5a73b --- /dev/null +++ b/ktnef/gui/ktnefview.cpp @@ -0,0 +1,136 @@ +/* + ktnefview.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "ktnefview.h" +#include <ktnef/ktnefattach.h> +#include "attachpropertydialog.h" + +#include <qheader.h> +#include <qpixmap.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kdebug.h> + +#include <kmimetype.h> + +class Attachment : public QListViewItem +{ +public: + Attachment(QListView *parent, KTNEFAttach *attach); + ~Attachment(); + + KTNEFAttach* getAttachment() const { return attach_; } + +private: + KTNEFAttach *attach_; +}; + +Attachment::Attachment(QListView *parent, KTNEFAttach *attach) + : QListViewItem(parent, attach->name()), attach_(attach) +{ + setText(2, QString::number( attach_->size() )); + if (!attach_->fileName().isEmpty()) setText(0, attach_->fileName()); + KMimeType::Ptr mimeType = KMimeType::mimeType( attach_->mimeTag() ); + setText(1, mimeType->comment()); + QPixmap pix = loadRenderingPixmap( attach, parent->colorGroup().base() ); + if ( !pix.isNull() ) + setPixmap( 0, pix ); + else + setPixmap(0, mimeType->pixmap(KIcon::Small)); + setDragEnabled( true ); +} + +Attachment::~Attachment() +{ +} + +//------------------------------------------------------------------------------------------------------ + +KTNEFView::KTNEFView(QWidget *parent, const char *name) + : KListView(parent,name) +{ + attachments_.setAutoDelete(false); + addColumn(i18n("File Name")); + addColumn(i18n("File Type")); + addColumn(i18n("Size")); + setFrameStyle(QFrame::WinPanel|QFrame::Sunken); + setLineWidth(1); + setSelectionMode(QListView::Extended); + setHScrollBarMode(QScrollView::AlwaysOff); + setVScrollBarMode(QScrollView::AlwaysOn); + QTimer::singleShot( 0, this, SLOT(adjustColumnWidth()) ); +} + +KTNEFView::~KTNEFView() +{ +} + +void KTNEFView::setAttachments(QPtrList<KTNEFAttach> *list) +{ + clear(); + if (list) + { + QPtrListIterator<KTNEFAttach> it(*list); + for (;it.current();++it) + new Attachment(this, it.current()); + } +} + +void KTNEFView::resizeEvent(QResizeEvent *e) +{ + adjustColumnWidth(); + resizeContents(visibleWidth(),visibleHeight()); + if (e) QListView::resizeEvent(e); +} + +QPtrList<KTNEFAttach>* KTNEFView::getSelection() +{ + attachments_.clear(); + QListViewItem *item = firstChild(); + while (item) + { + if (item->isSelected()) attachments_.append(((Attachment*)item)->getAttachment()); + item = item->nextSibling(); + } + return &attachments_; +} + +void KTNEFView::startDrag() +{ + QListViewItemIterator it( this, QListViewItemIterator::Selected ); + QValueList<KTNEFAttach*> list; + while ( it.current() ) + { + list << static_cast<Attachment*>( it.current() )->getAttachment(); + ++it; + } + if ( !list.isEmpty() ) + emit dragRequested( list ); +} + +void KTNEFView::adjustColumnWidth() +{ + int w = visibleWidth()/2; + setColumnWidth(0,w); + setColumnWidth(1,w/2); + setColumnWidth(2,w/2); +} + +#include "ktnefview.moc" diff --git a/ktnef/gui/ktnefview.h b/ktnef/gui/ktnefview.h new file mode 100644 index 000000000..00c49b7c7 --- /dev/null +++ b/ktnef/gui/ktnefview.h @@ -0,0 +1,52 @@ +/* + ktnefview.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFWIDGET_H +#define KTNEFWIDGET_H + +#include <klistview.h> +#include <qptrlist.h> +#include <kdepimmacros.h> + +class KTNEFAttach; + +class KDE_EXPORT KTNEFView : public KListView +{ + Q_OBJECT + +public: + KTNEFView(QWidget *parent = 0, const char *name = 0); + ~KTNEFView(); + + void setAttachments(QPtrList<KTNEFAttach> *list); + QPtrList<KTNEFAttach>* getSelection(); + +signals: + void dragRequested( const QValueList<KTNEFAttach*>& list ); + +protected: + void resizeEvent(QResizeEvent *e); + void startDrag(); + +private slots: + void adjustColumnWidth(); + +private: + QPtrList<KTNEFAttach> attachments_; +}; + +#endif diff --git a/ktnef/gui/main.cpp b/ktnef/gui/main.cpp new file mode 100644 index 000000000..e975630df --- /dev/null +++ b/ktnef/gui/main.cpp @@ -0,0 +1,57 @@ +/* + main.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <klocale.h> +#include <kapplication.h> + +#include "ktnefmain.h" + +static const char description[] = + I18N_NOOP("Viewer for mail attachments using TNEF format"); + + +static KCmdLineOptions options[] = +{ + { "+[file]", I18N_NOOP("An optional argument 'file'"), 0 }, + KCmdLineLastOption + // INSERT YOUR COMMANDLINE OPTIONS HERE +}; + +int main(int argc, char *argv[]) +{ + + KAboutData aboutData( "ktnef", I18N_NOOP("KTnef"), + "1.0", description, KAboutData::License_GPL, + "(c) 2000, Michael Goffioul"); + aboutData.addAuthor("Michael Goffioul",0, "kdeprint@swing.be"); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + KApplication::addCmdLineOptions(); + + KApplication a; + KTNEFMain *tnef = new KTNEFMain(); + a.setMainWidget(tnef); + tnef->show(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->count() > 0) + tnef->loadFile(args->arg(0)); + + return a.exec(); +} diff --git a/ktnef/gui/messagepropertydialog.cpp b/ktnef/gui/messagepropertydialog.cpp new file mode 100644 index 000000000..0e098c35c --- /dev/null +++ b/ktnef/gui/messagepropertydialog.cpp @@ -0,0 +1,46 @@ +/* + messagepropertydialog.cpp + + Copyright (C) 2003 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "messagepropertydialog.h" +#include "attachpropertydialog.h" +#include "ktnef/ktnefmessage.h" + +#include <klistview.h> +#include <klocale.h> + +MessagePropertyDialog::MessagePropertyDialog( QWidget *parent, KTNEFMessage *msg ) + : KDialogBase( parent, "MessagePropertyDialog", true, i18n( "Message Properties" ), + KDialogBase::Close|KDialogBase::User1, KDialogBase::Close, false, + KStdGuiItem::save() ) +{ + m_message = msg; + + m_listview = new KListView( this ); + m_listview->addColumn( i18n( "Name" ) ); + m_listview->addColumn( i18n( "Value" ) ); + m_listview->setAllColumnsShowFocus( true ); + setMainWidget( m_listview ); + + formatPropertySet( m_message, m_listview ); +} + +void MessagePropertyDialog::slotUser1() +{ + saveProperty( m_listview, m_message, this ); +} + +#include "messagepropertydialog.moc" diff --git a/ktnef/gui/messagepropertydialog.h b/ktnef/gui/messagepropertydialog.h new file mode 100644 index 000000000..7b357b716 --- /dev/null +++ b/ktnef/gui/messagepropertydialog.h @@ -0,0 +1,40 @@ +/* + messagepropertydialog.h + + Copyright (C) 2003 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef MESSAGEPROPERTYDIALOG_H +#define MESSAGEPROPERTYDIALOG_H + +#include <kdialogbase.h> + +class KListView; +class KTNEFMessage; + +class MessagePropertyDialog : public KDialogBase +{ + Q_OBJECT +public: + MessagePropertyDialog( QWidget *parent, KTNEFMessage *msg ); + +protected slots: + void slotUser1(); + +private: + KTNEFMessage *m_message; + KListView *m_listview; +}; + +#endif /* MESSAGEPROPERTYDIALOG_H */ diff --git a/ktnef/gui/metafuncs.h b/ktnef/gui/metafuncs.h new file mode 100644 index 000000000..8675851e0 --- /dev/null +++ b/ktnef/gui/metafuncs.h @@ -0,0 +1,90 @@ +/* WMF Metafile Function Description Table + * Author: Stefan Taferner <taferner@kde.org> + */ +#ifndef metafunc_h +#define metafunc_h + +class QWinMetaFile; + +static const struct MetaFuncRec +{ + const char* name; + unsigned short func; + void ( QWinMetaFile::*method )( long, short* ); +} metaFuncTab[] = + { + { "SETBKCOLOR", 0x0201, &QWinMetaFile::setBkColor }, + { "SETBKMODE", 0x0102, &QWinMetaFile::setBkMode }, + { "SETMAPMODE", 0x0103, &QWinMetaFile::noop }, + { "SETROP2", 0x0104, &QWinMetaFile::setRop }, + { "SETRELABS", 0x0105, &QWinMetaFile::noop }, + { "SETPOLYFILLMODE", 0x0106, &QWinMetaFile::setPolyFillMode }, + { "SETSTRETCHBLTMODE", 0x0107, &QWinMetaFile::noop }, + { "SETTEXTCHAREXTRA", 0x0108, &QWinMetaFile::noop }, + { "SETTEXTCOLOR", 0x0209, &QWinMetaFile::setTextColor }, + { "SETTEXTJUSTIFICATION", 0x020A, &QWinMetaFile::noop }, + { "SETWINDOWORG", 0x020B, &QWinMetaFile::setWindowOrg }, + { "SETWINDOWEXT", 0x020C, &QWinMetaFile::setWindowExt }, + { "SETVIEWPORTORG", 0x020D, &QWinMetaFile::noop }, + { "SETVIEWPORTEXT", 0x020E, &QWinMetaFile::noop }, + { "OFFSETWINDOWORG", 0x020F, &QWinMetaFile::noop }, + { "SCALEWINDOWEXT", 0x0410, &QWinMetaFile::noop }, + { "OFFSETVIEWPORTORG", 0x0211, &QWinMetaFile::noop }, + { "SCALEVIEWPORTEXT", 0x0412, &QWinMetaFile::noop }, + { "LINETO", 0x0213, &QWinMetaFile::lineTo }, + { "MOVETO", 0x0214, &QWinMetaFile::moveTo }, + { "EXCLUDECLIPRECT", 0x0415, &QWinMetaFile::excludeClipRect }, + { "INTERSECTCLIPRECT", 0x0416, &QWinMetaFile::intersectClipRect }, + { "ARC", 0x0817, &QWinMetaFile::arc }, + { "ELLIPSE", 0x0418, &QWinMetaFile::ellipse }, + { "FLOODFILL", 0x0419, &QWinMetaFile::noop }, + { "PIE", 0x081A, &QWinMetaFile::pie }, + { "RECTANGLE", 0x041B, &QWinMetaFile::rectangle }, + { "ROUNDRECT", 0x061C, &QWinMetaFile::roundRect }, + { "PATBLT", 0x061D, &QWinMetaFile::noop }, + { "SAVEDC", 0x001E, &QWinMetaFile::saveDC }, + { "SETPIXEL", 0x041F, &QWinMetaFile::setPixel }, + { "OFFSETCLIPRGN", 0x0220, &QWinMetaFile::noop }, + { "TEXTOUT", 0x0521, &QWinMetaFile::textOut }, + { "BITBLT", 0x0922, &QWinMetaFile::noop }, + { "STRETCHBLT", 0x0B23, &QWinMetaFile::noop }, + { "POLYGON", 0x0324, &QWinMetaFile::polygon }, + { "POLYLINE", 0x0325, &QWinMetaFile::polyline }, + { "ESCAPE", 0x0626, &QWinMetaFile::noop }, + { "RESTOREDC", 0x0127, &QWinMetaFile::restoreDC }, + { "FILLREGION", 0x0228, &QWinMetaFile::noop }, + { "FRAMEREGION", 0x0429, &QWinMetaFile::noop }, + { "INVERTREGION", 0x012A, &QWinMetaFile::noop }, + { "PAINTREGION", 0x012B, &QWinMetaFile::noop }, + { "SELECTCLIPREGION", 0x012C, &QWinMetaFile::noop }, + { "SELECTOBJECT", 0x012D, &QWinMetaFile::selectObject }, + { "SETTEXTALIGN", 0x012E, &QWinMetaFile::setTextAlign }, + { "CHORD", 0x0830, &QWinMetaFile::chord }, + { "SETMAPPERFLAGS", 0x0231, &QWinMetaFile::noop }, + { "EXTTEXTOUT", 0x0a32, &QWinMetaFile::extTextOut }, + { "SETDIBTODEV", 0x0d33, &QWinMetaFile::noop }, + { "SELECTPALETTE", 0x0234, &QWinMetaFile::noop }, + { "REALIZEPALETTE", 0x0035, &QWinMetaFile::noop }, + { "ANIMATEPALETTE", 0x0436, &QWinMetaFile::noop }, + { "SETPALENTRIES", 0x0037, &QWinMetaFile::noop }, + { "POLYPOLYGON", 0x0538, &QWinMetaFile::polyPolygon }, + { "RESIZEPALETTE", 0x0139, &QWinMetaFile::noop }, + { "DIBBITBLT", 0x0940, &QWinMetaFile::dibBitBlt }, + { "DIBSTRETCHBLT", 0x0b41, &QWinMetaFile::dibStretchBlt }, + { "DIBCREATEPATTERNBRUSH", 0x0142, &QWinMetaFile::dibCreatePatternBrush }, + { "STRETCHDIB", 0x0f43, &QWinMetaFile::stretchDib }, + { "EXTFLOODFILL", 0x0548, &QWinMetaFile::noop }, + { "DELETEOBJECT", 0x01f0, &QWinMetaFile::deleteObject }, + { "CREATEPALETTE", 0x00f7, &QWinMetaFile::createEmptyObject }, + { "CREATEPATTERNBRUSH", 0x01F9, &QWinMetaFile::createEmptyObject }, + { "CREATEPENINDIRECT", 0x02FA, &QWinMetaFile::createPenIndirect }, + { "CREATEFONTINDIRECT", 0x02FB, &QWinMetaFile::createFontIndirect }, + { "CREATEBRUSHINDIRECT", 0x02FC, &QWinMetaFile::createBrushIndirect }, + { "CREATEREGION", 0x06FF, &QWinMetaFile::createEmptyObject }, + { "END", 0, &QWinMetaFile::end }, + // always the latest in the table : in case of unknown function + { NULL, 0, &QWinMetaFile::noop }, + }; + + +#endif /*metafunc_h*/ diff --git a/ktnef/gui/ms-tnef.desktop b/ktnef/gui/ms-tnef.desktop new file mode 100644 index 000000000..309bd8cf5 --- /dev/null +++ b/ktnef/gui/ms-tnef.desktop @@ -0,0 +1,62 @@ +[Desktop Entry] +Type=MimeType +MimeType=application/ms-tnef +Icon=tar +Patterns=*.tnf; +Comment=TNEF File +Comment[af]=TNEF lêer +Comment[be]=TNEF файл +Comment[bg]=Файл TNEF +Comment[br]=Restr TNEF +Comment[bs]=TNEF datoteka +Comment[ca]=Fitxer TNEF +Comment[cs]=TNEF soubor +Comment[cy]=Ffeil TNEF +Comment[de]=TNEF-Datei +Comment[el]=ΑÏχείο TNEF +Comment[eo]=TNEF-dosiero +Comment[es]=Archivo TNEF +Comment[et]=TNEF-fail +Comment[eu]=TNEF fitxategia +Comment[fa]=پروندۀ TNEF +Comment[fi]=TNEF-tiedosto +Comment[fr]=Fichier TNEF +Comment[fy]=TNEF-triem +Comment[ga]=Comhad TNEF +Comment[gl]=Ficheiro TNEF +Comment[he]=קובץ TNEF +Comment[hi]=टीà¤à¤¨à¤ˆà¤à¤« फ़ाइल +Comment[hr]=TNEF datoteka +Comment[hu]=TNEF-fájl +Comment[is]=TNEF skrá +Comment[it]=File TNEF +Comment[ja]=TNEF ファイル +Comment[ka]=TNEF ფáƒáƒ˜áƒšáƒ˜ +Comment[kk]=TNEF файлы +Comment[km]=ឯកសារ TNEF +Comment[lt]=TNEF byla +Comment[mk]=TNEF-датотека +Comment[ms]=Fail TNEF +Comment[nb]=TNEF-fil +Comment[nds]=TNEF-Datei +Comment[ne]=TNEF फाइल +Comment[nl]=TNEF-bestand +Comment[nn]=TNEF-fil +Comment[pl]=Plik TNEF +Comment[pt]=Ficheiro TNEF +Comment[pt_BR]=Arquivo TNEF +Comment[ro]=FiÅŸier TNEF +Comment[ru]=Файл TNEF +Comment[sk]=TNEF súbor +Comment[sl]=Datoteka TNEF +Comment[sr]=TNEF фајл +Comment[sr@Latn]=TNEF fajl +Comment[sv]=TNEF-fil +Comment[ta]=TNEF கோபà¯à®ªà¯ +Comment[tg]=Файли TNEF +Comment[tr]=TNEF Dosyası +Comment[uk]=Файл TNEF +Comment[uz]=TNEF-fayli +Comment[uz@cyrillic]=TNEF-файли +Comment[zh_CN]=TNEF 文件 +Comment[zh_TW]=TNEF 檔案 diff --git a/ktnef/gui/pics/Makefile.am b/ktnef/gui/pics/Makefile.am new file mode 100644 index 000000000..43938ee02 --- /dev/null +++ b/ktnef/gui/pics/Makefile.am @@ -0,0 +1,4 @@ +KDE_ICON = ktnef + +icons_ICON = ktnef_extract_all_to ktnef_extract_to +iconsdir = $(kde_datadir)/ktnef/icons diff --git a/ktnef/gui/pics/hi22-action-ktnef_extract_all_to.png b/ktnef/gui/pics/hi22-action-ktnef_extract_all_to.png Binary files differnew file mode 100644 index 000000000..d49470a02 --- /dev/null +++ b/ktnef/gui/pics/hi22-action-ktnef_extract_all_to.png diff --git a/ktnef/gui/pics/hi22-action-ktnef_extract_to.png b/ktnef/gui/pics/hi22-action-ktnef_extract_to.png Binary files differnew file mode 100644 index 000000000..a52fb6af2 --- /dev/null +++ b/ktnef/gui/pics/hi22-action-ktnef_extract_to.png diff --git a/ktnef/gui/pics/hi48-app-ktnef.png b/ktnef/gui/pics/hi48-app-ktnef.png Binary files differnew file mode 100644 index 000000000..deee685e4 --- /dev/null +++ b/ktnef/gui/pics/hi48-app-ktnef.png diff --git a/ktnef/gui/pics/lo16-action-ktnef_extract_all_to.png b/ktnef/gui/pics/lo16-action-ktnef_extract_all_to.png Binary files differnew file mode 100644 index 000000000..8dc72abc6 --- /dev/null +++ b/ktnef/gui/pics/lo16-action-ktnef_extract_all_to.png diff --git a/ktnef/gui/pics/lo16-action-ktnef_extract_to.png b/ktnef/gui/pics/lo16-action-ktnef_extract_to.png Binary files differnew file mode 100644 index 000000000..c86449ae3 --- /dev/null +++ b/ktnef/gui/pics/lo16-action-ktnef_extract_to.png diff --git a/ktnef/gui/pics/lo16-app-ktnef.png b/ktnef/gui/pics/lo16-app-ktnef.png Binary files differnew file mode 100644 index 000000000..383df318b --- /dev/null +++ b/ktnef/gui/pics/lo16-app-ktnef.png diff --git a/ktnef/gui/pics/lo32-app-ktnef.png b/ktnef/gui/pics/lo32-app-ktnef.png Binary files differnew file mode 100644 index 000000000..b566ff91d --- /dev/null +++ b/ktnef/gui/pics/lo32-app-ktnef.png diff --git a/ktnef/gui/qwmf.cpp b/ktnef/gui/qwmf.cpp new file mode 100644 index 000000000..6a10cc006 --- /dev/null +++ b/ktnef/gui/qwmf.cpp @@ -0,0 +1,1258 @@ +/* Windows Meta File Loader/Painter Class Implementation + * + * Copyright ( C ) 1998 Stefan Taferner + * Modified 2002 thierry lorthiois + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. 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. + */ + +#include <math.h> +#include <assert.h> +#include <qfileinfo.h> +#include <qpixmap.h> +#include <qpainter.h> +#include <qdatastream.h> +#include <qapplication.h> +#include <qbuffer.h> +#include <kdebug.h> + +bool qwmfDebug = false; + +#include "qwmf.h" +#include "wmfstruct.h" +#include "metafuncs.h" + +#define QWMF_DEBUG 0 + + +class WmfCmd +{ +public: + ~WmfCmd() { if ( next ) delete next; } + WmfCmd* next; + unsigned short funcIndex; + long numParm; + short* parm; +}; + + +class WinObjHandle +{ +public: + virtual void apply( QPainter& p ) = 0; +}; + +class WinObjBrushHandle: public WinObjHandle +{ +public: + virtual void apply( QPainter& p ); + QBrush brush; + virtual ~WinObjBrushHandle() {}; +}; + +class WinObjPenHandle: public WinObjHandle +{ +public: + virtual void apply( QPainter& p ); + QPen pen; + virtual ~WinObjPenHandle() {}; +}; + +class WinObjPatternBrushHandle: public WinObjHandle +{ +public: + virtual void apply( QPainter& p ); + QBrush brush; + QPixmap image; + virtual ~WinObjPatternBrushHandle() {}; +}; + +class WinObjFontHandle: public WinObjHandle +{ +public: + virtual void apply( QPainter& p ); + QFont font; + int rotation; + virtual ~WinObjFontHandle() {}; +}; + +void WinObjBrushHandle::apply( QPainter& p ) +{ + p.setBrush( brush ); +} + +void WinObjPenHandle::apply( QPainter& p ) +{ + p.setPen( pen ); +} + +void WinObjPatternBrushHandle::apply( QPainter& p ) +{ + p.setBrush( brush ); +} + +void WinObjFontHandle::apply( QPainter& p ) +{ + p.setFont( font ); +} + +#define MAX_OBJHANDLE 64 + + + +//----------------------------------------------------------------------------- +QWinMetaFile::QWinMetaFile() +{ + mValid = false; + mFirstCmd = NULL; + mObjHandleTab = NULL; + mDpi = 1000; +} + + +//----------------------------------------------------------------------------- +QWinMetaFile::~QWinMetaFile() +{ + if ( mFirstCmd ) delete mFirstCmd; + if ( mObjHandleTab ) delete[] mObjHandleTab; +} + + +//----------------------------------------------------------------------------- +bool QWinMetaFile::load( const QString &filename ) +{ + QFile file( filename ); + + if ( !file.exists() ) + { + kdDebug() << "File " << QFile::encodeName(filename) << " does not exist" << endl; + return false; + } + + if ( !file.open( IO_ReadOnly ) ) + { + kdDebug() << "Cannot open file " << QFile::encodeName(filename) << endl; + return false; + } + + QByteArray ba = file.readAll(); + file.close(); + + QBuffer buffer( ba ); + buffer.open( IO_ReadOnly ); + return load( buffer ); +} + +//----------------------------------------------------------------------------- +bool QWinMetaFile::load( QBuffer &buffer ) +{ + QDataStream st; + WmfEnhMetaHeader eheader; + WmfMetaHeader header; + WmfPlaceableHeader pheader; + WORD checksum; + int filePos, idx, i; + WmfCmd *cmd, *last; + DWORD rdSize; + WORD rdFunc; + + mTextAlign = 0; + mRotation = 0; + mTextColor = Qt::black; + if ( mFirstCmd ) delete mFirstCmd; + mFirstCmd = NULL; + + st.setDevice( &buffer ); + st.setByteOrder( QDataStream::LittleEndian ); // Great, I love Qt ! + + //----- Read placeable metafile header + st >> pheader.key; + mIsPlaceable = ( pheader.key==( DWORD )APMHEADER_KEY ); + if ( mIsPlaceable ) + { + st >> pheader.hmf; + st >> pheader.bbox.left; + st >> pheader.bbox.top; + st >> pheader.bbox.right; + st >> pheader.bbox.bottom; + st >> pheader.inch; + st >> pheader.reserved; + st >> pheader.checksum; + checksum = calcCheckSum( &pheader ); + if ( pheader.checksum!=checksum ) mIsPlaceable = false; + + mDpi = pheader.inch; + mBBox.setLeft( pheader.bbox.left ); + mBBox.setTop( pheader.bbox.top ); + mBBox.setRight( pheader.bbox.right ); + mBBox.setBottom( pheader.bbox.bottom ); + mHeaderBoundingBox = mBBox; + if ( QWMF_DEBUG ) + { + kdDebug() << endl << "-------------------------------------------------" << endl; + kdDebug() << "WMF Placeable Header ( " << static_cast<int>(sizeof( pheader ) ) << "):" << endl; + kdDebug() << " bbox=( " << mBBox.left() << "; " << mBBox.top() << "; " << mBBox.width() + << "; " << mBBox.height() << ")" << endl; + kdDebug() << " inch=" << pheader.inch << endl; + kdDebug() << " checksum=" << pheader.checksum << "( " + << (pheader.checksum==checksum?"ok":"wrong") << " )" << endl; + } + } + else buffer.at( 0 ); + + //----- Read as enhanced metafile header + filePos = buffer.at(); + st >> eheader.iType; + st >> eheader.nSize; + st >> eheader.rclBounds.left; + st >> eheader.rclBounds.top; + st >> eheader.rclBounds.right; + st >> eheader.rclBounds.bottom; + st >> eheader.rclFrame.left; + st >> eheader.rclFrame.top; + st >> eheader.rclFrame.right; + st >> eheader.rclFrame.bottom; + st >> eheader.dSignature; + mIsEnhanced = ( eheader.dSignature==ENHMETA_SIGNATURE ); + if ( mIsEnhanced ) // is it really enhanced ? + { + st >> eheader.nVersion; + st >> eheader.nBytes; + st >> eheader.nRecords; + st >> eheader.nHandles; + st >> eheader.sReserved; + st >> eheader.nDescription; + st >> eheader.offDescription; + st >> eheader.nPalEntries; + st >> eheader.szlDevice.width; + st >> eheader.szlDevice.height; + st >> eheader.szlMillimeters.width; + st >> eheader.szlMillimeters.height; + + if ( QWMF_DEBUG ) + { + kdDebug() << endl << "-------------------------------------------------" << endl; + kdDebug() << "WMF Extended Header:" << endl; + kdDebug() << " iType=" << eheader.iType << endl; + kdDebug() << " nSize=" << eheader.nSize << endl; + kdDebug() << " rclBounds=( " << eheader.rclBounds.left << "; " << eheader.rclBounds.top << "; " + << eheader.rclBounds.right << "; " << eheader.rclBounds.bottom << ")" << endl; + kdDebug() << " rclFrame=( " << eheader.rclFrame.left << "; " << eheader.rclFrame.top << "; " + << eheader.rclFrame.right << "; " << eheader.rclFrame.bottom << ")" << endl; + kdDebug() << " nBytes=" << eheader.nBytes << endl; + kdDebug() << "\nNOT YET IMPLEMENTED, SORRY." << endl; + } + } + else // no, not enhanced + { + //----- Read as standard metafile header + buffer.at( filePos ); + st >> header.mtType; + st >> header.mtHeaderSize; + st >> header.mtVersion; + st >> header.mtSize; + st >> header.mtNoObjects; + st >> header.mtMaxRecord; + st >> header.mtNoParameters; + if ( QWMF_DEBUG ) { + kdDebug() << "WMF Header: " << "mtSize=" << header.mtSize << endl; + } + } + + //----- Test header validity + mValid = ((header.mtHeaderSize == 9) && (header.mtNoParameters == 0)) || mIsEnhanced || mIsPlaceable; + if ( mValid ) + { + //----- Read Metafile Records + last = NULL; + rdFunc = -1; + while ( !st.eof() && (rdFunc != 0) ) + { + st >> rdSize; + st >> rdFunc; + idx = findFunc( rdFunc ); + rdSize -= 3; + + cmd = new WmfCmd; + cmd->next = NULL; + if ( last ) last->next = cmd; + else mFirstCmd = cmd; + + cmd->funcIndex = idx; + cmd->numParm = rdSize; + cmd->parm = new WORD[ rdSize ]; + last = cmd; + + for ( i=0; i<rdSize && !st.eof(); i++ ) + st >> cmd->parm[ i ]; + + + if ( rdFunc == 0x020B ) { // SETWINDOWORG: dimensions + mBBox.setLeft( cmd->parm[ 1 ] ); + mBBox.setTop( cmd->parm[ 0 ] ); + } + if ( rdFunc == 0x020C ) { // SETWINDOWEXT: dimensions + mBBox.setWidth( cmd->parm[ 1 ] ); + mBBox.setHeight( cmd->parm[ 0 ] ); + } + + if ( i<rdSize ) + { + kdDebug() << "WMF : file truncated !" << endl; + return false; + } + } + //----- Test records validities + mValid = (rdFunc == 0) && (mBBox.width() != 0) && (mBBox.height() != 0); + if ( !mValid ) { + kdDebug() << "WMF : incorrect file format !" << endl; + } + } + else { + kdDebug() << "WMF Header : incorrect header !" << endl; + } + + buffer.close(); + return mValid; +} + + +//----------------------------------------------------------------------------- +bool QWinMetaFile::paint( const QPaintDevice* aTarget, bool absolute ) +{ + int idx, i; + WmfCmd* cmd; + + if ( !mValid ) return false; + + assert( aTarget!=NULL ); + if ( mPainter.isActive() ) return false; + + if ( mObjHandleTab ) delete[] mObjHandleTab; + mObjHandleTab = new WinObjHandle* [ MAX_OBJHANDLE ]; + for ( i=MAX_OBJHANDLE-1; i>=0; i-- ) + mObjHandleTab[ i ] = NULL; + + mPainter.resetXForm(); + mWinding = false; + mAbsoluteCoord = absolute; + + mPainter.begin( aTarget ); + if ( QWMF_DEBUG ) { + kdDebug() << "Bounding box : " << mBBox.left() + << " " << mBBox.top() << " " << mBBox.right() << " " << mBBox.bottom() << endl; + } + + if ( mAbsoluteCoord ) { + mPainter.setWindow( mBBox.top(), mBBox.left(), mBBox.width(), mBBox.height() ); + } + mInternalWorldMatrix.reset(); + + for ( cmd=mFirstCmd; cmd; cmd=cmd->next ) + { + idx = cmd->funcIndex; + ( this->*metaFuncTab[ idx ].method )( cmd->numParm, cmd->parm ); + + if ( QWMF_DEBUG ) { + QString str = "", param; + if ( metaFuncTab[ idx ].name == NULL ) { + str += "UNKNOWN "; + } + if ( metaFuncTab[ idx ].method == &QWinMetaFile::noop ) { + str += "UNIMPLEMENTED "; + } + str += metaFuncTab[ idx ].name; + str += " : "; + + for ( i=0 ; i < cmd->numParm ; i++ ) { + param.setNum( cmd->parm[ i ] ); + str += param; + str += " "; + } + kdDebug() << str << endl; + } + } +/* + // TODO: cleanup this code when QPicture::setBoundingBox() is possible in KOClipart (QT31) + // because actually QPicture::boundingBox() != mBBox() + mWindowsCoord += 1; + if ( mWindowsCoord == 2 ) { + kdDebug() << "DRAW ANGLES " << endl; + mPainter.setPen( Qt::white ); + mPainter.drawPoint( mBBox.left(), mBBox.top() ); + mPainter.drawPoint( mBBox.right(), mBBox.bottom() ); + } +*/ + mPainter.end(); + return true; +} + + +//----------------s------------------------------------------------------------- +// Metafile painter methods +//----------------------------------------------------------------------------- +void QWinMetaFile::setWindowOrg( long, short* parm ) +{ + if ( mAbsoluteCoord ) { + QRect r = mPainter.window(); + mPainter.setWindow( parm[ 1 ], parm[ 0 ], r.width(), r.height() ); + } + else { + double dx = mInternalWorldMatrix.dx(); + double dy = mInternalWorldMatrix.dy(); + + mInternalWorldMatrix.translate( -dx, -dy ); + mInternalWorldMatrix.translate( -parm[ 1 ], -parm[ 0 ] ); + mPainter.translate( -dx, -dy ); + mPainter.translate( -parm[ 1 ], -parm[ 0 ] ); + } +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setWindowExt( long, short* parm ) +{ + // negative value allowed for width and height : QABS() forbidden + if ( mAbsoluteCoord ) { + QRect r = mPainter.window(); + mPainter.setWindow( r.left(), r.top(), parm[ 1 ], parm[ 0 ] ); + } + else { + if ( (parm[ 0 ] != 0) && (parm[ 1 ] != 0) ) { + QRect r = mPainter.window(); + double dx = mInternalWorldMatrix.dx(); + double dy = mInternalWorldMatrix.dy(); + double sx = mInternalWorldMatrix.m11(); + double sy = mInternalWorldMatrix.m22(); + + mInternalWorldMatrix.translate( -dx, -dy ); + mInternalWorldMatrix.scale( 1/sx, 1/sy ); + mPainter.translate( -dx, -dy ); + mPainter.scale( 1/sx, 1/sy ); + + sx = (double)r.width() / (double)parm[ 1 ]; + sy = (double)r.height() / (double)parm[ 0 ]; + + mInternalWorldMatrix.scale( sx, sy ); + mInternalWorldMatrix.translate( dx, dy ); + mPainter.scale( sx, sy ); + mPainter.translate( dx, dy ); + } + } +} + + +//----------------------------------------------------------------------------- +// Drawing +//----------------------------------------------------------------------------- +void QWinMetaFile::lineTo( long, short* parm ) +{ + mPainter.lineTo( parm[ 1 ], parm[ 0 ] ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::moveTo( long, short* parm ) +{ + mPainter.moveTo( parm[ 1 ], parm[ 0 ] ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::ellipse( long, short* parm ) +{ + mPainter.drawEllipse( parm[ 3 ], parm[ 2 ], parm[ 1 ]-parm[ 3 ], parm[ 0 ]-parm[ 2 ] ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::polygon( long, short* parm ) +{ + QPointArray* pa; + + pa = pointArray( parm[ 0 ], &parm[ 1 ] ); + mPainter.drawPolygon( *pa, mWinding ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::polyPolygon( long, short* parm ) +{ + QRegion region; + int i, j, startPolygon; + + mPainter.save(); + + // define clipping region + QRect win = bbox(); + startPolygon = 1+parm[ 0 ]; + for ( i=0 ; i < parm[ 0 ] ; i++ ) { + QPointArray pa1( parm[ 1+i ] ); + for ( j=0 ; j < parm[ 1+i ] ; j++) { + pa1.setPoint ( j, parm[ startPolygon ], parm[ startPolygon+1 ] ); + startPolygon += 2; + } + QRegion r( pa1 ); + region = region.eor( r ); + } + mPainter.setClipRegion( region, QPainter::CoordPainter ); + + // fill polygons + mPainter.fillRect( win.left(), win.top(), win.width(), win.height(), mPainter.brush() ); + + // draw polygon's border if necessary + if ( mPainter.pen().style() != Qt::NoPen ) { + mPainter.setClipping( false ); + mPainter.setBrush( Qt::NoBrush ); + + QPointArray* pa; + int idxPolygon = 1 + parm[ 0 ]; + for ( i=0 ; i < parm[ 0 ] ; i++ ) { + pa = pointArray( parm[ 1+i ], &parm[ idxPolygon ] ); + mPainter.drawPolygon( *pa ); + idxPolygon += parm[ 1+i ] * 2; + } + } + + mPainter.restore(); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::polyline( long, short* parm ) +{ + QPointArray* pa; + + pa = pointArray( parm[ 0 ], &parm[ 1 ] ); + mPainter.drawPolyline( *pa ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::rectangle( long, short* parm ) +{ + mPainter.drawRect( parm[ 3 ], parm[ 2 ], parm[ 1 ]-parm[ 3 ], parm[ 0 ]-parm[ 2 ] ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::roundRect( long, short* parm ) +{ + int xRnd = 0, yRnd = 0; + + // convert (xRound, yRound) in percentage + if ( (parm[ 3 ] - parm[ 5 ]) != 0 ) + xRnd = (parm[ 1 ] * 100) / (parm[ 3 ] - parm[ 5 ]) ; + if ( (parm[ 2 ] - parm[ 4 ]) != 0 ) + yRnd = (parm[ 0 ] * 100) / (parm[ 2 ] - parm[ 4 ]) ; + + mPainter.drawRoundRect( parm[ 5 ], parm[ 4 ], parm[ 3 ]-parm[ 5 ], parm[ 2 ]-parm[ 4 ], xRnd, yRnd ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::arc( long, short* parm ) +{ + int xCenter, yCenter, angleStart, aLength; + + xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); + yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); + + xyToAngle ( parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength ); + + mPainter.drawArc( parm[ 7 ], parm[ 6 ], parm[ 5 ]-parm[ 7 ], parm[ 4 ]-parm[ 6 ], angleStart, aLength); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::chord( long, short* parm ) +{ + int xCenter, yCenter, angleStart, aLength; + + xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); + yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); + + xyToAngle ( parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength ); + + mPainter.drawChord( parm[ 7 ], parm[ 6 ], parm[ 5 ]-parm[ 7 ], parm[ 4 ]-parm[ 6 ], angleStart, aLength); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::pie( long, short* parm ) +{ + int xCenter, yCenter, angleStart, aLength; + + xCenter = parm[ 7 ] + ((parm[ 5 ] - parm[ 7 ]) / 2); + yCenter = parm[ 6 ] + ((parm[ 4 ] - parm[ 6 ]) / 2); + + xyToAngle ( parm[ 3 ] - xCenter, yCenter - parm[ 2 ], parm[ 1 ] - xCenter, yCenter - parm[ 0 ], angleStart, aLength ); + + mPainter.drawPie( parm[ 7 ], parm[ 6 ], parm[ 5 ]-parm[ 7 ], parm[ 4 ]-parm[ 6 ], angleStart, aLength); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setPolyFillMode( long, short* parm ) +{ + mWinding = parm[ 0 ]; +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setBkColor( long, short* parm ) +{ + mPainter.setBackgroundColor( color( parm ) ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setBkMode( long, short* parm ) +{ + if ( parm[ 0 ]==1 ) mPainter.setBackgroundMode( Qt::TransparentMode ); + else mPainter.setBackgroundMode( Qt::OpaqueMode ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setPixel( long, short* parm ) +{ + QPen pen = mPainter.pen(); + mPainter.setPen( color( parm ) ); + mPainter.drawPoint( parm[ 3 ], parm[ 2 ] ); + mPainter.setPen( pen ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setRop( long, short* parm ) +{ + mPainter.setRasterOp( winToQtRaster( parm[ 0 ] ) ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::saveDC( long, short* ) +{ + mPainter.save(); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::restoreDC( long, short *parm ) +{ + for ( int i=0; i > parm[ 0 ] ; i-- ) + mPainter.restore(); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::intersectClipRect( long, short* parm ) +{ +/* TODO: better implementation : need QT 3.0.2 + QRegion region = mPainter.clipRegion(); + if ( region.isEmpty() ) + region = bbox(); +*/ + QRegion region( bbox() ); + + QRegion newRegion( parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ] ); + region = region.intersect( newRegion ); + + mPainter.setClipRegion( region, QPainter::CoordPainter ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::excludeClipRect( long, short* parm ) +{ +/* TODO: better implementation : need QT 3.0.2 + QRegion region = mPainter.clipRegion(); + if ( region.isEmpty() ) + region = bbox(); +*/ + QRegion region( bbox() ); + + QRegion newRegion( parm[ 3 ], parm[ 2 ], parm[ 1 ] - parm[ 3 ], parm[ 0 ] - parm[ 2 ] ); + region = region.subtract( newRegion ); + + mPainter.setClipRegion( region, QPainter::CoordPainter ); +} + + +//----------------------------------------------------------------------------- +// Text +//----------------------------------------------------------------------------- +void QWinMetaFile::setTextColor( long, short* parm ) +{ + mTextColor = color( parm ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::setTextAlign( long, short* parm ) +{ + mTextAlign = parm[ 0 ]; +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::textOut( long num, short* parm ) +{ + + short *copyParm = new short[ num + 1 ]; + + // re-order parameters + int idxOffset = (parm[ 0 ] / 2) + 1 + (parm[ 0 ] & 1); + copyParm[ 0 ] = parm[ idxOffset ]; + copyParm[ 1 ] = parm[ idxOffset + 1 ]; + copyParm[ 2 ] = parm[ 0 ]; + copyParm[ 3 ] = 0; + memcpy( ©Parm[ 4 ], &parm[ 1 ], parm[ 0 ] ); + + extTextOut( num + 1, copyParm ); + delete [] copyParm; +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::extTextOut( long num, short* parm ) +{ + char* ptStr; + int x, y, width, height; + int idxOffset; + + if ( parm[ 3 ] != 0 ) // ETO_CLIPPED flag add 4 parameters + ptStr = (char*)&parm[ 8 ]; + else + ptStr = (char*)&parm[ 4 ]; + + QCString text( ptStr, parm[ 2 ] + 1 ); + + QFontMetrics fm( mPainter.font() ); + width = fm.width( text ) + fm.descent(); // because fm.width(text) isn't rigth with Italic text + height = fm.height(); + + mPainter.save(); + + if ( mTextAlign & 0x01 ) { // (left, top) position = current logical position + QPoint pos = mPainter.pos(); + x = pos.x(); + y = pos.y(); + } + else { // (left, top) position = parameters + x = parm[ 1 ]; + y = parm[ 0 ]; + } + + if ( mRotation ) { + mPainter.translate( parm[ 1 ], parm[ 0 ]); + mPainter.rotate ( mRotation ); + mPainter.translate( -parm[ 1 ], -parm[ 0 ] ); + } + + // alignment + if ( mTextAlign & 0x06 ) + x -= ( width / 2 ); + if ( mTextAlign & 0x08 ) + y -= (height - fm.descent()); + + mPainter.setPen( mTextColor ); + idxOffset = (parm[ 2 ] / 2) + 4 + (parm[ 2 ] & 1); + if ( ( parm[ 2 ] > 1 ) && ( num >= (idxOffset + parm[ 2 ]) ) && ( parm[ 3 ] == 0 ) ) { + // offset for each char + int left = x; + mPainter.drawText( left, y, width, height, Qt::AlignLeft | Qt::AlignTop, text.mid(0, 1) ); + for ( int i = 1; i < parm[ 2 ] ; i++ ) { + left += parm[ idxOffset + i - 1 ]; + mPainter.drawText( left, y, width, height, Qt::AlignLeft | Qt::AlignTop, text.mid(i, 1) ); + } + } + else { + mPainter.drawText( x, y, width, height, Qt::AlignLeft | Qt::AlignTop, text ); + } + + mPainter.restore(); + +} + + + +//----------------------------------------------------------------------------- +// Bitmap +//----------------------------------------------------------------------------- +void QWinMetaFile::dibBitBlt( long num, short* parm ) +{ + if ( num > 9 ) { // DIB image + QImage bmpSrc; + + if ( dibToBmp( bmpSrc, (char*)&parm[ 8 ], (num - 8) * 2 ) ) { + long raster = toDWord( parm ); + + mPainter.setRasterOp( winToQtRaster( raster ) ); + + // wmf file allow negative width or height + mPainter.save(); + if ( parm[ 5 ] < 0 ) { // width < 0 => horizontal flip + QWMatrix m( -1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F ); + mPainter.setWorldMatrix( m, true ); + } + if ( parm[ 4 ] < 0 ) { // height < 0 => vertical flip + QWMatrix m( 1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F ); + mPainter.setWorldMatrix( m, true ); + } + mPainter.drawImage( parm[ 7 ], parm[ 6 ], bmpSrc, parm[ 3 ], parm[ 2 ], parm[ 5 ], parm[ 4 ] ); + mPainter.restore(); + } + } + else { + kdDebug() << "QWinMetaFile::dibBitBlt without image: not implemented " << endl; + } +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::dibStretchBlt( long num, short* parm ) +{ + QImage bmpSrc; + + if ( dibToBmp( bmpSrc, (char*)&parm[ 10 ], (num - 10) * 2 ) ) { + long raster = toDWord( parm ); + + mPainter.setRasterOp( winToQtRaster( raster ) ); + + // wmf file allow negative width or height + mPainter.save(); + if ( parm[ 7 ] < 0 ) { // width < 0 => horizontal flip + QWMatrix m( -1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F ); + mPainter.setWorldMatrix( m, true ); + } + if ( parm[ 6 ] < 0 ) { // height < 0 => vertical flip + QWMatrix m( 1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F ); + mPainter.setWorldMatrix( m, true ); + } + bmpSrc = bmpSrc.copy( parm[ 5 ], parm[ 4 ], parm[ 3 ], parm[ 2 ] ); + // TODO: scale the bitmap ( QImage::scale(parm[ 7 ], parm[ 6 ]) is actually too slow ) + + mPainter.drawImage( parm[ 9 ], parm[ 8 ], bmpSrc ); + mPainter.restore(); + } +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::stretchDib( long num, short* parm ) +{ + QImage bmpSrc; + + if ( dibToBmp( bmpSrc, (char*)&parm[ 11 ], (num - 11) * 2 ) ) { + long raster = toDWord( parm ); + + mPainter.setRasterOp( winToQtRaster( raster ) ); + + // wmf file allow negative width or height + mPainter.save(); + if ( parm[ 8 ] < 0 ) { // width < 0 => horizontal flip + QWMatrix m( -1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F ); + mPainter.setWorldMatrix( m, true ); + } + if ( parm[ 7 ] < 0 ) { // height < 0 => vertical flip + QWMatrix m( 1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F ); + mPainter.setWorldMatrix( m, true ); + } + bmpSrc = bmpSrc.copy( parm[ 6 ], parm[ 5 ], parm[ 4 ], parm[ 3 ] ); + // TODO: scale the bitmap ( QImage::scale(parm[ 8 ], parm[ 7 ]) is actually too slow ) + + mPainter.drawImage( parm[ 10 ], parm[ 9 ], bmpSrc ); + mPainter.restore(); + } +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::dibCreatePatternBrush( long num, short* parm ) +{ + WinObjPatternBrushHandle* handle = new WinObjPatternBrushHandle; + addHandle( handle ); + QImage bmpSrc; + + if ( dibToBmp( bmpSrc, (char*)&parm[ 2 ], (num - 2) * 2 ) ) { + handle->image = bmpSrc; + handle->brush.setPixmap( handle->image ); + } +} + + +//----------------------------------------------------------------------------- +// Object handle +//----------------------------------------------------------------------------- +void QWinMetaFile::selectObject( long, short* parm ) +{ + int idx = parm[ 0 ]; + if ( idx>=0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ] ) + mObjHandleTab[ idx ]->apply( mPainter ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::deleteObject( long, short* parm ) +{ + deleteHandle( parm[ 0 ] ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::createEmptyObject( long, short* ) +{ + // allocation of an empty object (to keep object counting in sync) + WinObjPenHandle* handle = new WinObjPenHandle; + addHandle( handle ); + kdDebug() << "QWinMetaFile: unimplemented createObject " << endl; +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::createBrushIndirect( long, short* parm ) +{ + static Qt::BrushStyle hatchedStyleTab[] = + { + Qt::HorPattern, + Qt::FDiagPattern, + Qt::BDiagPattern, + Qt::CrossPattern, + Qt::DiagCrossPattern + }; + static Qt::BrushStyle styleTab[] = + { Qt::SolidPattern, + Qt::NoBrush, + Qt::FDiagPattern, /* hatched */ + Qt::Dense4Pattern, /* should be custom bitmap pattern */ + Qt::HorPattern, /* should be BS_INDEXED (?) */ + Qt::VerPattern, /* should be device-independent bitmap */ + Qt::Dense6Pattern, /* should be device-independent packed-bitmap */ + Qt::Dense2Pattern, /* should be BS_PATTERN8x8 */ + Qt::Dense3Pattern /* should be device-independent BS_DIBPATTERN8x8 */ + }; + Qt::BrushStyle style; + short arg; + WinObjBrushHandle* handle = new WinObjBrushHandle; + addHandle( handle ); + + arg = parm[ 0 ]; + if ( arg==2 ) + { + arg = parm[ 3 ]; + if ( arg>=0 && arg<5 ) style = hatchedStyleTab[ arg ]; + else + { + kdDebug() << "QWinMetaFile::createBrushIndirect: invalid hatched brush " << arg << endl; + style = Qt::SolidPattern; + } + } + else if ( arg>=0 && arg<9 ) + style = styleTab[ arg ]; + else + { + kdDebug() << "QWinMetaFile::createBrushIndirect: invalid brush " << arg << endl; + style = Qt::SolidPattern; + } + handle->brush.setStyle( style ); + handle->brush.setColor( color( parm+1 ) ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::createPenIndirect( long, short* parm ) +{ + static Qt::PenStyle styleTab[] = + { Qt::SolidLine, Qt::DashLine, Qt::DotLine, Qt::DashDotLine, Qt::DashDotDotLine, + Qt::NoPen, Qt::SolidLine }; + Qt::PenStyle style; + WinObjPenHandle* handle = new WinObjPenHandle; + addHandle( handle ); + + if ( parm[ 0 ]>=0 && parm[ 0 ]<6 ) style=styleTab[ parm[ 0 ] ]; + else + { + kdDebug() << "QWinMetaFile::createPenIndirect: invalid pen " << parm[ 0 ] << endl; + style = Qt::SolidLine; + } + + handle->pen.setStyle( style ); + handle->pen.setColor( color( parm+3 ) ); + handle->pen.setCapStyle( Qt::RoundCap ); + + //int width = 0; + // TODO : width of pen proportional to device context width + // DOESN'T WORK +/* + QRect devRec; + devRec = mPainter.xForm( mBBox ); + width = ( parm[ 0 ] * devRec.width() ) / mBBox.width() ; + kdDebug() << "CreatePenIndirect: " << endl; + kdDebug() << " log coord. : " << mBBox.width() << " " << mBBox.height() << endl; + kdDebug() << " log. pen : " << parm[ 1 ] << " " << parm[ 2 ] << endl; + kdDebug() << " dev. pen : " << width << endl; + handle->pen.setWidth( width ); +*/ +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::createFontIndirect( long , short* parm) +{ + WinObjFontHandle* handle = new WinObjFontHandle; + addHandle( handle ); + + QString family( (const char*)&parm[ 9 ] ); + + mRotation = -parm[ 2 ] / 10; // text rotation (in 1/10 degree) + // TODO: memorisation of rotation in object Font + handle->font.setFamily( family ); + handle->font.setFixedPitch( ((parm[ 8 ] & 0x01) == 0) ); + // TODO: investigation why some test case need -2. (size of font in logical point) + handle->font.setPointSize( QABS(parm[ 0 ]) - 2 ); + handle->font.setWeight( (parm[ 4 ] >> 3) ); + handle->font.setItalic( (parm[ 5 ] & 0x01) ); + handle->font.setUnderline( (parm[ 5 ] & 0x100) ); +} + + +//----------------------------------------------------------------------------- +// Misc +//----------------------------------------------------------------------------- +void QWinMetaFile::noop( long, short* ) +{ +} + + +void QWinMetaFile::end( long, short* ) +{ + // end of file : +// kdDebug() << "END bbox=( " << mBBox.left() << "; " << mBBox.top() << "; " << mBBox.width() << "; " << mBBox.height() << ")" << endl; +} + + +//----------------------------------------------------------------------------- +unsigned short QWinMetaFile::calcCheckSum( WmfPlaceableHeader* apmfh ) +{ + WORD* lpWord; + WORD wResult, i; + + // Start with the first word + wResult = *( lpWord = ( WORD* )( apmfh ) ); + // XOR in each of the other 9 words + for( i=1; i<=9; i++ ) + { + wResult ^= lpWord[ i ]; + } + return wResult; +} + + +//----------------------------------------------------------------------------- +int QWinMetaFile::findFunc( unsigned short aFunc ) const +{ + int i; + + for ( i=0; metaFuncTab[ i ].name; i++ ) + if ( metaFuncTab[ i ].func == aFunc ) return i; + + // here : unknown function + return i; +} + +//----------------------------------------------------------------------------- +QPointArray* QWinMetaFile::pointArray( short num, short* parm ) +{ + int i; + + mPoints.resize( num ); + + for ( i=0; i<num; i++, parm+=2 ) + mPoints.setPoint( i, parm[ 0 ], parm[ 1 ] ); + + return &mPoints; +} + +//----------------------------------------------------------------------------- +unsigned int QWinMetaFile::toDWord( short* parm ) +{ + unsigned int l; + +#if !defined( WORDS_BIGENDIAN ) + l = *( unsigned int* )( parm ); +#else + char *bytes; + char swap[ 4 ]; + bytes = ( char* )parm; + swap[ 0 ] = bytes[ 2 ]; + swap[ 1 ] = bytes[ 3 ]; + swap[ 2 ] = bytes[ 0 ]; + swap[ 3 ] = bytes[ 1 ]; + l = *( unsigned int* )( swap ); +#endif + + return l; +} + + +//----------------------------------------------------------------------------- +QColor QWinMetaFile::color( short* parm ) +{ + unsigned int colorRef; + int red, green, blue; + + colorRef = toDWord( parm ) & 0xffffff; + red = colorRef & 255; + green = ( colorRef>>8 ) & 255; + blue = ( colorRef>>16 ) & 255; + + return QColor( red, green, blue ); +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::xyToAngle( int xStart, int yStart, int xEnd, int yEnd, int& angleStart, int& angleLength ) +{ + float aStart, aLength; + + aStart = atan2( double(yStart), double(xStart) ); + aLength = atan2( double(yEnd), double(xEnd) ) - aStart; + + angleStart = (int)(aStart * 2880 / 3.14166); + angleLength = (int)(aLength * 2880 / 3.14166); + if ( angleLength < 0 ) angleLength = 5760 + angleLength; +} + + +//----------------------------------------------------------------------------- +void QWinMetaFile::addHandle( WinObjHandle* handle ) +{ + int idx; + + for ( idx=0; idx < MAX_OBJHANDLE ; idx++ ) + if ( mObjHandleTab[ idx ] == NULL ) break; + + if ( idx < MAX_OBJHANDLE ) + mObjHandleTab[ idx ] = handle; + else + kdDebug() << "QWinMetaFile error: handle table full !" << endl; +} + +//----------------------------------------------------------------------------- +void QWinMetaFile::deleteHandle( int idx ) +{ + if ( idx >= 0 && idx < MAX_OBJHANDLE && mObjHandleTab[ idx ] ) + { + delete mObjHandleTab[ idx ]; + mObjHandleTab[ idx ] = NULL; + } +} + +//----------------------------------------------------------------------------- +Qt::RasterOp QWinMetaFile::winToQtRaster( short parm ) const +{ + static const Qt::RasterOp opTab[] = + { + Qt::CopyROP, + Qt::ClearROP, Qt::NandROP, Qt::NotAndROP, Qt::NotCopyROP, + Qt::AndNotROP, Qt::NotROP, Qt::XorROP, Qt::NorROP, + Qt::AndROP, Qt::NotXorROP, Qt::NopROP, Qt::NotOrROP, + Qt::CopyROP, Qt::OrNotROP, Qt::OrROP, Qt::SetROP + }; + + if ( parm > 0 && parm <= 16 ) + return opTab[ parm ]; + else + return Qt::CopyROP; +} + +//----------------------------------------------------------------------------- +Qt::RasterOp QWinMetaFile::winToQtRaster( long parm ) const +{ + /* TODO: Ternary raster operations + 0x00C000CA dest = (source AND pattern) + 0x00F00021 dest = pattern + 0x00FB0A09 dest = DPSnoo + 0x005A0049 dest = pattern XOR dest */ + static const struct OpTab + { + long winRasterOp; + Qt::RasterOp qtRasterOp; + } opTab[] = + { + { 0x00CC0020, Qt::CopyROP }, + { 0x00EE0086, Qt::OrROP }, + { 0x008800C6, Qt::AndROP }, + { 0x00660046, Qt::XorROP }, + { 0x00440328, Qt::AndNotROP }, + { 0x00330008, Qt::NotCopyROP }, + { 0x001100A6, Qt::NandROP }, + { 0x00C000CA, Qt::CopyROP }, + { 0x00BB0226, Qt::NotOrROP }, + { 0x00F00021, Qt::CopyROP }, + { 0x00FB0A09, Qt::CopyROP }, + { 0x005A0049, Qt::CopyROP }, + { 0x00550009, Qt::NotROP }, + { 0x00000042, Qt::ClearROP }, + { 0x00FF0062, Qt::SetROP } + }; + + int i; + for ( i=0 ; i < 15 ; i++ ) + if ( opTab[ i ].winRasterOp == parm ) + break; + + if ( i < 15 ) + return opTab[ i ].qtRasterOp; + else + return Qt::CopyROP; +} + +//----------------------------------------------------------------------------- +bool QWinMetaFile::dibToBmp( QImage& bmp, const char* dib, long size ) +{ + typedef struct _BMPFILEHEADER { + WORD bmType; + DWORD bmSize; + WORD bmReserved1; + WORD bmReserved2; + DWORD bmOffBits; + } BMPFILEHEADER; + + int sizeBmp = size + 14; + + QByteArray pattern( sizeBmp ); // BMP header and DIB data + pattern.fill(0); + memcpy( &pattern[ 14 ], dib, size ); + + // add BMP header + BMPFILEHEADER* bmpHeader; + bmpHeader = (BMPFILEHEADER*)((const char*)pattern); + bmpHeader->bmType = 0x4D42; + bmpHeader->bmSize = sizeBmp; + + if ( !bmp.loadFromData( (const uchar*)bmpHeader, pattern.size(), "BMP" ) ) { + kdDebug() << "QWinMetaFile::dibToBmp: invalid bitmap " << endl; + return false; + } + else { +// if ( bmp.save("/home/software/kde-cvs/qt/examples/wmf/test.bmp", "BMP") ) +// if ( bmp.load( "/home/software/kde-cvs/qt/examples/wmf/test.bmp", "BMP" ) ) +// fprintf(stderr, "Bitmap ok \n"); + return true; + } +} + diff --git a/ktnef/gui/qwmf.h b/ktnef/gui/qwmf.h new file mode 100644 index 000000000..b655a3fd3 --- /dev/null +++ b/ktnef/gui/qwmf.h @@ -0,0 +1,228 @@ +/* Windows Meta File Loader + * + * Copyright ( C ) 1998 Stefan Taferner + * Modified 2002 thierry lorthiois + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. 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. + */ +#ifndef qwmf_h +#define qwmf_h + +#include <qstring.h> +#include <qpainter.h> +#include <qwmatrix.h> +#include <qpointarray.h> +#include <qpen.h> +#include <qcolor.h> +#include <qimage.h> +#include <qrect.h> + +class QBuffer; +class QString; +class WmfCmd; +class WinObjHandle; +class WinObjPenHandle; +class WinObjBrushHandle; +struct WmfPlaceableHeader; + +/** + * QWinMetaFile is a WMF viewer based on QT toolkit + * How to use QWinMetaFile : + * #include "qwmf.h" + * QWinMetaFile wmf; + * QPicture pic; // or QImage pic; + * if ( wmf.load( filename ) + * wmf.paint( &pic ); + */ + + +class QWinMetaFile +{ +public: + QWinMetaFile(); + virtual ~QWinMetaFile(); + + /** + * Load WMF file. Returns true on success. + */ + virtual bool load( const QString &fileName ); + virtual bool load( QBuffer &buffer ); + + /** + * Paint metafile to given paint-device using absolute or relative coordinate. + * abosute coord. reset the world transfomation Matrix + * relative coord. use the existing world transfomation Matrix + * Returns true on success. + */ + virtual bool paint( const QPaintDevice* target, bool absolute=false ); + + /** + * Returns true if the metafile is placeable. + */ + bool isPlaceable( void ) const { return mIsPlaceable; } + + /** + * Returns true if the metafile is enhanced. + */ + bool isEnhanced( void ) const { return mIsEnhanced; } + + /** + * Returns bounding rectangle + */ + QRect bbox( void ) const { return mBBox; } + void setBbox( QRect r ) { mBBox = r; } + +public: // should be protected but cannot + /** Metafile painter methods */ + + // set window origin + void setWindowOrg( long num, short* parms ); + // set window extents + void setWindowExt( long num, short* parms ); + + /****************** Drawing *******************/ + // draw line to coord + void lineTo( long num, short* parms ); + // move pen to coord + void moveTo( long num, short* parms ); + // draw ellipse + void ellipse( long num, short* parms ); + // draw polygon + void polygon( long num, short* parms ); + /* draw a list of polygons */ + void polyPolygon( long num, short* parms ); + // draw series of lines + void polyline( long num, short* parms ); + /* draw a rectangle */ + void rectangle( long num, short* parms ); + /* draw round rectangle */ + void roundRect( long num, short* parms ); + /* draw arc */ + void arc( long num, short* parms ); + /* draw chord */ + void chord( long num, short* parms ); + /* draw pie */ + void pie( long num, short* parms ); + // set polygon fill mode + void setPolyFillMode( long num, short* parms ); + // set background pen color + void setBkColor( long num, short* parms ); + // set background pen mode + void setBkMode( long num, short* parms ); + /* set a pixel */ + void setPixel( long num, short* parms ); + // Set raster operation mode + void setRop( long num, short* parms ); + /* save device context */ + void saveDC( long num, short* parms ); + /* restore device context */ + void restoreDC( long num, short* parms ); + /* clipping region is the intersection of this region and the original region */ + void intersectClipRect( long num, short* parms ); + /* delete a clipping rectangle of the original region */ + void excludeClipRect( long num, short* parms ); + + /****************** Text *******************/ + /* set text color */ + void setTextColor( long num, short* parms ); + /* set text alignment */ + void setTextAlign( long num, short* parms ); + /* draw text */ + void textOut( long num, short* parms ); + void extTextOut( long num, short* parms ); + + /****************** Bitmap *******************/ + /* copies a DIB into a dest location */ + void dibBitBlt( long num, short* parms ); + /* stretches a DIB into a dest location */ + void dibStretchBlt( long num, short* parms ); + void stretchDib( long num, short* parms ); + /* create a pattern brush */ + void dibCreatePatternBrush( long num, short* parms ); + + /****************** Object handle *******************/ + // Activate object handle + void selectObject( long num, short* parms ); + // Free object handle + void deleteObject( long num, short* parms ); + /* create an empty object in the object list */ + void createEmptyObject( long num, short* parms ); + // create a logical brush + void createBrushIndirect( long num, short* parms ); + // create a logical pen + void createPenIndirect( long num, short* parms ); + /* create a logical font */ + void createFontIndirect( long num, short* parms ); + + /****************** misc *******************/ + // nothing to do + void noop( long , short* ); + // end of meta file + void end( long /*num*/, short* /*parms*/ ); + // Resolution of the image in dots per inch + int dpi( void ) const { return mDpi; } + +protected: + /** Calculate header checksum */ + unsigned short calcCheckSum( WmfPlaceableHeader* ); + + /** Find function in metafunc table by metafile-function. + Returns index or -1 if not found. */ + virtual int findFunc( unsigned short aFunc ) const; + + /** Fills given parms into mPoints. */ + QPointArray* pointArray( short num, short* parms ); + + /** Returns color given by the two parameters */ + QColor color( short* parm ); + + /** Converts two parameters to long */ + unsigned int toDWord( short* parm ); + + /** Convert (x1,y1) and (x2, y2) positions in angle and angleLength */ + void xyToAngle( int xStart, int yStart, int xEnd, int yEnd, int& angle, int& aLength ); + + /** Handle win-object-handles */ + void addHandle( WinObjHandle* ); + void deleteHandle( int ); + + /** Convert windows rasterOp in QT rasterOp */ + Qt::RasterOp winToQtRaster( short parm ) const; + Qt::RasterOp winToQtRaster( long parm ) const; + + /** Converts DIB to BMP */ + bool dibToBmp( QImage& bmp, const char* dib, long size); + +protected: + QPainter mPainter; + bool mIsPlaceable, mIsEnhanced, mValid; + + // coordinate system + bool mAbsoluteCoord; + QWMatrix mInternalWorldMatrix; // memorisation of WMF matrix transformation + QRect mHeaderBoundingBox; + QRect mBBox; + + // informtion shared between Metafile Functions + QColor mTextColor; + int mTextAlign, mRotation; + bool mWinding; + + WmfCmd* mFirstCmd; + WinObjHandle** mObjHandleTab; + QPointArray mPoints; + int mDpi; +}; + +#endif /*qwmf_h*/ diff --git a/ktnef/gui/wmfstruct.h b/ktnef/gui/wmfstruct.h new file mode 100644 index 000000000..ca4f1f7db --- /dev/null +++ b/ktnef/gui/wmfstruct.h @@ -0,0 +1,107 @@ +/* WMF Metafile Structures + * Author: Stefan Taferner <taferner@kde.org> + */ +#ifndef wmfstruct_h +#define wmfstruct_h + +typedef short WORD; +typedef int DWORD; +typedef Q_INT32 LONG; +typedef void* _HANDLE; + +typedef struct _RECT +{ + WORD left; + WORD top; + WORD right; + WORD bottom; +} RECT; + +typedef struct _RECTL +{ + LONG left; + LONG top; + LONG right; + LONG bottom; +} RECTL; + +typedef struct _SIZE +{ + WORD width; + WORD height; +} SIZE; + +typedef struct _SIZEL +{ + LONG width; + LONG height; +} SIZEL; + + +struct WmfEnhMetaHeader +{ + DWORD iType; // Record type EMR_HEADER + DWORD nSize; // Record size in bytes. This may be greater + // than the sizeof( ENHMETAHEADER ). + RECTL rclBounds; // Inclusive-inclusive bounds in device units + RECTL rclFrame; // Inclusive-inclusive Picture Frame of metafile + // in .01 mm units + DWORD dSignature; // Signature. Must be ENHMETA_SIGNATURE. + DWORD nVersion; // Version number + DWORD nBytes; // Size of the metafile in bytes + DWORD nRecords; // Number of records in the metafile + WORD nHandles; // Number of handles in the handle table + // Handle index zero is reserved. + WORD sReserved; // Reserved. Must be zero. + DWORD nDescription; // Number of chars in the unicode description string + // This is 0 if there is no description string + DWORD offDescription; // Offset to the metafile description record. + // This is 0 if there is no description string + DWORD nPalEntries; // Number of entries in the metafile palette. + SIZEL szlDevice; // Size of the reference device in pels + SIZEL szlMillimeters; // Size of the reference device in millimeters +}; +#define ENHMETA_SIGNATURE 0x464D4520 + + +struct WmfMetaHeader +{ + WORD mtType; + WORD mtHeaderSize; + WORD mtVersion; + DWORD mtSize; + WORD mtNoObjects; + DWORD mtMaxRecord; + WORD mtNoParameters; +}; + + +struct WmfPlaceableHeader +{ + DWORD key; + WORD hmf; + RECT bbox; + WORD inch; + DWORD reserved; + WORD checksum; +}; +#define APMHEADER_KEY 0x9AC6CDD7 + + +struct WmfMetaRecord +{ + DWORD rdSize; // Record size ( in words ) of the function + WORD rdFunction; // Record function number + WORD rdParm[ 1 ]; // WORD array of parameters +}; + + +struct WmfEnhMetaRecord +{ + DWORD iType; // Record type EMR_xxx + DWORD nSize; // Record size in bytes + DWORD dParm[ 1 ]; // DWORD array of parameters +}; + + +#endif /*wmfstruct_h*/ diff --git a/ktnef/ktnef/Makefile.am b/ktnef/ktnef/Makefile.am new file mode 100644 index 000000000..c04f16440 --- /dev/null +++ b/ktnef/ktnef/Makefile.am @@ -0,0 +1,4 @@ +ktnefinclude_HEADERS = ktnefparser.h ktnefattach.h ktnefproperty.h \ + ktnefpropertyset.h ktnefmessage.h ktnefwriter.h ktnefdefs.h +ktnefincludedir = $(includedir)/ktnef +INCLUDES = $(all_includes) diff --git a/ktnef/ktnef/ktnefattach.h b/ktnef/ktnef/ktnefattach.h new file mode 100644 index 000000000..846c4a6b8 --- /dev/null +++ b/ktnef/ktnef/ktnefattach.h @@ -0,0 +1,70 @@ +/* + ktnefattach.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFATTACH_H +#define KTNEFATTACH_H + +#include <qstring.h> +#include <qmap.h> +#include <qvariant.h> +#include <ktnef/ktnefpropertyset.h> +#include <kdepimmacros.h> + +class KTNEFProperty; + +class KDE_EXPORT KTNEFAttach : public KTNEFPropertySet +{ +public: + enum ParseState { Unparsed = 0x0000, TitleParsed = 0x0001, DataParsed = 0x0002, InfoParsed = 0x0004}; + + KTNEFAttach(); + ~KTNEFAttach(); + + void setTitleParsed(); + void setDataParsed(); + void unsetDataParser(); + void setInfoParsed(); + bool titleParsed() const; + bool dataParsed() const; + bool infoParsed() const; + bool checkState(int state) const; + + int offset() const; + void setOffset(int n); + int size() const; + void setSize(int s); + int displaySize() const; + void setDisplaySize(int s); + QString name() const; + void setName(const QString& str); + int index() const; + void setIndex(int i); + QString fileName() const; + void setFileName(const QString& str); + QString displayName() const; + void setDisplayName(const QString& str); + QString mimeTag() const; + void setMimeTag(const QString& str); + QString extension() const; + void setExtension(const QString& str); + +private: + class AttachPrivate; + AttachPrivate *d; +}; + +#endif diff --git a/ktnef/ktnef/ktnefdefs.h b/ktnef/ktnef/ktnefdefs.h new file mode 100644 index 000000000..6e9e96fd8 --- /dev/null +++ b/ktnef/ktnef/ktnefdefs.h @@ -0,0 +1,256 @@ +#ifndef KTNEFDEFS_H +#define KTNEFDEFS_H + +#define TNEF_SIGNATURE 0x223e9f78 +#define LVL_MESSAGE 0x01 +#define LVL_ATTACHMENT 0x02 + +#define atpSTRING 0x0001 +#define atpTEXT 0x0002 +#define atpDATE 0x0003 +#define atpSHORT 0x0004 +#define atpLONG 0x0005 +#define atpBYTE 0x0006 +#define atpWORD 0x0007 +#define atpDWORD 0x0008 + +#define attDATESTART 0x0006 +#define attDATEEND 0x0007 +#define attAIDOWNER 0x0008 +#define attREQUESTRES 0x0009 +#define attFROM 0x8000 +#define attSUBJECT 0x8004 +#define attDATESENT 0x8005 +#define attDATERECD 0x8006 +#define attMSGSTATUS 0x8007 +#define attMSGCLASS 0x8008 +#define attMSGID 0x8009 +#define attBODY 0x800c +#define attMSGPRIORITY 0x800d +#define attATTACHDATA 0x800f /* Attachment Data */ +#define attATTACHTITLE 0x8010 /* Attachment File Name */ +#define attATTACHMETAFILE 0x8011 +#define attATTACHCREATEDATE 0x8012 +#define attATTACHMODDATE 0x8013 +#define attDATEMODIFIED 0x8020 +#define attATTACHRENDDATA 0x9002 +#define attMAPIPROPS 0x9003 +#define attRECIPTABLE 0x9004 +#define attATTACHMENT 0x9005 /* Attachment properties (?) */ +#define attTNEFVERSION 0x9006 +#define attOEMCODEPAGE 0x9007 + +/* These are found in TNEF documentation, but have so far not been implemented +#define attATTACHCREATEDATE 0x0000 +#define attATTACHTRANSPORTFILENAME 0x0000 +#define attCONVERSATIONID 0x0000 +#define attORIGINALMSGCLASS 0x0000 +#define attOWNER 0x0000 +#define attPARENTID 0x0000 +#define attNULL 0x0000 +#define attDELEGATE 0x0000 +#define attSENTFOR 0x0000 +*/ + +#define fmsModified 0x01 +#define fmsLocal 0x02 +#define fmsSubmitted 0x04 +#define fmsRead 0x20 +#define fmsHasAttach 0x80 +#define MSGFLAG_READ 0x00000001 +#define MSGFLAG_UNMODIFIED 0x00000002 +#define MSGFLAG_SUBMIT 0x00000004 +#define MSGFLAG_UNSENT 0x00000008 +#define MSGFLAG_HASATTACH 0x00000010 + +// supported MAPI types +#define MAPI_TYPE_NONE 0x0000 +#define MAPI_TYPE_UINT16 0x0002 +#define MAPI_TYPE_ULONG 0x0003 +#define MAPI_TYPE_FLOAT 0x0004 +#define MAPI_TYPE_DOUBLE 0x0005 +#define MAPI_TYPE_BOOLEAN 0x000b +#define MAPI_TYPE_OBJECT 0x000d +#define MAPI_TYPE_TIME 0x0040 +#define MAPI_TYPE_STRING8 0x001e +#define MAPI_TYPE_USTRING 0x001f +#define MAPI_TYPE_BINARY 0x0102 +#define MAPI_TYPE_VECTOR 0x1000 + +// supported MAPI tags +#define MAPI_TAG_INDEX 0x0e21 +#define MAPI_TAG_SIZE 0x0e20 +#define MAPI_TAG_FILENAME 0x3707 +#define MAPI_TAG_DISPLAYNAME 0x3001 +#define MAPI_TAG_DATA 0x3701 +#define MAPI_TAG_ATTACHENCODING 0x3702 +#define MAPI_TAG_EXTENSION 0x3703 +#define MAPI_TAG_ATTACHMETHOD 0x3705 +#define MAPI_TAG_RENDERINGPOS 0x370b +#define MAPI_TAG_MIMETAG 0x370e +#define MAPI_TAG_ATTACHFLAGS 0x3714 + +#define MAPI_IID_IMessage 0x00020307 + + +// unsupported Outlook Contact Properties +// note: properties beginning with MAPI_TAG_CONTACT +// are 'named' properties +#define MAPI_TAG_PR_MESSAGE_DELIVERY_TIME 0x0E060040 // CreationTime +#define MAPI_TAG_PR_MESSAGE_SIZE 0x0E080003 // Size +#define MAPI_TAG_PR_SENSITIVITY 0x00360003 // Sensitivity +#define MAPI_TAG_PR_MESSAGE_CLASS 0x001A001F // MessageClass +#define MAPI_TAG_PR_IMPORTANCE 0x00170003 // Importance +#define MAPI_TAG_PR_ENTRYID 0x0FFF0102 // EntryID +#define MAPI_TAG_PR_BODY 0x1000001F // Body +#define MAPI_TAG_CONTACT_Categories "Keywords" // Categories +#define MAPI_TAG_PR_DISPLAY_NAME 0x3001001F // FullName +#define MAPI_TAG_PR_TITLE 0x3A17001F // JobTitle +#define MAPI_TAG_PR_COMPANY_NAME 0x3A16001F // CompanyName +#define MAPI_TAG_PR_DISPLAY_NAME_PREFIX 0x3A45001E // Title +#define MAPI_TAG_PR_SURNAME 0x3A11001E // LastName +#define MAPI_TAG_PR_MIDDLE_NAME 0x3A44001F // MiddleName +#define MAPI_TAG_PR_GIVEN_NAME 0x3A06001F // FirstName +#define MAPI_TAG_PR_GENERATION 0x3A05001E // Suffix +#define MAPI_TAG_PR_BUSINESS_HOME_PAGE 0x3A51001F // BusinessHomePage +#define MAPI_TAG_PR_PERSONAL_HOME_PAGE 0x3A50001F // PersonalHomePage +#define MAPI_TAG_PR_FTP_SITE 0x3A4C001E // FTPSite +#define MAPI_TAG_PR_INITIALS 0x3A0A001E // Initials + +#define MAPI_TAG_CONTACT_FILEUNDER "0x8005" // FileAs + +#define MAPI_TAG_CONTACT_LASTNAMEANDFIRSTNAME "0x8017" // LastNameandFirstName +#define MAPI_TAG_CONTACT_COMPANYANDFULLNAME "0x8018" // CompanyAndFullName +#define MAPI_TAG_CONTACT_FULLNAMEANDCOMPANY "0x8019" // FullNameAndCompany + +#define MAPI_TAG_CONTACT_HOMEADDRESS "0x801A" // HomeAddress +#define MAPI_TAG_CONTACT_BUSINESSADDRESS "0x801B" // BusinessAddress +#define MAPI_TAG_CONTACT_OTHERADDRESS "0x801C" // OtherAddress +#define MAPI_TAG_CONTACT_SELECTEDADDRESS "0x8022" // SelectedMailingAddress: + // 0 = None + // 1 = Home + // 2 = Business + // 3 = Other + +#define MAPI_TAG_CONTACT_WEBPAGE "0x802B" // WebPage +#define MAPI_TAG_CONTACT_YOMIFIRSTNAME "0x802C" // YomiFirstName +#define MAPI_TAG_CONTACT_YOMILASTNAME "0x802D" // YomiLastName +#define MAPI_TAG_CONTACT_YOMICOMPANYNAME "0x802E" // YomiCompanyName + +#define MAPI_TAG_CONTACT_LASTFIRSTNOSPACE "0x8030" // LastFirstNoSpace +#define MAPI_TAG_CONTACT_LASTFIRSTSPACEONLY "0x8031" // LastFirstSpaceOnly +#define MAPI_TAG_CONTACT_COMPANYLASTFIRSTNOSPACE "0x8032" // CompanyLastFirstNoSpace +#define MAPI_TAG_CONTACT_COMPANYLASTFIRSTSpaceOnly "0x8033" // CompanyLastFirstSpaceOnly +#define MAPI_TAG_CONTACT_LASTFIRSTNOSPACECOMPANY "0x8034" // LastFirstNoSpaceCompany +#define MAPI_TAG_CONTACT_LASTFIRSTSPACEONLYCOMPANY "0x8035" // LastFirstSpaceOnlyCompany +#define MAPI_TAG_CONTACT_LASTFIRSTANDSuffix "0x8036" // LastFirstAndSuffix + +#define MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET "0x8045" // BusinessAddressStreet +#define MAPI_TAG_CONTACT_BUSINESSADDRESSCITY "0x8046" // BusinessAddressCity +#define MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE "0x8047" // BusinessAddressState +#define MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE "0x8048" // BusinessAddressPostalCode +#define MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY "0x8049" // BusinessAddressCountry +#define MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX "0x804A" // BusinessAddressPostOfficeBox + +#define MAPI_TAG_CONTACT_USERFIELD1 "0x804F" // UserField1 +#define MAPI_TAG_CONTACT_USERFIELD2 "0x8050" // UserField2 +#define MAPI_TAG_CONTACT_USERFIELD3 "0x8051" // UserField3 +#define MAPI_TAG_CONTACT_USERFIELD4 "0x8052" // UserField4 + +#define MAPI_TAG_CONTACT_IMADDRESS "0x8062" // InternetMailAddress (only >= Outlook 2002) + +#define MAPI_TAG_CONTACT_EMAIL1ADDRTYPE "0x8082" // EMail1AddressType +#define MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS "0x8083" // EMail1Address +#define MAPI_TAG_CONTACT_EMAIL1ORIGINALDISPLAYNAME "0x8084" // EMail1DisplayName +#define MAPI_TAG_CONTACT_EMAIL1ORIGINALENTRYID "0x8085" // EMail1EntryID + +#define MAPI_TAG_CONTACT_EMAIL2ADDRTYPE "0x8092" // EMail2AddressType +#define MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS "0x8093" // EMail2Address +#define MAPI_TAG_CONTACT_EMAIL2ORIGINALDISPLAYNAME "0x8094" // EMail2DisplayName +#define MAPI_TAG_CONTACT_EMAIL2ORIGINALENTRYID "0x8095" // EMail2EntryID + +#define MAPI_TAG_CONTACT_EMAIL3ADDRTYPE "0x80A2" // EMail3AddressType +#define MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS "0x80A3" // EMail3Address +#define MAPI_TAG_CONTACT_EMAIL3ORIGINALDISPLAYNAME "0x80A4" // EMail3DisplayName +#define MAPI_TAG_CONTACT_EMAIL3ORIGINALENTRYID "0x80A5" // EMail3EntryID + +#define MAPI_TAG_CONTACT_INTERNETFREEBUSYADDRESS "0x80D8" // InternetFreeBusyAddress + +#define MAPI_TAG_CONTACT_BILLINGINFORMATION "0x8535" // BillingInformation +#define MAPI_TAG_CONTACT_REMINDERTIME "0x8502" // N/A +#define MAPI_TAG_CONTACT_MILEAGE "0x8534" // Mileage + +#define MAPI_TAG_PR_ASSISTANT_TELEPHONE_NUMBER 0x3A2E001F // AssistantTelephoneNumber +#define MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER 0x3A08001F // BusinessTelephoneNumber +#define MAPI_TAG_PR_BUSINESS2_TELEPHONE_NUMBER 0x3A1B001F // Business2TelephoneNumber +#define MAPI_TAG_PR_BUSINESS_FAX_NUMBER 0x3A24001F // BusinessFaxNumber +#define MAPI_TAG_PR_CALLBACK_TELEPHONE_NUMBER 0x3A02001F // CallbackTelephoneNumber +#define MAPI_TAG_PR_CAR_TELEPHONE_NUMBER 0x3A1E001F // CarTelephoneNumber + +#define MAPI_TAG_PR_COMPANY_MAIN_PHONE_NUMBER 0x3A57001F // CompanyMainTelephoneNumber + +#define MAPI_TAG_PR_HOME_TELEPHONE_NUMBER 0x3A09001F // HomeTelephoneNumber + +#define MAPI_TAG_PR_HOME2_TELEPHONE_NUMBER 0x3A2F001F // Home2TelephoneNumber +#define MAPI_TAG_PR_HOME_FAX_NUMBER 0x3A25001F // HomeFaxNumber +#define MAPI_TAG_PR_ISDN_NUMBER 0x3A2D001F // ISDNNumber + +#define MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER 0x3A1C001F // MobileTelephoneNumber + +#define MAPI_TAG_PR_OTHER_TELEPHONE_NUMBER 0x3A1F001F // OtherTelephoneNumber +#define MAPI_TAG_PR_PRIMARY_FAX_NUMBER 0x3A23001F // OtherFaxNumber +#define MAPI_TAG_PR_PAGER_TELEPHONE_NUMBER 0x3A21001F // PagerNumber +#define MAPI_TAG_PR_PRIMARY_TELEPHONE_NUMBER 0x3A1A001F // PrimaryTelephoneNumber + +#define MAPI_TAG_PR_RADIO_TELEPHONE_NUMBER 0x3A1D001F // RadioTelephoneNumber + +#define MAPI_TAG_PR_TELEX_NUMBER 0x3A2C001F // TelexNumber +#define MAPI_TAG_PR_TTYTDD_PHONE_NUMBER 0x3A4B001F // TTYTDDTelephoneNumber +#define MAPI_TAG_PR_POSTAL_ADDRESS 0x3A15001F // MailingAddress + +#define MAPI_TAG_PR_BUSINESS_ADDRESS_COUNTRY 0x3A26001E // MailingAddressCountry +#define MAPI_TAG_PR_LOCALITY 0x3A27001F // MailingAddressCity +#define MAPI_TAG_PR_STATE_OR_PROVINCE 0x3A28001E // MailingAddressState +#define MAPI_TAG_PR_STREET_ADDRESS 0x3A29001F // MailingAddressStreet +#define MAPI_TAG_PR_POSTAL_CODE 0x3A2A001E // MailingAddressPostalCode +#define MAPI_TAG_PR_PO_BOX 0x3A2B001E // MailingAddressPostOfficeBox + +#define MAPI_TAG_PR_HOME_ADDRESS_CITY 0x3A59001E // HomeAddressCity +#define MAPI_TAG_PR_HOME_ADDRESS_STREET 0x3A5D001E // HomeAddressStreet +#define MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE 0x3A5C001E // HomeAddressState +#define MAPI_TAG_PR_HOME_ADDRESS_COUNTRY 0x3A5A001E // HomeAddressCountry +#define MAPI_TAG_PR_HOME_ADDRESS_PO_BOX 0x3A5E001E // HomeAddressPostOfficeBox +#define MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE 0x3A5B001E // HomeAddressPostalCode + +#define MAPI_TAG_PR_OTHER_ADDRESS_CITY 0x3A5F001E // OtherAddressCity +#define MAPI_TAG_PR_OTHER_ADDRESS_STREET 0x3A63001E // OtherAddressStreet +#define MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE 0x3A62001E // OtherAddressState +#define MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY 0x3A60001E // OtherAddressCountry +#define MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE 0x3A61001E // OtherAddressPostalCode +#define MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX 0x3A64001E // OtherAddressPostOfficeBox + +#define MAPI_TAG_PR_DEPARTMENT_NAME 0x3A18001F // Department +#define MAPI_TAG_PR_MANAGER_NAME 0x3A4E001F // ManagerName +#define MAPI_TAG_PR_OFFICE_LOCATION 0x3A19001F // Location +#define MAPI_TAG_PR_ASSISTANT 0x3A30001F // AssistantName +#define MAPI_TAG_PR_PROFESSION 0x3A46001F // Profession +#define MAPI_TAG_PR_NICKNAME 0x3A4F001F // NickName +#define MAPI_TAG_PR_BIRTHDAY 0x3A420040 // Birthday +#define MAPI_TAG_PR_SPOUSE_NAME 0x3A48001F // SpouseName +#define MAPI_TAG_PR_WEDDING_ANNIVERSARY 0x3A410040 // Anniversary +#define MAPI_TAG_PR_ACCOUNT 0x3A00001E // Account +#define MAPI_TAG_PR_COMPUTER_NETWORK_NAME 0x3A49001E // ComputerNetworkName +#define MAPI_TAG_PR_CHILDRENS_NAMES 0x3A58101E // Children +#define MAPI_TAG_PR_CUSTOMER_ID 0x3A4A001E // CustomerID +#define MAPI_TAG_PR_GENDER 0x3A4D0002 // Gender: + // 0 = Unspecified + // 1 = Female + // 2 = Male +#define MAPI_TAG_PR_GOVERNMENT_ID_NUMBER 0x3A07001E // GovernmentIDNumber +#define MAPI_TAG_PR_HOBBIES 0x3A43001E // Hobby +#define MAPI_TAG_PR_LANGUAGE 0x3A0C001E // Language +#define MAPI_TAG_PR_LOCATION 0x3A0D001E // OfficeLocation +#define MAPI_TAG_PR_ORGANIZATIONAL_ID_NUMBER 0x3A10001E // OrganizationalIDNumber +#define MAPI_TAG_PR_REFERRED_BY_NAME 0X3A47001E // ReferredBy + +#endif /* KTNEFDEFS_H */ diff --git a/ktnef/ktnef/ktnefmessage.h b/ktnef/ktnef/ktnefmessage.h new file mode 100644 index 000000000..efdc236a8 --- /dev/null +++ b/ktnef/ktnef/ktnefmessage.h @@ -0,0 +1,44 @@ +/* + ktnefmessage.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFMESSAGE_H +#define KTNEFMESSAGE_H + +#include <ktnef/ktnefpropertyset.h> +#include <qptrlist.h> +#include <kdepimmacros.h> + +class KTNEFAttach; + +class KDE_EXPORT KTNEFMessage : public KTNEFPropertySet +{ +public: + KTNEFMessage(); + ~KTNEFMessage(); + + const QPtrList<KTNEFAttach>& attachmentList() const; + KTNEFAttach* attachment( const QString& filename ) const; + void addAttachment( KTNEFAttach* attach ); + void clearAttachments(); + QString rtfString(); + +private: + class MessagePrivate; + MessagePrivate *d; +}; + +#endif /* KTNEFMESSAGE_H */ diff --git a/ktnef/ktnef/ktnefparser.h b/ktnef/ktnef/ktnefparser.h new file mode 100644 index 000000000..3696cd17c --- /dev/null +++ b/ktnef/ktnef/ktnefparser.h @@ -0,0 +1,58 @@ +/* + ktnefparser.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFPARSER_H +#define KTNEFPARSER_H + +#include <qptrlist.h> +#include <qstring.h> +#include <qmap.h> +#include <kdepimmacros.h> + +class KTNEFAttach; +class KTNEFMessage; +class KTNEFProperty; + +class KDE_EXPORT KTNEFParser +{ +public: + KTNEFParser(); + ~KTNEFParser(); + + bool openFile(const QString& filename); + bool openDevice( QIODevice *device ); + bool extractFile(const QString& filename); + bool extractFileTo(const QString& filename, const QString& dirname); + bool extractAll(); + void setDefaultExtractDir(const QString& dirname); + KTNEFMessage* message() const; + +private: + bool decodeAttachment(); + bool decodeMessage(); + bool extractAttachmentTo(KTNEFAttach *att, const QString& dirname); + bool parseDevice(); + void checkCurrent(int state); + bool readMAPIProperties(QMap<int,KTNEFProperty*>& pros, KTNEFAttach *attach = 0); + void deleteDevice(); + +private: + class ParserPrivate; + ParserPrivate *d; +}; + +#endif diff --git a/ktnef/ktnef/ktnefproperty.h b/ktnef/ktnef/ktnefproperty.h new file mode 100644 index 000000000..b56b9f802 --- /dev/null +++ b/ktnef/ktnef/ktnefproperty.h @@ -0,0 +1,63 @@ +/* + ktnefproperty.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFPROPERTY_H +#define KTNEFPROPERTY_H + +#include <qvariant.h> +#include <qstring.h> +#include <kdepimmacros.h> + +class KDE_EXPORT KTNEFProperty +{ +public: + enum MAPIType + { + UInt16 = 0x0002, + ULong = 0x0003, + Float = 0x0004, + Double = 0x0005, + Boolean = 0x000B, + Object = 0x000D, + Time = 0x0040, + String8 = 0x001E, + UString = 0x001F, + Binary = 0x0102 + }; + + KTNEFProperty(); + KTNEFProperty( int key_, int type_, const QVariant& value_, const QVariant& name_ = QVariant() ); + KTNEFProperty( const KTNEFProperty& p ); + + QString keyString(); + QString valueString(); + static QString formatValue( const QVariant& v, bool beautify=true ); + + int key() const; + int type() const; + QVariant value() const; + QVariant name() const; + bool isVector() const; + +private: + int _key; + int _type; + QVariant _value; + QVariant _name; +}; + +#endif /* KTNEFPROPERTY_H */ diff --git a/ktnef/ktnef/ktnefpropertyset.h b/ktnef/ktnef/ktnefpropertyset.h new file mode 100644 index 000000000..387acbd3a --- /dev/null +++ b/ktnef/ktnef/ktnefpropertyset.h @@ -0,0 +1,54 @@ +/* + ktnefpropertyset.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef KTNEFPROPERTYSET_H +#define KTNEFPROPERTYSET_H + +#include <qmap.h> +#include <qvariant.h> +#include <kdepimmacros.h> + +class KTNEFProperty; + +class KDE_EXPORT KTNEFPropertySet +{ +public: + KTNEFPropertySet(); + ~KTNEFPropertySet(); + + /* MAPI properties interface */ + void addProperty( int key, int type, const QVariant& value, const QVariant& name = QVariant(), bool overwrite = false ); + QString findProp( int key, const QString& fallback=QString::null, bool convertToUpper=false); + QString findNamedProp(const QString& name, const QString& fallback=QString::null, bool convertToUpper=false); + QMap<int,KTNEFProperty*>& properties(); + const QMap<int,KTNEFProperty*>& properties() const; + QVariant property( int key ) const; + + /* TNEF attributes interface */ + void addAttribute( int key, int type, const QVariant& value, bool overwrite = false ); + QMap<int,KTNEFProperty*>& attributes(); + const QMap<int,KTNEFProperty*>& attributes() const; + QVariant attribute( int key ) const; + + void clear( bool deleteAll = false ); + +private: + QMap<int,KTNEFProperty*> properties_; /* used to store MAPI properties */ + QMap<int,KTNEFProperty*> attributes_; /* used to store TNEF attributes */ +}; + +#endif /* KTNEFPROPERTYSET_H */ diff --git a/ktnef/ktnef/ktnefwriter.h b/ktnef/ktnef/ktnefwriter.h new file mode 100644 index 000000000..ab04c0b37 --- /dev/null +++ b/ktnef/ktnef/ktnefwriter.h @@ -0,0 +1,92 @@ +/* + 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 + */ + +#ifndef KTNEFWRITER_H +#define KTNEFWRITER_H + +class QString; +class QVariant; +class QIODevice; +class QDataStream; +class QDateTime; +class QStringList; + +class KTNEFWriter { +public: + KTNEFWriter(); + ~KTNEFWriter(); + + void addProperty( int tag, int type, const QVariant& value ); + + bool writeFile( QIODevice &file ); + bool writeFile( QDataStream &stream ); + + bool writeProperty( QDataStream &stream, int &bytes, int type); + + enum MessageType { + Appointment, MeetingCancelled, MeetingRequest, + MeetingNo, MeetingYes, MeetingTent + }; + + enum Method { + PublishNew, Obsolete, RequestNew, RequestUpdate, Unknown + }; + + enum Role { + ReqParticipant, OptParticipant, NonParticipant, Chair + }; + + enum PartStat { + NeedsAction, Accepted, Declined, Tentative, + Delegated, Completed, InProcess + }; + + enum Priority { + High = 2, Normal = 3, Low = 1 + }; + + enum AlarmAction { + Display + }; + + // This set of functions sets all properties on the file you want to save + void setSender(const QString &name, const QString &email); + void setMessageType(MessageType m); + void setMethod( Method m ); + void clearAttendees(); + void addAttendee( const QString& cn, Role r, PartStat p, bool rsvp, + const QString& mailto ); + void setOrganizer( const QString& organizer ); // Is that the same as sender??? + void setDtStart( const QDateTime& dtStart ); + void setDtEnd( const QDateTime& dtEnd ); + void setLocation( const QString& location ); + void setUID( const QString& uid ); + void setDtStamp( const QDateTime& dtStamp ); + void setCategories( const QStringList& ); + void setDescription( const QString& ); + void setSummary( const QString& ); + void setPriority( Priority p ); + void setAlarm( const QString& description, AlarmAction action, + const QDateTime& wakeBefore ); + +private: + class PrivateData; + PrivateData *mData; +}; + + +#endif // KTNEFWRITER_H diff --git a/ktnef/lib/Makefile.am b/ktnef/lib/Makefile.am new file mode 100644 index 000000000..731e3a377 --- /dev/null +++ b/ktnef/lib/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/ktnef $(all_includes) + +lib_LTLIBRARIES = libktnef.la + +libktnef_la_SOURCES = ktnefparser.cpp ktnefproperty.cpp ktnefattach.cpp mapi.cpp \ + ktnefpropertyset.cpp ktnefmessage.cpp ktnefwriter.cpp lzfu.cpp +libktnef_la_LIBADD = $(LIB_KDECORE) $(LIB_KIO) +libktnef_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -no-undefined +libktnef_la_METASOURCES = AUTO + +noinst_HEADERS = mapi.h lzfu.h diff --git a/ktnef/lib/ktnefattach.cpp b/ktnef/lib/ktnefattach.cpp new file mode 100644 index 000000000..76b1aeef0 --- /dev/null +++ b/ktnef/lib/ktnefattach.cpp @@ -0,0 +1,127 @@ +/* + ktnefattach.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "ktnef/ktnefattach.h" +#include "ktnef/ktnefproperty.h" + +class KTNEFAttach::AttachPrivate +{ +public: + int state_; + int size_; + int offset_; + int displaysize_; + QString name_; + int index_; + QString filename_; + QString displayname_; + QString mimetag_; + QString extension_; +}; + +KTNEFAttach::KTNEFAttach() +{ + d = new AttachPrivate; + d->state_ = Unparsed; + d->offset_ = -1; + d->size_ = 0; + d->displaysize_ = 0; + d->index_ = -1; +} + +KTNEFAttach::~KTNEFAttach() +{ + delete d; +} + +void KTNEFAttach::setTitleParsed() +{ d->state_ |= TitleParsed; } + +void KTNEFAttach::setDataParsed() +{ d->state_ |= DataParsed; } + +void KTNEFAttach::unsetDataParser() +{ d->state_ = ( d->state_ & ~DataParsed ); } + +void KTNEFAttach::setInfoParsed() +{ d->state_ |= InfoParsed; } + +bool KTNEFAttach::titleParsed() const +{ return (d->state_ & TitleParsed); } + +bool KTNEFAttach::dataParsed() const +{ return (d->state_ & DataParsed); } + +bool KTNEFAttach::infoParsed() const +{ return (d->state_ & InfoParsed); } + +bool KTNEFAttach::checkState(int state) const +{ return (d->state_ & state); } + +int KTNEFAttach::offset() const +{ return d->offset_; } + +void KTNEFAttach::setOffset(int n) +{ setDataParsed(); d->offset_ = n; } + +int KTNEFAttach::size() const +{ return d->size_; } + +void KTNEFAttach::setSize(int s) +{ d->size_ = s; } + +int KTNEFAttach::displaySize() const +{ return d->displaysize_; } + +void KTNEFAttach::setDisplaySize(int s) +{ d->displaysize_ = s; } + +QString KTNEFAttach::name() const +{ return d->name_; } + +void KTNEFAttach::setName(const QString& str) +{ setTitleParsed(); d->name_ = str; } + +int KTNEFAttach::index() const +{ return d->index_; } + +void KTNEFAttach::setIndex(int i) +{ setInfoParsed(); d->index_ = i; } + +QString KTNEFAttach::fileName() const +{ return d->filename_; } + +void KTNEFAttach::setFileName(const QString& str) +{ d->filename_ = str; } + +QString KTNEFAttach::displayName() const +{ return d->displayname_; } + +void KTNEFAttach::setDisplayName(const QString& str) +{ d->displayname_ = str; } + +QString KTNEFAttach::mimeTag() const +{ return d->mimetag_; } + +void KTNEFAttach::setMimeTag(const QString& str) +{ d->mimetag_ = str; } + +QString KTNEFAttach::extension() const +{ return d->extension_; } + +void KTNEFAttach::setExtension(const QString& str) +{ d->extension_ = str; } diff --git a/ktnef/lib/ktnefmessage.cpp b/ktnef/lib/ktnefmessage.cpp new file mode 100644 index 000000000..d94efb6c4 --- /dev/null +++ b/ktnef/lib/ktnefmessage.cpp @@ -0,0 +1,82 @@ +/* + ktnefmessage.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "ktnef/ktnefmessage.h" +#include "ktnef/ktnefattach.h" + +#include "lzfu.h" +#include <qbuffer.h> + +class KTNEFMessage::MessagePrivate +{ +public: + MessagePrivate() + { + attachments_.setAutoDelete( true ); + } + + QPtrList<KTNEFAttach> attachments_; +}; + +KTNEFMessage::KTNEFMessage() +{ + d = new MessagePrivate; +} + +KTNEFMessage::~KTNEFMessage() +{ + delete d; +} + +const QPtrList<KTNEFAttach>& KTNEFMessage::attachmentList() const +{ + return d->attachments_; +} + +KTNEFAttach* KTNEFMessage::attachment( const QString& filename ) const +{ + QPtrListIterator<KTNEFAttach> it( d->attachments_ ); + for ( ; it.current(); ++it ) + if ( it.current()->name() == filename ) + return it.current(); + return 0; +} + +void KTNEFMessage::addAttachment( KTNEFAttach *attach ) +{ + d->attachments_.append( attach ); +} + +void KTNEFMessage::clearAttachments() +{ + d->attachments_.clear(); +} + +QString KTNEFMessage::rtfString() +{ + QVariant prop = property( 0x1009 ); + if ( prop.isNull() || prop.type() != QVariant::ByteArray) + return QString::null; + else + { + QByteArray rtf; + QBuffer input( prop.asByteArray() ), output( rtf ); + if ( input.open( IO_ReadOnly ) && output.open( IO_WriteOnly ) ) + lzfu_decompress( &input, &output ); + return QString( rtf ); + } +} diff --git a/ktnef/lib/ktnefparser.cpp b/ktnef/lib/ktnefparser.cpp new file mode 100644 index 000000000..0c9881400 --- /dev/null +++ b/ktnef/lib/ktnefparser.cpp @@ -0,0 +1,883 @@ +/* + ktnefparser.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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/ktnefparser.h" +#include "ktnef/ktnefattach.h" +#include "ktnef/ktnefproperty.h" +#include "ktnef/ktnefmessage.h" + +#include <qdatetime.h> +#include <qdatastream.h> +#include <qfile.h> +#include <qvariant.h> +#include <kdebug.h> +#include <kmimetype.h> +#include <ksavefile.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif /* HAVE_INTTYPES_H */ + +#include "ktnef/ktnefdefs.h" + + +typedef struct { + Q_UINT16 type; + Q_UINT16 tag; + QVariant value; + struct { + Q_UINT32 type; + QVariant value; + } name; +} MAPI_value; + +void clearMAPIName( MAPI_value& mapi ); +void clearMAPIValue(MAPI_value& mapi, bool clearName = true); +QString readMAPIString( QDataStream& stream, bool isUnicode = false, bool align = true, int len = -1 ); +Q_UINT16 readMAPIValue(QDataStream& stream, MAPI_value& mapi); +QDateTime readTNEFDate( QDataStream& stream ); +QString readTNEFAddress( QDataStream& stream ); +QByteArray readTNEFData( QDataStream& stream, Q_UINT32 len ); +QVariant readTNEFAttribute( QDataStream& stream, Q_UINT16 type, Q_UINT32 len ); +QDateTime formatTime( Q_UINT32 lowB, Q_UINT32 highB ); +QString formatRecipient( const QMap<int,KTNEFProperty*>& props ); + +//------------------------------------------------------------------------------------ + +class KTNEFParser::ParserPrivate +{ +public: + ParserPrivate() + { + defaultdir_ = "/tmp/"; + current_ = 0; + deleteDevice_ = false; + device_ = 0; + message_ = new KTNEFMessage; + } + ~ParserPrivate() + { + delete message_; + } + + QDataStream stream_; + QIODevice *device_; + bool deleteDevice_; + QString 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() +{ + Q_UINT32 i1, i2, off; + Q_UINT16 u, tag, type; + QVariant 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 = QString( "< %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: + { + Q_UINT8 c; + Q_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: + { + Q_UINT32 rows; + QValueList<QVariant> recipTable; + d->stream_ >> rows; + for ( uint i=0; i<rows; i++ ) + { + QMap<int,KTNEFProperty*> 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() +{ + Q_UINT32 i; + Q_UINT16 tag, type, u; + QVariant value; + QString 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 = QString( "< 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 = QString( "< %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 = QString( "< 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 QString& dirname) +{ + d->defaultdir_ = dirname; +} + +bool KTNEFParser::parseDevice() +{ + Q_UINT16 u; + Q_UINT32 i; + Q_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( QDataStream::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 QString& filename) +{ + KTNEFAttach *att = d->message_->attachment(filename); + if (!att) return false; + return extractAttachmentTo(att, d->defaultdir_); +} + +bool KTNEFParser::extractAttachmentTo(KTNEFAttach *att, const QString& dirname) +{ + QString filename = dirname + "/" + att->name(); + if (!d->device_->isOpen()) + return false; + if (!d->device_->at(att->offset())) + return false; + KSaveFile saveFile( filename ); + QFile *outfile = saveFile.file(); + if ( !outfile ) + return false; + + Q_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,QMIN(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() +{ + QPtrListIterator<KTNEFAttach> it(d->message_->attachmentList()); + for (;it.current();++it) + if (!extractAttachmentTo(it.current(),d->defaultdir_)) return false; + return true; +} + +bool KTNEFParser::extractFileTo(const QString& filename, const QString& 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 QString& filename) +{ + deleteDevice(); + delete d->message_; + d->message_ = new KTNEFMessage(); + d->device_ = new QFile( filename ); + d->deleteDevice_ = true; + return parseDevice(); +} + +bool KTNEFParser::openDevice( QIODevice *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(); + QByteArray buffer( QMIN( 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 ); +} + +QDateTime formatTime( Q_UINT32 lowB, Q_UINT32 highB ) +{ + QDateTime 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; +} + +QString formatRecipient( const QMap<int,KTNEFProperty*>& props ) +{ + QString s, dn, addr, t; + QMap<int,KTNEFProperty*>::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(); +} + +QDateTime readTNEFDate( QDataStream& stream ) +{ + // 14-bytes long + Q_UINT16 y, m, d, hh, mm, ss, dm; + stream >> y >> m >> d >> hh >> mm >> ss >> dm; + return QDateTime( QDate( y, m, d ), QTime( hh, mm, ss ) ); +} + +QString readTNEFAddress( QDataStream& stream ) +{ + Q_UINT16 totalLen, strLen, addrLen; + QString s; + stream >> totalLen >> totalLen >> strLen >> addrLen; + s.append( readMAPIString( stream, false, false, strLen ) ); + s.append( " <" ); + s.append( readMAPIString( stream, false, false, addrLen ) ); + s.append( ">" ); + Q_UINT8 c; + for ( int i=8+strLen+addrLen; i<totalLen; i++ ) + stream >> c; + return s; +} + +QByteArray readTNEFData( QDataStream& stream, Q_UINT32 len ) +{ + QByteArray array( len ); + if ( len > 0 ) + stream.readRawBytes( array.data(), len ); + return array; +} + +QVariant readTNEFAttribute( QDataStream& stream, Q_UINT16 type, Q_UINT32 len ) +{ + switch ( type ) + { + case atpTEXT: + case atpSTRING: + return readMAPIString( stream, false, false, len ); + case atpDATE: + return readTNEFDate( stream ); + default: + return readTNEFData( stream, len ); + } +} + +QString readMAPIString( QDataStream& stream, bool isUnicode, bool align, int len_ ) +{ + Q_UINT32 len; + char *buf = 0; + if ( len_ == -1 ) + stream >> len; + else + len = len_; + Q_UINT32 fullLen = len; + if ( align ) + ALIGN( fullLen, 4 ); + buf = new char[ len ]; + stream.readRawBytes( buf, len ); + Q_UINT8 c; + for ( uint i=len; i<fullLen; i++ ) + stream >> c; + QString res; + if ( isUnicode ) + res = QString::fromUcs2( ( const unsigned short* )buf ); + else + res = QString::fromLocal8Bit( buf ); + delete [] buf; + return res; +} + +Q_UINT16 readMAPIValue(QDataStream& stream, MAPI_value& mapi) +{ + Q_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; + QVariant value; + if ( ISVECTOR( mapi ) ) + { + stream >> n; + mapi.value = QValueList<QVariant>(); + } + for ( int i=0; i<n; i++ ) + { + value.clear(); + switch(mapi.type & 0x0FFF) + { + case MAPI_TYPE_UINT16: + stream >> 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: + { + Q_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;i++) + { + value.clear(); + value.asString() = readMAPIString( stream ); + } + break; + case MAPI_TYPE_USTRING: + mapi.type = MAPI_TYPE_NONE; + break; + case MAPI_TYPE_OBJECT: + case MAPI_TYPE_BINARY: + if ( ISVECTOR( mapi ) ) + d = 1; + else + stream >> d; + for (uint i=0;i<d;i++) + { + value.clear(); + Q_UINT32 len; + stream >> len; + value = QByteArray( len ); + if (len > 0) + { + int fullLen = len; + ALIGN(fullLen, 4); + stream.readRawBytes(value.asByteArray().data(), len); + Q_UINT8 c; + for ( int i=len; i<fullLen; i++ ) + stream >> c; + } + } + break; + default: + mapi.type = MAPI_TYPE_NONE; + break; + } + if ( ISVECTOR( mapi ) ) + mapi.value.asList().append( value ); + else + mapi.value = value; + } + return mapi.tag; +} + +bool KTNEFParser::readMAPIProperties( QMap<int,KTNEFProperty*>& props, KTNEFAttach *attach ) +{ + Q_UINT32 n; + MAPI_value mapi; + KTNEFProperty *p; + QMap<int,KTNEFProperty*>::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;i<n;i++) + { + if (d->stream_.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 ) + { + QByteArray data = mapi.value.toByteArray(); + int len = data.size(); + ALIGN( len, 4 ); + d->device_->at( d->device_->at()-len ); + Q_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, QString( "< size=%1 >" ).arg( len ), false ); + } + } + kdDebug().form( "MAPI data: size=%d\n", mapi.value.toByteArray().size() ); + break; + default: + { + QString mapiname = ""; + if ( mapi.tag >= 0x8000 && mapi.tag <= 0xFFFE ) + { + if ( mapi.name.type == 0 ) + mapiname = QString().sprintf( " [name = 0x%04x]", mapi.name.value.toUInt() ); + else + mapiname = QString( " [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: + 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() ); + QString 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; +} diff --git a/ktnef/lib/ktnefproperty.cpp b/ktnef/lib/ktnefproperty.cpp new file mode 100644 index 000000000..3845b8d88 --- /dev/null +++ b/ktnef/lib/ktnefproperty.cpp @@ -0,0 +1,99 @@ +/* + ktnefproperty.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "ktnef/ktnefproperty.h" +#include "mapi.h" +#include <qdatetime.h> +#include <ctype.h> + +KTNEFProperty::KTNEFProperty() +{ +} + +KTNEFProperty::KTNEFProperty( int key_, int type_, const QVariant& value_, const QVariant& name_ ) + : _key( key_ ), _type( type_ ), _value( value_ ), _name( name_ ) +{ +} + +KTNEFProperty::KTNEFProperty( const KTNEFProperty& p ) + : _key( p._key ), _type( p._type ), _value( p._value ), _name( p._name ) +{ +} + +QString KTNEFProperty::keyString() +{ + if ( _name.isValid() ) + { + if ( _name.type() == QVariant::String ) + return _name.asString(); + else + return mapiNamedTagString( _name.asUInt(), _key ); + } + else + return mapiTagString( _key ); +} + +QString KTNEFProperty::formatValue( const QVariant& value, bool beautify ) +{ + if ( value.type() == QVariant::ByteArray ) + { + // check the first bytes (up to 8) if they are + // printable characters + QByteArray arr = value.toByteArray(); + bool printable = true; + for ( int i=QMIN( arr.size(), 8 )-1; i>=0 && printable; i-- ) + printable = ( isprint( arr[ i ] ) != 0 ); + if ( !printable ) + { + QString s; + uint i; + uint txtCount = beautify ? QMIN( arr.size(), 32 ) : arr.size(); + for ( i=0; i < txtCount; ++i ) + { + s.append( QString().sprintf( "%02X", ( uchar )arr[ i ] ) ); + if( beautify ) + s.append( " " ); + } + if ( i < arr.size() ) + s.append( "... (size=" + QString::number( arr.size() ) + ")" ); + return s; + } + } + //else if ( value.type() == QVariant::DateTime ) + // return value.toDateTime().toString(); + return value.toString(); +} + +QString KTNEFProperty::valueString() +{ + return formatValue( _value ); +} + +int KTNEFProperty::key() const +{ return _key; } + +int KTNEFProperty::type() const +{ return _type; } + +QVariant KTNEFProperty::value() const +{ return _value; } + +QVariant KTNEFProperty::name() const +{ return _name; } + +bool KTNEFProperty::isVector() const +{ return ( _value.type() == QVariant::List ); } diff --git a/ktnef/lib/ktnefpropertyset.cpp b/ktnef/lib/ktnefpropertyset.cpp new file mode 100644 index 000000000..cf35362fe --- /dev/null +++ b/ktnef/lib/ktnefpropertyset.cpp @@ -0,0 +1,155 @@ +/* + ktnefpropertyset.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "ktnef/ktnefpropertyset.h" +#include "ktnef/ktnefproperty.h" +#include <kdebug.h> + +KTNEFPropertySet::KTNEFPropertySet() +{ +} + +KTNEFPropertySet::~KTNEFPropertySet() +{ + clear( true ); +} + +void KTNEFPropertySet::addProperty( int key, int type, const QVariant& value, const QVariant& name, bool overwrite ) +{ + QMap<int,KTNEFProperty*>::ConstIterator it = properties_.find( key ); + if ( it != properties_.end() ) + { + if ( overwrite ) + delete ( *it ); + else + return; + } + KTNEFProperty *p = new KTNEFProperty( key, type, value, name ); + properties_[ p->key() ] = p; +} + + +QString KTNEFPropertySet::findProp(int key, const QString& fallback, bool upper) +{ + QMap<int,KTNEFProperty*>::Iterator it = properties_.find( key ); + if( properties_.end() != it ) + return upper ? KTNEFProperty::formatValue( (*it)->value(), false ).upper() + : KTNEFProperty::formatValue( (*it)->value(), false ); + else + return fallback; +} + + +QString KTNEFPropertySet::findNamedProp(const QString& name, const QString& fallback, bool upper) +{ + for ( QMap<int,KTNEFProperty*>::Iterator it = properties_.begin(); + it != properties_.end(); + ++it ){ + if ( (*it)->name().isValid() ){ + QString s; + if ( (*it)->name().type() == QVariant::String ) + s = (*it)->name().asString(); + else + s = QString().sprintf( "0X%04X", (*it)->name().asUInt() ); + + if( s.upper() == name.upper() ){ + QVariant value = ( *it )->value(); + if( value.type() == QVariant::List ){ + s = ""; + for ( QValueList<QVariant>::ConstIterator lit = value.listBegin(); + lit != value.listEnd(); + ++lit ){ + if( !s.isEmpty() ) + s += ','; + s += KTNEFProperty::formatValue( *lit, false ); + } + }else{ + s = KTNEFProperty::formatValue( value, false ); + } + return upper ? s.upper() : s; + } + } + } + return fallback; +} + + +QMap<int,KTNEFProperty*>& KTNEFPropertySet::properties() +{ + return properties_; +} + +const QMap<int,KTNEFProperty*>& KTNEFPropertySet::properties() const +{ + return properties_; +} + +QVariant KTNEFPropertySet::property( int key ) const +{ + QMap<int,KTNEFProperty*>::ConstIterator it = properties_.find( key ); + if ( it == properties_.end() ) + return QVariant(); + else + return ( *it )->value(); +} + +void KTNEFPropertySet::clear( bool deleteAll ) +{ + if ( deleteAll ) + { + for ( QMap<int,KTNEFProperty*>::ConstIterator it=properties_.begin(); it!=properties_.end(); ++it ) + delete ( *it ); + for ( QMap<int,KTNEFProperty*>::ConstIterator it=attributes_.begin(); it!=attributes_.end(); ++it ) + delete ( *it ); + } + properties_.clear(); + attributes_.clear(); +} + +void KTNEFPropertySet::addAttribute( int key, int type, const QVariant& value, bool overwrite ) +{ + QMap<int,KTNEFProperty*>::ConstIterator it = attributes_.find( key ); + if ( it != attributes_.end() ) + { + if ( overwrite ) + delete ( *it ); + else + return; + } + KTNEFProperty *p = new KTNEFProperty( key, type, value, QVariant() ); + attributes_[ p->key() ] = p; +} + +QMap<int,KTNEFProperty*>& KTNEFPropertySet::attributes() +{ + return attributes_; +} + +const QMap<int,KTNEFProperty*>& KTNEFPropertySet::attributes() const +{ + return attributes_; +} + +QVariant KTNEFPropertySet::attribute( int key ) const +{ + QMap<int,KTNEFProperty*>::ConstIterator it = attributes_.find( key ); + if ( it == attributes_.end() ) + return QVariant(); + else + return ( *it )->value(); +} + 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*/ ) +{ + +} diff --git a/ktnef/lib/lzfu.cpp b/ktnef/lib/lzfu.cpp new file mode 100644 index 000000000..ab4fa36a0 --- /dev/null +++ b/ktnef/lib/lzfu.cpp @@ -0,0 +1,178 @@ +/* + lzfu.cpp + + Copyright (C) 2003 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "config.h" + +#include "lzfu.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +#include <sys/types.h> +#include <string.h> +#include <qiodevice.h> +#include <stdio.h> + +//#define DO_DEBUG + +#define LZFU_COMPRESSED 0x75465a4c +#define LZFU_UNCOMPRESSED 0x414c454d + +#define LZFU_INITDICT "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}" \ + "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscrip" \ + "t \\fdecor MS Sans SerifSymbolArialTimes Ne" \ + "w RomanCourier{\\colortbl\\red0\\green0\\blue0" \ + "\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab" \ + "\\tx" +#define LZFU_INITLENGTH 207 + +typedef struct _lzfuheader { + uint32_t cbSize; + uint32_t cbRawSize; + uint32_t dwMagic; + uint32_t dwCRC; +} lzfuheader; + +#define FLAG(f,n) (f>>n)&0x1 + +/*typedef struct _blockheader { + unsigned int offset:12; + unsigned int length:4; +} blockheader;*/ + +#define OFFSET(b) (b>>4)&0xFFF +#define LENGTH(b) ((b&0xF)+2) + +int lzfu_decompress(QIODevice *input, QIODevice *output) +{ + unsigned char window[4096]; + unsigned int wlength = 0, cursor = 0, ocursor = 0; + lzfuheader lzfuhdr; + //blockheader blkhdr; + uint16_t blkhdr; + char bFlags; + int nFlags; + + memcpy(window, LZFU_INITDICT, LZFU_INITLENGTH); + wlength = LZFU_INITLENGTH; + if (input->readBlock((char*)&lzfuhdr, sizeof(lzfuhdr)) != sizeof(lzfuhdr)) + { + fprintf(stderr, "unexpected eof, cannot read LZFU header\n"); + return -1; + } + cursor += sizeof( lzfuhdr ); +#ifdef DO_DEBUG + fprintf(stdout, "total size : %d\n", lzfuhdr.cbSize+4); + fprintf(stdout, "raw size : %d\n", lzfuhdr.cbRawSize); + fprintf(stdout, "compressed : %s\n", (lzfuhdr.dwMagic == LZFU_COMPRESSED ? "yes" : "no")); + fprintf(stdout, "CRC : %x\n", lzfuhdr.dwCRC); + fprintf(stdout, "\n"); +#endif + + while (cursor < lzfuhdr.cbSize+4 && ocursor < lzfuhdr.cbRawSize && !input->atEnd()) + { + if (input->readBlock(&bFlags, 1) != 1) + { + fprintf(stderr, "unexpected eof, cannot read chunk flag\n"); + return -1; + } + nFlags = 8; + cursor++; +#ifdef DO_DEBUG + fprintf(stdout, "Flags : "); + for (int i=nFlags-1; i>=0; i--) + fprintf(stdout, "%d", FLAG(bFlags, i)); + fprintf(stdout, "\n"); +#endif + for (int i=0; i<nFlags && ocursor<lzfuhdr.cbRawSize && cursor<lzfuhdr.cbSize+4; i++) + { + if (FLAG(bFlags, i)) + { + // compressed chunck + char c1, c2; + if (input->readBlock(&c1, 1) != 1 || input->readBlock(&c2, 1) != 1) + { + fprintf(stderr, "unexpected eof, cannot read block header\n"); + return -1; + } + blkhdr = c1; + blkhdr <<= 8; + blkhdr |= (0xFF&c2); + unsigned int offset = OFFSET(blkhdr), length = LENGTH(blkhdr); + cursor += 2; +#ifdef DO_DEBUG + fprintf( stdout, "block : offset=%.4d [%d], length=%.2d (0x%04X)\n", OFFSET( blkhdr ), wlength, LENGTH( blkhdr ), blkhdr ); +#endif + //if (offset >= wlength) + //{ + // break; + //} +#ifdef DO_DEBUG + fprintf( stdout, "block : " ); +#endif + for (unsigned int i=0; i<length; i++) + { + c1 = window[( offset+i ) % 4096]; + //if (wlength < 4096) + //{ + window[wlength] = c1; + wlength = ( wlength+1 ) % 4096; + //} +#ifdef DO_DEBUG + if ( c1 == '\n' ) + fprintf( stdout, "\nblock : " ); + else + fprintf( stdout, "%c", c1 ); +#endif + output->putch(c1); + ocursor++; + } +#ifdef DO_DEBUG + fprintf( stdout, "\n" ); +#endif + } + else + { + // uncompressed chunk (char) + signed char c = (char)input->getch(); + if (c == -1) + { + if (!input->atEnd()) + { + fprintf(stderr, "unexpected eof, cannot read character\n"); + return -1; + } + break; + } +#ifdef DO_DEBUG + fprintf( stdout, "char : %c\n", c ); +#endif + cursor++; + //if (wlength < 4096) + //{ + window[wlength] = c; + wlength = ( wlength+1 ) % 4096; + //} + output->putch(c); + ocursor++; + } + } + + } + + return 0; +} diff --git a/ktnef/lib/lzfu.h b/ktnef/lib/lzfu.h new file mode 100644 index 000000000..4f8365586 --- /dev/null +++ b/ktnef/lib/lzfu.h @@ -0,0 +1,25 @@ +/* + lzfu.h + + Copyright (C) 2003 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef LZFU_H +#define LZFU_H + +class QIODevice; + +int lzfu_decompress( QIODevice *input, QIODevice *output ); + +#endif /* LZFU_H */ diff --git a/ktnef/lib/mapi.cpp b/ktnef/lib/mapi.cpp new file mode 100644 index 000000000..8122153ce --- /dev/null +++ b/ktnef/lib/mapi.cpp @@ -0,0 +1,222 @@ +/* + mapi.cpp + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#include "mapi.h" +#include <qmap.h> +#include <klocale.h> + +static struct +{ + int tag; + const char *str; +} MAPI_TagStrings[] = +{ + { 0x0002, I18N_NOOP( "Alternate Recipient Allowed" ) }, + { 0x001A, I18N_NOOP( "Message Class" ) }, + { 0x0023, I18N_NOOP( "Originator Delivery Report Requested" ) }, + { 0x0024, I18N_NOOP( "Originator Return Address" ) }, + { 0x0026, I18N_NOOP( "Priority" ) }, + { 0x0029, I18N_NOOP( "Read Receipt Requested" ) }, + { 0x002B, I18N_NOOP( "Recipient Reassignment Prohibited" ) }, + { 0x002E, I18N_NOOP( "Original Sensitivity" ) }, + { 0x0031, I18N_NOOP( "Report Tag" ) }, + { 0x0036, I18N_NOOP( "Sensitivity" ) }, + { 0x0037, I18N_NOOP( "Subject" ) }, + { 0x0039, I18N_NOOP( "Client Submit Time" ) }, + { 0x003B, I18N_NOOP( "Sent Representing Search Key" ) }, + { 0x003D, I18N_NOOP( "Subject Prefix" ) }, + { 0x0041, I18N_NOOP( "Sent Representing Entry ID" ) }, + { 0x0042, I18N_NOOP( "Sent Representing Name" ) }, + { 0x0047, I18N_NOOP( "Message Submission ID" ) }, + { 0x004D, I18N_NOOP( "Original Author Name" ) }, + { 0x0062, I18N_NOOP( "Owner Appointment ID" ) }, + { 0x0063, I18N_NOOP( "Response Requested" ) }, + { 0x0064, I18N_NOOP( "Sent Representing Address Type" ) }, + { 0x0065, I18N_NOOP( "Sent Representing E-mail Address" ) }, + { 0x0070, I18N_NOOP( "Conversation Topic" ) }, + { 0x0071, I18N_NOOP( "Conversation Index" ) }, + { 0x007F, I18N_NOOP( "TNEF Correlation Key" ) }, + { 0x0C17, I18N_NOOP( "Reply Requested" ) }, + { 0x0C1A, I18N_NOOP( "Sender Name" ) }, + { 0x0C1D, I18N_NOOP( "Sender Search Key" ) }, + { 0x0C1E, I18N_NOOP( "Sender Address Type" ) }, + { 0x0C1F, I18N_NOOP( "Sender E-mail Address" ) }, + { 0x0E01, I18N_NOOP( "Delete After Submit" ) }, + { 0x0E02, I18N_NOOP( "Display Bcc" ) }, + { 0x0E03, I18N_NOOP( "Display Cc" ) }, + { 0x0E04, I18N_NOOP( "Display To" ) }, + { 0x0E06, I18N_NOOP( "Message Delivery Time" ) }, + { 0x0E07, I18N_NOOP( "Message Flags" ) }, + { 0x0E08, I18N_NOOP( "Message Size" ) }, + { 0x0E09, I18N_NOOP( "Parent Entry ID" ) }, + { 0x0E0A, I18N_NOOP( "Sent-Mail Entry ID" ) }, + { 0x0E12, I18N_NOOP( "Message Recipients" ) }, + { 0x0E14, I18N_NOOP( "Submit Flags" ) }, + { 0x0E1B, I18N_NOOP( "Has Attachment" ) }, + { 0x0E1D, I18N_NOOP( "Normalized Subject" ) }, + { 0x0E1F, I18N_NOOP( "RTF In Sync" ) }, + { 0x0E20, I18N_NOOP( "Attachment Size" ) }, + { 0x0E21, I18N_NOOP( "Attachment Number" ) }, + { 0x0FF4, I18N_NOOP( "Access" ) }, + { 0x0FF7, I18N_NOOP( "Access Level" ) }, + { 0x0FF8, I18N_NOOP( "Mapping Signature" ) }, + { 0x0FF9, I18N_NOOP( "Record Key" ) }, + { 0x0FFA, I18N_NOOP( "Store Record Key" ) }, + { 0x0FFB, I18N_NOOP( "Store Entry ID" ) }, + { 0x0FFE, I18N_NOOP( "Object Type" ) }, + { 0x0FFF, I18N_NOOP( "Entry ID" ) }, + { 0x1000, I18N_NOOP( "Message Body" ) }, + { 0x1006, I18N_NOOP( "RTF Sync Body CRC" ) }, + { 0x1007, I18N_NOOP( "RTF Sync Body Count" ) }, + { 0x1008, I18N_NOOP( "RTF Sync Body Tag" ) }, + { 0x1009, I18N_NOOP( "RTF Compressed" ) }, + { 0x1010, I18N_NOOP( "RTF Sync Prefix Count" ) }, + { 0x1011, I18N_NOOP( "RTF Sync Trailing Count" ) }, + { 0x1013, I18N_NOOP( "HTML Message Body" ) }, + { 0x1035, I18N_NOOP( "Message ID" ) }, + { 0x1042, I18N_NOOP( "Parent's Message ID" ) }, + { 0x1080, I18N_NOOP( "Action" ) }, + { 0x1081, I18N_NOOP( "Action Flag" ) }, + { 0x1082, I18N_NOOP( "Action Date" ) }, + { 0x3001, I18N_NOOP( "Display Name" ) }, + { 0x3007, I18N_NOOP( "Creation Time" ) }, + { 0x3008, I18N_NOOP( "Last Modification Time" ) }, + { 0x300B, I18N_NOOP( "Search Key" ) }, + { 0x340D, I18N_NOOP( "Store Support Mask" ) }, + { 0x3414, I18N_NOOP( "MDB Provider" ) }, + { 0x3701, I18N_NOOP( "Attachment Data" ) }, + { 0x3702, I18N_NOOP( "Attachment Encoding" ) }, + { 0x3703, I18N_NOOP( "Attachment Extension" ) }, + { 0x3705, I18N_NOOP( "Attachment Method" ) }, + { 0x3707, I18N_NOOP( "Attachment Long File Name" ) }, + { 0x370B, I18N_NOOP( "Attachment Rendering Position" ) }, + { 0x370E, I18N_NOOP( "Attachment Mime Tag" ) }, + { 0x3714, I18N_NOOP( "Attachment Flags" ) }, + { 0x3A00, I18N_NOOP( "Account" ) }, + { 0x3A05, I18N_NOOP( "Generation" ) }, + { 0x3A06, I18N_NOOP( "Given Name" ) }, + { 0x3A0A, I18N_NOOP( "Initials" ) }, + { 0x3A0B, I18N_NOOP( "Keyword" ) }, + { 0x3A0C, I18N_NOOP( "Language" ) }, + { 0x3A0D, I18N_NOOP( "Location" ) }, + { 0x3A11, I18N_NOOP( "Surname" ) }, + { 0x3A16, I18N_NOOP( "Company Name" ) }, + { 0x3A17, I18N_NOOP( "Title" ) }, + { 0x3A18, I18N_NOOP( "Department Name" ) }, + { 0x3A26, I18N_NOOP( "Country" ) }, + { 0x3A27, I18N_NOOP( "Locality" ) }, + { 0x3A28, I18N_NOOP( "State/Province" ) }, + { 0x3A44, I18N_NOOP( "Middle Name" ) }, + { 0x3A45, I18N_NOOP( "Display Name Prefix" ) }, + + /* Some TNEF attributes */ + { 0x0008, I18N_NOOP( "Owner Appointment ID" ) }, + { 0x0009, I18N_NOOP( "Response Requested" ) }, + { 0x8000, I18N_NOOP( "From" ) }, + { 0x8004, I18N_NOOP( "Subject" ) }, + { 0x8005, I18N_NOOP( "Date Sent" ) }, + { 0x8006, I18N_NOOP( "Date Received" ) }, + { 0x8007, I18N_NOOP( "Message Status" ) }, + { 0x8008, I18N_NOOP( "Message Class" ) }, + { 0x8009, I18N_NOOP( "Message ID" ) }, + { 0x800A, I18N_NOOP( "Parent ID" ) }, + { 0x800B, I18N_NOOP( "Conversation ID" ) }, + { 0x800C, I18N_NOOP( "Body" ) }, + { 0x800D, I18N_NOOP( "Priority" ) }, + { 0x800F, I18N_NOOP( "Attachment Data" ) }, + { 0x8010, I18N_NOOP( "Attachment Title" ) }, + { 0x8011, I18N_NOOP( "Attachment Meta File" ) }, + { 0x8012, I18N_NOOP( "Attachment Create Date" ) }, + { 0x8013, I18N_NOOP( "Attachment Modify Date" ) }, + { 0x8020, I18N_NOOP( "Date Modified" ) }, + { 0x9001, I18N_NOOP( "Attachment Transport File Name" ) }, + { 0x9002, I18N_NOOP( "Attachment Rendering Data" ) }, + { 0x9003, I18N_NOOP( "MAPI Properties" ) }, + { 0x9004, I18N_NOOP( "Recipients Table" ) }, + { 0x9005, I18N_NOOP( "Attachment MAPI Properties" ) }, + { 0x9006, I18N_NOOP( "TNEF Version" ) }, + { 0x9007, I18N_NOOP( "OEM Code Page" ) }, + + { 0, 0 } +}, +MAPI_NamedTagStrings[] = +{ + { 0x8005, I18N_NOOP( "Contact File Under" ) }, + { 0x8017, I18N_NOOP( "Contact Last Name And First Name" ) }, + { 0x8018, I18N_NOOP( "Contact Company And Full Name" ) }, + + { 0x8080, I18N_NOOP( "Contact EMail-1 Full" ) }, + { 0x8082, I18N_NOOP( "Contact EMail-1 Address Type" ) }, + { 0x8083, I18N_NOOP( "Contact EMail-1 Address" ) }, + { 0x8084, I18N_NOOP( "Contact EMail-1 Display Name" ) }, + { 0x8085, I18N_NOOP( "Contact EMail-1 Entry ID" ) }, + + { 0x8090, I18N_NOOP( "Contact EMail-2 Full" ) }, + { 0x8092, I18N_NOOP( "Contact EMail-2 Address Type" ) }, + { 0x8093, I18N_NOOP( "Contact EMail-2 Address" ) }, + { 0x8094, I18N_NOOP( "Contact EMail-2 Display Name" ) }, + { 0x8095, I18N_NOOP( "Contact EMail-2 Entry ID" ) }, + + { 0x8208, I18N_NOOP( "Appointment Location" ) }, + { 0x8208, I18N_NOOP( "Appointment Location" ) }, + { 0x820D, I18N_NOOP( "Appointment Start Date" ) }, + { 0x820E, I18N_NOOP( "Appointment End Date" ) }, + { 0x8213, I18N_NOOP( "Appointment Duration" ) }, + { 0x8218, I18N_NOOP( "Appointment Response Status" ) }, + { 0x8223, I18N_NOOP( "Appointment Is Recurring" ) }, + { 0x8231, I18N_NOOP( "Appointment Recurrence Type" ) }, + { 0x8232, I18N_NOOP( "Appointment Recurrence Pattern" ) }, + { 0x8502, I18N_NOOP( "Reminder Time" ) }, + { 0x8503, I18N_NOOP( "Reminder Set" ) }, + { 0x8516, I18N_NOOP( "Start Date" ) }, + { 0x8517, I18N_NOOP( "End Date" ) }, + { 0x8560, I18N_NOOP( "Reminder Next Time" ) }, + { 0, 0 } +}; +static QMap<int,QString> MAPI_TagMap; +static QMap<int,QString> MAPI_NamedTagMap; + +QString mapiTagString( int key ) +{ + if ( MAPI_TagMap.count() == 0 ) + { + for ( int i=0; MAPI_TagStrings[ i ].str; i++ ) + MAPI_TagMap[ MAPI_TagStrings[ i ].tag ] = i18n(MAPI_TagStrings[ i ].str); + } + QMap<int,QString>::ConstIterator it = MAPI_TagMap.find( key ); + if ( it == MAPI_TagMap.end() ) + return QString().sprintf( "0x%04X", key ); + else + return QString().sprintf( "0x%04X ________: ", key ) + *it; +} + +QString mapiNamedTagString( int key, int tag ) +{ + if ( MAPI_NamedTagMap.count() == 0 ) + { + for ( int i=0; MAPI_NamedTagStrings[ i ].str; i++ ) + MAPI_NamedTagMap[ MAPI_NamedTagStrings[ i ].tag ] = i18n(MAPI_NamedTagStrings[ i ].str); + } + QMap<int,QString>::ConstIterator it = MAPI_NamedTagMap.find( key ); + if ( it == MAPI_NamedTagMap.end() ) + if ( tag >= 0 ) + return QString().sprintf( "0x%04X [0x%04X]: ", tag, key ) + *it; + else + return QString().sprintf( "0x%04X ________:", key ) + *it; + else + return *it; +} diff --git a/ktnef/lib/mapi.h b/ktnef/lib/mapi.h new file mode 100644 index 000000000..fe4a37bf1 --- /dev/null +++ b/ktnef/lib/mapi.h @@ -0,0 +1,26 @@ +/* + mapi.h + + Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be> + + 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 + */ + +#ifndef MAPI_H +#define MAPI_H + +#include <qstring.h> + +QString mapiTagString( int key ); +QString mapiNamedTagString( int key, int tag = -1 ); + +#endif diff --git a/ktnef/tests/Makefile.am b/ktnef/tests/Makefile.am new file mode 100644 index 000000000..a2619c0ef --- /dev/null +++ b/ktnef/tests/Makefile.am @@ -0,0 +1,10 @@ +AM_CPPFLAGS = -I$(top_srcdir)/ktnef $(all_includes) -DKDESRCDIR=\"$(srcdir)\" +LDADD = ../lib/libktnef.la $(LIB_KDECORE) + +check_PROGRAMS = parsertest +TESTS = parsertest + +parsertest_LDFLAGS = $(all_libraries) $(KDE_RPATH) +parsertest_SOURCES = parsertest.cpp + +METASOURCES = AUTO diff --git a/ktnef/tests/README b/ktnef/tests/README new file mode 100644 index 000000000..5cff55b70 --- /dev/null +++ b/ktnef/tests/README @@ -0,0 +1 @@ +TNEF test files are taken from tnef.sf.net. diff --git a/ktnef/tests/mapi_attach_data_obj.tnef b/ktnef/tests/mapi_attach_data_obj.tnef Binary files differnew file mode 100644 index 000000000..aea529ec2 --- /dev/null +++ b/ktnef/tests/mapi_attach_data_obj.tnef diff --git a/ktnef/tests/one-file.tnef b/ktnef/tests/one-file.tnef Binary files differnew file mode 100644 index 000000000..fbb08ffa1 --- /dev/null +++ b/ktnef/tests/one-file.tnef diff --git a/ktnef/tests/parsertest.cpp b/ktnef/tests/parsertest.cpp new file mode 100644 index 000000000..2cbe11e91 --- /dev/null +++ b/ktnef/tests/parsertest.cpp @@ -0,0 +1,104 @@ +/* + Copyright (c) 2007 Volker Krause <vkrause@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "parsertest.h" + +#include <ktnef/ktnefparser.h> +#include <ktnef/ktnefmessage.h> +#include <ktnef/ktnefattach.h> + +#include "assert.h" + +void ParserTest::testSingleAttachment() +{ + KTNEFParser parser; + assert( parser.openFile( KDESRCDIR "/one-file.tnef" ) == true ); + + KTNEFMessage *msg = parser.message(); + assert( msg != 0 ); + + QPtrList<KTNEFAttach> atts = msg->attachmentList(); + assert( atts.count() == 1 ); + + KTNEFAttach* att = atts.first(); + assert( att != 0 ); + assert( att->size() == 244 ); + assert( att->name() == QString( "AUTHORS" ) ); +} + +void ParserTest::testTwoAttachments() +{ + KTNEFParser parser; + assert( parser.openFile( KDESRCDIR "/two-files.tnef" ) == true ); + + KTNEFMessage *msg = parser.message(); + assert( msg != 0 ); + + QPtrList<KTNEFAttach> atts = msg->attachmentList(); + assert( atts.count() == 2 ); + + KTNEFAttach* att = atts.at( 0 ); + assert( att != 0 ); + assert( att->size() == 244 ); + assert( att->name() == QString( "AUTHORS" ) ); + + att = atts.at( 1 ); + assert( att != 0 ); + assert( att->size() == 893 ); + assert( att->name() == QString( "README" ) ); +} + +void ParserTest::testMAPIAttachments() +{ + KTNEFParser parser; + assert( parser.openFile( KDESRCDIR "/mapi_attach_data_obj.tnef" ) == true ); + + KTNEFMessage *msg = parser.message(); + assert( msg != 0 ); + + QPtrList<KTNEFAttach> atts = msg->attachmentList(); + assert( atts.count() == 3 ); + + KTNEFAttach* att = atts.at( 0 ); + assert( att != 0 ); + assert( att->size() == 61952 ); + assert( att->name() == QString( "VIA_Nytt_1402.doc" ) ); + + att = atts.at( 1 ); + assert( att != 0 ); + assert( att->size() == 213688 ); + assert( att->name() == QString( "VIA_Nytt_1402.pdf" ) ); + + att = atts.at( 2 ); + assert( att != 0 ); + assert( att->size() == 68920 ); + assert( att->name() == QString( "VIA_Nytt_14021.htm" ) ); +} + +#include <kinstance.h> + +int main( int argc, char** argv ) +{ + KInstance inst( "ktnef-parsertest" ); + ParserTest test; + test.testSingleAttachment(); + test.testTwoAttachments(); + test.testMAPIAttachments(); + return 0; +} diff --git a/ktnef/tests/parsertest.h b/ktnef/tests/parsertest.h new file mode 100644 index 000000000..4c0bacbda --- /dev/null +++ b/ktnef/tests/parsertest.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2007 Volker Krause <vkrause@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef PARSERTEST_H +#define PARSERTEST_H + +class ParserTest +{ + public: + void testSingleAttachment(); + void testTwoAttachments(); + void testMAPIAttachments(); +}; + + +#endif diff --git a/ktnef/tests/two-files.tnef b/ktnef/tests/two-files.tnef Binary files differnew file mode 100644 index 000000000..b33b64dc4 --- /dev/null +++ b/ktnef/tests/two-files.tnef |