kmail Library API Documentation

kmcomposewin.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmcomposewin.cpp
00003 // Author: Markus Wuebben <markus.wuebben@kde.org>
00004 // This code is published under the GPL.
00005 
00006 // keep this in sync with the define in configuredialog.h
00007 #define DEFAULT_EDITOR_STR "kate %f"
00008 
00009 //#define STRICT_RULES_OF_GERMAN_GOVERNMENT_01
00010 
00011 #undef GrayScale
00012 #undef Color
00013 #include <config.h>
00014 
00015 #include "kmcomposewin.h"
00016 
00017 #include "kmmainwin.h"
00018 #include "kmreaderwin.h"
00019 #include "kmreadermainwin.h"
00020 #include "kmsender.h"
00021 #include "identitymanager.h"
00022 #include "identitycombo.h"
00023 #include "kmidentity.h"
00024 #include "kfileio.h"
00025 #include "kmmsgpartdlg.h"
00026 #include <kpgpblock.h>
00027 #include "kmaddrbook.h"
00028 #include "kmmsgdict.h"
00029 #include "kmfolderimap.h"
00030 #include "kmfoldermgr.h"
00031 #include "kmfoldercombobox.h"
00032 #include "kmtransport.h"
00033 #include "kmcommands.h"
00034 #include "kcursorsaver.h"
00035 #include "kmkernel.h"
00036 #include "attachmentlistview.h"
00037 using KMail::AttachmentListView;
00038 #include "dictionarycombobox.h"
00039 using KMail::DictionaryComboBox;
00040 #include "addressesdialog.h"
00041 using KPIM::AddressesDialog;
00042 #include <maillistdrag.h>
00043 using KPIM::MailListDrag;
00044 #include "recentaddresses.h"
00045 using KRecentAddress::RecentAddresses;
00046 
00047 #include <cryptplugwrapperlist.h>
00048 
00049 #include "klistboxdialog.h"
00050 
00051 #include <kcharsets.h>
00052 #include <kcompletionbox.h>
00053 #include <kcursor.h>
00054 #include <kcombobox.h>
00055 #include <kstdaccel.h>
00056 #include <kpopupmenu.h>
00057 #include <kedittoolbar.h>
00058 #include <kkeydialog.h>
00059 #include <kdebug.h>
00060 #include <kfiledialog.h>
00061 #include <kwin.h>
00062 #include <kinputdialog.h>
00063 #include <kmessagebox.h>
00064 #include <kurldrag.h>
00065 #include <kio/scheduler.h>
00066 #include <ktempfile.h>
00067 #include <klocale.h>
00068 #include <kapplication.h>
00069 #include <kstatusbar.h>
00070 #include <kaction.h>
00071 #include <kdirwatch.h>
00072 #include <kstdguiitem.h>
00073 #include <kiconloader.h>
00074 
00075 #include <kspell.h>
00076 #include <kspelldlg.h>
00077 #include <spellingfilter.h>
00078 #include <ksyntaxhighlighter.h>
00079 
00080 #include <qtabdialog.h>
00081 #include <qregexp.h>
00082 #include <qbuffer.h>
00083 #include <qtooltip.h>
00084 #include <qtextcodec.h>
00085 #include <qheader.h>
00086 #include <qpopupmenu.h>
00087 
00088 #include <mimelib/mimepp.h>
00089 #include <sys/stat.h>
00090 #include <sys/types.h>
00091 #include <stdlib.h>
00092 #include <unistd.h>
00093 #include <errno.h>
00094 #include <fcntl.h>
00095 #include <assert.h>
00096 
00097 #include "kmcomposewin.moc"
00098 
00099 //-----------------------------------------------------------------------------
00100 KMComposeWin::KMComposeWin( KMMessage *aMsg, uint id  )
00101   : MailComposerIface(), KMTopLevelWidget("kmail-composer#"),
00102     mMsg( 0 ),
00103     mAutoRequestMDN( false ),
00104     mId( id ), mNeverSign( false ), mNeverEncrypt( false )
00105 {
00106   if (kmkernel->xmlGuiInstance())
00107     setInstance( kmkernel->xmlGuiInstance() );
00108   mMainWidget = new QWidget(this);
00109 
00110   // Initialize the plugin selection according to 'active' flag that
00111   // was set via the global configuration dialog.
00112   mSelectedCryptPlug = kmkernel->cryptPlugList() ? kmkernel->cryptPlugList()->active() : 0;
00113 
00114   mIdentity = new IdentityCombo(mMainWidget);
00115   mDictionaryCombo = new DictionaryComboBox( mMainWidget );
00116   mFcc = new KMFolderComboBox(mMainWidget);
00117   mFcc->showOutboxFolder( FALSE );
00118   mTransport = new QComboBox(true, mMainWidget);
00119   mEdtFrom = new KMLineEdit(this,false,mMainWidget);
00120   mEdtReplyTo = new KMLineEdit(this,false,mMainWidget);
00121   mEdtTo = new KMLineEdit(this,true,mMainWidget);
00122   mEdtCc = new KMLineEdit(this,true,mMainWidget);
00123   mEdtBcc = new KMLineEdit(this,true,mMainWidget);
00124   mEdtSubject = new KMLineEditSpell(this,false,mMainWidget, "subjectLine");
00125   mLblIdentity = new QLabel(mMainWidget);
00126   mDictionaryLabel = new QLabel( mMainWidget );
00127   mLblFcc = new QLabel(mMainWidget);
00128   mLblTransport = new QLabel(mMainWidget);
00129   mLblFrom = new QLabel(mMainWidget);
00130   mLblReplyTo = new QLabel(mMainWidget);
00131   mLblTo = new QLabel(mMainWidget);
00132   mLblCc = new QLabel(mMainWidget);
00133   mLblBcc = new QLabel(mMainWidget);
00134   mLblSubject = new QLabel(mMainWidget);
00135   QString sticky = i18n("Sticky");
00136   mBtnIdentity = new QCheckBox(sticky,mMainWidget);
00137   mBtnFcc = new QCheckBox(sticky,mMainWidget);
00138   mBtnTransport = new QCheckBox(sticky,mMainWidget);
00139   mBtnTo = new QPushButton("...",mMainWidget);
00140   mBtnCc = new QPushButton("...",mMainWidget);
00141   mBtnBcc = new QPushButton("...",mMainWidget);
00142   //mBtnFrom = new QPushButton("...",mMainWidget);
00143   mBtnReplyTo = new QPushButton("...",mMainWidget);
00144 
00145   //setWFlags( WType_TopLevel | WStyle_Dialog );
00146   mDone = false;
00147   mGrid = 0;
00148   mAtmListView = 0;
00149   mAtmList.setAutoDelete(TRUE);
00150   mAtmTempList.setAutoDelete(TRUE);
00151   mAtmModified = FALSE;
00152   mAutoDeleteMsg = FALSE;
00153   mFolder = 0;
00154   mAutoCharset = TRUE;
00155   mFixedFontAction = 0;
00156   mEditor = new KMEdit( mMainWidget, this, mDictionaryCombo->spellConfig() );
00157   mEditor->setTextFormat(Qt::PlainText);
00158   mEditor->setAcceptDrops( true );
00159 
00160   mDisableBreaking = false;
00161   QString tip = i18n("Select email address(es)");
00162   QToolTip::add( mBtnTo, tip );
00163   QToolTip::add( mBtnCc, tip );
00164   QToolTip::add( mBtnReplyTo, tip );
00165 
00166   mSpellCheckInProgress=FALSE;
00167 
00168   setCaption( i18n("Composer") );
00169   setMinimumSize(200,200);
00170 
00171   mBtnIdentity->setFocusPolicy(QWidget::NoFocus);
00172   mBtnFcc->setFocusPolicy(QWidget::NoFocus);
00173   mBtnTransport->setFocusPolicy(QWidget::NoFocus);
00174   mBtnTo->setFocusPolicy(QWidget::NoFocus);
00175   mBtnCc->setFocusPolicy(QWidget::NoFocus);
00176   mBtnBcc->setFocusPolicy(QWidget::NoFocus);
00177   //mBtnFrom->setFocusPolicy(QWidget::NoFocus);
00178   mBtnReplyTo->setFocusPolicy(QWidget::NoFocus);
00179 
00180   mAtmListView = new AttachmentListView( this, mMainWidget,
00181                                          "attachment list view" );
00182   mAtmListView->setSelectionMode( QListView::Extended );
00183   mAtmListView->setFocusPolicy( QWidget::NoFocus );
00184   mAtmListView->addColumn( i18n("Name"), 200 );
00185   mAtmListView->addColumn( i18n("Size"), 80 );
00186   mAtmListView->addColumn( i18n("Encoding"), 120 );
00187   int atmColType = mAtmListView->addColumn( i18n("Type"), 120 );
00188   // Stretch "Type".
00189   mAtmListView->header()->setStretchEnabled( true, atmColType );
00190   mAtmEncryptColWidth = 80;
00191   mAtmSignColWidth = 80;
00192   mAtmColEncrypt = mAtmListView->addColumn( i18n("Encrypt"),
00193                                             mAtmEncryptColWidth );
00194   mAtmColSign    = mAtmListView->addColumn( i18n("Sign"),
00195                                             mAtmSignColWidth );
00196   if( mSelectedCryptPlug ) {
00197     mAtmListView->setColumnWidth( mAtmColEncrypt, mAtmEncryptColWidth );
00198     mAtmListView->setColumnWidth( mAtmColSign,    mAtmSignColWidth );
00199   }
00200   else {
00201     mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
00202     mAtmListView->setColumnWidth( mAtmColSign,    0 );
00203   }
00204   mAtmListView->setAllColumnsShowFocus( true );
00205 
00206   connect( mAtmListView,
00207            SIGNAL( doubleClicked( QListViewItem* ) ),
00208            SLOT( slotAttachProperties() ) );
00209   connect( mAtmListView,
00210            SIGNAL( rightButtonPressed( QListViewItem*, const QPoint&, int ) ),
00211            SLOT( slotAttachPopupMenu( QListViewItem*, const QPoint&, int ) ) );
00212   connect( mAtmListView,
00213            SIGNAL( selectionChanged() ),
00214            SLOT( slotUpdateAttachActions() ) );
00215   mAttachMenu = 0;
00216 
00217   readConfig();
00218   setupStatusBar();
00219   setupEditor();
00220   setupActions();
00221   applyMainWindowSettings(KMKernel::config(), "Composer");
00222 
00223   connect(mEdtSubject,SIGNAL(textChanged(const QString&)),
00224           SLOT(slotUpdWinTitle(const QString&)));
00225   connect(mBtnTo,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00226   connect(mBtnCc,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00227   connect(mBtnBcc,SIGNAL(clicked()),SLOT(slotAddrBookTo()));
00228   connect(mBtnReplyTo,SIGNAL(clicked()),SLOT(slotAddrBookReplyTo()));
00229   //connect(mBtnFrom,SIGNAL(clicked()),SLOT(slotAddrBookFrom()));
00230   connect(mIdentity,SIGNAL(identityChanged(uint)),
00231           SLOT(slotIdentityChanged(uint)));
00232 
00233   connect(mEdtTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00234           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00235   connect(mEdtCc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00236           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00237   connect(mEdtBcc,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00238           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00239   connect(mEdtReplyTo,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00240           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00241   connect(mEdtFrom,SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
00242           SLOT(slotCompletionModeChanged(KGlobalSettings::Completion)));
00243         connect(kmkernel->folderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00244                                         SLOT(slotFolderRemoved(KMFolder*)));
00245         connect(kmkernel->imapFolderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00246                                         SLOT(slotFolderRemoved(KMFolder*)));
00247         connect(kmkernel->dimapFolderMgr(),SIGNAL(folderRemoved(KMFolder*)),
00248                                         SLOT(slotFolderRemoved(KMFolder*)));
00249   connect( kmkernel, SIGNAL( configChanged() ),
00250            this, SLOT( slotConfigChanged() ) );
00251 
00252   connect (mEditor, SIGNAL (spellcheck_done(int)),
00253     this, SLOT (slotSpellcheckDone (int)));
00254 
00255   mMainWidget->resize(480,510);
00256   setCentralWidget(mMainWidget);
00257   rethinkFields();
00258 
00259   if (mUseExtEditor) {
00260     mEditor->setUseExternalEditor(true);
00261     mEditor->setExternalEditorPath(mExtEditor);
00262   }
00263 
00264   mMsg = 0;
00265   mBccMsgList.setAutoDelete( false );
00266   if (aMsg)
00267     setMsg(aMsg);
00268 
00269   mEdtTo->setFocus();
00270   mErrorProcessingStructuringInfo =
00271     i18n("<qt><p>Structuring information returned by the Crypto plug-in "
00272          "could not be processed correctly; the plug-in might be damaged.</p>"
00273          "<p>Please contact your system administrator.</p></qt>");
00274   mErrorNoCryptPlugAndNoBuildIn =
00275     i18n("<p>No active Crypto Plug-In was found and the built-in OpenPGP code "
00276          "did not run successfully.</p>"
00277          "<p>You can do two things to change this:</p>"
00278          "<ul><li><em>either</em> activate a Plug-In using the "
00279          "Settings->Configure KMail->Plug-In dialog.</li>"
00280          "<li><em>or</em> specify traditional OpenPGP settings on the same dialog's "
00281          "Identity->Advanced tab.</li></ul>");
00282 
00283   if(getenv("KMAIL_DEBUG_COMPOSER_CRYPTO") != 0){
00284     QCString cE = getenv("KMAIL_DEBUG_COMPOSER_CRYPTO");
00285     mDebugComposerCrypto = cE == "1" || cE.upper() == "ON" || cE.upper() == "TRUE";
00286     kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = TRUE" << endl;
00287   }else{
00288     mDebugComposerCrypto = false;
00289     kdDebug(5006) << "KMAIL_DEBUG_COMPOSER_CRYPTO = FALSE" << endl;
00290   }
00291   mDone = true;
00292 }
00293 
00294 
00295 //-----------------------------------------------------------------------------
00296 KMComposeWin::~KMComposeWin()
00297 {
00298   writeConfig();
00299   if (mFolder && mMsg)
00300   {
00301     mAutoDeleteMsg = FALSE;
00302     mFolder->addMsg(mMsg);
00303     // Ensure that the message is correctly and fully parsed
00304     mFolder->unGetMsg( mFolder->count() - 1 );
00305     emit messageQueuedOrDrafted();
00306   }
00307   if (mAutoDeleteMsg) {
00308     delete mMsg;
00309     mMsg = 0;
00310   }
00311   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.begin();
00312   while ( it != mMapAtmLoadData.end() )
00313   {
00314     KIO::Job *job = it.key();
00315     mMapAtmLoadData.remove( it );
00316     job->kill();
00317     it = mMapAtmLoadData.begin();
00318   }
00319   mBccMsgList.clear();
00320 }
00321 
00322 //-----------------------------------------------------------------------------
00323 void KMComposeWin::send(int how)
00324 {
00325   switch (how) {
00326     case 1:
00327       slotSendNow();
00328       break;
00329     default:
00330     case 0:
00331       // TODO: find out, what the default send method is and send it this way
00332     case 2:
00333       slotSendLater();
00334       break;
00335   }
00336 }
00337 
00338 //-----------------------------------------------------------------------------
00339 void KMComposeWin::addAttachment(KURL url,QString /*comment*/)
00340 {
00341   addAttach(url);
00342 }
00343 
00344 //-----------------------------------------------------------------------------
00345 void KMComposeWin::addAttachment(const QString &name,
00346                                  const QCString &/*cte*/,
00347                                  const QByteArray &data,
00348                                  const QCString &type,
00349                                  const QCString &subType,
00350                                  const QCString &paramAttr,
00351                                  const QString &paramValue,
00352                                  const QCString &contDisp)
00353 {
00354   if (!data.isEmpty()) {
00355     KMMessagePart *msgPart = new KMMessagePart;
00356     msgPart->setName(name);
00357     QValueList<int> dummy;
00358     msgPart->setBodyAndGuessCte(data, dummy,
00359                                 kmkernel->msgSender()->sendQuotedPrintable());
00360     msgPart->setTypeStr(type);
00361     msgPart->setSubtypeStr(subType);
00362     msgPart->setParameter(paramAttr,paramValue);
00363     msgPart->setContentDisposition(contDisp);
00364     addAttach(msgPart);
00365   }
00366 }
00367 
00368 //-----------------------------------------------------------------------------
00369 void KMComposeWin::setBody(QString body)
00370 {
00371   mEditor->setText(body);
00372 }
00373 
00374 //-----------------------------------------------------------------------------
00375 bool KMComposeWin::event(QEvent *e)
00376 {
00377   if (e->type() == QEvent::ApplicationPaletteChange)
00378   {
00379      readColorConfig();
00380   }
00381   return KMTopLevelWidget::event(e);
00382 }
00383 
00384 
00385 //-----------------------------------------------------------------------------
00386 void KMComposeWin::readColorConfig(void)
00387 {
00388   KConfig *config = KMKernel::config();
00389   KConfigGroupSaver saver(config, "Reader");
00390   QColor c1=QColor(kapp->palette().active().text());
00391   QColor c4=QColor(kapp->palette().active().base());
00392 
00393   if (!config->readBoolEntry("defaultColors",TRUE)) {
00394     mForeColor = config->readColorEntry("ForegroundColor",&c1);
00395     mBackColor = config->readColorEntry("BackgroundColor",&c4);
00396   }
00397   else {
00398     mForeColor = c1;
00399     mBackColor = c4;
00400   }
00401 
00402   // Color setup
00403   mPalette = kapp->palette();
00404   QColorGroup cgrp  = mPalette.active();
00405   cgrp.setColor( QColorGroup::Base, mBackColor);
00406   cgrp.setColor( QColorGroup::Text, mForeColor);
00407   mPalette.setDisabled(cgrp);
00408   mPalette.setActive(cgrp);
00409   mPalette.setInactive(cgrp);
00410 
00411   mEdtTo->setPalette(mPalette);
00412   mEdtFrom->setPalette(mPalette);
00413   mEdtCc->setPalette(mPalette);
00414   mEdtSubject->setPalette(mPalette);
00415   mEdtReplyTo->setPalette(mPalette);
00416   mEdtBcc->setPalette(mPalette);
00417   mTransport->setPalette(mPalette);
00418   mEditor->setPalette(mPalette);
00419   mFcc->setPalette(mPalette);
00420 }
00421 
00422 //-----------------------------------------------------------------------------
00423 void KMComposeWin::readConfig(void)
00424 {
00425   KConfig *config = KMKernel::config();
00426   QCString str;
00427   //  int w, h,
00428   int maxTransportItems;
00429 
00430   KConfigGroupSaver saver(config, "Composer");
00431 
00432   mDefCharset = KMMessage::defaultCharset();
00433   mForceReplyCharset = config->readBoolEntry("force-reply-charset", false );
00434   mAutoSign = config->readEntry("signature","auto") == "auto";
00435   mShowHeaders = config->readNumEntry("headers", HDR_STANDARD);
00436   mWordWrap = config->readBoolEntry("word-wrap", true);
00437   mLineBreak = config->readNumEntry("break-at", 78);
00438   mBtnIdentity->setChecked(config->readBoolEntry("sticky-identity", false));
00439   if (mBtnIdentity->isChecked())
00440     mId = config->readUnsignedNumEntry("previous-identity", mId );
00441   mBtnFcc->setChecked(config->readBoolEntry("sticky-fcc", false));
00442   QString previousFcc = kmkernel->sentFolder()->idString();
00443   if (mBtnFcc->isChecked())
00444     previousFcc = config->readEntry("previous-fcc", previousFcc );
00445   mBtnTransport->setChecked(config->readBoolEntry("sticky-transport", false));
00446   mTransportHistory = config->readListEntry("transport-history");
00447   QString currentTransport = config->readEntry("current-transport");
00448   maxTransportItems = config->readNumEntry("max-transport-items",10);
00449 
00450   if ((mLineBreak == 0) || (mLineBreak > 78))
00451     mLineBreak = 78;
00452   if (mLineBreak < 30)
00453     mLineBreak = 30;
00454   mAutoPgpSign = config->readBoolEntry("pgp-auto-sign", false);
00455   mAutoPgpEncrypt = config->readBoolEntry("pgp-auto-encrypt", false);
00456   mConfirmSend = config->readBoolEntry("confirm-before-send", false);
00457   mAutoRequestMDN = config->readBoolEntry("request-mdn", false);
00458 
00459   int mode = config->readNumEntry("Completion Mode",
00460                                   KGlobalSettings::completionMode() );
00461   mEdtFrom->setCompletionMode( (KGlobalSettings::Completion) mode );
00462   mEdtReplyTo->setCompletionMode( (KGlobalSettings::Completion) mode );
00463   mEdtTo->setCompletionMode( (KGlobalSettings::Completion) mode );
00464   mEdtCc->setCompletionMode( (KGlobalSettings::Completion) mode );
00465   mEdtBcc->setCompletionMode( (KGlobalSettings::Completion) mode );
00466 
00467   readColorConfig();
00468 
00469   { // area for config group "General"
00470     KConfigGroupSaver saver(config, "General");
00471     mExtEditor = config->readPathEntry("external-editor", DEFAULT_EDITOR_STR);
00472     mUseExtEditor = config->readBoolEntry("use-external-editor", FALSE);
00473 
00474     int headerCount = config->readNumEntry("mime-header-count", 0);
00475     mCustHeaders.clear();
00476     mCustHeaders.setAutoDelete(true);
00477     for (int i = 0; i < headerCount; i++) {
00478       QString thisGroup;
00479       _StringPair *thisItem = new _StringPair;
00480       thisGroup.sprintf("Mime #%d", i);
00481       KConfigGroupSaver saver(config, thisGroup);
00482       thisItem->name = config->readEntry("name");
00483       if ((thisItem->name).length() > 0) {
00484         thisItem->value = config->readEntry("value");
00485         mCustHeaders.append(thisItem);
00486       } else {
00487         delete thisItem;
00488         thisItem = 0;
00489       }
00490     }
00491   }
00492 
00493   { // area fo config group "Fonts"
00494     KConfigGroupSaver saver(config, "Fonts");
00495     mBodyFont = KGlobalSettings::generalFont();
00496     mFixedFont = KGlobalSettings::fixedFont();
00497     if (!config->readBoolEntry("defaultFonts",TRUE)) {
00498       mBodyFont = config->readFontEntry("composer-font", &mBodyFont);
00499       mFixedFont = config->readFontEntry("fixed-font", &mFixedFont);
00500     }
00501     slotUpdateFont();
00502     mEdtFrom->setFont(mBodyFont);
00503     mEdtReplyTo->setFont(mBodyFont);
00504     mEdtTo->setFont(mBodyFont);
00505     mEdtCc->setFont(mBodyFont);
00506     mEdtBcc->setFont(mBodyFont);
00507     mEdtSubject->setFont(mBodyFont);
00508   }
00509 
00510   { // area fo config group "Fonts"
00511     KConfigGroupSaver saver(config, "Geometry");
00512     QSize defaultSize(480,510);
00513     QSize siz = config->readSizeEntry("composer", &defaultSize);
00514     if (siz.width() < 200) siz.setWidth(200);
00515     if (siz.height() < 200) siz.setHeight(200);
00516     resize(siz);
00517   }
00518 
00519   mIdentity->setCurrentIdentity( mId );
00520 
00521   kdDebug(5006) << "KMComposeWin::readConfig. " << mIdentity->currentIdentityName() << endl;
00522   const KMIdentity & ident =
00523     kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() );
00524 
00525   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
00526 
00527   mTransport->clear();
00528   mTransport->insertStringList( KMTransportInfo::availableTransports() );
00529   while (mTransportHistory.count() > (uint)maxTransportItems)
00530     mTransportHistory.remove( mTransportHistory.last() );
00531   mTransport->insertStringList( mTransportHistory );
00532   if (mBtnTransport->isChecked() && !currentTransport.isEmpty())
00533   {
00534     for (int i = 0; i < mTransport->count(); i++)
00535       if (mTransport->text(i) == currentTransport)
00536         mTransport->setCurrentItem(i);
00537     mTransport->setEditText( currentTransport );
00538   }
00539 
00540   if ( !mBtnFcc->isChecked() )
00541   {
00542       kdDebug(5006) << "KMComposeWin::readConfig: identity.fcc()='"
00543                     << ident.fcc() << "'" << endl;
00544       if ( ident.fcc().isEmpty() )
00545         previousFcc = kmkernel->sentFolder()->idString();
00546       else
00547         previousFcc = ident.fcc();
00548       kdDebug(5006) << "KMComposeWin::readConfig: previousFcc="
00549                 << previousFcc <<  endl;
00550   }
00551 
00552   setFcc( previousFcc );
00553 }
00554 
00555 //-----------------------------------------------------------------------------
00556 void KMComposeWin::writeConfig(void)
00557 {
00558   KConfig *config = KMKernel::config();
00559   QString str;
00560 
00561   {
00562     KConfigGroupSaver saver(config, "Composer");
00563     config->writeEntry("signature", mAutoSign?"auto":"manual");
00564     config->writeEntry("headers", mShowHeaders);
00565     config->writeEntry("sticky-transport", mBtnTransport->isChecked());
00566     config->writeEntry("sticky-identity", mBtnIdentity->isChecked());
00567     config->writeEntry("sticky-fcc", mBtnFcc->isChecked());
00568     config->writeEntry("previous-identity", mIdentity->currentIdentity() );
00569     config->writeEntry("current-transport", mTransport->currentText());
00570     config->writeEntry("previous-fcc", mFcc->getFolder()->idString() );
00571     config->writeEntry( "autoSpellChecking",
00572                         mAutoSpellCheckingAction->isChecked() );
00573     mTransportHistory.remove(mTransport->currentText());
00574     if (KMTransportInfo::availableTransports().findIndex(mTransport
00575       ->currentText()) == -1)
00576         mTransportHistory.prepend(mTransport->currentText());
00577     config->writeEntry("transport-history", mTransportHistory );
00578   }
00579 
00580   {
00581     KConfigGroupSaver saver(config, "Geometry");
00582     config->writeEntry("composer", size());
00583 
00584     saveMainWindowSettings(config, "Composer");
00585     config->sync();
00586   }
00587 }
00588 
00589 
00590 //-----------------------------------------------------------------------------
00591 void KMComposeWin::deadLetter(void)
00592 {
00593   if (!mMsg) return;
00594 
00595   // This method is called when KMail crashed, so we better use as
00596   // basic functions as possible here.
00597   // temporarily disable signing/encryption
00598   bool bSaveNeverSign    = mNeverSign;    mNeverSign    = true;
00599   bool bSaveNeverEncrypt = mNeverEncrypt; mNeverEncrypt = true;
00600   applyChanges( true );
00601   mNeverSign    = bSaveNeverSign;
00602   mNeverEncrypt = bSaveNeverEncrypt;
00603   QCString msgStr = mMsg->asString();
00604   QCString fname = getenv("HOME");
00605   fname += "/dead.letter.tmp";
00606   // Security: the file is created in the user's home directory, which
00607   // might be readable by other users. So the file only gets read/write
00608   // permissions for the user himself. Note that we create the file with
00609   // correct permissions, we do not set them after creating the file!
00610   // (dnaber, 2000-02-27):
00611   int fd = open(fname, O_CREAT|O_APPEND|O_WRONLY, S_IWRITE|S_IREAD);
00612   if (fd != -1)
00613   {
00614     QCString startStr = "From " + mMsg->fromEmail() + " " + mMsg->dateShortStr() + "\n";
00615     ::write(fd, startStr, startStr.length());
00616     ::write(fd, msgStr, msgStr.length());
00617     ::write(fd, "\n", 1);
00618     ::close(fd);
00619     fprintf(stderr,"appending message to ~/dead.letter.tmp\n");
00620   } else {
00621     perror("cannot open ~/dead.letter.tmp for saving the current message");
00622     kmkernel->emergencyExit( i18n("cannot open ~/dead.letter.tmp for saving the current message: ")  +
00623                               QString::fromLocal8Bit(strerror(errno)));
00624   }
00625 }
00626 
00627 
00628 
00629 //-----------------------------------------------------------------------------
00630 void KMComposeWin::slotView(void)
00631 {
00632   if (!mDone)
00633     return; // otherwise called from rethinkFields during the construction
00634             // which is not the intended behavior
00635   int id;
00636 
00637   //This sucks awfully, but no, I cannot get an activated(int id) from
00638   // actionContainer()
00639   if (!sender()->isA("KToggleAction"))
00640     return;
00641   KToggleAction *act = (KToggleAction *) sender();
00642 
00643   if (act == mAllFieldsAction)
00644     id = 0;
00645   else if (act == mIdentityAction)
00646     id = HDR_IDENTITY;
00647   else if (act == mTransportAction)
00648     id = HDR_TRANSPORT;
00649   else if (act == mFromAction)
00650     id = HDR_FROM;
00651   else if (act == mReplyToAction)
00652     id = HDR_REPLY_TO;
00653   else if (act == mToAction)
00654     id = HDR_TO;
00655   else if (act == mCcAction)
00656     id = HDR_CC;
00657   else  if (act == mBccAction)
00658     id = HDR_BCC;
00659   else if (act == mSubjectAction)
00660     id = HDR_SUBJECT;
00661   else if (act == mFccAction)
00662     id = HDR_FCC;
00663   else if ( act == mDictionaryAction )
00664     id = HDR_DICTIONARY;
00665   else
00666    {
00667      id = 0;
00668      kdDebug(5006) << "Something is wrong (Oh, yeah?)" << endl;
00669      return;
00670    }
00671 
00672   // sanders There's a bug here this logic doesn't work if no
00673   // fields are shown and then show all fields is selected.
00674   // Instead of all fields being shown none are.
00675   if (!act->isChecked())
00676   {
00677     // hide header
00678     if (id > 0) mShowHeaders = mShowHeaders & ~id;
00679     else mShowHeaders = abs(mShowHeaders);
00680   }
00681   else
00682   {
00683     // show header
00684     if (id > 0) mShowHeaders |= id;
00685     else mShowHeaders = -abs(mShowHeaders);
00686   }
00687   rethinkFields(true);
00688 
00689 }
00690 
00691 void KMComposeWin::rethinkFields(bool fromSlot)
00692 {
00693   //This sucks even more but again no ids. sorry (sven)
00694   int mask, row, numRows;
00695   long showHeaders;
00696 
00697   if (mShowHeaders < 0)
00698     showHeaders = HDR_ALL;
00699   else
00700     showHeaders = mShowHeaders;
00701 
00702   for (mask=1,mNumHeaders=0; mask<=showHeaders; mask<<=1)
00703     if ((showHeaders&mask) != 0) mNumHeaders++;
00704 
00705   numRows = mNumHeaders + 2;
00706 
00707   delete mGrid;
00708   mGrid = new QGridLayout(mMainWidget, numRows, 3, 4, 4);
00709   mGrid->setColStretch(0, 1);
00710   mGrid->setColStretch(1, 100);
00711   mGrid->setColStretch(2, 1);
00712   mGrid->setRowStretch(mNumHeaders, 100);
00713 
00714   mEdtList.clear();
00715   row = 0;
00716   kdDebug(5006) << "KMComposeWin::rethinkFields" << endl;
00717   if (!fromSlot) mAllFieldsAction->setChecked(showHeaders==HDR_ALL);
00718 
00719   if (!fromSlot) mIdentityAction->setChecked(abs(mShowHeaders)&HDR_IDENTITY);
00720   rethinkHeaderLine(showHeaders,HDR_IDENTITY, row, i18n("&Identity:"),
00721                     mLblIdentity, mIdentity, mBtnIdentity);
00722   if (!fromSlot) mDictionaryAction->setChecked(abs(mShowHeaders)&HDR_DICTIONARY);
00723   rethinkHeaderLine(showHeaders,HDR_DICTIONARY, row, i18n("&Dictionary:"),
00724                     mDictionaryLabel, mDictionaryCombo, 0 );
00725   if (!fromSlot) mFccAction->setChecked(abs(mShowHeaders)&HDR_FCC);
00726   rethinkHeaderLine(showHeaders,HDR_FCC, row, i18n("Se&nt-Mail folder:"),
00727                     mLblFcc, mFcc, mBtnFcc);
00728   if (!fromSlot) mTransportAction->setChecked(abs(mShowHeaders)&HDR_TRANSPORT);
00729   rethinkHeaderLine(showHeaders,HDR_TRANSPORT, row, i18n("Mai&l transport:"),
00730                     mLblTransport, mTransport, mBtnTransport);
00731   if (!fromSlot) mFromAction->setChecked(abs(mShowHeaders)&HDR_FROM);
00732   rethinkHeaderLine(showHeaders,HDR_FROM, row, i18n("&From:"),
00733                     mLblFrom, mEdtFrom /*, mBtnFrom */ );
00734   if (!fromSlot) mReplyToAction->setChecked(abs(mShowHeaders)&HDR_REPLY_TO);
00735   rethinkHeaderLine(showHeaders,HDR_REPLY_TO,row,i18n("&Reply to:"),
00736                     mLblReplyTo, mEdtReplyTo, mBtnReplyTo);
00737   if (!fromSlot) mToAction->setChecked(abs(mShowHeaders)&HDR_TO);
00738   rethinkHeaderLine(showHeaders,HDR_TO, row, i18n("To:"),
00739                     mLblTo, mEdtTo, mBtnTo);
00740   if (!fromSlot) mCcAction->setChecked(abs(mShowHeaders)&HDR_CC);
00741   rethinkHeaderLine(showHeaders,HDR_CC, row, i18n("&CC:"),
00742                     mLblCc, mEdtCc, mBtnCc);
00743   if (!fromSlot) mBccAction->setChecked(abs(mShowHeaders)&HDR_BCC);
00744   rethinkHeaderLine(showHeaders,HDR_BCC, row, i18n("&BCC:"),
00745                     mLblBcc, mEdtBcc, mBtnBcc);
00746   if (!fromSlot) mSubjectAction->setChecked(abs(mShowHeaders)&HDR_SUBJECT);
00747   rethinkHeaderLine(showHeaders,HDR_SUBJECT, row, i18n("S&ubject:"),
00748                     mLblSubject, mEdtSubject);
00749   assert(row<=mNumHeaders);
00750 
00751   mGrid->addMultiCellWidget(mEditor, row, mNumHeaders, 0, 2);
00752   mGrid->addMultiCellWidget(mAtmListView, mNumHeaders+1, mNumHeaders+1, 0, 2);
00753 
00754   if( !mAtmList.isEmpty() )
00755     mAtmListView->show();
00756   else
00757     mAtmListView->hide();
00758   resize(this->size());
00759   repaint();
00760 
00761   mGrid->activate();
00762 
00763   slotUpdateAttachActions();
00764   mIdentityAction->setEnabled(!mAllFieldsAction->isChecked());
00765   mDictionaryAction->setEnabled( !mAllFieldsAction->isChecked() );
00766   mTransportAction->setEnabled(!mAllFieldsAction->isChecked());
00767   mFromAction->setEnabled(!mAllFieldsAction->isChecked());
00768   mReplyToAction->setEnabled(!mAllFieldsAction->isChecked());
00769   mToAction->setEnabled(!mAllFieldsAction->isChecked());
00770   mCcAction->setEnabled(!mAllFieldsAction->isChecked());
00771   mBccAction->setEnabled(!mAllFieldsAction->isChecked());
00772   mFccAction->setEnabled(!mAllFieldsAction->isChecked());
00773   mSubjectAction->setEnabled(!mAllFieldsAction->isChecked());
00774 }
00775 
00776 
00777 //-----------------------------------------------------------------------------
00778 void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
00779                                      const QString &aLabelStr, QLabel* aLbl,
00780                                      QLineEdit* aEdt, QPushButton* aBtn)
00781 {
00782   if (aValue & aMask)
00783   {
00784     aLbl->setText(aLabelStr);
00785     aLbl->adjustSize();
00786     aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
00787     aLbl->setMinimumSize(aLbl->size());
00788     aLbl->show();
00789     aLbl->setBuddy(aEdt);
00790     mGrid->addWidget(aLbl, aRow, 0);
00791 
00792     aEdt->setBackgroundColor( mBackColor );
00793     aEdt->show();
00794     aEdt->setMinimumSize(100, aLbl->height()+2);
00795     mEdtList.append(aEdt);
00796 
00797     mGrid->addWidget(aEdt, aRow, 1);
00798     if (aBtn)
00799     {
00800       mGrid->addWidget(aBtn, aRow, 2);
00801       aBtn->setFixedSize(aBtn->sizeHint().width(), aLbl->height());
00802       aBtn->show();
00803     }
00804     aRow++;
00805   }
00806   else
00807   {
00808     aLbl->hide();
00809     aEdt->hide();
00810     if (aBtn) aBtn->hide();
00811   }
00812 }
00813 
00814 //-----------------------------------------------------------------------------
00815 void KMComposeWin::rethinkHeaderLine(int aValue, int aMask, int& aRow,
00816                                      const QString &aLabelStr, QLabel* aLbl,
00817                                      QComboBox* aCbx, QCheckBox* aChk)
00818 {
00819   if (aValue & aMask)
00820   {
00821     aLbl->setText(aLabelStr);
00822     aLbl->adjustSize();
00823     aLbl->resize((int)aLbl->sizeHint().width(),aLbl->sizeHint().height() + 6);
00824     aLbl->setMinimumSize(aLbl->size());
00825     aLbl->show();
00826     aLbl->setBuddy(aCbx);
00827     mGrid->addWidget(aLbl, aRow, 0);
00828 
00829     //    aCbx->setBackgroundColor( mBackColor );
00830     aCbx->show();
00831     aCbx->setMinimumSize(100, aLbl->height()+2);
00832 
00833     mGrid->addWidget(aCbx, aRow, 1);
00834     if ( aChk ) {
00835       mGrid->addWidget(aChk, aRow, 2);
00836       aChk->setFixedSize(aChk->sizeHint().width(), aLbl->height());
00837       aChk->show();
00838     }
00839     aRow++;
00840   }
00841   else
00842   {
00843     aLbl->hide();
00844     aCbx->hide();
00845     if ( aChk )
00846       aChk->hide();
00847   }
00848 }
00849 
00850 //-----------------------------------------------------------------------------
00851 void KMComposeWin::setupActions(void)
00852 {
00853   if (kmkernel->msgSender()->sendImmediate()) //default == send now?
00854   {
00855     //default = send now, alternative = queue
00856     (void) new KAction (i18n("&Send"), "mail_send", CTRL+Key_Return,
00857                         this, SLOT(slotSendNow()), actionCollection(),
00858                         "send_default");
00859     (void) new KAction (i18n("&Queue"), "queue", 0,
00860                         this, SLOT(slotSendLater()),
00861                         actionCollection(), "send_alternative");
00862   }
00863   else //no, default = send later
00864   {
00865     //default = queue, alternative = send now
00866     (void) new KAction (i18n("&Queue"), "queue",
00867                         CTRL+Key_Return,
00868                         this, SLOT(slotSendLater()), actionCollection(),
00869                         "send_default");
00870     (void) new KAction (i18n("&Send Now"), "mail_send", 0,
00871                         this, SLOT(slotSendNow()),
00872                         actionCollection(), "send_alternative");
00873   }
00874 
00875   (void) new KAction (i18n("Save in &Drafts Folder"), "filesave", 0,
00876                       this, SLOT(slotSaveDraft()),
00877                       actionCollection(), "save_in_drafts");
00878   (void) new KAction (i18n("&Insert File..."), "fileopen", 0,
00879                       this,  SLOT(slotInsertFile()),
00880                       actionCollection(), "insert_file");
00881   (void) new KAction (i18n("&Address Book"), "contents",0,
00882                       this, SLOT(slotAddrBook()),
00883                       actionCollection(), "addressbook");
00884   (void) new KAction (i18n("&New Composer"), "mail_new",
00885                       KStdAccel::shortcut(KStdAccel::New),
00886                       this, SLOT(slotNewComposer()),
00887                       actionCollection(), "new_composer");
00888   (void) new KAction (i18n("New Main &Window"), "window_new", 0,
00889                       this, SLOT(slotNewMailReader()),
00890                       actionCollection(), "open_mailreader");
00891 
00892 
00893   //KStdAction::save(this, SLOT(), actionCollection(), "save_message");
00894   KStdAction::print (this, SLOT(slotPrint()), actionCollection());
00895   KStdAction::close (this, SLOT(slotClose()), actionCollection());
00896 
00897   KStdAction::undo (this, SLOT(slotUndo()), actionCollection());
00898   KStdAction::redo (this, SLOT(slotRedo()), actionCollection());
00899   KStdAction::cut (this, SLOT(slotCut()), actionCollection());
00900   KStdAction::copy (this, SLOT(slotCopy()), actionCollection());
00901   KStdAction::pasteText (this, SLOT(slotPaste()), actionCollection());
00902   KStdAction::selectAll (this, SLOT(slotMarkAll()), actionCollection());
00903 
00904   KStdAction::find (this, SLOT(slotFind()), actionCollection());
00905   KStdAction::replace (this, SLOT(slotReplace()), actionCollection());
00906   KStdAction::spelling (this, SLOT(slotSpellcheck()), actionCollection(), "spellcheck");
00907 
00908   (void) new KAction (i18n("Pa&ste as Quotation"),0,this,SLOT( slotPasteAsQuotation()),
00909                       actionCollection(), "paste_quoted");
00910 
00911   (void) new KAction(i18n("Add &Quote Characters"), 0, this,
00912               SLOT(slotAddQuotes()), actionCollection(), "tools_quote");
00913 
00914   (void) new KAction(i18n("Re&move Quote Characters"), 0, this,
00915               SLOT(slotRemoveQuotes()), actionCollection(), "tools_unquote");
00916 
00917 
00918   (void) new KAction (i18n("Cl&ean Spaces"), 0, this, SLOT(slotCleanSpace()),
00919                       actionCollection(), "clean_spaces");
00920 
00921   mFixedFontAction = new KToggleAction( i18n("Use Fi&xed Font"), 0, this,
00922                       SLOT(slotUpdateFont()), actionCollection(), "toggle_fixedfont" );
00923 
00924   //these are checkable!!!
00925   mUrgentAction = new KToggleAction (i18n("&Urgent"), 0,
00926                                     actionCollection(),
00927                                     "urgent");
00928   mRequestMDNAction = new KToggleAction ( i18n("&Request Disposition Notification"), 0,
00929                                          actionCollection(),
00930                                          "options_request_mdn");
00931   mRequestMDNAction->setChecked(mAutoRequestMDN);
00932   //----- Message-Encoding Submenu
00933   mEncodingAction = new KSelectAction( i18n( "Se&t Encoding" ), "charset",
00934                                       0, this, SLOT(slotSetCharset() ),
00935                                       actionCollection(), "charsets" );
00936   mWordWrapAction = new KToggleAction (i18n("&Wordwrap"), 0,
00937                       actionCollection(), "wordwrap");
00938   mWordWrapAction->setChecked(mWordWrap);
00939   connect(mWordWrapAction, SIGNAL(toggled(bool)), SLOT(slotWordWrapToggled(bool)));
00940 
00941   mAutoSpellCheckingAction =
00942     new KToggleAction( i18n( "&Automatic Spellchecking" ), "spellcheck", 0,
00943                        actionCollection(), "options_auto_spellchecking" );
00944   KConfigGroup composerConfig( KMKernel::config(), "Composer" );
00945   const bool spellChecking =
00946     composerConfig.readBoolEntry( "autoSpellChecking", true );
00947   mAutoSpellCheckingAction->setEnabled( !mUseExtEditor );
00948   mAutoSpellCheckingAction->setChecked( !mUseExtEditor && spellChecking );
00949   mEditor->slotAutoSpellCheckingToggled( !mUseExtEditor && spellChecking );
00950   connect( mAutoSpellCheckingAction, SIGNAL( toggled( bool ) ),
00951            mEditor, SLOT( slotAutoSpellCheckingToggled( bool ) ) );
00952 
00953   QStringList encodings = KMMsgBase::supportedEncodings(TRUE);
00954   encodings.prepend( i18n("Auto-Detect"));
00955   mEncodingAction->setItems( encodings );
00956   mEncodingAction->setCurrentItem( -1 );
00957 
00958   //these are checkable!!!
00959   mAllFieldsAction = new KToggleAction (i18n("&All Fields"), 0, this,
00960                                        SLOT(slotView()),
00961                                        actionCollection(), "show_all_fields");
00962   mIdentityAction = new KToggleAction (i18n("&Identity"), 0, this,
00963                                       SLOT(slotView()),
00964                                       actionCollection(), "show_identity");
00965   mDictionaryAction = new KToggleAction (i18n("&Dictionary"), 0, this,
00966                                          SLOT(slotView()),
00967                                          actionCollection(), "show_dictionary");
00968   mFccAction = new KToggleAction (i18n("Sent-Mail F&older"), 0, this,
00969                                  SLOT(slotView()),
00970                                  actionCollection(), "show_fcc");
00971   mTransportAction = new KToggleAction (i18n("&Mail Transport"), 0, this,
00972                                       SLOT(slotView()),
00973                                       actionCollection(), "show_transport");
00974   mFromAction = new KToggleAction (i18n("&From"), 0, this,
00975                                   SLOT(slotView()),
00976                                   actionCollection(), "show_from");
00977   mReplyToAction = new KToggleAction (i18n("&Reply To"), 0, this,
00978                                      SLOT(slotView()),
00979                                      actionCollection(), "show_reply_to");
00980   mToAction = new KToggleAction (i18n("&To"), 0, this,
00981                                 SLOT(slotView()),
00982                                 actionCollection(), "show_to");
00983   mCcAction = new KToggleAction (i18n("&CC"), 0, this,
00984                                 SLOT(slotView()),
00985                                 actionCollection(), "show_cc");
00986   mBccAction = new KToggleAction (i18n("&BCC"), 0, this,
00987                                  SLOT(slotView()),
00988                                  actionCollection(), "show_bcc");
00989   mSubjectAction = new KToggleAction (i18n("&Subject"), 0, this,
00990                                      SLOT(slotView()),
00991                                      actionCollection(), "show_subject");
00992   //end of checkable
00993 
00994   (void) new KAction (i18n("Append S&ignature"), 0, this,
00995                       SLOT(slotAppendSignature()),
00996                       actionCollection(), "append_signature");
00997   mAttachPK  = new KAction (i18n("Attach &Public Key..."), 0, this,
00998                            SLOT(slotInsertPublicKey()),
00999                            actionCollection(), "attach_public_key");
01000   mAttachMPK = new KAction (i18n("Attach &My Public Key"), 0, this,
01001                            SLOT(slotInsertMyPublicKey()),
01002                            actionCollection(), "attach_my_public_key");
01003   (void) new KAction (i18n("&Attach File..."), "attach",
01004                       0, this, SLOT(slotAttachFile()),
01005                       actionCollection(), "attach");
01006   mAttachRemoveAction = new KAction (i18n("&Remove Attachment"), 0, this,
01007                       SLOT(slotAttachRemove()),
01008                       actionCollection(), "remove");
01009   mAttachSaveAction = new KAction (i18n("&Save Attachment As..."), "filesave",0,
01010                       this, SLOT(slotAttachSave()),
01011                       actionCollection(), "attach_save");
01012   mAttachPropertiesAction = new KAction (i18n("Attachment Pr&operties..."), 0, this,
01013                       SLOT(slotAttachProperties()),
01014                       actionCollection(), "attach_properties");
01015 
01016   createStandardStatusBarAction();
01017   setStandardToolBarMenuEnabled(true);
01018 
01019   KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection());
01020   KStdAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection());
01021   KStdAction::preferences(kmkernel, SLOT(slotShowConfigurationDialog()), actionCollection());
01022 
01023   (void) new KAction (i18n("&Spellchecker..."), 0, this, SLOT(slotSpellcheckConfig()),
01024                       actionCollection(), "setup_spellchecker");
01025 
01026   mEncryptAction = new KToggleAction (i18n("&Encrypt Message"),
01027                                      "decrypted", 0,
01028                                      actionCollection(), "encrypt_message");
01029   mSignAction = new KToggleAction (i18n("&Sign Message"),
01030                                   "signature", 0,
01031                                   actionCollection(), "sign_message");
01032   // get PGP user id for the chosen identity
01033   const KMIdentity & ident =
01034     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
01035   QCString pgpUserId = ident.pgpIdentity();
01036   mLastIdentityHasOpenPgpKey = !pgpUserId.isEmpty();
01037 
01038   mLastEncryptActionState =
01039     ( mSelectedCryptPlug && EncryptEmail_EncryptAll == mSelectedCryptPlug->encryptEmail() );
01040   mLastSignActionState =
01041     (    (!mSelectedCryptPlug && mAutoPgpSign)
01042       || ( mSelectedCryptPlug && SignEmail_SignAll == mSelectedCryptPlug->signEmail()) );
01043 
01044   // "Attach public key" is only possible if the built-in OpenPGP support is
01045   // used
01046   mAttachPK->setEnabled(Kpgp::Module::getKpgp()->usePGP());
01047 
01048   // "Attach my public key" is only possible if the built-in OpenPGP support is
01049   // used and the user specified his key for the current identity
01050   mAttachMPK->setEnabled( Kpgp::Module::getKpgp()->usePGP() &&
01051                          !pgpUserId.isEmpty() );
01052 
01053   if ( !mSelectedCryptPlug && !Kpgp::Module::getKpgp()->usePGP() ) {
01054     mEncryptAction->setEnabled( false );
01055     setEncryption( false );
01056     mSignAction->setEnabled( false );
01057     setSigning( false );
01058   }
01059   else if ( !mSelectedCryptPlug && pgpUserId.isEmpty() ) {
01060     setEncryption( false );
01061     setSigning( false );
01062   }
01063   else {
01064     setEncryption( mLastEncryptActionState );
01065     setSigning( mLastSignActionState );
01066   }
01067 
01068   connect(mEncryptAction, SIGNAL(toggled(bool)),
01069                          SLOT(slotEncryptToggled( bool )));
01070   connect(mSignAction,    SIGNAL(toggled(bool)),
01071                          SLOT(slotSignToggled(    bool )));
01072 
01073   if( kmkernel->cryptPlugList() && kmkernel->cryptPlugList()->count() ){
01074     QStringList lst;
01075     lst << i18n( "inline OpenPGP (built-in)" );
01076     CryptPlugWrapper* current;
01077     QPtrListIterator<CryptPlugWrapper> it( *kmkernel->cryptPlugList() );
01078     int idx=0;
01079     int i=1;
01080     while( ( current = it.current() ) ) {
01081         lst << i18n("%1 (plugin)").arg(current->displayName());
01082         if( mSelectedCryptPlug == current )
01083           idx = i;
01084         ++it;
01085         ++i;
01086     }
01087 
01088     mCryptoModuleAction = new KSelectAction( i18n( "Select &Crypto Module" ),
01089                              0, // no accel
01090                              this, SLOT( slotSelectCryptoModule() ),
01091                              actionCollection(),
01092                              "options_select_crypto" );
01093     mCryptoModuleAction->setItems( lst );
01094     mCryptoModuleAction->setCurrentItem( idx );
01095   }
01096 
01097   createGUI("kmcomposerui.rc");
01098 }
01099 
01100 //-----------------------------------------------------------------------------
01101 void KMComposeWin::setupStatusBar(void)
01102 {
01103   statusBar()->insertItem("", 0, 1);
01104   statusBar()->setItemAlignment(0, AlignLeft | AlignVCenter);
01105 
01106   statusBar()->insertItem(i18n(" Column: %1 ").arg("     "),2,0,true);
01107   statusBar()->insertItem(i18n(" Line: %1 ").arg("     "),1,0,true);
01108 }
01109 
01110 
01111 //-----------------------------------------------------------------------------
01112 void KMComposeWin::updateCursorPosition()
01113 {
01114   int col,line;
01115   QString temp;
01116   line = mEditor->currentLine();
01117   col = mEditor->currentColumn();
01118   temp = i18n(" Line: %1 ").arg(line+1);
01119   statusBar()->changeItem(temp,1);
01120   temp = i18n(" Column: %1 ").arg(col+1);
01121   statusBar()->changeItem(temp,2);
01122 }
01123 
01124 
01125 //-----------------------------------------------------------------------------
01126 void KMComposeWin::setupEditor(void)
01127 {
01128   //QPopupMenu* menu;
01129   mEditor->setModified(FALSE);
01130   QFontMetrics fm(mBodyFont);
01131   mEditor->setTabStopWidth(fm.width(QChar(' ')) * 8);
01132   //mEditor->setFocusPolicy(QWidget::ClickFocus);
01133 
01134   if (mWordWrap)
01135   {
01136     mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth );
01137     mEditor->setWrapColumnOrWidth(mLineBreak);
01138   }
01139   else
01140   {
01141     mEditor->setWordWrap( QMultiLineEdit::NoWrap );
01142   }
01143 
01144   // Font setup
01145   slotUpdateFont();
01146 
01147   /* installRBPopup() is broken in kdelibs, we should wait for
01148           the new klibtextedit (dnaber, 2002-01-01)
01149   menu = new QPopupMenu(this);
01150   //#ifdef BROKEN
01151   menu->insertItem(i18n("Undo"),mEditor,
01152                    SLOT(undo()), KStdAccel::shortcut(KStdAccel::Undo));
01153   menu->insertItem(i18n("Redo"),mEditor,
01154                    SLOT(redo()), KStdAccel::shortcut(KStdAccel::Redo));
01155   menu->insertSeparator();
01156   //#endif //BROKEN
01157   menu->insertItem(i18n("Cut"), this, SLOT(slotCut()));
01158   menu->insertItem(i18n("Copy"), this, SLOT(slotCopy()));
01159   menu->insertItem(i18n("Paste"), this, SLOT(slotPaste()));
01160   menu->insertItem(i18n("Mark All"),this, SLOT(slotMarkAll()));
01161   menu->insertSeparator();
01162   menu->insertItem(i18n("Find..."), this, SLOT(slotFind()));
01163   menu->insertItem(i18n("Replace..."), this, SLOT(slotReplace()));
01164   menu->insertSeparator();
01165   menu->insertItem(i18n("Fixed Font Widths"), this, SLOT(slotUpdateFont()));
01166   mEditor->installRBPopup(menu);
01167   */
01168   updateCursorPosition();
01169   connect(mEditor,SIGNAL(CursorPositionChanged()),SLOT(updateCursorPosition()));
01170 }
01171 
01172 
01173 //-----------------------------------------------------------------------------
01174 void KMComposeWin::verifyWordWrapLengthIsAdequate(const QString &body)
01175 {
01176   int maxLineLength = 0;
01177   int curPos;
01178   int oldPos = 0;
01179   if (mEditor->QMultiLineEdit::wordWrap() == QMultiLineEdit::FixedColumnWidth) {
01180     for (curPos = 0; curPos < (int)body.length(); ++curPos)
01181         if (body[curPos] == '\n') {
01182           if ((curPos - oldPos) > maxLineLength)
01183             maxLineLength = curPos - oldPos;
01184           oldPos = curPos;
01185         }
01186     if ((curPos - oldPos) > maxLineLength)
01187       maxLineLength = curPos - oldPos;
01188     if (mEditor->wrapColumnOrWidth() < maxLineLength) // column
01189       mEditor->setWrapColumnOrWidth(maxLineLength);
01190   }
01191 }
01192 
01193 //-----------------------------------------------------------------------------
01194 void KMComposeWin::decryptOrStripOffCleartextSignature( QCString& body )
01195 {
01196   QPtrList<Kpgp::Block> pgpBlocks;
01197   QStrList nonPgpBlocks;
01198   if( Kpgp::Module::prepareMessageForDecryption( body,
01199                                                  pgpBlocks, nonPgpBlocks ) )
01200   {
01201     // Only decrypt/strip off the signature if there is only one OpenPGP
01202     // block in the message
01203     if( pgpBlocks.count() == 1 )
01204     {
01205       Kpgp::Block* block = pgpBlocks.first();
01206       if( ( block->type() == Kpgp::PgpMessageBlock ) ||
01207           ( block->type() == Kpgp::ClearsignedBlock ) )
01208       {
01209         if( block->type() == Kpgp::PgpMessageBlock )
01210           // try to decrypt this OpenPGP block
01211           block->decrypt();
01212         else
01213           // strip off the signature
01214           block->verify();
01215 
01216         body = nonPgpBlocks.first()
01217              + block->text()
01218              + nonPgpBlocks.last();
01219       }
01220     }
01221   }
01222 }
01223 
01224 //-----------------------------------------------------------------------------
01225 void KMComposeWin::setMsg(KMMessage* newMsg, bool mayAutoSign,
01226                           bool allowDecryption, bool isModified)
01227 {
01228   KMMessagePart bodyPart, *msgPart;
01229   int i, num;
01230 
01231   //assert(newMsg!=0);
01232   if(!newMsg)
01233     {
01234       kdDebug(5006) << "KMComposeWin::setMsg() : newMsg == 0!\n" << endl;
01235       return;
01236     }
01237   mMsg = newMsg;
01238 
01239   mEdtTo->setText(mMsg->to());
01240   mEdtFrom->setText(mMsg->from());
01241   mEdtCc->setText(mMsg->cc());
01242   mEdtSubject->setText(mMsg->subject());
01243   mEdtReplyTo->setText(mMsg->replyTo());
01244   mEdtBcc->setText(mMsg->bcc());
01245 
01246   if (!mBtnIdentity->isChecked() && !newMsg->headerField("X-KMail-Identity").isEmpty())
01247     mId = newMsg->headerField("X-KMail-Identity").stripWhiteSpace().toUInt();
01248 
01249   // don't overwrite the header values with identity specific values
01250   // unless the identity is sticky
01251   if ( !mBtnIdentity->isChecked() ) {
01252     disconnect(mIdentity,SIGNAL(identityChanged(uint)),
01253                this, SLOT(slotIdentityChanged(uint)));
01254   }
01255    mIdentity->setCurrentIdentity( mId );
01256   if ( !mBtnIdentity->isChecked() ) {
01257     connect(mIdentity,SIGNAL(identityChanged(uint)),
01258             this, SLOT(slotIdentityChanged(uint)));
01259   }
01260   else {
01261     // make sure the header values are overwritten with the values of the
01262     // sticky identity (the slot isn't called by the signal for new messages
01263     // since the identity has already been set before the signal was connected)
01264     slotIdentityChanged( mId );
01265   }
01266 
01267   IdentityManager * im = kmkernel->identityManager();
01268 
01269   const KMIdentity & ident = im->identityForUoid( mIdentity->currentIdentity() );
01270 
01271   mOldSigText = ident.signatureText();
01272 
01273   // check for the presence of a DNT header, indicating that MDN's were
01274   // requested
01275   QString mdnAddr = newMsg->headerField("Disposition-Notification-To");
01276   mRequestMDNAction->setChecked( ( !mdnAddr.isEmpty() &&
01277                                   im->thatIsMe( mdnAddr ) ) || mAutoRequestMDN );
01278 
01279   // check for presence of a priority header, indicating urgent mail:
01280   mUrgentAction->setChecked( newMsg->isUrgent() );
01281 
01282   // enable/disable encryption if the message was/wasn't encrypted
01283   switch ( mMsg->encryptionState() ) {
01284     case KMMsgFullyEncrypted: // fall through
01285     case KMMsgPartiallyEncrypted:
01286       mLastEncryptActionState = true;
01287       break;
01288     case KMMsgNotEncrypted:
01289       mLastEncryptActionState = false;
01290       break;
01291     default: // nothing
01292       break;
01293   }
01294 
01295   // enable/disable signing if the message was/wasn't signed
01296   switch ( mMsg->signatureState() ) {
01297     case KMMsgFullySigned: // fall through
01298     case KMMsgPartiallySigned:
01299       mLastSignActionState = true;
01300       break;
01301     case KMMsgNotSigned:
01302       mLastSignActionState = false;
01303       break;
01304     default: // nothing
01305       break;
01306   }
01307 
01308   // get PGP user id for the currently selected identity
01309   QCString pgpUserId = ident.pgpIdentity();
01310   mLastIdentityHasOpenPgpKey = !pgpUserId.isEmpty();
01311 
01312   if ( mSelectedCryptPlug || Kpgp::Module::getKpgp()->usePGP() ) {
01313     if ( !mSelectedCryptPlug && pgpUserId.isEmpty() ) {
01314       setEncryption( false );
01315       setSigning( false );
01316     }
01317     else {
01318       setEncryption( mLastEncryptActionState );
01319       setSigning( mLastSignActionState );
01320     }
01321   }
01322 
01323   // "Attach my public key" is only possible if the user uses the built-in
01324   // OpenPGP support and he specified his key
01325   mAttachMPK->setEnabled( Kpgp::Module::getKpgp()->usePGP() &&
01326                          !pgpUserId.isEmpty() );
01327 
01328   QString transport = newMsg->headerField("X-KMail-Transport");
01329   if (!mBtnTransport->isChecked() && !transport.isEmpty())
01330   {
01331     for (int i = 0; i < mTransport->count(); i++)
01332       if (mTransport->text(i) == transport)
01333         mTransport->setCurrentItem(i);
01334     mTransport->setEditText( transport );
01335   }
01336 
01337   if (!mBtnFcc->isChecked())
01338   {
01339     if (!mMsg->fcc().isEmpty())
01340       setFcc(mMsg->fcc());
01341     else
01342       setFcc(ident.fcc());
01343   }
01344 
01345   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
01346 
01347   num = mMsg->numBodyParts();
01348 
01349   if (num > 0)
01350   {
01351     QCString bodyDecoded;
01352     mMsg->bodyPart(0, &bodyPart);
01353 
01354     int firstAttachment = (bodyPart.typeStr().lower() == "text") ? 1 : 0;
01355     if (firstAttachment)
01356     {
01357       mCharset = bodyPart.charset();
01358       if ( mCharset.isEmpty() || mCharset == "default" )
01359         mCharset = mDefCharset;
01360 
01361       bodyDecoded = bodyPart.bodyDecoded();
01362 
01363       if( allowDecryption )
01364         decryptOrStripOffCleartextSignature( bodyDecoded );
01365 
01366       // As nobody seems to know the purpose of the following line and
01367       // as it breaks word wrapping of long lines if drafts with attachments
01368       // are opened for editting in the composer (cf. Bug#41102) I comment it
01369       // out. Ingo, 2002-04-21
01370       //verifyWordWrapLengthIsAdequate(bodyDecoded);
01371 
01372       const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
01373       if (codec)
01374         mEditor->setText(codec->toUnicode(bodyDecoded));
01375       else
01376         mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
01377       mEditor->insertLine("\n", -1);
01378     } else mEditor->setText("");
01379     for(i=firstAttachment; i<num; i++)
01380     {
01381       msgPart = new KMMessagePart;
01382       mMsg->bodyPart(i, msgPart);
01383       QCString mimeType = msgPart->typeStr().lower() + '/'
01384                         + msgPart->subtypeStr().lower();
01385       // don't add the detached signature as attachment when editting a
01386       // PGP/MIME signed message
01387       if( mimeType != "application/pgp-signature" ) {
01388         addAttach(msgPart);
01389       }
01390     }
01391   } else{
01392     mCharset=mMsg->charset();
01393     if ( mCharset.isEmpty() ||  mCharset == "default" )
01394       mCharset = mDefCharset;
01395 
01396     QCString bodyDecoded = mMsg->bodyDecoded();
01397 
01398     if( allowDecryption )
01399       decryptOrStripOffCleartextSignature( bodyDecoded );
01400 
01401     const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
01402     if (codec) {
01403       mEditor->setText(codec->toUnicode(bodyDecoded));
01404     } else
01405       mEditor->setText(QString::fromLocal8Bit(bodyDecoded));
01406   }
01407 
01408   setCharset(mCharset);
01409 
01410   if( mAutoSign && mayAutoSign ) {
01411     //
01412     // Espen 2000-05-16
01413     // Delay the signature appending. It may start a fileseletor.
01414     // Not user friendy if this modal fileseletor opens before the
01415     // composer.
01416     //
01417     QTimer::singleShot( 0, this, SLOT(slotAppendSignature()) );
01418   } else {
01419     kmkernel->dumpDeadLetters();
01420   }
01421   mEditor->setModified(isModified);
01422 }
01423 
01424 
01425 //-----------------------------------------------------------------------------
01426 void KMComposeWin::setFcc( const QString &idString )
01427 {
01428   // check if the sent-mail folder still exists
01429   KMFolder *folder = kmkernel->folderMgr()->findIdString( idString );
01430   if ( !folder )
01431     folder = kmkernel->imapFolderMgr()->findIdString( idString );
01432   if ( !folder )
01433     folder = kmkernel->dimapFolderMgr()->findIdString( idString );
01434   if ( folder )
01435     mFcc->setFolder( idString );
01436   else
01437     mFcc->setFolder( kmkernel->sentFolder() );
01438 }
01439 
01440 
01441 //-----------------------------------------------------------------------------
01442 bool KMComposeWin::queryClose ()
01443 {
01444   if ( !mEditor->checkExternalEditorFinished() )
01445     return false;
01446   if (kmkernel->shuttingDown() || kapp->sessionSaving())
01447     return true;
01448 
01449   if(mEditor->isModified() || mEdtFrom->edited() || mEdtReplyTo->edited() ||
01450      mEdtTo->edited() || mEdtCc->edited() || mEdtBcc->edited() ||
01451      mEdtSubject->edited() || mAtmModified ||
01452      (mTransport->lineEdit() && mTransport->lineEdit()->edited()))
01453   {
01454     const int rc = KMessageBox::warningYesNoCancel(this,
01455            i18n("Do you want to discard the message or save it for later?"),
01456            i18n("Discard or Save Message"),
01457            i18n("&Save as Draft"),
01458            KStdGuiItem::discard() );
01459     if (rc == KMessageBox::Cancel)
01460       return false;
01461     else if (rc == KMessageBox::Yes)
01462       return slotSaveDraft();
01463   }
01464   return true;
01465 }
01466 
01467 //-----------------------------------------------------------------------------
01468 bool KMComposeWin::userForgotAttachment()
01469 {
01470   KConfigGroup composer( KMKernel::config(), "Composer" );
01471   bool checkForForgottenAttachments =
01472     composer.readBoolEntry( "showForgottenAttachmentWarning", true );
01473 
01474   if ( !checkForForgottenAttachments || ( mAtmList.count() > 0 ) )
01475     return false;
01476 
01477 
01478   QStringList attachWordsList =
01479     composer.readListEntry( "attachment-keywords" );
01480 
01481   if ( attachWordsList.isEmpty() ) {
01482     // default value (FIXME: this is duplicated in configuredialog.cpp)
01483     attachWordsList << QString::fromLatin1("attachment")
01484                     << QString::fromLatin1("attached");
01485     if ( QString::fromLatin1("attachment") != i18n("attachment") )
01486       attachWordsList << i18n("attachment");
01487     if ( QString::fromLatin1("attached") != i18n("attached") )
01488       attachWordsList << i18n("attached");
01489   }
01490 
01491   QRegExp rx ( QString::fromLatin1("\\b") +
01492                attachWordsList.join("\\b|\\b") +
01493                QString::fromLatin1("\\b") );
01494   rx.setCaseSensitive( false );
01495 
01496   bool gotMatch = false;
01497 
01498   // check whether the subject contains one of the attachment key words
01499   // unless the message is a reply or a forwarded message
01500   QString subj = mEdtSubject->text();
01501   gotMatch =    ( KMMessage::stripOffPrefixes( subj ) == subj )
01502              && ( rx.search( subj ) >= 0 );
01503 
01504   if ( !gotMatch ) {
01505     // check whether the non-quoted text contains one of the attachment key
01506     // words
01507     QRegExp quotationRx ("^([ \\t]*([|>:}#]|[A-Za-z]+>))+");
01508     for ( int i = 0; i < mEditor->numLines(); ++i ) {
01509       QString line = mEditor->textLine( i );
01510       gotMatch =    ( quotationRx.search( line ) < 0 )
01511                  && ( rx.search( line ) >= 0 );
01512       if ( gotMatch )
01513         break;
01514     }
01515   }
01516 
01517   if ( !gotMatch )
01518     return false;
01519 
01520   int rc = KMessageBox::warningYesNoCancel( this,
01521              i18n("The message you have composed seems to refer to an "
01522                   "attached file but you have not attached anything.\n"
01523                   "Do you want to attach a file to your message?"),
01524              i18n("File Attachment Reminder"),
01525              i18n("&Attach file..."),
01526              i18n("&Send as is") );
01527   if ( rc == KMessageBox::Cancel )
01528     return true;
01529   if ( rc == KMessageBox::Yes ) {
01530     slotAttachFile();
01531     //preceed with editing
01532     return true;
01533   }
01534   return false;
01535 }
01536 
01537 //-----------------------------------------------------------------------------
01538 bool KMComposeWin::applyChanges( bool backgroundMode )
01539 {
01540   QString str, atmntStr;
01541   QString temp, replyAddr;
01542 
01543   //assert(mMsg!=0);
01544   if(!mMsg)
01545   {
01546     kdDebug(5006) << "KMComposeWin::applyChanges() : mMsg == 0!\n" << endl;
01547     return FALSE;
01548   }
01549 
01550   mBccMsgList.clear();
01551 
01552   if (mAutoCharset) {
01553     QCString charset = KMMsgBase::autoDetectCharset(mCharset, KMMessage::preferredCharsets(), mEditor->text());
01554     if (charset.isEmpty())
01555     {
01556       KMessageBox::sorry(this,
01557            i18n("No suitable encoding could be found for your message.\n"
01558                 "Please set an encoding using the 'Options' menu."));
01559       return false;
01560     }
01561     mCharset = charset;
01562   }
01563   mMsg->setCharset(mCharset);
01564 
01565 //  kdDebug(5006) << "\n\n\n\nKMComposeWin::applyChanges: 1" << endl;
01566   mMsg->setTo(to());
01567   mMsg->setFrom(from());
01568   mMsg->setCc(cc());
01569   mMsg->setSubject(subject());
01570   mMsg->setReplyTo(replyTo());
01571   mMsg->setBcc(bcc());
01572 
01573   const KMIdentity & id
01574     = kmkernel->identityManager()->identityForUoid( mIdentity->currentIdentity() );
01575   kdDebug(5006) << "\n\n\n\nKMComposeWin::applyChanges: " << mFcc->currentText() << "=="
01576             << id.fcc() << "?" << endl;
01577 
01578   KMFolder *f = mFcc->getFolder();
01579   assert( f != 0 );
01580   if ( f->idString() == id.fcc() )
01581     mMsg->removeHeaderField("X-KMail-Fcc");
01582   else
01583     mMsg->setFcc( f->idString() );
01584 
01585   // set the correct drafts folder
01586   mMsg->setDrafts( id.drafts() );
01587 
01588   if (id.isDefault())
01589     mMsg->removeHeaderField("X-KMail-Identity");
01590   else mMsg->setHeaderField("X-KMail-Identity", QString::number( id.uoid() ));
01591 
01592   if (!replyTo().isEmpty()) replyAddr = replyTo();
01593   else replyAddr = from();
01594 
01595   if (mRequestMDNAction->isChecked())
01596     mMsg->setHeaderField("Disposition-Notification-To", replyAddr);
01597   else
01598     mMsg->removeHeaderField("Disposition-Notification-To");
01599 
01600   if (mUrgentAction->isChecked()) {
01601     mMsg->setHeaderField("X-PRIORITY", "2 (High)");
01602     mMsg->setHeaderField("Priority", "urgent");
01603   } else {
01604     mMsg->removeHeaderField("X-PRIORITY");
01605     mMsg->removeHeaderField("Priority");
01606   }
01607 
01608   _StringPair *pCH;
01609   for (pCH  = mCustHeaders.first();
01610        pCH != 0;
01611        pCH  = mCustHeaders.next()) {
01612     mMsg->setHeaderField(KMMsgBase::toUsAscii(pCH->name), pCH->value);
01613   }
01614 
01615   // we have to remember the Bcc because it might have been overwritten
01616   // by a custom header (therefore we can't use bcc() later) and because
01617   // mimelib removes addresses without domain part (therefore we can't use
01618   // mMsg->bcc() later)
01619   mBcc = mMsg->bcc();
01620 
01621   bool doSign    = mSignAction->isChecked()    && !mNeverSign;
01622   bool doEncrypt = mEncryptAction->isChecked() && !mNeverEncrypt;
01623 
01624   // get PGP user id for the chosen identity
01625   const KMIdentity & ident =
01626     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
01627   QCString pgpUserId = ident.pgpIdentity();
01628 
01629   // check settings of composer buttons *and* attachment check boxes
01630   bool doSignCompletely    = doSign;
01631   bool doEncryptCompletely = doEncrypt;
01632   bool doEncryptPartially  = doEncrypt;
01633   if( mSelectedCryptPlug && ( !mAtmList.isEmpty() ) ) {
01634     int idx=0;
01635     KMMessagePart *attachPart;
01636     for( attachPart = mAtmList.first();
01637          attachPart;
01638          attachPart=mAtmList.next(), ++idx ) {
01639       if( encryptFlagOfAttachment( idx ) ) {
01640         doEncryptPartially = true;
01641       }
01642       else {
01643         doEncryptCompletely = false;
01644       }
01645       if( !signFlagOfAttachment(    idx ) )
01646         doSignCompletely = false;
01647     }
01648   }
01649 
01650   bool bOk = true;
01651 
01652   if( !doSignCompletely ) {
01653     if( mSelectedCryptPlug ) {
01654       // note: only ask for signing if "Warn me" flag is set! (khz)
01655       if( mSelectedCryptPlug->warnSendUnsigned() && !mNeverSign ) {
01656         int ret =
01657         KMessageBox::warningYesNoCancel( this,
01658           QString( "<qt><b>"
01659             + i18n("Warning:")
01660             + "</b><br>"
01661             + ((doSign && !doSignCompletely)
01662               ? i18n("You specified not to sign some parts of this message, but"
01663                      " you wanted to be warned not to send unsigned messages!")
01664               : i18n("You specified not to sign this message, but"
01665                      " you wanted to be warned not to send unsigned messages!") )
01666             + "<br>&nbsp;<br><b>"
01667             + i18n("Sign all parts of this message?")
01668             + "</b></qt>" ),
01669           i18n("Signature Warning"),
01670           KGuiItem( i18n("&Sign All Parts") ),
01671           KGuiItem( i18n("Send &as is") ) );
01672         if( ret == KMessageBox::Cancel )
01673           bOk = false;
01674         else if( ret == KMessageBox::Yes ) {
01675           doSign = true;
01676           doSignCompletely = true;
01677         }
01678       }
01679     } else {
01680       // ask if the message should be encrypted via old build-in pgp code
01681       // pending (who ever wants to implement it)
01682     }
01683   }
01684 
01685   if( bOk ) {
01686     if( mNeverEncrypt )
01687       doEncrypt = false;
01688     else {
01689       // check whether all encrypted messages should be encrypted to self
01690       bool bEncryptToSelf = mSelectedCryptPlug
01691         ? mSelectedCryptPlug->alwaysEncryptToSelf()
01692         : Kpgp::Module::getKpgp()->encryptToSelf();
01693       // check whether we have the user's key if necessary
01694       bool bEncryptionPossible = !bEncryptToSelf || !pgpUserId.isEmpty();
01695       // check whether we are using OpenPGP (built-in or plug-in)
01696       bool bUsingOpenPgp = !mSelectedCryptPlug || ( mSelectedCryptPlug &&
01697                                                     ( -1 != mSelectedCryptPlug->libName().find( "openpgp" ) ) );
01698       // only try automatic encryption if all of the following conditions hold
01699       // a) the user enabled automatic encryption
01700       // b) we have the user's key if he wants to encrypt to himself
01701       // c) we are using OpenPGP
01702       // d) no message part is marked for encryption
01703       if( mAutoPgpEncrypt && bEncryptionPossible && bUsingOpenPgp &&
01704           !doEncryptPartially ) {
01705         // check if encryption is possible and if yes suggest encryption
01706         // first determine the complete list of recipients
01707         QString _to = to().simplifyWhiteSpace();
01708         if( !cc().isEmpty() ) {
01709           if( !_to.endsWith(",") )
01710             _to += ",";
01711           _to += cc().simplifyWhiteSpace();
01712         }
01713         if( !mBcc.isEmpty() ) {
01714           if( !_to.endsWith(",") )
01715             _to += ",";
01716           _to += mBcc.simplifyWhiteSpace();
01717         }
01718         QStringList allRecipients = KMMessage::splitEmailAddrList(_to);
01719         // now check if encrypting to these recipients is possible and desired
01720         Kpgp::Module *pgp = Kpgp::Module::getKpgp();
01721         int status = pgp->encryptionPossible( allRecipients );
01722         if( 1 == status ) {
01723           // encrypt all message parts
01724           doEncrypt = true;
01725           doEncryptCompletely = true;
01726         }
01727         else if( 2 == status ) {
01728           // the user wants to be asked or has to be asked
01729           KCursorSaver idle(KBusyPtr::idle());
01730           int ret;
01731           if( doSign )
01732             ret = KMessageBox::questionYesNoCancel( this,
01733                                                     i18n("<qt><p>You have a trusted OpenPGP key for every "
01734                                                          "recipient of this message and the message will "
01735                                                          "be signed.</p>"
01736                                                          "<p>Should this message also be "
01737                                                          "encrypted?</p></qt>"),
01738                                                     i18n("Encrypt Message?"),
01739                                                     KGuiItem( i18n("Sign && &Encrypt") ),
01740                                                     KGuiItem( i18n("&Sign Only") ) );
01741           else
01742             ret = KMessageBox::questionYesNoCancel( this,
01743                                                     i18n("<qt><p>You have a trusted OpenPGP key for every "
01744                                                          "recipient of this message.</p>"
01745                                                          "<p>Should this message be encrypted?</p></qt>"),
01746                                                     i18n("Encrypt Message?"),
01747                                                     KGuiItem( i18n("&Encrypt") ),
01748                                                     KGuiItem( i18n("&Don't Encrypt") ) );
01749           if( KMessageBox::Cancel == ret )
01750             return false;
01751           else if( KMessageBox::Yes == ret ) {
01752             // encrypt all message parts
01753             doEncrypt = true;
01754             doEncryptCompletely = true;
01755           }
01756         }
01757         else if( status == -1 )
01758         {
01759           // warn the user that there are conflicting encryption preferences
01760           KCursorSaver idle(KBusyPtr::idle());
01761           int ret =
01762             KMessageBox::warningYesNoCancel( this,
01763                                              i18n("<qt><p>There are conflicting encryption "
01764                                                   "preferences!</p>"
01765                                                   "<p>Should this message be encrypted?</p></qt>"),
01766                                              i18n("Encrypt Message?"),
01767                                              KGuiItem( i18n("&Encrypt") ),
01768                                              KGuiItem( i18n("&Don't Encrypt") ) );
01769           if( KMessageBox::Cancel == ret )
01770             bOk = false;
01771           else if( KMessageBox::Yes == ret ) {
01772             // encrypt all message parts
01773             doEncrypt = true;
01774             doEncryptCompletely = true;
01775           }
01776         }
01777       }
01778       else if( !doEncryptCompletely && mSelectedCryptPlug ) {
01779         // note: only ask for encrypting if "Warn me" flag is set! (khz)
01780         if( mSelectedCryptPlug->warnSendUnencrypted() ) {
01781           int ret =
01782             KMessageBox::warningYesNoCancel( this,
01783                                              QString( "<qt><b>"
01784                                                       + i18n("Warning:")
01785                                                       + "</b><br>"
01786                                                       + ((doEncrypt && !doEncryptCompletely)
01787                                                          ? i18n("You specified not to encrypt some parts of this message, but"
01788                                                                 " you wanted to be warned not to send unencrypted messages!")
01789                                                          : i18n("You specified not to encrypt this message, but"
01790                                                                 " you wanted to be warned not to send unencrypted messages!") )
01791                                                       + "<br>&nbsp;<br><b>"
01792                                                       + i18n("Encrypt all parts of this message?")
01793                                                       + "</b></qt>" ),
01794                                              i18n("Encryption Warning"),
01795                                              KGuiItem( i18n("&Encrypt All Parts") ),
01796                                              KGuiItem( i18n("Send &as is") ) );
01797           if( ret == KMessageBox::Cancel )
01798             bOk = false;
01799           else if( ret == KMessageBox::Yes ) {
01800             doEncrypt = true;
01801             doEncryptCompletely = true;
01802           }
01803         }
01804 
01805         /*
01806           note: Processing the mSelectedCryptPlug->encryptEmail() flag here would
01807           be absolutely wrong: this is used for specifying
01808           if messages should be encrypted 'in general'.
01809           --> This sets the initial state of a freshly started Composer.
01810           --> This does *not* mean overriding user setting made while
01811           editing in that composer window!         (khz, 2002/06/26)
01812         */
01813 
01814       }
01815     }
01816   }
01817 
01818   if( bOk ) {
01819     // if necessary mark all attachments for signing/encryption
01820     if( mSelectedCryptPlug && ( !mAtmList.isEmpty() ) &&
01821         ( doSignCompletely || doEncryptCompletely ) ) {
01822       for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
01823            lvi;
01824            lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
01825         if( doSignCompletely )
01826           lvi->setSign( true );
01827         if( doEncryptCompletely )
01828           lvi->setEncrypt( true );
01829       }
01830     }
01831   }
01832   // This c-string (init empty here) is set by *first* testing of expiring
01833   // signature certificate and stops us from repeatedly asking same questions.
01834   QCString signCertFingerprint;
01835 
01836   // note: Must create extra message *before* calling compose on mMsg.
01837   KMMessage* extraMessage = new KMMessage( *mMsg );
01838 
01839   if( bOk )
01840     bOk = (composeMessage( pgpUserId,
01841                            *mMsg, doSign, doEncrypt, false,
01842                            signCertFingerprint ) == Kpgp::Ok);
01843   if( bOk ) {
01844     bool saveMessagesEncrypted = mSelectedCryptPlug ? mSelectedCryptPlug->saveMessagesEncrypted()
01845                                                     : true;
01846 
01847     kdDebug(5006) << "\n\n" << endl;
01848     kdDebug(5006) << "KMComposeWin::applyChanges(void)  -  Send encrypted=" << doEncrypt << "  Store encrypted=" << saveMessagesEncrypted << endl;
01849 // note: The following define is specified on top of this file. To compile
01850 //       a less strict version of KMail just comment it out there above.
01851 #ifdef STRICT_RULES_OF_GERMAN_GOVERNMENT_01
01852     // Hack to make sure the S/MIME CryptPlugs follows the strict requirement
01853     // of german government:
01854     // --> Encrypted messages *must* be stored in unencrypted form after sending.
01855     //     ( "Abspeichern ausgegangener Nachrichten in entschluesselter Form" )
01856     // --> Signed messages *must* be stored including the signature after sending.
01857     //     ( "Aufpraegen der Signatur" )
01858     // So we provide the user with a non-deactivateble warning and let her/him
01859     // choose to obey the rules or to ignore them explicitly.
01860     if(    mSelectedCryptPlug
01861         && ( 0 <= mSelectedCryptPlug->libName().find( "smime",   0, false ) )
01862         && ( doEncrypt && saveMessagesEncrypted ) ){
01863 
01864       if( doEncrypt && saveMessagesEncrypted ) {
01865         QString headTxt =
01866           i18n("Warning: Your S/MIME Plug-in configuration is unsafe.");
01867         QString encrTxt =
01868           i18n("Encrypted messages should be stored in unencrypted form; saving locally in encrypted form is not allowed.");
01869         QString footTxt =
01870           i18n("Please correct the wrong settings in KMail's Plug-in configuration pages as soon as possible.");
01871         QString question =
01872           i18n("Store message in the recommended way?");
01873 
01874         if( KMessageBox::Yes == KMessageBox::warningYesNo(this,
01875                  "<qt><p><b>" + headTxt + "</b><br>" + encrTxt + "</p><p>"
01876                  + footTxt + "</p><p><b>" + question + "</b></p></qt>",
01877                  i18n("Unsafe S/MIME Configuration"),
01878                  KGuiItem( i18n("Save &Unencrypted") ),
01879                  KGuiItem( i18n("Save &Encrypted") ) ) ) {
01880             saveMessagesEncrypted = false;
01881         }
01882       }
01883     }
01884     kdDebug(5006) << "KMComposeWin::applyChanges(void)  -  Send encrypted=" << doEncrypt << "  Store encrypted=" << saveMessagesEncrypted << endl;
01885 #endif
01886     if( doEncrypt && ! saveMessagesEncrypted ){
01887       if( mSelectedCryptPlug ){
01888         for( KMAtmListViewItem* entry = (KMAtmListViewItem*)mAtmItemList.first();
01889              entry;
01890              entry = (KMAtmListViewItem*)mAtmItemList.next() )
01891           entry->setEncrypt( false );
01892       }
01893       bOk = (composeMessage( pgpUserId,
01894                              *extraMessage,
01895                              doSign,
01896                              false,
01897                              true,
01898                              signCertFingerprint ) == Kpgp::Ok);
01899 kdDebug(5006) << "KMComposeWin::applyChanges(void)  -  Store message in decrypted form." << endl;
01900       extraMessage->cleanupHeader();
01901       mMsg->setUnencryptedMsg( extraMessage );
01902     }
01903   }
01904   return bOk;
01905 }
01906 
01907 
01908 Kpgp::Result KMComposeWin::composeMessage( QCString pgpUserId,
01909                                            KMMessage& theMessage,
01910                                            bool doSign,
01911                                            bool doEncrypt,
01912                                            bool ignoreBcc,
01913                                            QCString& signCertFingerprint )
01914 {
01915   Kpgp::Result result = Kpgp::Ok;
01916   // create informative header for those that have no mime-capable
01917   // email client
01918   theMessage.setBody( "This message is in MIME format." );
01919 
01920   // preprocess the body text
01921   QCString body = breakLinesAndApplyCodec();
01922 
01923   if (body.isNull()) return Kpgp::Failure;
01924 
01925   if (body.isEmpty()) body = "\n"; // don't crash
01926 
01927   // From RFC 3156:
01928   //  Note: The accepted OpenPGP convention is for signed data to end
01929   //  with a <CR><LF> sequence.  Note that the <CR><LF> sequence
01930   //  immediately preceding a MIME boundary delimiter line is considered
01931   //  to be part of the delimiter in [3], 5.1.  Thus, it is not part of
01932   //  the signed data preceding the delimiter line.  An implementation
01933   //  which elects to adhere to the OpenPGP convention has to make sure
01934   //  it inserts a <CR><LF> pair on the last line of the data to be
01935   //  signed and transmitted (signed message and transmitted message
01936   //  MUST be identical).
01937   // So make sure that the body ends with a <LF>.
01938   if( body[body.length()-1] != '\n' ) {
01939     kdDebug(5006) << "Added an <LF> on the last line" << endl;
01940     body += "\n";
01941   }
01942 
01943   // set the main headers
01944   theMessage.deleteBodyParts();
01945   theMessage.removeHeaderField("Content-Type");
01946   theMessage.removeHeaderField("Content-Transfer-Encoding");
01947   theMessage.setAutomaticFields(TRUE); // == multipart/mixed
01948 
01949   // this is our *final* body part
01950   KMMessagePart newBodyPart;
01951 
01952   // this is the boundary depth of the surrounding MIME part
01953   int previousBoundaryLevel = 0;
01954 
01955 
01956   // create temporary bodyPart for editor text
01957   // (and for all attachments, if mail is to be singed and/or encrypted)
01958   bool earlyAddAttachments =
01959     mSelectedCryptPlug && ( !mAtmList.isEmpty() ) && (doSign || doEncrypt);
01960 
01961   bool allAttachmentsAreInBody = earlyAddAttachments ? true : false;
01962 
01963   // test whether there ARE attachments that can be included into the body
01964   if( earlyAddAttachments ) {
01965     bool someOk = false;
01966     int idx;
01967     KMMessagePart *attachPart;
01968     for( idx=0, attachPart = mAtmList.first();
01969         attachPart;
01970         attachPart=mAtmList.next(),
01971         ++idx )
01972       if(    doEncrypt == encryptFlagOfAttachment( idx )
01973           && doSign    == signFlagOfAttachment(    idx ) )
01974         someOk = true;
01975       else
01976         allAttachmentsAreInBody = false;
01977     if( !allAttachmentsAreInBody && !someOk )
01978       earlyAddAttachments = false;
01979   }
01980 
01981   KMMessagePart oldBodyPart;
01982   oldBodyPart.setTypeStr(   earlyAddAttachments ? "multipart" : "text" );
01983   oldBodyPart.setSubtypeStr(earlyAddAttachments ? "mixed"     : "plain");
01984   oldBodyPart.setContentDisposition( "inline" );
01985 
01986   QCString boundaryCStr;
01987 
01988   bool isQP = kmkernel->msgSender()->sendQuotedPrintable();
01989 
01990   if( earlyAddAttachments ) {
01991     // calculate a boundary string
01992     ++previousBoundaryLevel;
01993     DwMediaType tmpCT;
01994     tmpCT.CreateBoundary( previousBoundaryLevel );
01995     boundaryCStr = tmpCT.Boundary().c_str();
01996     // add the normal body text
01997     KMMessagePart innerBodyPart;
01998     innerBodyPart.setTypeStr(   "text" );
01999     innerBodyPart.setSubtypeStr("plain");
02000     innerBodyPart.setContentDisposition( "inline" );
02001     QValueList<int> allowedCTEs;
02002     // the signed body must not be 8bit encoded
02003     innerBodyPart.setBodyAndGuessCte(body, allowedCTEs, !isQP && !doSign,
02004                                      doSign);
02005     innerBodyPart.setCharset(mCharset);
02006     innerBodyPart.setBodyEncoded( body );
02007     DwBodyPart* innerDwPart = theMessage.createDWBodyPart( &innerBodyPart );
02008     innerDwPart->Assemble();
02009     body  = "--";
02010     body +=     boundaryCStr;
02011     body +=                 "\n";
02012     body += innerDwPart->AsString().c_str();
02013     delete innerDwPart;
02014     innerDwPart = 0;
02015     // add all matching Attachments
02016     // NOTE: This code will be changed when KMime is complete.
02017     int idx;
02018     KMMessagePart *attachPart;
02019     for( idx=0, attachPart = mAtmList.first();
02020         attachPart;
02021         attachPart=mAtmList.next(),
02022         ++idx ) {
02023       bool bEncrypt = encryptFlagOfAttachment( idx );
02024       bool bSign = signFlagOfAttachment( idx );
02025       if( !mSelectedCryptPlug
02026           || ( ( doEncrypt == bEncrypt )  && ( doSign == bSign ) ) ) {
02027         // signed/encrypted body parts must be either QP or base64 encoded
02028         // Why not 7 bit? Because the LF->CRLF canonicalization would render
02029         // e.g. 7 bit encoded shell scripts unusable because of the CRs.
02030         if( bSign || bEncrypt ) {
02031           QCString cte = attachPart->cteStr().lower();
02032           if( ( "8bit" == cte )
02033               || ( ( attachPart->type() == DwMime::kTypeText )
02034                    && ( "7bit" == cte ) ) ) {
02035             QByteArray body = attachPart->bodyDecodedBinary();
02036             QValueList<int> dummy;
02037             attachPart->setBodyAndGuessCte(body, dummy, false, bSign);
02038             kdDebug(5006) << "Changed encoding of message part from "
02039                           << cte << " to " << attachPart->cteStr() << endl;
02040           }
02041         }
02042         innerDwPart = theMessage.createDWBodyPart( attachPart );
02043         innerDwPart->Assemble();
02044         body += "\n--";
02045         body +=       boundaryCStr;
02046         body +=                   "\n";
02047         body += innerDwPart->AsString().c_str();
02048         delete innerDwPart;
02049         innerDwPart = 0;
02050       }
02051     }
02052     body += "\n--";
02053     body +=       boundaryCStr;
02054     body +=                   "--\n";
02055   }
02056   else
02057   {
02058     QValueList<int> allowedCTEs;
02059     // the signed body must not be 8bit encoded
02060     oldBodyPart.setBodyAndGuessCte(body, allowedCTEs, !isQP && !doSign,
02061                                    doSign);
02062     oldBodyPart.setCharset(mCharset);
02063   }
02064   // create S/MIME body part for signing and/or encrypting
02065   oldBodyPart.setBodyEncoded( body );
02066 
02067   QCString encodedBody; // only needed if signing and/or encrypting
02068 
02069   if( doSign || doEncrypt ) {
02070     if( mSelectedCryptPlug ) {
02071       // get string representation of body part (including the attachments)
02072       DwBodyPart* dwPart = theMessage.createDWBodyPart( &oldBodyPart );
02073       dwPart->Assemble();
02074       encodedBody = dwPart->AsString().c_str();
02075       delete dwPart;
02076       dwPart = 0;
02077 
02078       // manually add a boundary definition to the Content-Type header
02079       if( !boundaryCStr.isEmpty() ) {
02080         int boundPos = encodedBody.find( '\n' );
02081         if( -1 < boundPos ) {
02082           // insert new "boundary" parameter
02083           QCString bStr( ";\n  boundary=\"" );
02084           bStr += boundaryCStr;
02085           bStr += "\"";
02086           encodedBody.insert( boundPos, bStr );
02087         }
02088       }
02089 
02090       // kdDebug(5006) << "\n\n\n******* a) encodedBody = \"" << encodedBody << "\"******\n\n" << endl;
02091 
02092       if( (0 <= mSelectedCryptPlug->libName().find( "smime",   0, false )) ||
02093           (0 <= mSelectedCryptPlug->libName().find( "openpgp", 0, false )) ) {
02094         // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
02095         // according to RfC 2633, 3.1.1 Canonicalization
02096         kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
02097         encodedBody = KMMessage::lf2crlf( encodedBody );
02098         kdDebug(5006) << "                                                       done." << endl;
02099         // kdDebug(5006) << "\n\n\n******* b) encodedBody = \"" << encodedBody << "\"******\n\n" << endl;
02100       }
02101     } else {
02102       encodedBody = body;
02103     }
02104   }
02105 
02106   if( doSign ) {
02107     if( mSelectedCryptPlug ) {
02108       StructuringInfoWrapper structuring( mSelectedCryptPlug );
02109 
02110       // kdDebug(5006) << "\n\n\n******* c) encodedBody = \"" << encodedBody << "\"******\n\n" << endl;
02111 
02112       QByteArray signature = pgpSignedMsg( encodedBody,
02113                                            structuring,
02114                                            signCertFingerprint );
02115       kdDebug(5006) << "                           size of signature: " << signature.count() << "\n" << endl;
02116       result = signature.isEmpty() ? Kpgp::Failure : Kpgp::Ok;
02117       if( result == Kpgp::Ok ) {
02118         result = processStructuringInfo( QString::fromUtf8( mSelectedCryptPlug->bugURL() ),
02119                                       previousBoundaryLevel + doEncrypt ? 3 : 2,
02120                                       oldBodyPart.contentDescription(),
02121                                       oldBodyPart.typeStr(),
02122                                       oldBodyPart.subtypeStr(),
02123                                       oldBodyPart.contentDisposition(),
02124                                       oldBodyPart.contentTransferEncodingStr(),
02125                                       encodedBody,
02126                                       "signature",
02127                                       signature,
02128                                       structuring,
02129                                       newBodyPart ) ? Kpgp::Ok : Kpgp::Failure;
02130         if( result == Kpgp::Ok ) {
02131           if( newBodyPart.name().isEmpty() )
02132             newBodyPart.setName("signed message part");
02133           newBodyPart.setCharset( mCharset );
02134         } else
02135           KMessageBox::sorry(this, mErrorProcessingStructuringInfo );
02136       }
02137     }
02138     else if ( !doEncrypt ) {
02139       // we try calling the *old* build-in code for OpenPGP clearsigning
02140       Kpgp::Block block;
02141       block.setText( encodedBody );
02142 
02143       // clearsign the message
02144       result = block.clearsign( pgpUserId, mCharset );
02145 
02146       if( result == Kpgp::Ok ) {
02147         newBodyPart.setType(                       oldBodyPart.type() );
02148         newBodyPart.setSubtype(                    oldBodyPart.subtype() );
02149         newBodyPart.setCharset(                    oldBodyPart.charset() );
02150         newBodyPart.setContentTransferEncodingStr( oldBodyPart.contentTransferEncodingStr() );
02151         newBodyPart.setContentDescription(         oldBodyPart.contentDescription() );
02152         newBodyPart.setContentDisposition(         oldBodyPart.contentDisposition() );
02153         newBodyPart.setBodyEncoded( block.text() );
02154       }
02155       else if ( result == Kpgp::Failure )
02156         KMessageBox::sorry(this,
02157                    i18n("<qt><p>This message could not be signed.</p>%1</qt>")
02158                    .arg( mErrorNoCryptPlugAndNoBuildIn ));
02159     }
02160   }
02161 
02162   if( result == Kpgp::Ok ) {
02163     // determine the list of public recipients
02164     QString _to = to().simplifyWhiteSpace();
02165     if( !cc().isEmpty() ) {
02166       if( !_to.endsWith(",") )
02167         _to += ",";
02168       _to += cc().simplifyWhiteSpace();
02169     }
02170     QStringList recipientsWithoutBcc = KMMessage::splitEmailAddrList(_to);
02171 
02172     // run encrypting(s) for Bcc recipient(s)
02173     if( doEncrypt && !ignoreBcc && !theMessage.bcc().isEmpty() ) {
02174       QStringList bccRecips = KMMessage::splitEmailAddrList( theMessage.bcc() );
02175       for( QStringList::ConstIterator it = bccRecips.begin();
02176           it != bccRecips.end();
02177           ++it ) {
02178         QStringList tmpRecips( recipientsWithoutBcc );
02179         tmpRecips << *it;
02180         //kdDebug(5006) << "###BEFORE \"" << theMessage.asString() << "\""<< endl;
02181         KMMessage* yetAnotherMessageForBCC = new KMMessage( theMessage );
02182         KMMessagePart tmpNewBodyPart = newBodyPart;
02183         result = encryptMessage( yetAnotherMessageForBCC,
02184                               tmpRecips,
02185                               doSign, doEncrypt, encodedBody,
02186                               previousBoundaryLevel,
02187                               oldBodyPart,
02188                               earlyAddAttachments, allAttachmentsAreInBody,
02189                               tmpNewBodyPart,
02190                                  signCertFingerprint );
02191         if( result == Kpgp::Ok ){
02192           yetAnotherMessageForBCC->setHeaderField( "X-KMail-Recipients", *it );
02193           mBccMsgList.append( yetAnotherMessageForBCC );
02194           //kdDebug(5006) << "###BCC AFTER \"" << theMessage.asString() << "\""<<endl;
02195         }
02196       }
02197       theMessage.setHeaderField( "X-KMail-Recipients", recipientsWithoutBcc.join(",") );
02198     }
02199 
02200     // run encrypting for public recipient(s)
02201     if( result == Kpgp::Ok ){
02202       result = encryptMessage( &theMessage,
02203                             recipientsWithoutBcc,
02204                             doSign, doEncrypt, encodedBody,
02205                             previousBoundaryLevel,
02206                             oldBodyPart,
02207                             earlyAddAttachments, allAttachmentsAreInBody,
02208                             newBodyPart,
02209                                signCertFingerprint );
02210     }
02211     //        kdDebug(5006) << "###AFTER ENCRYPTION\"" << theMessage.asString() << "\""<<endl;
02212   }
02213   return result;
02214 }
02215 
02216 
02217 bool KMComposeWin::queryExit ()
02218 {
02219   return true;
02220 }
02221 
02222 Kpgp::Result KMComposeWin::getEncryptionCertificates(
02223                                              const QStringList& recipients,
02224                                              QCString& encryptionCertificates )
02225 {
02226   Kpgp::Result result = Kpgp::Ok;
02227 
02228   // find out whether we are dealing with the OpenPGP or the S/MIME plugin
02229   if ( -1 != mSelectedCryptPlug->libName().find( "openpgp" ) ) {
02230     // We are dealing with the OpenPGP plugin. Use Kpgp to determine
02231     // the encryption keys.
02232     // get the OpenPGP key ID for the chosen identity
02233     const KMIdentity & ident =
02234       kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
02235     const QCString userKeyId = ident.pgpIdentity();
02236     Kpgp::Module *pgp = Kpgp::Module::getKpgp();
02237     Kpgp::KeyIDList encryptionKeyIds;
02238 
02239     // temporarily set encrypt_to_self to the value specified in the
02240     // plugin configuration. this value is used implicitely by the
02241     // function which determines the encryption keys.
02242     const bool bEncryptToSelf_Old = pgp->encryptToSelf();
02243     pgp->setEncryptToSelf( mSelectedCryptPlug->alwaysEncryptToSelf() );
02244     result = pgp->getEncryptionKeys( encryptionKeyIds, recipients, userKeyId );
02245     // reset encrypt_to_self to the old value
02246     pgp->setEncryptToSelf( bEncryptToSelf_Old );
02247 
02248     if ( result == Kpgp::Ok && !encryptionKeyIds.isEmpty() ) {
02249       // loop over all key IDs
02250       for ( Kpgp::KeyIDList::ConstIterator it = encryptionKeyIds.begin();
02251            it != encryptionKeyIds.end(); ++it ) {
02252         const Kpgp::Key* key = pgp->publicKey( *it );
02253         if ( key ) {
02254           QCString certFingerprint = key->primaryFingerprint();
02255           kdDebug(5006) << "Fingerprint of encryption key: "
02256                         << certFingerprint << endl;
02257           // add this key to the list of encryption keys
02258           if( !encryptionCertificates.isEmpty() )
02259             encryptionCertificates += '\1';
02260           encryptionCertificates += certFingerprint;
02261         }
02262       }
02263     }
02264   }
02265   else {
02266     // S/MIME
02267     QStringList allRecipients = recipients;
02268     if ( mSelectedCryptPlug->alwaysEncryptToSelf() )
02269       allRecipients << from();
02270     for ( QStringList::ConstIterator it = allRecipients.begin();
02271           it != allRecipients.end();
02272           ++it ) {
02273       QCString certFingerprint = getEncryptionCertificate( *it );
02274 
02275       if ( certFingerprint.isEmpty() ) {
02276         // most likely the user canceled the certificate selection
02277         encryptionCertificates.truncate( 0 );
02278         return Kpgp::Canceled;
02279       }
02280 
02281       certFingerprint.remove( 0, certFingerprint.findRev( '(' ) + 1 );
02282       certFingerprint.truncate( certFingerprint.length() - 1 );
02283       kdDebug(5006) << "\n\n                    Recipient: " << *it
02284                     <<   "\nFingerprint of encryption key: "
02285                     << certFingerprint << "\n\n" << endl;
02286 
02287       const bool certOkay =
02288         checkForEncryptCertificateExpiry( *it, certFingerprint );
02289       if( certOkay ) {
02290         if( !encryptionCertificates.isEmpty() )
02291           encryptionCertificates += '\1';
02292         encryptionCertificates += certFingerprint;
02293       }
02294       else {
02295         // ###### This needs to be improved: Tell the user that the certificate
02296         // ###### expired and let him choose a different one.
02297         encryptionCertificates.truncate( 0 );
02298         return Kpgp::Failure;
02299       }
02300     }
02301   }
02302   return result;
02303 }
02304 
02305 Kpgp::Result KMComposeWin::encryptMessage( KMMessage* msg,
02306                                    const QStringList& recipients,
02307                                    bool doSign,
02308                                    bool doEncrypt,
02309                                    const QCString& encodedBody,
02310                                    int previousBoundaryLevel,
02311                                    const KMMessagePart& oldBodyPart,
02312                                    bool earlyAddAttachments,
02313                                    bool allAttachmentsAreInBody,
02314                                    KMMessagePart newBodyPart,
02315                                    QCString& signCertFingerprint )
02316 {
02317   Kpgp::Result result = Kpgp::Ok;
02318   if(!msg)
02319   {
02320     kdDebug(5006) << "KMComposeWin::encryptMessage() : msg == 0!\n" << endl;
02321     return Kpgp::Failure;
02322   }
02323 
02324   // This c-string (init empty here) is set by *first* testing of expiring
02325   // encryption certificate: stops us from repeatedly asking same questions.
02326   QCString encryptCertFingerprints;
02327 
02328   // determine the encryption certificates in case we need them
02329   if ( mSelectedCryptPlug ) {
02330     bool encrypt = doEncrypt;
02331     if( !encrypt ) {
02332       // check whether at least one attachment is marked for encryption
02333       for ( KMAtmListViewItem* atmlvi =
02334               static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
02335             atmlvi;
02336             atmlvi = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) ) {
02337         if ( atmlvi->isEncrypt() ) {
02338           encrypt = true;
02339           break;
02340         }
02341       }
02342     }
02343     if ( encrypt ) {
02344       result = getEncryptionCertificates( recipients,
02345                                           encryptCertFingerprints );
02346       if ( result != Kpgp::Ok )
02347         return result;
02348       if ( encryptCertFingerprints.isEmpty() ) {
02349         // the user wants to send the message unencrypted
02350         setEncryption( false, false );
02351         doEncrypt = false;
02352       }
02353     }
02354   }
02355 
02356   // encrypt message
02357   if( doEncrypt ) {
02358     QCString innerContent;
02359     if( doSign && mSelectedCryptPlug ) {
02360       DwBodyPart* dwPart = msg->createDWBodyPart( &newBodyPart );
02361       dwPart->Assemble();
02362       innerContent = dwPart->AsString().c_str();
02363       delete dwPart;
02364       dwPart = 0;
02365     } else
02366       innerContent = encodedBody;
02367 
02368     // now do the encrypting:
02369     {
02370       if( mSelectedCryptPlug ) {
02371         if( (0 <= mSelectedCryptPlug->libName().find( "smime",   0, false )) ||
02372             (0 <= mSelectedCryptPlug->libName().find( "openpgp", 0, false )) ) {
02373           // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
02374           // according to RfC 2633, 3.1.1 Canonicalization
02375           kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
02376           innerContent = KMMessage::lf2crlf( innerContent );
02377           kdDebug(5006) << "                                                       done." << endl;
02378         }
02379 
02380         StructuringInfoWrapper structuring( mSelectedCryptPlug );
02381 
02382         QByteArray encryptedBody;
02383         result = pgpEncryptedMsg( encryptedBody, innerContent,
02384                                   structuring,
02385                                   encryptCertFingerprints );
02386 
02387         if( Kpgp::Ok == result ) {
02388           result = processStructuringInfo( QString::fromUtf8( mSelectedCryptPlug->bugURL() ),
02389                                         previousBoundaryLevel + doEncrypt ? 2 : 1,
02390                                         newBodyPart.contentDescription(),
02391                                         newBodyPart.typeStr(),
02392                                         newBodyPart.subtypeStr(),
02393                                         newBodyPart.contentDisposition(),
02394                                         newBodyPart.contentTransferEncodingStr(),
02395                                         innerContent,
02396                                         "encrypted data",
02397                                         encryptedBody,
02398                                         structuring,
02399                                         newBodyPart ) ? Kpgp::Ok : Kpgp::Failure;
02400           if( Kpgp::Ok == result ) {
02401             if( newBodyPart.name().isEmpty() )
02402               newBodyPart.setName("encrypted message part");
02403           } else if ( Kpgp::Failure == result )
02404             KMessageBox::sorry(this, mErrorProcessingStructuringInfo);
02405         } else if ( Kpgp::Failure == result )
02406           KMessageBox::sorry(this,
02407             i18n("<qt><p><b>This message could not be encrypted!</b></p>"
02408                  "<p>The Crypto Plug-in '%1' did not return an encoded text "
02409                  "block.</p>"
02410                  "<p>Probably a recipient's public key was not found or is "
02411                  "untrusted.</p></qt>")
02412             .arg(mSelectedCryptPlug->libName()));
02413       } else {
02414         // we try calling the *old* build-in code for OpenPGP encrypting
02415         Kpgp::Block block;
02416         block.setText( innerContent );
02417 
02418         // get PGP user id for the chosen identity
02419         const KMIdentity & ident =
02420           kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
02421         QCString pgpUserId = ident.pgpIdentity();
02422 
02423         // encrypt the message
02424         result = block.encrypt( recipients, pgpUserId, doSign, mCharset );
02425 
02426         if( Kpgp::Ok == result ) {
02427           newBodyPart.setBodyEncodedBinary( block.text() );
02428           newBodyPart.setCharset( oldBodyPart.charset() );
02429         }
02430         else if( Kpgp::Failure == result ) {
02431           KMessageBox::sorry(this,
02432             i18n("<qt><p>This message could not be encrypted!</p>%1</qt>")
02433            .arg( mErrorNoCryptPlugAndNoBuildIn ));
02434         }
02435       }
02436     }
02437   }
02438 
02439   // process the attachments that are not included into the body
02440   if( Kpgp::Ok == result ) {
02441     const KMMessagePart& ourFineBodyPart( (doSign || doEncrypt)
02442                                           ? newBodyPart
02443                                           : oldBodyPart );
02444     if( !mAtmList.isEmpty()
02445         && ( !earlyAddAttachments || !allAttachmentsAreInBody ) ) {
02446       // set the content type header
02447       msg->headers().ContentType().FromString( "Multipart/Mixed" );
02448 kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type to Multipart/Mixed" << endl;
02449 //      msg->setBody( "This message is in MIME format.\n"
02450 //                    "Since your mail reader does not understand this format,\n"
02451 //                    "some or all parts of this message may not be legible." );
02452       // add our Body Part
02453       msg->addBodyPart( &ourFineBodyPart );
02454 
02455       // add Attachments
02456       // create additional bodyparts for the attachments (if any)
02457       int idx;
02458       KMMessagePart newAttachPart;
02459       KMMessagePart *attachPart;
02460       for( idx=0, attachPart = mAtmList.first();
02461            attachPart;
02462            attachPart = mAtmList.next(), ++idx ) {
02463 kdDebug(5006) << "                                 processing " << idx << ". attachment" << endl;
02464 
02465         const bool cryptFlagsDifferent = mSelectedCryptPlug
02466                         ? (    (encryptFlagOfAttachment( idx ) != doEncrypt)
02467                             || (signFlagOfAttachment(    idx ) != doSign) )
02468                         : false;
02469         const bool encryptThisNow = !mNeverEncrypt && ( cryptFlagsDifferent ? encryptFlagOfAttachment( idx ) : false );
02470         const bool signThisNow    = !mNeverSign && ( cryptFlagsDifferent ? signFlagOfAttachment(    idx ) : false );
02471 
02472         if( cryptFlagsDifferent || !earlyAddAttachments ) {
02473 
02474           if( encryptThisNow || signThisNow ) {
02475 
02476             KMMessagePart& rEncryptMessagePart( *attachPart );
02477 
02478             // prepare the attachment's content
02479             // signed/encrypted body parts must be either QP or base64 encoded
02480             QCString cte = attachPart->cteStr().lower();
02481             if( ( "8bit" == cte )
02482                 || ( ( attachPart->type() == DwMime::kTypeText )
02483                      && ( "7bit" == cte ) ) ) {
02484               QByteArray body = attachPart->bodyDecodedBinary();
02485               QValueList<int> dummy;
02486               attachPart->setBodyAndGuessCte(body, dummy, false, true);
02487               kdDebug(5006) << "Changed encoding of message part from "
02488                             << cte << " to " << attachPart->cteStr() << endl;
02489             }
02490             DwBodyPart* innerDwPart = msg->createDWBodyPart( attachPart );
02491             innerDwPart->Assemble();
02492             QCString encodedAttachment = innerDwPart->AsString().c_str();
02493             delete innerDwPart;
02494             innerDwPart = 0;
02495 
02496             if( (0 <= mSelectedCryptPlug->libName().find( "smime",   0, false )) ||
02497                 (0 <= mSelectedCryptPlug->libName().find( "openpgp", 0, false )) ) {
02498               // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
02499               // according to RfC 2633, 3.1.1 Canonicalization
02500               kdDebug(5006) << "Converting LF to CRLF (see RfC 2633, 3.1.1 Canonicalization)" << endl;
02501               encodedAttachment = KMMessage::lf2crlf( encodedAttachment );
02502               kdDebug(5006) << "                                                       done." << endl;
02503             }
02504 
02505             // sign this attachment
02506             if( signThisNow ) {
02507 kdDebug(5006) << "                                 sign " << idx << ". attachment separately" << endl;
02508               StructuringInfoWrapper structuring( mSelectedCryptPlug );
02509 
02510               QByteArray signature = pgpSignedMsg( encodedAttachment,
02511                                                    structuring,
02512                                                    signCertFingerprint );
02513               result = signature.isEmpty() ? Kpgp::Failure : Kpgp::Ok;
02514               if( Kpgp::Ok == result ) {
02515                 result = processStructuringInfo( QString::fromUtf8( mSelectedCryptPlug->bugURL() ),
02516                                               previousBoundaryLevel + 10 + idx,
02517                                               attachPart->contentDescription(),
02518                                               attachPart->typeStr(),
02519                                               attachPart->subtypeStr(),
02520                                               attachPart->contentDisposition(),
02521                                               attachPart->contentTransferEncodingStr(),
02522                                               encodedAttachment,
02523                                               "signature",
02524                                               signature,
02525                                               structuring,
02526                                               newAttachPart ) ? Kpgp::Ok : Kpgp::Failure;
02527                 if( Kpgp::Ok == result ) {
02528                   if( newAttachPart.name().isEmpty() )
02529                     newAttachPart.setName("signed attachment");
02530                   if( encryptThisNow ) {
02531                     rEncryptMessagePart = newAttachPart;
02532                     DwBodyPart* dwPart = msg->createDWBodyPart( &newAttachPart );
02533                     dwPart->Assemble();
02534                     encodedAttachment = dwPart->AsString().c_str();
02535                     delete dwPart;
02536                     dwPart = 0;
02537                   }
02538                 } else
02539                     KMessageBox::sorry(this, mErrorProcessingStructuringInfo );
02540               } else {
02541                 // quit the attachments' loop
02542                 break;
02543               }
02544             }
02545             if( encryptThisNow ) {
02546 kdDebug(5006) << "                                 encrypt " << idx << ". attachment separately" << endl;
02547               StructuringInfoWrapper structuring( mSelectedCryptPlug );
02548               QByteArray encryptedBody;
02549               result = pgpEncryptedMsg( encryptedBody, encodedAttachment,
02550                                         structuring,
02551                                         encryptCertFingerprints );
02552 
02553               if( Kpgp::Ok == result ) {
02554                 result = processStructuringInfo( QString::fromUtf8( mSelectedCryptPlug->bugURL() ),
02555                                               previousBoundaryLevel + 11 + idx,
02556                                               rEncryptMessagePart.contentDescription(),
02557                                               rEncryptMessagePart.typeStr(),
02558                                               rEncryptMessagePart.subtypeStr(),
02559                                               rEncryptMessagePart.contentDisposition(),
02560                                               rEncryptMessagePart.contentTransferEncodingStr(),
02561                                               encodedAttachment,
02562                                               "encrypted data",
02563                                               encryptedBody,
02564                                               structuring,
02565                                               newAttachPart ) ? Kpgp::Ok : Kpgp::Failure;
02566                 if( Kpgp::Ok == result ) {
02567                   if( newAttachPart.name().isEmpty() ) {
02568                     newAttachPart.setName("encrypted attachment");
02569                   }
02570                 } else if ( Kpgp::Failure == result )
02571                   KMessageBox::sorry(this, mErrorProcessingStructuringInfo);
02572               }
02573             }
02574             msg->addBodyPart( &newAttachPart );
02575           } else
02576             msg->addBodyPart( attachPart );
02577 
02578 kdDebug(5006) << "                                 added " << idx << ". attachment to this Multipart/Mixed" << endl;
02579         } else {
02580 kdDebug(5006) << "                                 " << idx << ". attachment was part of the BODY already" << endl;
02581         }
02582       }
02583     } else {
02584       if( ourFineBodyPart.originalContentTypeStr() ) {
02585         //msg->headers().Assemble();
02586         //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      A.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02587         msg->headers().ContentType().FromString( ourFineBodyPart.originalContentTypeStr() );
02588         //msg->headers().Assemble();
02589         //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      B.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02590         msg->headers().ContentType().Parse();
02591         //msg->headers().Assemble();
02592         //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      C.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02593 kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type from originalContentTypeStr()" << endl;
02594       } else {
02595         msg->headers().ContentType().FromString( ourFineBodyPart.typeStr() + "/" + ourFineBodyPart.subtypeStr() );
02596 kdDebug(5006) << "KMComposeWin::encryptMessage() : set top level Content-Type from typeStr()/subtypeStr()" << endl;
02597       }
02598       //msg->headers().Assemble();
02599       //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      D.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02600       if ( !ourFineBodyPart.charset().isEmpty() )
02601         msg->setCharset( ourFineBodyPart.charset() );
02602       //msg->headers().Assemble();
02603       //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      E.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02604       msg->setHeaderField( "Content-Transfer-Encoding",
02605                             ourFineBodyPart.contentTransferEncodingStr() );
02606       //msg->headers().Assemble();
02607       //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      F.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02608       msg->setHeaderField( "Content-Description",
02609                             ourFineBodyPart.contentDescription() );
02610       msg->setHeaderField( "Content-Disposition",
02611                             ourFineBodyPart.contentDisposition() );
02612 
02613 kdDebug(5006) << "KMComposeWin::encryptMessage() : top level headers and body adjusted" << endl;
02614 
02615       // set body content
02616       // msg->setBody( ourFineBodyPart.body() );
02617       msg->setMultiPartBody( ourFineBodyPart.body() );
02618       //kdDebug(5006) << "\n\n\n\n\n\n\nKMComposeWin::composeMessage():\n      99.:\n\n\n\n|||" << msg->asString() << "|||\n\n\n\n\n\n" << endl;
02619       //msg->headers().Assemble();
02620       //kdDebug(5006) << "\n\n\nKMComposeWin::composeMessage():\n      Z.:\n\n" << msg->headerAsString() << "|||\n\n\n\n\n" << endl;
02621     }
02622 
02623   }
02624   return result;
02625 }
02626 
02627 //-----------------------------------------------------------------------------
02628 bool KMComposeWin::processStructuringInfo( const QString   bugURL,
02629                                            uint            boundaryLevel,
02630                                            const QString   contentDescClear,
02631                                            const QCString  contentTypeClear,
02632                                            const QCString  contentSubtypeClear,
02633                                            const QCString  contentDispClear,
02634                                            const QCString  contentTEncClear,
02635                                            const QCString& clearCStr,
02636                                            const QString   contentDescCiph,
02637                                            const QByteArray& ciphertext,
02638                                            const StructuringInfoWrapper& structuring,
02639                                            KMMessagePart&  resultingPart )
02640 {
02641 #ifdef DEBUG
02642   kdDebug(5006) << "||| entering KMComposeWin::processStructuringInfo()" << endl;
02643 #endif
02644   //assert(mMsg!=0);
02645   if(!mMsg)
02646   {
02647     kdDebug(5006) << "KMComposeWin::processStructuringInfo() : mMsg == 0!\n" << endl;
02648     return FALSE;
02649   }
02650 
02651   bool bOk = true;
02652 
02653   if( structuring.data.makeMimeObject ) {
02654 
02655     QCString mainHeader;
02656 
02657     if(    structuring.data.contentTypeMain
02658         && 0 < strlen( structuring.data.contentTypeMain ) ) {
02659       mainHeader = "Content-Type: ";
02660       mainHeader += structuring.data.contentTypeMain;
02661     } else {
02662       mainHeader = "Content-Type: ";
02663       if( structuring.data.makeMultiMime )
02664         mainHeader += "text/plain";
02665       else {
02666         mainHeader += contentTypeClear;
02667         mainHeader += '/';
02668         mainHeader += contentSubtypeClear;
02669       }
02670     }
02671 
02672     QCString boundaryCStr;  // storing boundary string data
02673     // add "boundary" parameter
02674 
02675     if( structuring.data.makeMultiMime ) {
02676 
02677       // calculate boundary value
02678       DwMediaType tmpCT;
02679       tmpCT.CreateBoundary( boundaryLevel );
02680       boundaryCStr = tmpCT.Boundary().c_str();
02681       // remove old "boundary" parameter
02682       int boundA = mainHeader.find("boundary=", 0,false);
02683       int boundZ;
02684       if( -1 < boundA ) {
02685         // take into account a leading ";  " string
02686         while(    0 < boundA
02687                && ' ' == mainHeader[ boundA-1 ] )
02688             --boundA;
02689         if(    0 < boundA
02690             && ';' == mainHeader[ boundA-1 ] )
02691             --boundA;
02692         boundZ = mainHeader.find(';', boundA+1);
02693         if( -1 == boundZ )
02694           mainHeader.truncate( boundA );
02695         else
02696           mainHeader.remove( boundA, (1 + boundZ - boundA) );
02697       }
02698       // insert new "boundary" parameter
02699       QCString bStr( ";boundary=\"" );
02700       bStr += boundaryCStr;
02701       bStr += "\"";
02702       mainHeader += bStr;
02703     }
02704 
02705     if(    structuring.data.contentTypeMain
02706         && 0 < strlen( structuring.data.contentTypeMain ) ) {
02707 
02708       if(    structuring.data.contentDispMain
02709           && 0 < strlen( structuring.data.contentDispMain ) ) {
02710         mainHeader += "\nContent-Disposition: ";
02711         mainHeader += structuring.data.contentDispMain;
02712       }
02713       if(    structuring.data.contentTEncMain
02714           && 0 < strlen( structuring.data.contentTEncMain ) ) {
02715 
02716         mainHeader += "\nContent-Transfer-Encoding: ";
02717         mainHeader += structuring.data.contentTEncMain;
02718       }
02719 
02720     } else {
02721       if( 0 < contentDescClear.length() ) {
02722         mainHeader += "\nContent-Description: ";
02723         mainHeader += contentDescClear.utf8();
02724       }
02725       if( 0 < contentDispClear.length() ) {
02726         mainHeader += "\nContent-Disposition: ";
02727         mainHeader += contentDispClear;
02728       }
02729       if( 0 < contentTEncClear.length() ) {
02730         mainHeader += "\nContent-Transfer-Encoding: ";
02731         mainHeader += contentTEncClear;
02732       }
02733     }
02734 
02735 
02736     DwString mainDwStr;
02737     mainDwStr = mainHeader;
02738     DwBodyPart mainDwPa( mainDwStr, 0 );
02739     mainDwPa.Parse();
02740     KMMessage::bodyPart(&mainDwPa, &resultingPart);
02741 /*
02742 kdDebug(5006) << "***************************************" << endl;
02743 kdDebug(5006) << "***************************************" << endl;
02744 kdDebug(5006) << "***************************************" << endl;
02745 kdDebug(5006) << mainHeader << endl;
02746 kdDebug(5006) << "***************************************" << endl;
02747 kdDebug(5006) << "***************************************" << endl;
02748 kdDebug(5006) << "***************************************" << endl;
02749 kdDebug(5006) << resultingPart.additionalCTypeParamStr() << endl;
02750 kdDebug(5006) << "***************************************" << endl;
02751 kdDebug(5006) << "***************************************" << endl;
02752 kdDebug(5006) << "***************************************" << endl;
02753 */
02754     if( ! structuring.data.makeMultiMime ) {
02755 
02756       if( structuring.data.includeCleartext ) {
02757         QCString bodyText( clearCStr );
02758         bodyText += '\n';
02759         bodyText += ciphertext;
02760         resultingPart.setBodyEncoded( bodyText );
02761       } else
02762         resultingPart.setBodyEncodedBinary( ciphertext );
02763 
02764     } else { //  OF  if( ! structuring.data.makeMultiMime )
02765 
02766       QCString versCStr, codeCStr;
02767 
02768       // Build the encapsulated MIME parts.
02769 
02770 /*
02771       if( structuring.data.includeCleartext ) {
02772         // Build a MIME part holding the cleartext.
02773         // using the original cleartext's headers and by
02774         // taking it's original body text.
02775         KMMessagePart clearKmPa;
02776         clearKmPa.setContentDescription(         contentDescClear    );
02777         clearKmPa.setTypeStr(                    contentTypeClear    );
02778         clearKmPa.setSubtypeStr(                 contentSubtypeClear );
02779         clearKmPa.setContentDisposition(         contentDispClear    );
02780         clearKmPa.setContentTransferEncodingStr( contentTEncClear    );
02781         // store string representation of the cleartext headers
02782         DwBodyPart* tmpDwPa = mMsg->createDWBodyPart( &clearKmPa );
02783         tmpDwPa->Headers().SetModified();
02784         tmpDwPa->Headers().Assemble();
02785         clearCStr = tmpDwPa->Headers().AsString().c_str();
02786         delete tmpDwPa;
02787         tmpDwPa = 0;
02788         // store string representation of encoded cleartext
02789         clearKmPa.setBodyEncoded( cleartext );
02790         clearCStr += clearKmPa.body();
02791       }
02792 */
02793 
02794       // Build a MIME part holding the version information
02795       // taking the body contents returned in
02796       // structuring.data.bodyTextVersion.
02797       if(    structuring.data.contentTypeVersion
02798           && 0 < strlen( structuring.data.contentTypeVersion ) ) {
02799 
02800         DwString versStr( "Content-Type: " );
02801         versStr += structuring.data.contentTypeVersion;
02802 
02803         versStr += "\nContent-Description: ";
02804         versStr += "version code";
02805 
02806         if(    structuring.data.contentDispVersion
02807             && 0 < strlen( structuring.data.contentDispVersion ) ) {
02808           versStr += "\nContent-Disposition: ";
02809           versStr += structuring.data.contentDispVersion;
02810         }
02811         if(    structuring.data.contentTEncVersion
02812             && 0 < strlen( structuring.data.contentTEncVersion ) ) {
02813           versStr += "\nContent-Transfer-Encoding: ";
02814           versStr += structuring.data.contentTEncVersion;
02815         }
02816 
02817         DwBodyPart versDwPa( versStr, 0 );
02818         versDwPa.Parse();
02819         KMMessagePart versKmPa;
02820         KMMessage::bodyPart(&versDwPa, &versKmPa);
02821         versKmPa.setBodyEncoded( structuring.data.bodyTextVersion );
02822         // store string representation of the cleartext headers
02823         versCStr = versDwPa.Headers().AsString().c_str();
02824         // store string representation of encoded cleartext
02825         versCStr += "\n\n";
02826         versCStr += versKmPa.body();
02827       }
02828 
02829       // Build a MIME part holding the code information
02830       // taking the body contents returned in ciphertext.
02831       if(    structuring.data.contentTypeCode
02832           && 0 < strlen( structuring.data.contentTypeCode ) ) {
02833 
02834         DwString codeStr( "Content-Type: " );
02835         codeStr += structuring.data.contentTypeCode;
02836         if(    structuring.data.contentTEncCode
02837             && 0 < strlen( structuring.data.contentTEncCode ) ) {
02838           codeStr += "\nContent-Transfer-Encoding: ";
02839           codeStr += structuring.data.contentTEncCode;
02840         //} else {
02841         //  codeStr += "\nContent-Transfer-Encoding: ";
02842         //  codeStr += "base64";
02843         }
02844         if( !contentDescCiph.isEmpty() ) {
02845           codeStr += "\nContent-Description: ";
02846           codeStr += contentDescCiph.utf8();
02847         }
02848         if(    structuring.data.contentDispCode
02849             && 0 < strlen( structuring.data.contentDispCode ) ) {
02850           codeStr += "\nContent-Disposition: ";
02851           codeStr += structuring.data.contentDispCode;
02852         }
02853 
02854         DwBodyPart codeDwPa( codeStr, 0 );
02855         codeDwPa.Parse();
02856         KMMessagePart codeKmPa;
02857         KMMessage::bodyPart(&codeDwPa, &codeKmPa);
02858         //if(    structuring.data.contentTEncCode
02859         //    && 0 < strlen( structuring.data.contentTEncCode ) ) {
02860         //  codeKmPa.setCteStr( structuring.data.contentTEncCode );
02861         //} else {
02862         //  codeKmPa.setCteStr("base64");
02863         //}
02864         codeKmPa.setBodyEncodedBinary( ciphertext );
02865         // store string representation of the cleartext headers
02866         codeCStr = codeDwPa.Headers().AsString().c_str();
02867         // store string representation of encoded cleartext
02868         codeCStr += "\n\n";
02869         codeCStr += codeKmPa.body();
02870 #if 0
02871         kdDebug(5006) << "***************************************" << endl;
02872         kdDebug(5006) << "***************************************" << endl;
02873         kdDebug(5006) << codeCStr << endl;
02874         kdDebug(5006) << "***************************************" << endl;
02875         kdDebug(5006) << "***************************************" << endl;
02876 #endif
02877       } else {
02878 
02879         // Plugin error!
02880         KMessageBox::sorry( this,
02881           i18n("<qt><p>Error: The Crypto Plug-in '%1' returned<br>"
02882                "       \" structuring.makeMultiMime \"<br>"
02883                "but did <b>not</b> specify a Content-Type header "
02884                "for the ciphertext that was generated.</p>"
02885                "<p>Please report this bug:<br>%2</p></qt>")
02886           .arg(mSelectedCryptPlug->libName())
02887           .arg(bugURL) );
02888         bOk = false;
02889       }
02890 
02891       QCString mainStr;
02892 
02893       mainStr  = "--";
02894       mainStr +=       boundaryCStr;
02895 
02896       if( structuring.data.includeCleartext && (0 < clearCStr.length()) ) {
02897         mainStr += "\n";
02898         mainStr +=     clearCStr;
02899         mainStr += "\n--";
02900         mainStr +=       boundaryCStr;
02901       }
02902       if( 0 < versCStr.length() ) {
02903         mainStr += "\n";
02904         mainStr +=     versCStr;
02905         mainStr += "\n\n--";
02906         mainStr +=       boundaryCStr;
02907       }
02908       if( 0 < codeCStr.length() ) {
02909         mainStr += "\n";
02910         mainStr +=     codeCStr;
02911         // add the closing boundary string
02912         mainStr += "\n--";
02913         mainStr +=       boundaryCStr;
02914       }
02915       mainStr +=                     "--\n";
02916 
02917       resultingPart.setBodyEncoded( mainStr );
02918 
02919     } //  OF  if( ! structuring.data.makeMultiMime ) .. else
02920 
02921     /*
02922     resultingData += mainHeader;
02923     resultingData += '\n';
02924     resultingData += mainKmPa.body();
02925     */
02926 
02927   } else { //  OF  if( structuring.data.makeMimeObject )
02928 
02929     // Build a plain message body
02930     // based on the values returned in structInf.
02931     // Note: We do _not_ insert line breaks between the parts since
02932     //       it is the plugin job to provide us with ready-to-use
02933     //       texts containing all necessary line breaks.
02934     resultingPart.setContentDescription(         contentDescClear    );
02935     resultingPart.setTypeStr(                    contentTypeClear    );
02936     resultingPart.setSubtypeStr(                 contentSubtypeClear );
02937     resultingPart.setContentDisposition(         contentDispClear    );
02938     resultingPart.setContentTransferEncodingStr( contentTEncClear    );
02939     QCString resultingBody;
02940 
02941     if(    structuring.data.flatTextPrefix
02942         && strlen( structuring.data.flatTextPrefix ) )
02943       resultingBody += structuring.data.flatTextPrefix;
02944     if( structuring.data.includeCleartext ) {
02945       if( !clearCStr.isEmpty() )
02946         resultingBody += clearCStr;
02947       if(    structuring.data.flatTextSeparator
02948           && strlen( structuring.data.flatTextSeparator ) )
02949         resultingBody += structuring.data.flatTextSeparator;
02950     }
02951     if(    ciphertext
02952         && strlen( ciphertext ) )
02953       resultingBody += *ciphertext;
02954     else {
02955         // Plugin error!
02956         KMessageBox::sorry(this,
02957           i18n("<qt><p>Error: The Crypto Plug-in '%1' did not return "
02958                "any encoded data.</p>"
02959                "<p>Please report this bug:<br>%2</p></qt>")
02960           .arg(mSelectedCryptPlug->libName())
02961           .arg(bugURL) );
02962         bOk = false;
02963     }
02964     if(    structuring.data.flatTextPostfix
02965         && strlen( structuring.data.flatTextPostfix ) )
02966       resultingBody += structuring.data.flatTextPostfix;
02967 
02968     resultingPart.setBodyEncoded( resultingBody );
02969 
02970   } //  OF  if( structuring.data.makeMimeObject ) .. else
02971 
02972   // No need to free the memory that was allocated for the ciphertext
02973   // since this memory is freed by it's QCString destructor.
02974 
02975   // Neither do we free the memory that was allocated
02976   // for our structuring info data's char* members since we are using
02977   // not the pure cryptplug's StructuringInfo struct
02978   // but the convenient CryptPlugWrapper's StructuringInfoWrapper class.
02979 
02980 #ifdef DEBUG
02981   kdDebug(5006) << "||| leaving KMComposeWin::processStructuringInfo()\n||| returning: " << bOk << endl;
02982 #endif
02983 
02984   return bOk;
02985 }
02986 
02987 //-----------------------------------------------------------------------------
02988 QCString KMComposeWin::breakLinesAndApplyCodec()
02989 {
02990   QString text;
02991   QCString cText;
02992 
02993   if (mDisableBreaking)
02994       text = mEditor->text();
02995   else
02996       text = mEditor->brokenText();
02997 
02998   text.truncate(text.length()); // to ensure text.size()==text.length()+1
02999 
03000   {
03001     // Provide a local scope for newText.
03002     QString newText;
03003     const QTextCodec *codec = KMMsgBase::codecForName(mCharset);
03004 
03005     if (mCharset == "us-ascii") {
03006       cText = KMMsgBase::toUsAscii(text);
03007       newText = QString::fromLatin1(cText);
03008     } else if (codec == 0) {
03009       kdDebug(5006) << "Something is wrong and I can not get a codec." << endl;
03010       cText = text.local8Bit();
03011       newText = QString::fromLocal8Bit(cText);
03012     } else {
03013       cText = codec->fromUnicode(text);
03014       newText = codec->toUnicode(cText);
03015     }
03016     if (cText.isNull()) cText = "";
03017 
03018     if (!text.isEmpty() && (newText != text))
03019     {
03020       QString oldText = mEditor->text();
03021       mEditor->setText(newText);
03022       KCursorSaver idle(KBusyPtr::idle());
03023       bool anyway = (KMessageBox::warningYesNo(this,
03024                                                i18n("<qt>Not all characters fit into the chosen"
03025                                                     " encoding.<br><br>Send the message anyway?</qt>"),
03026                                                i18n("Some characters will be lost"),
03027                                                KStdGuiItem::yes(), i18n("No, let me change the encoding") ) == KMessageBox::Yes);
03028       if (!anyway)
03029       {
03030         mEditor->setText(oldText);
03031         return QCString();
03032       }
03033     }
03034   }
03035 
03036   return cText;
03037 }
03038 
03039 
03040 //-----------------------------------------------------------------------------
03041 QByteArray KMComposeWin::pgpSignedMsg( QCString cText,
03042                                        StructuringInfoWrapper& structuring,
03043                                        QCString& signCertFingerprint )
03044 {
03045   QByteArray signature;
03046 
03047   // we call the cryptplug for signing
03048   if( mSelectedCryptPlug ) {
03049     kdDebug(5006) << "\nKMComposeWin::pgpSignedMsg calling CRYPTPLUG "
03050                   << mSelectedCryptPlug->libName() << endl;
03051 
03052     bool bSign = true;
03053 
03054     if( signCertFingerprint.isEmpty() ) {
03055       // find out whether we are dealing with the OpenPGP or the S/MIME plugin
03056       if( -1 != mSelectedCryptPlug->libName().find( "openpgp" ) ) {
03057         // We are dealing with the OpenPGP plugin. Use Kpgp to determine
03058         // the signing key.
03059         // get the OpenPGP key ID for the chosen identity
03060         const KMIdentity & ident =
03061           kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
03062         QCString userKeyId = ident.pgpIdentity();
03063         if( !userKeyId.isEmpty() ) {
03064           Kpgp::Module *pgp = Kpgp::Module::getKpgp();
03065           Kpgp::Key* key = pgp->publicKey( userKeyId );
03066           if( key ) {
03067             signCertFingerprint = key->primaryFingerprint();
03068             kdDebug(5006) << "                          Signer: " << from()
03069                           << "\nFingerprint of signature key: "
03070                           << QString( signCertFingerprint ) << endl;
03071           }
03072           else {
03073             KMessageBox::sorry( this,
03074                                 i18n("<qt>This message could not be signed "
03075                                      "because the OpenPGP key which should be "
03076                                      "used for signing messages with this "
03077                                      "identity couldn't be found in your "
03078                                      "keyring.<br><br>"
03079                                      "You can change the OpenPGP key "
03080                                      "which should be used with the current "
03081                                      "identity in the identity configuration.</qt>"),
03082                                 i18n("Missing Signing Key") );
03083             bSign = false;
03084           }
03085         }
03086         else {
03087           KMessageBox::sorry( this,
03088                               i18n("<qt>This message could not be signed "
03089                                    "because you didn't define the OpenPGP "
03090                                    "key which should be used for signing "
03091                                    "messages with this identity.<br><br>"
03092                                    "You can define the OpenPGP key "
03093                                    "which should be used with the current "
03094                                    "identity in the identity configuration.</qt>"),
03095                               i18n("Undefined Signing Key") );
03096           bSign = false;
03097         }
03098       }
03099       else { // S/MIME
03100         int certSize = 0;
03101         QByteArray certificate;
03102         QString selectedCert;
03103         KListBoxDialog dialog( selectedCert, "", i18n( "&Select certificate:") );
03104         dialog.resize( 700, 200 );
03105 
03106         QCString signer = from().utf8();
03107         signer.replace('\x001', ' ');
03108 
03109         kdDebug(5006) << "\n\nRetrieving keys for: " << from() << endl;
03110         char* certificatePtr = 0;
03111         bool findCertsOk = mSelectedCryptPlug->findCertificates(
03112                                             &(*signer),
03113                                             &certificatePtr,
03114                                             &certSize,
03115                                             true )
03116                           && (0 < certSize);
03117         kdDebug(5006) << "keys retrieved ok: " << findCertsOk << endl;
03118 
03119         bool useDialog = false;
03120         if( findCertsOk ) {
03121             kdDebug(5006) << "findCertificates() returned " << certificatePtr << endl;
03122             certificate.assign( certificatePtr, certSize );
03123 
03124             // fill selection dialog listbox
03125             dialog.entriesLB->clear();
03126             int iA = 0;
03127             int iZ = 0;
03128             while( iZ < certSize ) {
03129                 if( (certificate[iZ] == '\1') || (certificate[iZ] == '\0') ) {
03130                     char c = certificate[iZ];
03131                     if( (c == '\1') && !useDialog ) {
03132                         // set up selection dialog
03133                         useDialog = true;
03134                         dialog.setCaption( i18n("Select Certificate [%1]")
03135                                            .arg( from() ) );
03136                     }
03137                     certificate[iZ] = '\0';
03138                     QString s = QString::fromUtf8( &certificate[iA] );
03139                     certificate[iZ] = c;
03140                     if( useDialog )
03141                         dialog.entriesLB->insertItem( s );
03142                     else
03143                         selectedCert = s;
03144                     ++iZ;
03145                     iA = iZ;
03146                 }
03147                 ++iZ;
03148             }
03149 
03150             // run selection dialog and retrieve user choice
03151             // OR take the single entry (if only one was found)
03152             if( useDialog ) {
03153                 dialog.entriesLB->setFocus();
03154                 dialog.entriesLB->setSelected( 0, true );
03155                 bSign = (dialog.exec() == QDialog::Accepted);
03156             }
03157 
03158             if (bSign) {
03159                 signCertFingerprint = selectedCert.utf8();
03160                 signCertFingerprint.remove( 0, signCertFingerprint.findRev( '(' )+1 );
03161                 signCertFingerprint.truncate( signCertFingerprint.length()-1 );
03162                 kdDebug(5006) << "\n\n                      Signer: " << from()
03163                               <<   "\nFingerprint of signature key: " << QString( signCertFingerprint ) << "\n\n" << endl;
03164                 if( signCertFingerprint.isEmpty() )
03165                     bSign = false;
03166             }
03167         }
03168       }
03169 
03170 /* ----------------------------- */
03171 #ifdef DEBUG
03172         QString ds( "\n\nBEFORE calling cryptplug:" );
03173         ds += "\nstructuring.contentTypeMain:   \"";
03174         ds += structuring.data.contentTypeMain;
03175         ds += "\"";
03176         ds += "\nstructuring.contentTypeVersion:\"";
03177         ds += structuring.data.contentTypeVersion;
03178         ds += "\"";
03179         ds += "\nstructuring.contentTypeCode:   \"";
03180         ds += structuring.data.contentTypeCode;
03181         ds += "\"";
03182         ds += "\nstructuring.flatTextPrefix:    \"";
03183         ds += structuring.data.flatTextPrefix;
03184         ds += "\"";
03185         ds += "\nstructuring.flatTextSeparator: \"";
03186         ds += structuring.data.flatTextSeparator;
03187         ds += "\"";
03188         ds += "\nstructuring.flatTextPostfix:   \"";
03189         ds += structuring.data.flatTextPostfix;
03190         ds += "\"";
03191         kdDebug(5006) << ds << endl;
03192 #endif
03193 
03194         // Check for expiry of the signer, CA, and Root certificate.
03195         // Only do the expiry check if the plugin has this feature
03196         // and if there in *no* fingerprint in signCertFingerprint already.
03197         if( mSelectedCryptPlug->hasFeature( Feature_WarnSignCertificateExpiry ) ){
03198             int sigDaysLeft = mSelectedCryptPlug->signatureCertificateDaysLeftToExpiry( signCertFingerprint );
03199             if( mSelectedCryptPlug->signatureCertificateExpiryNearWarning() &&
03200                 sigDaysLeft <
03201                 mSelectedCryptPlug->signatureCertificateExpiryNearInterval() ) {
03202                 QString txt1;
03203                 if( 0 < sigDaysLeft )
03204                     txt1 = i18n( "The certificate you want to use for signing expires in %1 days.<br>This means that after this period, the recipients will not be able to check your signature any longer." ).arg( sigDaysLeft );
03205                 else if( 0 > sigDaysLeft )
03206                     txt1 = i18n( "The certificate you want to use for signing expired %1 days ago.<br>This means that the recipients will not be able to check your signature." ).arg( -sigDaysLeft );
03207                 else
03208                     txt1 = i18n( "The certificate you want to use for signing expires today.<br>This means that, starting from tomorrow, the recipients will not be able to check your signature any longer." );
03209                 int ret = KMessageBox::warningYesNo( this,
03210                             i18n( "<qt><p>%1</p>"
03211                                   "<p>Do you still want to use this "
03212                                   "certificate?</p></qt>" )
03213                             .arg( txt1 ),
03214                             i18n( "Certificate Warning" ),
03215                             KGuiItem( i18n("&Use Certificate") ),
03216                             KGuiItem( i18n("&Don't Use Certificate") ) );
03217                 if( ret == KMessageBox::No )
03218                     bSign = false;
03219             }
03220 
03221             if( bSign && ( 0 <= mSelectedCryptPlug->libName().find( "smime", 0, false ) ) ) {
03222                 int rootDaysLeft = mSelectedCryptPlug->rootCertificateDaysLeftToExpiry( signCertFingerprint );
03223                 if( mSelectedCryptPlug->rootCertificateExpiryNearWarning() &&
03224                     rootDaysLeft <
03225                     mSelectedCryptPlug->rootCertificateExpiryNearInterval() ) {
03226                     QString txt1;
03227                     if( 0 < rootDaysLeft )
03228                         txt1 = i18n( "The root certificate of the certificate you want to use for signing expires in %1 days.<br>This means that after this period, the recipients will not be able to check your signature any longer." ).arg( rootDaysLeft );
03229                     else if( 0 > rootDaysLeft )
03230                         txt1 = i18n( "The root certificate of the certificate you want to use for signing expired %1 days ago.<br>This means that the recipients will not be able to check your signature." ).arg( -rootDaysLeft );
03231                     else
03232                         txt1 = i18n( "The root certificate of the certificate you want to use for signing expires today.<br>This means that beginning from tomorrow, the recipients will not be able to check your signature any longer." );
03233                     int ret = KMessageBox::warningYesNo( this,
03234                                 i18n( "<qt><p>%1</p>"
03235                                       "<p>Do you still want to use this "
03236                                       "certificate?</p></qt>" )
03237                                 .arg( txt1 ),
03238                                 i18n( "Certificate Warning" ),
03239                                 KGuiItem( i18n("&Use Certificate") ),
03240                                 KGuiItem( i18n("&Don't Use Certificate") ) );
03241                     if( ret == KMessageBox::No )
03242                         bSign = false;
03243                 }
03244             }
03245 
03246 
03247             if( bSign && ( 0 <= mSelectedCryptPlug->libName().find( "smime", 0, false ) ) ) {
03248                 int caDaysLeft = mSelectedCryptPlug->caCertificateDaysLeftToExpiry( signCertFingerprint );
03249                 if( mSelectedCryptPlug->caCertificateExpiryNearWarning() &&
03250                     caDaysLeft <
03251                     mSelectedCryptPlug->caCertificateExpiryNearInterval() ) {
03252                     QString txt1;
03253                     if( 0 < caDaysLeft )
03254                         txt1 = i18n( "The CA certificate of the certificate you want to use for signing expires in %1 days.<br>This means that after this period, the recipients will not be able to check your signature any longer." ).arg( caDaysLeft );
03255                     else if( 0 > caDaysLeft )
03256                         txt1 = i18n( "The CA certificate of the certificate you want to use for signing expired %1 days ago.<br>This means that the recipients will not be able to check your signature." ).arg( -caDaysLeft );
03257                     else
03258                         txt1 = i18n( "The CA certificate of the certificate you want to use for signing expires today.<br>This means that beginning from tomorrow, the recipients will not be able to check your signature any longer." );
03259                     int ret = KMessageBox::warningYesNo( this,
03260                                 i18n( "<qt><p>%1</p>"
03261                                       "<p>Do you still want to use this "
03262                                       "certificate?</p></qt>" )
03263                                 .arg( txt1 ),
03264                                 i18n( "Certificate Warning" ),
03265                                 KGuiItem( i18n("&Use Certificate") ),
03266                                 KGuiItem( i18n("&Don't Use Certificate") ) );
03267                     if( ret == KMessageBox::No )
03268                         bSign = false;
03269                 }
03270             }
03271         }
03272         // Check whether the sender address of the signer is contained in
03273         // the certificate, but only do this if the plugin has this feature.
03274         if( mSelectedCryptPlug->hasFeature( Feature_WarnSignEmailNotInCertificate ) ) {
03275             if( bSign && mSelectedCryptPlug->warnNoCertificate() &&
03276                 !mSelectedCryptPlug->isEmailInCertificate( QString( KMMessage::getEmailAddr( from() ) ).utf8(), signCertFingerprint ) )  {
03277                 QString txt1 = i18n( "The certificate you want to use for signing does not contain your sender email address.<br>This means that it is not possible for the recipients to check whether the email really came from you." );
03278                 int ret = KMessageBox::warningYesNo( this,
03279                             i18n( "<qt><p>%1</p>"
03280                                   "<p>Do you still want to use this "
03281                                   "certificate?</p></qt>" )
03282                             .arg( txt1 ),
03283                             i18n( "Certificate Warning" ),
03284                             KGuiItem( i18n("&Use Certificate") ),
03285                             KGuiItem( i18n("&Don't Use Certificate") ) );
03286                 if( ret == KMessageBox::No )
03287                     bSign = false;
03288             }
03289         }
03290     } // if( signCertFingerprint.isEmpty() )
03291 
03292 
03293     // Finally sign the message, but only if the plugin has this feature.
03294     if( mSelectedCryptPlug->hasFeature( Feature_SignMessages ) ) {
03295         size_t cipherLen;
03296 
03297         const char* cleartext = cText;
03298         char* ciphertext  = 0;
03299 
03300         if( mDebugComposerCrypto ){
03301             QFile fileS( "dat_11_sign.input" );
03302             if( fileS.open( IO_WriteOnly ) ) {
03303                 QDataStream ds( &fileS );
03304                 ds.writeRawBytes( cleartext, strlen( cleartext ) );
03305                 fileS.close();
03306             }
03307         }
03308 
03309         if ( bSign ){
03310             int   errId = 0;
03311             char* errTxt = 0;
03312             if ( mSelectedCryptPlug->signMessage( cleartext,
03313                                          &ciphertext, &cipherLen,
03314                                          signCertFingerprint,
03315                                          structuring,
03316                                          &errId,
03317                                          &errTxt ) ){
03318                 if( mDebugComposerCrypto ){
03319                     QFile fileD( "dat_12_sign.output" );
03320                     if( fileD.open( IO_WriteOnly ) ) {
03321                         QDataStream ds( &fileD );
03322                         ds.writeRawBytes( ciphertext, cipherLen );
03323                         fileD.close();
03324                     }
03325                     QString ds( "\nAFTER calling cryptplug:" );
03326                     ds += "\nstructuring.contentTypeMain:   \"";
03327                     ds += structuring.data.contentTypeMain;
03328                     ds += "\"";
03329                     ds += "\nstructuring.contentTypeVersion:\"";
03330                     ds += structuring.data.contentTypeVersion;
03331                     ds += "\"";
03332                     ds += "\nstructuring.contentTypeCode:   \"";
03333                     ds += structuring.data.contentTypeCode;
03334                     ds += "\"";
03335                     ds += "\nstructuring.flatTextPrefix:    \"";
03336                     ds += structuring.data.flatTextPrefix;
03337                     ds += "\"";
03338                     ds += "\nstructuring.flatTextSeparator: \"";
03339                     ds += structuring.data.flatTextSeparator;
03340                     ds += "\"";
03341                     ds += "\nstructuring.flatTextPostfix:   \"";
03342                     ds += structuring.data.flatTextPostfix;
03343                     ds += "\"";
03344                     ds += "\n\nresulting signature bloc:\n\"";
03345                     ds += ciphertext;
03346                     ds += "\"\n\n";
03347                     ds += "signature length: ";
03348                     ds += cipherLen;
03349                     kdDebug(5006) << ds << endl << endl;
03350                 }
03351                 signature.assign( ciphertext, cipherLen );
03352             } else if ( errId == /*GPGME_Canceled*/20 ) {
03353                 return false;
03354             } else {
03355                 QString error("#");
03356                 error += QString::number( errId );
03357                 error += "  :  ";
03358                 if( errTxt )
03359                   error += errTxt;
03360                 else
03361                   error += i18n("[unknown error]");
03362                 KMessageBox::sorry(this,
03363                   i18n("<qt><p><b>This message could not be signed!</b></p>"
03364                        "<p>The Crypto Plug-In '%1' reported the following "
03365                        "details:</p>"
03366                        "<p><i>%2</i></p>"
03367                        "<p>Your configuration might be invalid or the Plug-In "
03368                        "damaged.</p>"
03369                        "<p><b>Please contact your system "
03370                        "administrator.</b></p></qt>")
03371                   .arg(mSelectedCryptPlug->libName())
03372                   .arg( error ) );
03373             }
03374             // we do NOT call a "delete ciphertext" !
03375             // since "signature" will take care for it (is a QByteArray)
03376             delete errTxt;
03377             errTxt = 0;
03378         }
03379     }
03380 
03381     // PENDING(khz,kalle) Warn if there was no signature? (because of
03382     // a problem or because the plugin does not allow signing?
03383 
03384 /* ----------------------------- */
03385 
03386 
03387     kdDebug(5006) << "\nKMComposeWin::pgpSignedMsg returning from CRYPTPLUG.\n" << endl;
03388   } else
03389       KMessageBox::sorry(this,
03390         i18n("<qt>No active Crypto Plug-In could be found.<br><br>"
03391              "Please activate a Plug-In in the configuration dialog.</qt>"));
03392   return signature;
03393 }
03394 
03395 
03396 //-----------------------------------------------------------------------------
03397 Kpgp::Result KMComposeWin::pgpEncryptedMsg( QByteArray & encryptedBody,
03398                                             QCString cText,
03399                                             StructuringInfoWrapper& structuring,
03400                                             QCString& encryptCertFingerprints )
03401 {
03402   Kpgp::Result result = Kpgp::Ok;
03403 
03404   // we call the cryptplug
03405   if( mSelectedCryptPlug ) {
03406     kdDebug(5006) << "\nKMComposeWin::pgpEncryptedMsg: going to call CRYPTPLUG "
03407                   << mSelectedCryptPlug->libName() << endl;
03408 
03409 
03410 #if 0
03411     // ### This has been removed since according to the Sphinx specs the CRLs
03412     // have to be refreshed every day. This means warning that the CRL will
03413     // expire in one day is pointless. Disabling this has been recommended
03414     // by Karl-Heinz Zimmer.
03415 
03416     // Check for CRL expiry, but only if the plugin has this
03417     // feature.
03418     if( encryptCertFingerprints.isEmpty() &&
03419         mSelectedCryptPlug->hasFeature( Feature_WarnEncryptCertificateExpiry ) &&
03420         mSelectedCryptPlug->hasFeature( Feature_EncryptionCRLs ) ) {
03421         int crlDaysLeft = mSelectedCryptPlug->encryptionCRLsDaysLeftToExpiry();
03422         if( mSelectedCryptPlug->encryptionUseCRLs() &&
03423             mSelectedCryptPlug->encryptionCRLExpiryNearWarning() &&
03424             crlDaysLeft <
03425             mSelectedCryptPlug->encryptionCRLNearExpiryInterval() ) {
03426             int ret = KMessageBox::warningYesNo( this,
03427                         i18n( "<qt><p>The certification revocation lists, that "
03428                               "are used for checking the validity of the "
03429                               "certificate you want to use for encrypting, "
03430                               "expire in %1 days.</p>"
03431                               "<p>Do you still want to encrypt this message?"
03432                               "</p></qt>" )
03433                         .arg( crlDaysLeft ),
03434                         i18n( "Certificate Warning" ),
03435                         KGuiItem( i18n( "&Encrypt" ) ),
03436                         KGuiItem( i18n( "&Don't Encrypt" ) ) );
03437             if( ret == KMessageBox::No )
03438               return Kpgp::Canceled;
03439         }
03440     }
03441 #endif
03442 
03443     // PENDING(khz,kalle) Warn if no encryption?
03444 
03445     const char* cleartext  = cText;
03446     const char* ciphertext = 0;
03447 
03448     // Actually do the encryption, if the plugin supports this
03449     size_t cipherLen;
03450 
03451     int errId = 0;
03452     char* errTxt = 0;
03453     if( mSelectedCryptPlug->hasFeature( Feature_EncryptMessages ) &&
03454         mSelectedCryptPlug->encryptMessage( cleartext,
03455                                             &ciphertext, &cipherLen,
03456                                             encryptCertFingerprints,
03457                                             structuring,
03458                                             &errId,
03459                                             &errTxt )
03460         && ciphertext )
03461       encryptedBody.assign( ciphertext, cipherLen );
03462     else {
03463       QString error("#");
03464       error += QString::number( errId );
03465       error += "  :  ";
03466       if( errTxt )
03467         error += errTxt;
03468       else
03469         error += i18n("[unknown error]");
03470       KMessageBox::sorry(this,
03471                   i18n("<qt><p><b>This message could not be encrypted!</b></p>"
03472                        "<p>The Crypto Plug-In '%1' reported the following "
03473                        "details:</p>"
03474                        "<p><i>%2</i></p>"
03475                        "<p>Your configuration might be invalid or the Plug-In "
03476                        "damaged.</p>"
03477                        "<p><b>Please contact your system "
03478                        "administrator.</b></p></qt>")
03479                   .arg(mSelectedCryptPlug->libName())
03480                   .arg( error ) );
03481     }
03482     delete errTxt;
03483     errTxt = 0;
03484 
03485     // we do NOT delete the "ciphertext" !
03486     // bacause "encoding" will take care for it (is a QByteArray)
03487 
03488     kdDebug(5006) << "\nKMComposeWin::pgpEncryptedMsg: returning from CRYPTPLUG.\n" << endl;
03489 
03490   } else
03491       KMessageBox::sorry(this,
03492         i18n("<qt>No active Crypto Plug-In could be found.<br><br>"
03493              "Please activate a Plug-In in the configuration dialog.</qt>"));
03494 
03495   return result;
03496 }
03497 
03498 
03499 //-----------------------------------------------------------------------------
03500 QCString
03501 KMComposeWin::getEncryptionCertificate( const QString& recipient )
03502 {
03503   bool bEncrypt = true;
03504 
03505   QCString addressee = recipient.utf8();
03506   addressee.replace('\x001', ' ');
03507   kdDebug(5006) << "\n\n1st try:  Retrieving keys for: " << recipient << endl;
03508 
03509 
03510   QString selectedCert;
03511   KListBoxDialog dialog( selectedCert, "", i18n( "&Select certificate:" ) );
03512   dialog.resize( 700, 200 );
03513   bool useDialog;
03514   int certSize = 0;
03515   QByteArray certificateList;
03516 
03517   bool askForDifferentSearchString = false;
03518   do {
03519 
03520     certSize = 0;
03521     char* certificatePtr = 0;
03522     bool findCertsOk;
03523     if( askForDifferentSearchString )
03524       findCertsOk = false;
03525     else {
03526       findCertsOk = mSelectedCryptPlug->findCertificates( &(*addressee),
03527                                                 &certificatePtr,
03528                                                 &certSize,
03529                                                 false )
03530                     && (0 < certSize);
03531       kdDebug(5006) << "         keys retrieved successfully: " << findCertsOk << "\n" << endl;
03532       kdDebug(5006) << "findCertificates() 1st try returned " << certificatePtr << endl;
03533       if( findCertsOk )
03534         certificateList.assign( certificatePtr, certSize );
03535     }
03536     while( !findCertsOk ) {
03537       bool bOk = false;
03538       addressee = KInputDialog::getText(
03539                     askForDifferentSearchString
03540                     ? i18n("Look for Other Certificates")
03541                     : i18n("No Certificate Found"),
03542                     i18n("Enter different address for recipient %1 "
03543                         "or enter \" * \" to see all certificates:")
03544                     .arg(recipient),
03545                     addressee, &bOk, this )
03546                   .stripWhiteSpace().utf8();
03547       askForDifferentSearchString = false;
03548       if( bOk ) {
03549         addressee = addressee.simplifyWhiteSpace();
03550         if( ("\"*\"" == addressee) ||
03551             ("\" *\"" == addressee) ||
03552             ("\"* \"" == addressee) ||
03553             ("\" * \"" == addressee))  // You never know what users type.  :-)
03554           addressee = "*";
03555         kdDebug(5006) << "\n\nnext try: Retrieving keys for: " << addressee << endl;
03556         certSize = 0;
03557         char* certificatePtr = 0;
03558         findCertsOk = mSelectedCryptPlug->findCertificates(
03559                                       &(*addressee),
03560                                       &certificatePtr,
03561                                       &certSize,
03562                                       false )
03563                       && (0 < certSize);
03564         kdDebug(5006) << "         keys retrieved successfully: " << findCertsOk << "\n" << endl;
03565         kdDebug(5006) << "findCertificates() 2nd try returned " << certificatePtr << endl;
03566         if( findCertsOk )
03567           certificateList.assign( certificatePtr, certSize );
03568       } else {
03569         bEncrypt = false;
03570         break;
03571       }
03572     }
03573     if( bEncrypt && findCertsOk ) {
03574 
03575       // fill selection dialog listbox
03576       dialog.entriesLB->clear();
03577       // show dialog even if only one entry to allow specifying of
03578       // another search string _instead_of_ the recipients address
03579       bool bAlwaysShowDialog = true;
03580 
03581       useDialog = false;
03582       int iA = 0;
03583       int iZ = 0;
03584       while( iZ < certSize ) {
03585         if( (certificateList.at(iZ) == '\1') || (certificateList.at(iZ) == '\0') ) {
03586           kdDebug(5006) << "iA=" << iA << " iZ=" << iZ << endl;
03587           char c = certificateList.at(iZ);
03588           if( (bAlwaysShowDialog || (c == '\1')) && !useDialog ) {
03589             // set up selection dialog
03590             useDialog = true;
03591             dialog.setCaption( i18n( "Select Certificate for Encryption [%1]" )
03592                               .arg( recipient ) );
03593             dialog.setLabelAbove(
03594               i18n( "&Select certificate for recipient %1:" )
03595               .arg( recipient ) );
03596           }
03597           certificateList.at(iZ) = '\0';
03598           QString s = QString::fromUtf8( &certificateList.at(iA) );
03599           certificateList.at(iZ) = c;
03600           if( useDialog )
03601             dialog.entriesLB->insertItem( s );
03602           else
03603             selectedCert = s;
03604           ++iZ;
03605           iA = iZ;
03606         }
03607         ++iZ;
03608       }
03609       // run selection dialog and retrieve user choice
03610       // OR take the single entry (if only one was found)
03611       if( useDialog ) {
03612         dialog.setCommentBelow(
03613           i18n("(Certificates matching address \"%1\", "
03614                "press [Cancel] to use different address for recipient %2.)")
03615           .arg( addressee )
03616           .arg( recipient ) );
03617         dialog.entriesLB->setFocus();
03618         dialog.entriesLB->setSelected( 0, true );
03619         askForDifferentSearchString = (dialog.exec() != QDialog::Accepted);
03620       }
03621     }
03622   } while ( askForDifferentSearchString );
03623 
03624   if( bEncrypt )
03625     return selectedCert.utf8();
03626   else
03627     return QCString();
03628 }
03629 
03630 
03631 bool KMComposeWin::checkForEncryptCertificateExpiry( const QString& recipient,
03632                                                      const QCString& certFingerprint )
03633 {
03634   bool bEncrypt = true;
03635 
03636   // Check for expiry of various certificates, but only if the
03637   // plugin supports this.
03638   if( mSelectedCryptPlug->hasFeature( Feature_WarnEncryptCertificateExpiry ) ) {
03639     QString captionWarn = i18n( "Certificate Warning [%1]" ).arg( recipient );
03640 
03641     int encRecvDaysLeft =
03642       mSelectedCryptPlug->receiverCertificateDaysLeftToExpiry( certFingerprint );
03643     if( mSelectedCryptPlug->receiverCertificateExpiryNearWarning() &&
03644         encRecvDaysLeft <
03645         mSelectedCryptPlug->receiverCertificateExpiryNearWarningInterval() ) {
03646       QString txt1;
03647       if( 0 < encRecvDaysLeft )
03648         txt1 = i18n( "The certificate of the recipient you want to send this "
03649                      "message to expires in %1 days.<br>This means that after "
03650                      "this period, the recipient will not be able to read "
03651                      "your message any longer." )
03652                .arg( encRecvDaysLeft );
03653       else if( 0 > encRecvDaysLeft )
03654         txt1 = i18n( "The certificate of the recipient you want to send this "
03655                      "message to expired %1 days ago.<br>This means that the "
03656                      "recipient will not be able to read your message." )
03657                .arg( -encRecvDaysLeft );
03658       else
03659         txt1 = i18n( "The certificate of the recipient you want to send this "
03660                      "message to expires today.<br>This means that beginning "
03661                      "from tomorrow, the recipient will not be able to read "
03662                      "your message any longer." );
03663       int ret = KMessageBox::warningYesNo( this,
03664                                   i18n( "<qt><p>%1</p>"
03665                                         "<p>Do you still want to use "
03666                                         "this certificate?</p></qt>" )
03667                                   .arg( txt1 ),
03668                                   captionWarn,
03669                                   KGuiItem( i18n("&Use Certificate") ),
03670                                   KGuiItem( i18n("&Don't Use Certificate") ) );
03671       if( ret == KMessageBox::No )
03672         bEncrypt = false;
03673     }
03674 
03675     if( bEncrypt ) {
03676       int certInChainDaysLeft =
03677         mSelectedCryptPlug->certificateInChainDaysLeftToExpiry( certFingerprint );
03678       if( mSelectedCryptPlug->certificateInChainExpiryNearWarning() &&
03679           certInChainDaysLeft <
03680           mSelectedCryptPlug->certificateInChainExpiryNearWarningInterval() ) {
03681         QString txt1;
03682         if( 0 < certInChainDaysLeft )
03683           txt1 = i18n( "One of the certificates in the chain of the "
03684                        "certificate of the recipient you want to send this "
03685                        "message to expires in %1 days.<br>"
03686                        "This means that after this period, the recipient "
03687                        "might not be able to read your message any longer." )
03688                  .arg( certInChainDaysLeft );
03689         else if( 0 > certInChainDaysLeft )
03690           txt1 = i18n( "One of the certificates in the chain of the "
03691                        "certificate of the recipient you want to send this "
03692                        "message to expired %1 days ago.<br>"
03693                        "This means that the recipient might not be able to "
03694                        "read your message." )
03695                  .arg( -certInChainDaysLeft );
03696         else
03697           txt1 = i18n( "One of the certificates in the chain of the "
03698                        "certificate of the recipient you want to send this "
03699                        "message to expires today.<br>This means that "
03700                        "beginning from tomorrow, the recipient might not be "
03701                        "able to read your message any longer." );
03702         int ret = KMessageBox::warningYesNo( this,
03703                                   i18n( "<qt><p>%1</p>"
03704                                         "<p>Do you still want to use this "
03705                                         "certificate?</p></qt>" )
03706                                   .arg( txt1 ),
03707                                   captionWarn,
03708                                   KGuiItem( i18n("&Use Certificate") ),
03709                                   KGuiItem( i18n("&Don't Use Certificate") ) );
03710         if( ret == KMessageBox::No )
03711           bEncrypt = false;
03712       }
03713     }
03714 
03715       /*  The following test is not necessary, since we _got_ the certificate
03716           by looking for all certificates of our addressee - so it _must_ be valid
03717           for the respective address!
03718 
03719           // Check whether the receiver address is contained in
03720           // the certificate.
03721           if( bEncrypt && mSelectedCryptPlug->receiverEmailAddressNotInCertificateWarning() &&
03722           !mSelectedCryptPlug->isEmailInCertificate( QString( KMMessage::getEmailAddr( recipient ) ).utf8(),
03723           certFingerprint ) )  {
03724           int ret = KMessageBox::warningYesNo( this,
03725           i18n( "The certificate does not contain the email address of the sender.\nThis means that it will not be possible for the recipient to read this message.\n\nDo you still want to use this certificate?" ),
03726           captionWarn );
03727           if( ret == KMessageBox::No )
03728           bEncrypt = false;
03729           }
03730       */
03731   }
03732 
03733   return bEncrypt;
03734 }
03735 
03736 
03737 //-----------------------------------------------------------------------------
03738 void KMComposeWin::addAttach(const KURL aUrl)
03739 {
03740   if ( !aUrl.isValid() ) {
03741     KMessageBox::sorry( this, i18n( "<qt><p>KMail couldn't recognize the location of the attachment (%1).</p>"
03742                                  "<p>You have to specify the full path if you wish to attach a file.</p></qt>" )
03743                         .arg( aUrl.prettyURL() ) );
03744     return;
03745   }
03746   KIO::TransferJob *job = KIO::get(aUrl);
03747   KIO::Scheduler::scheduleJob( job );
03748   atmLoadData ld;
03749   ld.url = aUrl;
03750   ld.data = QByteArray();
03751   ld.insert = false;
03752   mMapAtmLoadData.insert(job, ld);
03753   connect(job, SIGNAL(result(KIO::Job *)),
03754           this, SLOT(slotAttachFileResult(KIO::Job *)));
03755   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
03756           this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
03757 }
03758 
03759 
03760 //-----------------------------------------------------------------------------
03761 void KMComposeWin::addAttach(const KMMessagePart* msgPart)
03762 {
03763   mAtmList.append(msgPart);
03764 
03765   // show the attachment listbox if it does not up to now
03766   if (mAtmList.count()==1)
03767   {
03768     mGrid->setRowStretch(mNumHeaders+1, 50);
03769     mAtmListView->setMinimumSize(100, 80);
03770     mAtmListView->setMaximumHeight( 100 );
03771     mAtmListView->show();
03772     resize(size());
03773   }
03774 
03775   // add a line in the attachment listbox
03776   KMAtmListViewItem *lvi = new KMAtmListViewItem( mAtmListView );
03777   msgPartToItem(msgPart, lvi);
03778   mAtmItemList.append(lvi);
03779 
03780   slotUpdateAttachActions();
03781 }
03782 
03783 
03784 //-----------------------------------------------------------------------------
03785 void KMComposeWin::slotUpdateAttachActions()
03786 {
03787   int selectedCount = 0;
03788   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it ) {
03789     if ( (*it)->isSelected() ) {
03790       ++selectedCount;
03791     }
03792   }
03793 
03794   mAttachRemoveAction->setEnabled( selectedCount >= 1 );
03795   mAttachSaveAction->setEnabled( selectedCount == 1 );
03796   mAttachPropertiesAction->setEnabled( selectedCount == 1 );
03797 }
03798 
03799 
03800 //-----------------------------------------------------------------------------
03801 
03802 QString KMComposeWin::prettyMimeType( const QString& type )
03803 {
03804   QString t = type.lower();
03805   KServiceType::Ptr st = KServiceType::serviceType( t );
03806   return st ? st->comment() : t;
03807 }
03808 
03809 void KMComposeWin::msgPartToItem(const KMMessagePart* msgPart,
03810                                  KMAtmListViewItem *lvi)
03811 {
03812   assert(msgPart != 0);
03813 
03814   if (!msgPart->fileName().isEmpty())
03815     lvi->setText(0, msgPart->fileName());
03816   else
03817     lvi->setText(0, msgPart->name());
03818   lvi->setText(1, KIO::convertSize( msgPart->decodedSize()));
03819   lvi->setText(2, msgPart->contentTransferEncodingStr());
03820   lvi->setText(3, prettyMimeType(msgPart->typeStr() + "/" + msgPart->subtypeStr()));
03821   if( mSelectedCryptPlug ) {
03822     lvi->enableCryptoCBs( true );
03823     lvi->setEncrypt( mEncryptAction->isChecked() );
03824     lvi->setSign(    mSignAction->isChecked() );
03825   } else {
03826     lvi->enableCryptoCBs( false );
03827   }
03828 }
03829 
03830 
03831 //-----------------------------------------------------------------------------
03832 void KMComposeWin::removeAttach(const QString &aUrl)
03833 {
03834   int idx;
03835   KMMessagePart* msgPart;
03836   for(idx=0,msgPart=mAtmList.first(); msgPart;
03837       msgPart=mAtmList.next(),idx++) {
03838     if (msgPart->name() == aUrl) {
03839       removeAttach(idx);
03840       return;
03841     }
03842   }
03843 }
03844 
03845 
03846 //-----------------------------------------------------------------------------
03847 void KMComposeWin::removeAttach(int idx)
03848 {
03849   mAtmModified = TRUE;
03850   mAtmList.remove(idx);
03851   delete mAtmItemList.take(idx);
03852 
03853   if( mAtmList.isEmpty() )
03854   {
03855     mAtmListView->hide();
03856     mGrid->setRowStretch(mNumHeaders+1, 0);
03857     mAtmListView->setMinimumSize(0, 0);
03858     resize(size());
03859   }
03860 }
03861 
03862 
03863 //-----------------------------------------------------------------------------
03864 bool KMComposeWin::encryptFlagOfAttachment(int idx)
03865 {
03866   return (int)(mAtmItemList.count()) > idx
03867     ? ((KMAtmListViewItem*)(mAtmItemList.at( idx )))->isEncrypt()
03868     : false;
03869 }
03870 
03871 
03872 //-----------------------------------------------------------------------------
03873 bool KMComposeWin::signFlagOfAttachment(int idx)
03874 {
03875   return (int)(mAtmItemList.count()) > idx
03876     ? ((KMAtmListViewItem*)(mAtmItemList.at( idx )))->isSign()
03877     : false;
03878 }
03879 
03880 
03881 //-----------------------------------------------------------------------------
03882 void KMComposeWin::addrBookSelInto()
03883 {
03884   AddressesDialog dlg( this );
03885   QString txt;
03886   QStringList lst;
03887 
03888   txt = mEdtTo->text().stripWhiteSpace();
03889   if ( !txt.isEmpty() ) {
03890       lst = KMMessage::splitEmailAddrList( txt );
03891       dlg.setSelectedTo( lst );
03892   }
03893 
03894   txt = mEdtCc->text().stripWhiteSpace();
03895   if ( !txt.isEmpty() ) {
03896       lst = KMMessage::splitEmailAddrList( txt );
03897       dlg.setSelectedCC( lst );
03898   }
03899 
03900   txt = mEdtBcc->text().stripWhiteSpace();
03901   if ( !txt.isEmpty() ) {
03902       lst = KMMessage::splitEmailAddrList( txt );
03903       dlg.setSelectedBCC( lst );
03904   }
03905 
03906   dlg.setRecentAddresses( RecentAddresses::self( KMKernel::config() )->kabcAddresses() );
03907 
03908   if (dlg.exec()==QDialog::Rejected) return;
03909 
03910   mEdtTo->setText( dlg.to().join(", ") );
03911   mEdtTo->setEdited( true );
03912 
03913   mEdtCc->setText( dlg.cc().join(", ") );
03914   mEdtCc->setEdited( true );
03915 
03916   mEdtBcc->setText( dlg.bcc().join(", ") );
03917   mEdtBcc->setEdited( true );
03918 }
03919 
03920 
03921 //-----------------------------------------------------------------------------
03922 void KMComposeWin::setCharset(const QCString& aCharset, bool forceDefault)
03923 {
03924   if ((forceDefault && mForceReplyCharset) || aCharset.isEmpty())
03925     mCharset = mDefCharset;
03926   else
03927     mCharset = aCharset.lower();
03928 
03929   if ( mCharset.isEmpty() || mCharset == "default" )
03930      mCharset = mDefCharset;
03931 
03932   if (mAutoCharset)
03933   {
03934     mEncodingAction->setCurrentItem( 0 );
03935     return;
03936   }
03937 
03938   QStringList encodings = mEncodingAction->items();
03939   int i = 0;
03940   bool charsetFound = FALSE;
03941   for ( QStringList::Iterator it = encodings.begin(); it != encodings.end();
03942      ++it, i++ )
03943   {
03944     if (i > 0 && ((mCharset == "us-ascii" && i == 1) ||
03945      (i != 1 && KGlobal::charsets()->codecForName(
03946       KGlobal::charsets()->encodingForName(*it))
03947       == KGlobal::charsets()->codecForName(mCharset))))
03948     {
03949       mEncodingAction->setCurrentItem( i );
03950       slotSetCharset();
03951       charsetFound = TRUE;
03952       break;
03953     }
03954   }
03955   if (!aCharset.isEmpty() && !charsetFound) setCharset("", TRUE);
03956 }
03957 
03958 
03959 //-----------------------------------------------------------------------------
03960 void KMComposeWin::slotAddrBook()
03961 {
03962   KMAddrBookExternal::openAddressBook(this);
03963 }
03964 
03965 
03966 //-----------------------------------------------------------------------------
03967 void KMComposeWin::slotAddrBookFrom()
03968 {
03969   addrBookSelInto();
03970 }
03971 
03972 
03973 //-----------------------------------------------------------------------------
03974 void KMComposeWin::slotAddrBookReplyTo()
03975 {
03976   addrBookSelInto();
03977 }
03978 
03979 
03980 //-----------------------------------------------------------------------------
03981 void KMComposeWin::slotAddrBookTo()
03982 {
03983   addrBookSelInto();
03984 }
03985 
03986 //-----------------------------------------------------------------------------
03987 void KMComposeWin::slotAttachFile()
03988 {
03989   // Create File Dialog and return selected file(s)
03990   // We will not care about any permissions, existence or whatsoever in
03991   // this function.
03992 
03993   KURL::List files = KFileDialog::getOpenURLs(QString::null, QString::null,
03994         this, i18n("Attach File"));
03995   for (KURL::List::Iterator it = files.begin(); it != files.end(); ++it)
03996     addAttach(*it);
03997 }
03998 
03999 
04000 //-----------------------------------------------------------------------------
04001 void KMComposeWin::slotAttachFileData(KIO::Job *job, const QByteArray &data)
04002 {
04003   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
04004   assert(it != mMapAtmLoadData.end());
04005   QBuffer buff((*it).data);
04006   buff.open(IO_WriteOnly | IO_Append);
04007   buff.writeBlock(data.data(), data.size());
04008   buff.close();
04009 }
04010 
04011 
04012 //-----------------------------------------------------------------------------
04013 void KMComposeWin::slotAttachFileResult(KIO::Job *job)
04014 {
04015   QMap<KIO::Job*, atmLoadData>::Iterator it = mMapAtmLoadData.find(job);
04016   assert(it != mMapAtmLoadData.end());
04017   if (job->error())
04018   {
04019     mMapAtmLoadData.remove(it);
04020     job->showErrorDialog();
04021     return;
04022   }
04023   if ((*it).insert)
04024   {
04025     (*it).data.resize((*it).data.size() + 1);
04026     (*it).data[(*it).data.size() - 1] = '\0';
04027     if ( const QTextCodec * codec = KGlobal::charsets()->codecForName((*it).encoding) )
04028       mEditor->insert( codec->toUnicode( (*it).data ) );
04029     else
04030       mEditor->insert( QString::fromLocal8Bit( (*it).data ) );
04031     mMapAtmLoadData.remove(it);
04032     return;
04033   }
04034   QString name;
04035   QString urlStr = (*it).url.prettyURL();
04036   KMMessagePart* msgPart;
04037   int i;
04038 
04039   KCursorSaver busy(KBusyPtr::busy());
04040 
04041   // ask the job for the mime type of the file
04042   QString mimeType = static_cast<KIO::MimetypeJob*>(job)->mimetype();
04043 
04044   i = urlStr.findRev('/');
04045   if( i == -1 )
04046     name = urlStr;
04047   else if( i + 1 < int( urlStr.length() ) )
04048     name = urlStr.mid( i + 1, 256 );
04049   else {
04050     // URL ends with '/' (e.g. http://www.kde.org/)
04051     // guess a reasonable filename
04052     if( mimeType == "text/html" )
04053       name = "index.html";
04054     else {
04055       // try to determine a reasonable extension
04056       QStringList patterns( KMimeType::mimeType( mimeType )->patterns() );
04057       QString ext;
04058       if( !patterns.isEmpty() ) {
04059         ext = patterns[0];
04060         int i = ext.findRev( '.' );
04061         if( i == -1 )
04062           ext.prepend( '.' );
04063         else if( i > 0 )
04064           ext = ext.mid( i );
04065       }
04066       name = QString("unknown") += ext;
04067     }
04068   }
04069 
04070   QCString encoding = KMMsgBase::autoDetectCharset(mCharset,
04071     KMMessage::preferredCharsets(), name);
04072   if (encoding.isEmpty()) encoding = "utf-8";
04073   QCString encName = KMMsgBase::encodeRFC2231String(name, encoding);
04074   bool RFC2231encoded = name != QString(encName);
04075 
04076   // create message part
04077   msgPart = new KMMessagePart;
04078   msgPart->setName(name);
04079   QValueList<int> allowedCTEs;
04080   msgPart->setBodyAndGuessCte((*it).data, allowedCTEs,
04081                               !kmkernel->msgSender()->sendQuotedPrintable());
04082   kdDebug(5006) << "autodetected cte: " << msgPart->cteStr() << endl;
04083   int slash = mimeType.find( '/' );
04084   if( slash == -1 )
04085     slash = mimeType.length();
04086   msgPart->setTypeStr( mimeType.left( slash ).latin1() );
04087   msgPart->setSubtypeStr( mimeType.mid( slash + 1 ).latin1() );
04088   msgPart->setContentDisposition(QCString("attachment;\n\tfilename")
04089     + ((RFC2231encoded) ? "*" : "") +  "=\"" + encName + "\"");
04090 
04091   mMapAtmLoadData.remove(it);
04092 
04093   msgPart->setCharset(mCharset);
04094 
04095   // show message part dialog, if not configured away (default):
04096   KConfigGroup composer(KMKernel::config(), "Composer");
04097   if (!composer.hasKey("showMessagePartDialogOnAttach"))
04098     // make it visible in the config file:
04099     composer.writeEntry("showMessagePartDialogOnAttach", false);
04100   if (composer.readBoolEntry("showMessagePartDialogOnAttach", false)) {
04101     KMMsgPartDialogCompat dlg;
04102     int encodings = 0;
04103     for ( QValueListConstIterator<int> it = allowedCTEs.begin() ;
04104           it != allowedCTEs.end() ; ++it )
04105       switch ( *it ) {
04106       case DwMime::kCteBase64: encodings |= KMMsgPartDialog::Base64; break;
04107       case DwMime::kCteQp: encodings |= KMMsgPartDialog::QuotedPrintable; break;
04108       case DwMime::kCte7bit: encodings |= KMMsgPartDialog::SevenBit; break;
04109       case DwMime::kCte8bit: encodings |= KMMsgPartDialog::EightBit; break;
04110       default: ;
04111       }
04112     dlg.setShownEncodings( encodings );
04113     dlg.setMsgPart(msgPart);
04114     if (!dlg.exec()) {
04115       delete msgPart;
04116       msgPart = 0;
04117       return;
04118     }
04119   }
04120   mAtmModified = TRUE;
04121   if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
04122 
04123   // add the new attachment to the list
04124   addAttach(msgPart);
04125 }
04126 
04127 
04128 //-----------------------------------------------------------------------------
04129 void KMComposeWin::slotInsertFile()
04130 {
04131   KFileDialog fdlg(QString::null, QString::null, this, 0, TRUE);
04132   fdlg.setCaption(i18n("Insert File"));
04133   fdlg.toolBar()->insertCombo(KMMsgBase::supportedEncodings(FALSE), 4711,
04134     false, 0, 0, 0);
04135   KComboBox *combo = fdlg.toolBar()->getCombo(4711);
04136   for (int i = 0; i < combo->count(); i++)
04137     if (KGlobal::charsets()->codecForName(KGlobal::charsets()->
04138       encodingForName(combo->text(i)))
04139       == QTextCodec::codecForLocale()) combo->setCurrentItem(i);
04140   if (!fdlg.exec()) return;
04141 
04142   KURL u = fdlg.selectedURL();
04143 
04144   if (u.fileName().isEmpty()) return;
04145 
04146   KIO::Job *job = KIO::get(u);
04147   atmLoadData ld;
04148   ld.url = u;
04149   ld.data = QByteArray();
04150   ld.insert = true;
04151   ld.encoding = KGlobal::charsets()->encodingForName(
04152     combo->currentText()).latin1();
04153   mMapAtmLoadData.insert(job, ld);
04154   connect(job, SIGNAL(result(KIO::Job *)),
04155           this, SLOT(slotAttachFileResult(KIO::Job *)));
04156   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
04157           this, SLOT(slotAttachFileData(KIO::Job *, const QByteArray &)));
04158 }
04159 
04160 
04161 //-----------------------------------------------------------------------------
04162 void KMComposeWin::slotSetCharset()
04163 {
04164   if (mEncodingAction->currentItem() == 0)
04165   {
04166     mAutoCharset = true;
04167     return;
04168   }
04169   mAutoCharset = false;
04170 
04171   mCharset = KGlobal::charsets()->encodingForName( mEncodingAction->
04172     currentText() ).latin1();
04173 }
04174 
04175 
04176 //-----------------------------------------------------------------------------
04177 void KMComposeWin::slotSelectCryptoModule()
04178 {
04179   mSelectedCryptPlug = 0;
04180   int sel = mCryptoModuleAction->currentItem();
04181   int i = 1;  // start at 1 since 0'th entry is "inline OpenPGP (builtin)"
04182   for ( CryptPlugWrapperListIterator it( *(kmkernel->cryptPlugList()) ) ;
04183         it.current() ;
04184         ++it, ++i )
04185     if( i == sel ){
04186       mSelectedCryptPlug = it.current();
04187       break;
04188     }
04189   if( mSelectedCryptPlug ) {
04190     // if the encrypt/sign columns are hidden then show them
04191     if( 0 == mAtmListView->columnWidth( mAtmColEncrypt ) ) {
04192       // set/unset signing/encryption for all attachments according to the
04193       // state of the global sign/encrypt action
04194       if( !mAtmList.isEmpty() ) {
04195         for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
04196              lvi;
04197              lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
04198           lvi->setSign( mSignAction->isChecked() );
04199           lvi->setEncrypt( mEncryptAction->isChecked() );
04200         }
04201       }
04202       int totalWidth = 0;
04203       // determine the total width of the columns
04204       for( int col=0; col < mAtmColEncrypt; col++ )
04205         totalWidth += mAtmListView->columnWidth( col );
04206       int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
04207                                          - mAtmSignColWidth;
04208       // reduce the width of all columns so that the encrypt and sign column
04209       // fit
04210       int usedWidth = 0;
04211       for( int col=0; col < mAtmColEncrypt-1; col++ ) {
04212         int newWidth = mAtmListView->columnWidth( col ) * reducedTotalWidth
04213                                                        / totalWidth;
04214         mAtmListView->setColumnWidth( col, newWidth );
04215         usedWidth += newWidth;
04216       }
04217       // the last column before the encrypt column gets the remaining space
04218       // (because of rounding errors the width of this column isn't calculated
04219       // the same way as the width of the other columns)
04220       mAtmListView->setColumnWidth( mAtmColEncrypt-1,
04221                                     reducedTotalWidth - usedWidth );
04222       mAtmListView->setColumnWidth( mAtmColEncrypt, mAtmEncryptColWidth );
04223       mAtmListView->setColumnWidth( mAtmColSign,    mAtmSignColWidth );
04224       for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
04225            lvi;
04226            lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
04227         lvi->enableCryptoCBs( true );
04228       }
04229     }
04230   } else {
04231     // if the encrypt/sign columns are visible then hide them
04232     if( 0 != mAtmListView->columnWidth( mAtmColEncrypt ) ) {
04233       mAtmEncryptColWidth = mAtmListView->columnWidth( mAtmColEncrypt );
04234       mAtmSignColWidth = mAtmListView->columnWidth( mAtmColSign );
04235       int totalWidth = 0;
04236       // determine the total width of the columns
04237       for( int col=0; col < mAtmListView->columns(); col++ )
04238         totalWidth += mAtmListView->columnWidth( col );
04239       int reducedTotalWidth = totalWidth - mAtmEncryptColWidth
04240                                          - mAtmSignColWidth;
04241       // increase the width of all columns so that the visible columns take
04242       // up the whole space
04243       int usedWidth = 0;
04244       for( int col=0; col < mAtmColEncrypt-1; col++ ) {
04245         int newWidth = mAtmListView->columnWidth( col ) * totalWidth
04246                                                        / reducedTotalWidth;
04247         mAtmListView->setColumnWidth( col, newWidth );
04248         usedWidth += newWidth;
04249       }
04250       // the last column before the encrypt column gets the remaining space
04251       // (because of rounding errors the width of this column isn't calculated
04252       // the same way as the width of the other columns)
04253       mAtmListView->setColumnWidth( mAtmColEncrypt-1, totalWidth - usedWidth );
04254       mAtmListView->setColumnWidth( mAtmColEncrypt, 0 );
04255       mAtmListView->setColumnWidth( mAtmColSign,    0 );
04256       for( KMAtmListViewItem* lvi = (KMAtmListViewItem*)mAtmItemList.first();
04257            lvi;
04258            lvi = (KMAtmListViewItem*)mAtmItemList.next() ) {
04259         lvi->enableCryptoCBs( false );
04260       }
04261     }
04262   }
04263 }
04264 
04265 
04266 //-----------------------------------------------------------------------------
04267 void KMComposeWin::slotInsertMyPublicKey()
04268 {
04269   KMMessagePart* msgPart;
04270 
04271   KCursorSaver busy(KBusyPtr::busy());
04272 
04273   // get PGP user id for the chosen identity
04274   QCString pgpUserId =
04275     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() ).pgpIdentity();
04276 
04277   QCString armoredKey = Kpgp::Module::getKpgp()->getAsciiPublicKey(pgpUserId);
04278   if (armoredKey.isEmpty())
04279   {
04280     KCursorSaver idle(KBusyPtr::idle());
04281     KMessageBox::sorry( this, i18n("Unable to obtain your public key.") );
04282     return;
04283   }
04284 
04285   // create message part
04286   msgPart = new KMMessagePart;
04287   msgPart->setName(i18n("My OpenPGP key"));
04288   msgPart->setTypeStr("application");
04289   msgPart->setSubtypeStr("pgp-keys");
04290   QValueList<int> dummy;
04291   msgPart->setBodyAndGuessCte(armoredKey, dummy, false);
04292   msgPart->setContentDisposition("attachment;\n\tfilename=public_key.asc");
04293 
04294   // add the new attachment to the list
04295   addAttach(msgPart);
04296   rethinkFields(); //work around initial-size bug in Qt-1.32
04297 }
04298 
04299 //-----------------------------------------------------------------------------
04300 void KMComposeWin::slotInsertPublicKey()
04301 {
04302   QCString keyID;
04303   KMMessagePart* msgPart;
04304   Kpgp::Module *pgp;
04305 
04306   if ( !(pgp = Kpgp::Module::getKpgp()) )
04307     return;
04308 
04309   keyID = pgp->selectPublicKey( i18n("Attach Public OpenPGP Key"),
04310                                 i18n("Select the public key which should "
04311                                      "be attached.") );
04312 
04313   if (keyID.isEmpty())
04314     return;
04315 
04316   QCString armoredKey = pgp->getAsciiPublicKey(keyID);
04317   if (!armoredKey.isEmpty()) {
04318     // create message part
04319     msgPart = new KMMessagePart;
04320     msgPart->setName(i18n("OpenPGP key 0x%1").arg(keyID));
04321     msgPart->setTypeStr("application");
04322     msgPart->setSubtypeStr("pgp-keys");
04323     QValueList<int> dummy;
04324     msgPart->setBodyAndGuessCte(armoredKey, dummy, false);
04325     msgPart->setContentDisposition("attachment;\n\tfilename=0x" + keyID + ".asc");
04326 
04327     // add the new attachment to the list
04328     addAttach(msgPart);
04329     rethinkFields(); //work around initial-size bug in Qt-1.32
04330   } else {
04331     KMessageBox::sorry( this,
04332                         i18n( "Unable to obtain the selected public key." ) );
04333   }
04334 }
04335 
04336 
04337 //-----------------------------------------------------------------------------
04338 void KMComposeWin::slotAttachPopupMenu(QListViewItem *, const QPoint &, int)
04339 {
04340   if (!mAttachMenu)
04341   {
04342      mAttachMenu = new QPopupMenu(this);
04343 
04344      mAttachMenu->insertItem(i18n("to view", "View"), this,
04345                              SLOT(slotAttachView()));
04346      mAttachMenu->insertItem(i18n("Remove"), this, SLOT(slotAttachRemove()));
04347      mSaveAsId = mAttachMenu->insertItem( SmallIcon("filesaveas"), i18n("Save As..."), this,
04348                                           SLOT( slotAttachSave() ) );
04349      mPropertiesId = mAttachMenu->insertItem( i18n("Properties"), this,
04350                                               SLOT( slotAttachProperties() ) );
04351      mAttachMenu->insertSeparator();
04352      mAttachMenu->insertItem(i18n("Add Attachment..."), this, SLOT(slotAttachFile()));
04353   }
04354 
04355   int selectedCount = 0;
04356   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it ) {
04357     if ( (*it)->isSelected() ) {
04358       ++selectedCount;
04359     }
04360   }
04361   bool multiSelection = ( selectedCount > 1 );
04362   mAttachMenu->setItemEnabled( mSaveAsId, !multiSelection );
04363   mAttachMenu->setItemEnabled( mPropertiesId, !multiSelection );
04364 
04365   mAttachMenu->popup(QCursor::pos());
04366 }
04367 
04368 //-----------------------------------------------------------------------------
04369 int KMComposeWin::currentAttachmentNum()
04370 {
04371   int i = 0;
04372   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i )
04373     if ( *it == mAtmListView->currentItem() )
04374       return i;
04375   return -1;
04376 }
04377 
04378 //-----------------------------------------------------------------------------
04379 void KMComposeWin::slotAttachProperties()
04380 {
04381   int idx = currentAttachmentNum();
04382 
04383   if (idx < 0) return;
04384 
04385   KMMessagePart* msgPart = mAtmList.at(idx);
04386   msgPart->setCharset(mCharset);
04387 
04388   KMMsgPartDialogCompat dlg;
04389   dlg.setMsgPart(msgPart);
04390   KMAtmListViewItem* listItem = (KMAtmListViewItem*)(mAtmItemList.at(idx));
04391   if( mSelectedCryptPlug && listItem ) {
04392     dlg.setCanSign(    true );
04393     dlg.setCanEncrypt( true );
04394     dlg.setSigned(    listItem->isSign()    );
04395     dlg.setEncrypted( listItem->isEncrypt() );
04396   } else {
04397     dlg.setCanSign(    false );
04398     dlg.setCanEncrypt( false );
04399   }
04400   if (dlg.exec())
04401   {
04402     mAtmModified = TRUE;
04403     // values may have changed, so recreate the listbox line
04404     if( listItem ) {
04405       msgPartToItem(msgPart, listItem);
04406       if( mSelectedCryptPlug ) {
04407         listItem->setSign(    dlg.isSigned()    );
04408         listItem->setEncrypt( dlg.isEncrypted() );
04409       }
04410     }
04411   }
04412   if (msgPart->typeStr().lower() != "text") msgPart->setCharset(QCString());
04413 }
04414 
04415 
04416 //-----------------------------------------------------------------------------
04417 void KMComposeWin::slotAttachView()
04418 {
04419   int i = 0;
04420   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ++it, ++i ) {
04421     if ( (*it)->isSelected() ) {
04422       viewAttach( i );
04423     }
04424   }
04425 }
04426 
04427 
04428 //-----------------------------------------------------------------------------
04429 void KMComposeWin::viewAttach( int index )
04430 {
04431   QString str, pname;
04432   KMMessagePart* msgPart;
04433   msgPart = mAtmList.at(index);
04434   pname = msgPart->name().stripWhiteSpace();
04435   if (pname.isEmpty()) pname=msgPart->contentDescription();
04436   if (pname.isEmpty()) pname="unnamed";
04437 
04438   KTempFile* atmTempFile = new KTempFile();
04439   mAtmTempList.append( atmTempFile );
04440   atmTempFile->setAutoDelete( true );
04441   kByteArrayToFile(msgPart->bodyDecodedBinary(), atmTempFile->name(), false, false,
04442     false);
04443   KMReaderMainWin *win = new KMReaderMainWin(msgPart, false,
04444     atmTempFile->name(), pname, KMMsgBase::codecForName(mCharset) );
04445   win->show();
04446 }
04447 
04448 
04449 //-----------------------------------------------------------------------------
04450 void KMComposeWin::slotAttachSave()
04451 {
04452   KMMessagePart* msgPart;
04453   QString fileName, pname;
04454   int idx = currentAttachmentNum();
04455 
04456   if (idx < 0) return;
04457 
04458   msgPart = mAtmList.at(idx);
04459   pname = msgPart->name();
04460   if (pname.isEmpty()) pname="unnamed";
04461 
04462   KURL url = KFileDialog::getSaveURL(QString::null, QString::null, 0, i18n("Save Attachment As"));
04463 
04464   if( url.isEmpty() )
04465     return;
04466 
04467   kmkernel->byteArrayToRemoteFile(msgPart->bodyDecodedBinary(), url);
04468 }
04469 
04470 
04471 //-----------------------------------------------------------------------------
04472 void KMComposeWin::slotAttachRemove()
04473 {
04474   bool attachmentRemoved = false;
04475   int i = 0;
04476   for ( QPtrListIterator<QListViewItem> it(mAtmItemList); *it; ) {
04477     if ( (*it)->isSelected() ) {
04478       removeAttach( i );
04479       attachmentRemoved = true;
04480     }
04481     else {
04482       ++it;
04483       ++i;
04484     }
04485   }
04486 
04487   if ( attachmentRemoved ) {
04488     mEditor->setModified( true );
04489     slotUpdateAttachActions();
04490   }
04491 }
04492 
04493 //-----------------------------------------------------------------------------
04494 void KMComposeWin::slotFind()
04495 {
04496   mEditor->search();
04497 }
04498 
04499 
04500 //-----------------------------------------------------------------------------
04501 void KMComposeWin::slotReplace()
04502 {
04503   mEditor->replace();
04504 }
04505 
04506 //-----------------------------------------------------------------------------
04507 void KMComposeWin::slotUpdateFont()
04508 {
04509   mEditor->setFont( mFixedFontAction && (mFixedFontAction->isChecked())
04510     ? mFixedFont : mBodyFont );
04511 }
04512 
04513 QString KMComposeWin::quotePrefixName() const
04514 {
04515     if ( !msg() )
04516         return QString::null;
04517 
04518     KConfig *config=KMKernel::config();
04519     KConfigGroupSaver saver(config, "General");
04520 
04521     int languageNr = config->readNumEntry("reply-current-language",0);
04522     config->setGroup( QString("KMMessage #%1").arg(languageNr) );
04523 
04524     QString quotePrefix = config->readEntry("indent-prefix", ">%_");
04525     quotePrefix = msg()->formatString(quotePrefix);
04526     return quotePrefix;
04527 }
04528 
04529 void KMComposeWin::slotPasteAsQuotation()
04530 {
04531     if( mEditor->hasFocus() && msg() )
04532     {
04533         QString quotePrefix = quotePrefixName();
04534         QString s = QApplication::clipboard()->text();
04535         if (!s.isEmpty()) {
04536             for (int i=0; (uint)i<s.length(); i++) {
04537                 if ( s[i] < ' ' && s[i] != '\n' && s[i] != '\t' )
04538                     s[i] = ' ';
04539             }
04540             s.prepend(quotePrefix);
04541             s.replace("\n","\n"+quotePrefix);
04542             mEditor->insert(s);
04543         }
04544     }
04545 }
04546 
04547 
04548 void KMComposeWin::slotAddQuotes()
04549 {
04550     if( mEditor->hasFocus() && msg() )
04551     {
04552         if ( mEditor->hasMarkedText()) {
04553             QString s =  mEditor->markedText();
04554             QString quotePrefix = quotePrefixName();
04555             s.prepend(quotePrefix);
04556             s.replace("\n", "\n"+quotePrefix);
04557             mEditor->insert(s);
04558         } else {
04559             int l =  mEditor->currentLine();
04560             int c =  mEditor->currentColumn();
04561             QString s =  mEditor->textLine(l);
04562             s.prepend("> ");
04563             mEditor->insertLine(s,l);
04564             mEditor->removeLine(l+1);
04565             mEditor->setCursorPosition(l,c+2);
04566         }
04567     }
04568 }
04569 
04570 
04571 void KMComposeWin::slotRemoveQuotes()
04572 {
04573     if( mEditor->hasFocus() && msg() )
04574     {
04575         QString quotePrefix = quotePrefixName();
04576         if (mEditor->hasMarkedText()) {
04577             QString s = mEditor->markedText();
04578             QString quotePrefix = quotePrefixName();
04579             if (s.left(2) == quotePrefix )
04580                 s.remove(0,2);
04581             s.replace("\n"+quotePrefix,"\n");
04582             mEditor->insert(s);
04583         } else {
04584             int l = mEditor->currentLine();
04585             int c = mEditor->currentColumn();
04586             QString s = mEditor->textLine(l);
04587             if (s.left(2) == quotePrefix) {
04588                 s.remove(0,2);
04589                 mEditor->insertLine(s,l);
04590                 mEditor->removeLine(l+1);
04591                 mEditor->setCursorPosition(l,c-2);
04592             }
04593         }
04594     }
04595 }
04596 
04597 
04598 //-----------------------------------------------------------------------------
04599 void KMComposeWin::slotUndo()
04600 {
04601   QWidget* fw = focusWidget();
04602   if (!fw) return;
04603 
04604   if (fw->inherits("KEdit"))
04605     ((QMultiLineEdit*)fw)->undo();
04606   else if (fw->inherits("QLineEdit"))
04607     ((QLineEdit*)fw)->undo();
04608 }
04609 
04610 void KMComposeWin::slotRedo()
04611 {
04612   QWidget* fw = focusWidget();
04613   if (!fw) return;
04614 
04615   if (fw->inherits("KEdit"))
04616     ((QMultiLineEdit*)fw)->redo();
04617   else if (fw->inherits("QLineEdit"))
04618     ((QLineEdit*)fw)->redo();
04619 }
04620 
04621 //-----------------------------------------------------------------------------
04622 void KMComposeWin::slotCut()
04623 {
04624   QWidget* fw = focusWidget();
04625   if (!fw) return;
04626 
04627   if (fw->inherits("KEdit"))
04628     ((QMultiLineEdit*)fw)->cut();
04629   else if (fw->inherits("QLineEdit"))
04630     ((QLineEdit*)fw)->cut();
04631   else kdDebug(5006) << "wrong focus widget" << endl;
04632 }
04633 
04634 
04635 //-----------------------------------------------------------------------------
04636 void KMComposeWin::slotCopy()
04637 {
04638   QWidget* fw = focusWidget();
04639   if (!fw) return;
04640 
04641 #ifdef KeyPress
04642 #undef KeyPress
04643 #endif
04644 
04645   QKeyEvent k(QEvent::KeyPress, Key_C , 0 , ControlButton);
04646   kapp->notify(fw, &k);
04647 }
04648 
04649 
04650 //-----------------------------------------------------------------------------
04651 void KMComposeWin::slotPaste()
04652 {
04653   QWidget* fw = focusWidget();
04654   if (!fw) return;
04655 
04656 #ifdef KeyPress
04657 #undef KeyPress
04658 #endif
04659 
04660   QKeyEvent k(QEvent::KeyPress, Key_V , 0 , ControlButton);
04661   kapp->notify(fw, &k);
04662 }
04663 
04664 
04665 //-----------------------------------------------------------------------------
04666 void KMComposeWin::slotMarkAll()
04667 {
04668   QWidget* fw = focusWidget();
04669   if (!fw) return;
04670 
04671   if (fw->inherits("QLineEdit"))
04672       ((QLineEdit*)fw)->selectAll();
04673   else if (fw->inherits("QMultiLineEdit"))
04674     ((QMultiLineEdit*)fw)->selectAll();
04675 }
04676 
04677 
04678 //-----------------------------------------------------------------------------
04679 void KMComposeWin::slotClose()
04680 {
04681   close(FALSE);
04682 }
04683 
04684 
04685 //-----------------------------------------------------------------------------
04686 void KMComposeWin::slotNewComposer()
04687 {
04688   KMComposeWin* win;
04689   KMMessage* msg = new KMMessage;
04690 
04691   msg->initHeader();
04692   win = new KMComposeWin(msg);
04693   win->show();
04694 }
04695 
04696 
04697 //-----------------------------------------------------------------------------
04698 void KMComposeWin::slotNewMailReader()
04699 {
04700   KMMainWin *kmmwin = new KMMainWin(0);
04701   kmmwin->show();
04702   //d->resize(d->size());
04703 }
04704 
04705 
04706 //-----------------------------------------------------------------------------
04707 void KMComposeWin::slotUpdWinTitle(const QString& text)
04708 {
04709   if (text.isEmpty())
04710        setCaption("("+i18n("unnamed")+")");
04711   else setCaption(text);
04712 }
04713 
04714 
04715 //-----------------------------------------------------------------------------
04716 void KMComposeWin::slotEncryptToggled(bool on)
04717 {
04718   setEncryption( on, true /* set by the user */ );
04719 }
04720 
04721 
04722 //-----------------------------------------------------------------------------
04723 void KMComposeWin::setEncryption( bool encrypt, bool setByUser )
04724 {
04725   if ( !mEncryptAction->isEnabled() )
04726     encrypt = false;
04727 
04728   // check if the user wants to encrypt messages to himself and if he defined
04729   // an encryption key for the current identity
04730   if ( encrypt && Kpgp::Module::getKpgp()->encryptToSelf()
04731                && !mLastIdentityHasOpenPgpKey
04732                // ### hack needed as long as we don't specify S/MIME keys in identities.
04733                && ( !mSelectedCryptPlug || mSelectedCryptPlug->protocol() == "openpgp" ) ) {
04734     if ( setByUser ) {
04735       KMessageBox::sorry( this,
04736                           i18n("<qt><p>In order to be able to encrypt "
04737                                "this message you first have to "
04738                                "define the OpenPGP key, which should be "
04739                                "used to encrypt the message to "
04740                                "yourself.</p>"
04741                                "<p>You can define the OpenPGP key, "
04742                                "which should be used with the current "
04743                                "identity, in the identity configuration.</p>"
04744                                "</qt>"),
04745                           i18n("Undefined Encryption Key") );
04746     }
04747     encrypt = false;
04748   }
04749 
04750   // make sure the mEncryptAction is in the right state
04751   mEncryptAction->setChecked( encrypt );
04752 
04753   // show the appropriate icon
04754   if ( encrypt )
04755     mEncryptAction->setIcon("encrypted");
04756   else
04757     mEncryptAction->setIcon("decrypted");
04758 
04759   // mark the attachments for (no) encryption
04760   if ( mSelectedCryptPlug ) {
04761     for ( KMAtmListViewItem* entry =
04762             static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
04763           entry;
04764           entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
04765       entry->setEncrypt( encrypt );
04766   }
04767 }
04768 
04769 
04770 //-----------------------------------------------------------------------------
04771 void KMComposeWin::slotSignToggled(bool on)
04772 {
04773   setSigning( on, true /* set by the user */ );
04774 }
04775 
04776 
04777 //-----------------------------------------------------------------------------
04778 void KMComposeWin::setSigning( bool sign, bool setByUser )
04779 {
04780   if ( !mSignAction->isEnabled() )
04781     sign = false;
04782 
04783   // check if the user defined a signing key for the current identity
04784   if ( sign && !mLastIdentityHasOpenPgpKey
04785             // ### hack needed as long as we don't specify S/MIME keys in identities.
04786             && ( !mSelectedCryptPlug || mSelectedCryptPlug->protocol() == "openpgp" ) ) {
04787     if ( setByUser ) {
04788       KMessageBox::sorry( this,
04789                           i18n("<qt><p>In order to be able to sign "
04790                                "this message you first have to "
04791                                "define the OpenPGP key which should be "
04792                                "used for this.</p>"
04793                                "<p>You can define the OpenPGP key "
04794                                "which should be used with the current "
04795                                "identity in the identity configuration.</p>"
04796                                "</qt>"),
04797                           i18n("Undefined Signing Key") );
04798     }
04799     sign = false;
04800   }
04801 
04802   // make sure the mSignAction is in the right state
04803   mSignAction->setChecked( sign );
04804 
04805   // mark the attachments for (no) signing
04806   if ( mSelectedCryptPlug ) {
04807     for ( KMAtmListViewItem* entry =
04808             static_cast<KMAtmListViewItem*>( mAtmItemList.first() );
04809           entry;
04810           entry = static_cast<KMAtmListViewItem*>( mAtmItemList.next() ) )
04811       entry->setSign( sign );
04812   }
04813 }
04814 
04815 
04816 //-----------------------------------------------------------------------------
04817 void KMComposeWin::slotWordWrapToggled(bool on)
04818 {
04819   if (on)
04820   {
04821     mEditor->setWordWrap( QMultiLineEdit::FixedColumnWidth );
04822     mEditor->setWrapColumnOrWidth(mLineBreak);
04823   }
04824   else
04825   {
04826     mEditor->setWordWrap( QMultiLineEdit::NoWrap );
04827   }
04828 }
04829 
04830 
04831 //-----------------------------------------------------------------------------
04832 void KMComposeWin::slotPrint()
04833 {
04834   bool bMessageWasModified = ( mEditor->isModified() || mEdtFrom->edited() ||
04835                                mEdtReplyTo->edited() || mEdtTo->edited() ||
04836                                mEdtCc->edited() || mEdtBcc->edited() ||
04837                                mEdtSubject->edited() || mAtmModified ||
04838                                ( mTransport->lineEdit() &&
04839                                  mTransport->lineEdit()->edited() ) );
04840   applyChanges( true );
04841   KMCommand *command = new KMPrintCommand( this, mMsg );
04842   command->start();
04843   mEditor->setModified( bMessageWasModified );
04844 }
04845 
04846 
04847 //----------------------------------------------------------------------------
04848 bool KMComposeWin::doSend(int aSendNow, bool saveInDrafts)
04849 {
04850   if (!saveInDrafts)
04851   {
04852      if (to().isEmpty())
04853      {
04854         mEdtTo->setFocus();
04855         KMessageBox::information( this,
04856                                   i18n("You must specify at least one "
04857                                        "receiver in the To: field.") );
04858         return false;
04859      }
04860 
04861      if (subject().isEmpty())
04862      {
04863         mEdtSubject->setFocus();
04864         int rc =
04865           KMessageBox::questionYesNo( this,
04866                                       i18n("You did not specify a subject. "
04867                                            "Send message anyway?"),
04868                                       i18n("No Subject Specified"),
04869                                       i18n("&Yes, Send as Is"),
04870                                       i18n("&No, Let Me Specify the Subject"),
04871                                       "no_subject_specified" );
04872         if( rc == KMessageBox::No )
04873         {
04874            return false;
04875         }
04876      }
04877 
04878      if ( userForgotAttachment() )
04879        return false;
04880   }
04881 
04882   KCursorSaver busy(KBusyPtr::busy());
04883   mMsg->setDateToday();
04884 
04885   // If a user sets up their outgoing messages preferences wrong and then
04886   // sends mail that gets 'stuck' in their outbox, they should be able to
04887   // rectify the problem by editing their outgoing preferences and
04888   // resending.
04889   // Hence this following conditional
04890   QString hf = mMsg->headerField("X-KMail-Transport");
04891   if ((mTransport->currentText() != mTransport->text(0)) ||
04892       (!hf.isEmpty() && (hf != mTransport->text(0))))
04893     mMsg->setHeaderField("X-KMail-Transport", mTransport->currentText());
04894 
04895   mDisableBreaking = saveInDrafts;
04896 
04897   mBccMsgList.clear();
04898   bool sentOk = applyChanges();
04899   if( sentOk ) {
04900     if (!mAutoDeleteMsg) mEditor->setModified(FALSE);
04901     mEdtFrom->setEdited(FALSE);
04902     mEdtReplyTo->setEdited(FALSE);
04903     mEdtTo->setEdited(FALSE);
04904     mEdtCc->setEdited(FALSE);
04905     mEdtBcc->setEdited(FALSE);
04906     mEdtSubject->setEdited(FALSE);
04907     if (mTransport->lineEdit())
04908       mTransport->lineEdit()->setEdited(FALSE);
04909     mAtmModified = FALSE;
04910 
04911     // remove fields that contain no data (e.g. an empty Cc: or Bcc:)
04912     mMsg->cleanupHeader();
04913   }
04914 
04915   mDisableBreaking = false;
04916 
04917   if (!sentOk)
04918       return false;
04919 
04920   // needed for imap
04921   mMsg->setComplete( true );
04922 
04923   if (saveInDrafts)
04924   {
04925     KMFolder* draftsFolder = 0, *imapDraftsFolder = 0;
04926     // get the draftsFolder
04927     if ( !mMsg->drafts().isEmpty() )
04928     {
04929       draftsFolder = kmkernel->folderMgr()->findIdString( mMsg->drafts() );
04930       if ( draftsFolder == 0 )
04931         // This is *NOT* supposed to be "imapDraftsFolder", because a
04932         // dIMAP folder works like a normal folder
04933         draftsFolder = kmkernel->imapFolderMgr()->findIdString( mMsg->drafts() );
04934       if ( draftsFolder == 0 )
04935         imapDraftsFolder = kmkernel->imapFolderMgr()->findIdString( mMsg->drafts() );
04936       if ( !draftsFolder && !imapDraftsFolder )
04937       {
04938         const KMIdentity & id = kmkernel->identityManager()
04939           ->identityForUoidOrDefault( mMsg->headerField( "X-KMail-Identity" ).stripWhiteSpace().toUInt() );
04940         KMessageBox::information(0, i18n("The custom drafts folder for identity "
04941               "\"%1\" doesn't exist (anymore). "
04942               "Therefore the default drafts folder "
04943               "will be used.")
04944             .arg( id.identityName() ) );
04945       }
04946     }
04947     if (imapDraftsFolder && imapDraftsFolder->noContent())
04948       imapDraftsFolder = 0;
04949 
04950     if ( draftsFolder == 0 ) {
04951       draftsFolder = kmkernel->draftsFolder();
04952     } else {
04953       draftsFolder->open();
04954     }
04955     kdDebug(5006) << "saveindrafts: drafts=" << draftsFolder->name() << endl;
04956     if (imapDraftsFolder)
04957       kdDebug(5006) << "saveindrafts: imapdrafts="
04958         << imapDraftsFolder->name() << endl;
04959 
04960     sentOk = !(draftsFolder->addMsg(mMsg));
04961     if (imapDraftsFolder)
04962     {
04963       // move the message to the imap-folder and highlight it
04964       imapDraftsFolder->moveMsg(mMsg);
04965       (static_cast<KMFolderImap*>(imapDraftsFolder))->getFolder();
04966     }
04967 
04968   } else {
04969     mMsg->setTo( KMMessage::expandAliases( to() ));
04970     mMsg->setCc( KMMessage::expandAliases( cc() ));
04971     if( !mBcc.isEmpty() )
04972       mMsg->setBcc( KMMessage::expandAliases( mBcc ));
04973     QString recips = mMsg->headerField( "X-KMail-Recipients" );
04974     if( !recips.isEmpty() ) {
04975       mMsg->setHeaderField( "X-KMail-Recipients", KMMessage::expandAliases( recips ) );
04976     }
04977     mMsg->cleanupHeader();
04978     sentOk = kmkernel->msgSender()->send(mMsg, aSendNow);
04979     KMMessage* msg;
04980     for( msg = mBccMsgList.first(); msg; msg = mBccMsgList.next() ) {
04981       msg->setTo( KMMessage::expandAliases( to() ));
04982       msg->setCc( KMMessage::expandAliases( cc() ));
04983       msg->setBcc( KMMessage::expandAliases( bcc() ));
04984       QString recips = msg->headerField( "X-KMail-Recipients" );
04985       if( !recips.isEmpty() ) {
04986         msg->setHeaderField( "X-KMail-Recipients", KMMessage::expandAliases( recips ) );
04987       }
04988       msg->cleanupHeader();
04989       sentOk &= kmkernel->msgSender()->send(msg, aSendNow);
04990     }
04991   }
04992 
04993   if (!sentOk)
04994      return false;
04995 
04996   if (saveInDrafts || !aSendNow)
04997       emit messageQueuedOrDrafted();
04998 
04999   RecentAddresses::self(KMKernel::config())->add( bcc() );
05000   RecentAddresses::self(KMKernel::config())->add( cc() );
05001   RecentAddresses::self(KMKernel::config())->add( to() );
05002 
05003   mAutoDeleteMsg = FALSE;
05004   mFolder = 0;
05005   close();
05006   return true;
05007 }
05008 
05009 
05010 
05011 //----------------------------------------------------------------------------
05012 void KMComposeWin::slotSendLater()
05013 {
05014   if ( mEditor->checkExternalEditorFinished() )
05015     doSend( false );
05016 }
05017 
05018 
05019 //----------------------------------------------------------------------------
05020 bool KMComposeWin::slotSaveDraft() {
05021   return mEditor->checkExternalEditorFinished() && doSend( false, true );
05022 }
05023 
05024 
05025 //----------------------------------------------------------------------------
05026 void KMComposeWin::slotSendNow() {
05027   if ( !mEditor->checkExternalEditorFinished() )
05028     return;
05029   if (mConfirmSend) {
05030     switch(KMessageBox::warningYesNoCancel(mMainWidget,
05031                                     i18n("About to send email..."),
05032                                     i18n("Send Confirmation"),
05033                                     i18n("Send &Now"),
05034                                     i18n("Send &Later"))) {
05035     case KMessageBox::Yes:        // send now
05036         doSend(TRUE);
05037       break;
05038     case KMessageBox::No:        // send later
05039         doSend(FALSE);
05040       break;
05041     case KMessageBox::Cancel:        // cancel
05042       break;
05043     default:
05044       ;    // whoa something weird happened here!
05045     }
05046     return;
05047   }
05048 
05049   doSend(TRUE);
05050 }
05051 
05052 
05053 //----------------------------------------------------------------------------
05054 void KMComposeWin::slotAppendSignature()
05055 {
05056   bool mod = mEditor->isModified();
05057 
05058   const KMIdentity & ident =
05059     kmkernel->identityManager()->identityForUoidOrDefault( mIdentity->currentIdentity() );
05060   mOldSigText = ident.signatureText();
05061   if( !mOldSigText.isEmpty() )
05062   {
05063     mEditor->sync();
05064     mEditor->append(mOldSigText);
05065     mEditor->update();
05066     mEditor->setModified(mod);
05067     mEditor->setContentsPos( 0, 0 );
05068   }
05069   kmkernel->dumpDeadLetters();
05070 }
05071 
05072 
05073 //-----------------------------------------------------------------------------
05074 void KMComposeWin::slotHelp()
05075 {
05076   kapp->invokeHelp();
05077 }
05078 
05079 //-----------------------------------------------------------------------------
05080 void KMComposeWin::slotCleanSpace()
05081 {
05082   mEditor->cleanWhiteSpace();
05083 }
05084 
05085 
05086 //-----------------------------------------------------------------------------
05087 void KMComposeWin::slotSpellcheck()
05088 {
05089   if (mSpellCheckInProgress) return;
05090 
05091   mSpellCheckInProgress=TRUE;
05092   /*
05093     connect (mEditor, SIGNAL (spellcheck_progress (unsigned)),
05094     this, SLOT (spell_progress (unsigned)));
05095     */
05096 
05097   mEditor->spellcheck();
05098 }
05099 
05100 
05101 //-----------------------------------------------------------------------------
05102 void KMComposeWin::slotSpellcheckDone(int result)
05103 {
05104   kdDebug(5006) << "spell check complete: result = " << result << endl;
05105   mSpellCheckInProgress=FALSE;
05106 
05107   switch( result )
05108   {
05109     case KS_CANCEL:
05110       statusBar()->changeItem(i18n(" Spell check canceled."),0);
05111       break;
05112     case KS_STOP:
05113       statusBar()->changeItem(i18n(" Spell check stopped."),0);
05114       break;
05115     default:
05116       statusBar()->changeItem(i18n(" Spell check complete."),0);
05117       break;
05118   }
05119   QTimer::singleShot( 2000, this, SLOT(slotSpellcheckDoneClearStatus()) );
05120 }
05121 
05122 void KMComposeWin::slotSpellcheckDoneClearStatus()
05123 {
05124   statusBar()->changeItem("", 0);
05125 }
05126 
05127 
05128 //-----------------------------------------------------------------------------
05129 void KMComposeWin::focusNextPrevEdit(const QWidget* aCur, bool aNext)
05130 {
05131   QWidget* cur;
05132 
05133   if (!aCur)
05134   {
05135     cur=mEdtList.last();
05136   }
05137   else
05138   {
05139     for (cur=mEdtList.first(); aCur!=cur && cur; cur=mEdtList.next())
05140       ;
05141     if (!cur) return;
05142     if (aNext) cur = mEdtList.next();
05143     else cur = mEdtList.prev();
05144   }
05145   if (cur) cur->setFocus();
05146   else if (aNext) mEditor->setFocus(); //Key up from first doea nothing (sven)
05147 }
05148 
05149 //-----------------------------------------------------------------------------
05150 void KMComposeWin::slotIdentityChanged(uint uoid)
05151 {
05152   const KMIdentity & ident =
05153     kmkernel->identityManager()->identityForUoid( uoid );
05154   if ( ident.isNull() ) return;
05155 
05156   if(!ident.fullEmailAddr().isNull())
05157     mEdtFrom->setText(ident.fullEmailAddr());
05158   mEdtReplyTo->setText(ident.replyToAddr());
05159   // don't overwrite the BCC field when the user has edited it and the
05160   // BCC field of the new identity is empty
05161   if( !mEdtBcc->edited() || !ident.bcc().isEmpty() )
05162     mEdtBcc->setText(ident.bcc());
05163   // make sure the BCC field is shown because else it's ignored
05164   if (! ident.bcc().isEmpty()) {
05165     mShowHeaders |= HDR_BCC;
05166   }
05167   if (ident.organization().isEmpty())
05168     mMsg->removeHeaderField("Organization");
05169   else
05170     mMsg->setHeaderField("Organization", ident.organization());
05171 
05172   if (!mBtnTransport->isChecked()) {
05173     QString transp = ident.transport();
05174     if (transp.isEmpty())
05175     {
05176       mMsg->removeHeaderField("X-KMail-Transport");
05177       transp = mTransport->text(0);
05178     }
05179     else
05180       mMsg->setHeaderField("X-KMail-Transport", transp);
05181     bool found = false;
05182     int i;
05183     for (i = 0; i < mTransport->count(); i++) {
05184       if (mTransport->text(i) == transp) {
05185         found = true;
05186         mTransport->setCurrentItem(i);
05187         break;
05188       }
05189     }
05190     if (found == false) {
05191       if (i == mTransport->maxCount()) mTransport->setMaxCount(i + 1);
05192       mTransport->insertItem(transp,i);
05193       mTransport->setCurrentItem(i);
05194     }
05195   }
05196 
05197   mDictionaryCombo->setCurrentByDictionary( ident.dictionary() );
05198 
05199   if ( !mBtnFcc->isChecked() )
05200   {
05201     if ( ident.fcc().isEmpty() )
05202       mFcc->setFolder( kmkernel->sentFolder() );
05203     else
05204       setFcc( ident.fcc() );
05205   }
05206 
05207   QString edtText = mEditor->text();
05208   bool appendNewSig = true;
05209   // try to truncate the old sig
05210   if( !mOldSigText.isEmpty() )
05211   {
05212     if( edtText.endsWith( mOldSigText ) )
05213       edtText.truncate( edtText.length() - mOldSigText.length() );
05214     else
05215       appendNewSig = false;
05216   }
05217   // now append the new sig
05218   mOldSigText = ident.signatureText();
05219   if( appendNewSig )
05220   {
05221     if( !mOldSigText.isEmpty() && mAutoSign )
05222       edtText.append( mOldSigText );
05223     mEditor->setText( edtText );
05224   }
05225 
05226   // disable certain actions if there is no PGP user identity set
05227   // for this profile
05228   bool bNewIdentityHasOpenPgpKey = !ident.pgpIdentity().isEmpty();
05229   if( !mSelectedCryptPlug && !bNewIdentityHasOpenPgpKey )
05230   {
05231     mAttachMPK->setEnabled(false);
05232     if( mLastIdentityHasOpenPgpKey )
05233     { // save the state of the sign and encrypt button
05234       mLastEncryptActionState = mEncryptAction->isChecked();
05235       setEncryption( false );
05236       mLastSignActionState = mSignAction->isChecked();
05237       setSigning( false );
05238     }
05239   }
05240   else
05241   {
05242     mAttachMPK->setEnabled(true);
05243     if( !mLastIdentityHasOpenPgpKey )
05244     { // restore the last state of the sign and encrypt button
05245       setEncryption( mLastEncryptActionState );
05246       setSigning( mLastSignActionState );
05247     }
05248   }
05249   mLastIdentityHasOpenPgpKey = bNewIdentityHasOpenPgpKey;
05250 
05251   mEditor->setModified(TRUE);
05252   mId = uoid;
05253 
05254   // make sure the BCC field is shown if necessary
05255   rethinkFields( false );
05256 }
05257 
05258 //-----------------------------------------------------------------------------
05259 void KMComposeWin::slotSpellcheckConfig()
05260 {
05261   KWin kwin;
05262   QTabDialog qtd (this, "tabdialog", true);
05263   KSpellConfig mKSpellConfig (&qtd);
05264 
05265   qtd.addTab (&mKSpellConfig, i18n("Spellchecker"));
05266   qtd.setCancelButton ();
05267 
05268   kwin.setIcons (qtd.winId(), kapp->icon(), kapp->miniIcon());
05269   qtd.setCancelButton(KStdGuiItem::cancel().text());
05270   qtd.setOkButton(KStdGuiItem::ok().text());
05271 
05272   if (qtd.exec())
05273     mKSpellConfig.writeGlobalSettings();
05274 }
05275 
05276 //-----------------------------------------------------------------------------
05277 void KMComposeWin::slotStatusMessage(const QString &message)
05278 {
05279     statusBar()->changeItem( message, 0 );
05280 }
05281 
05282 void KMComposeWin::slotEditToolbars()
05283 {
05284   saveMainWindowSettings(KMKernel::config(), "Composer");
05285   KEditToolbar dlg(guiFactory(), this);
05286 
05287   connect( &dlg, SIGNAL(newToolbarConfig()),
05288            SLOT(slotUpdateToolbars()) );
05289 
05290   dlg.exec();
05291 }
05292 
05293 void KMComposeWin::slotUpdateToolbars()
05294 {
05295   createGUI("kmcomposerui.rc");
05296   applyMainWindowSettings(KMKernel::config(), "Composer");
05297 }
05298 
05299 void KMComposeWin::slotEditKeys()
05300 {
05301   KKeyDialog::configure( actionCollection(),
05302                          false /*don't allow one-letter shortcuts*/
05303                          );
05304 }
05305 
05306 void KMComposeWin::setReplyFocus( bool hasMessage )
05307 {
05308   mEditor->setFocus();
05309   if ( hasMessage )
05310     mEditor->setCursorPosition( 1, 0 );
05311 }
05312 
05313 void KMComposeWin::setFocusToSubject()
05314 {
05315   mEdtSubject->setFocus();
05316 }
05317 
05318 void KMComposeWin::slotCompletionModeChanged( KGlobalSettings::Completion mode)
05319 {
05320     KConfig *config = KMKernel::config();
05321     KConfigGroupSaver cs( config, "Composer" );
05322     config->writeEntry( "Completion Mode", (int) mode );
05323     config->sync(); // maybe not?
05324 
05325     // sync all the lineedits to the same completion mode
05326     mEdtFrom->setCompletionMode( mode );
05327     mEdtReplyTo->setCompletionMode( mode );
05328     mEdtTo->setCompletionMode( mode );
05329     mEdtCc->setCompletionMode( mode );
05330     mEdtBcc->setCompletionMode( mode );
05331 }
05332 
05333 void KMComposeWin::slotConfigChanged()
05334 {
05335     readConfig();
05336 }
05337 
05338 /*
05339 * checks if the drafts-folder has been deleted
05340 * that is not nice so we set the system-drafts-folder
05341 */
05342 void KMComposeWin::slotFolderRemoved(KMFolder* folder)
05343 {
05344         if ( (mFolder) && (folder->idString() == mFolder->idString()) )
05345         {
05346                 mFolder = kmkernel->draftsFolder();
05347                 kdDebug(5006) << "restoring drafts to " << mFolder->idString() << endl;
05348         }
05349         if (mMsg) mMsg->setParent(0);
05350 }
05351 
05352 
05353 
05354 void KMComposeWin::slotSetAlwaysSend( bool bAlways )
05355 {
05356     mAlwaysSend = bAlways;
05357 }
05358 
05359 void KMEdit::contentsDragEnterEvent(QDragEnterEvent *e)
05360 {
05361     if (e->provides(MailListDrag::format()))
05362         e->accept(true);
05363     else
05364         return KEdit::dragEnterEvent(e);
05365 }
05366 
05367 void KMEdit::contentsDragMoveEvent(QDragMoveEvent *e)
05368 {
05369     if (e->provides(MailListDrag::format()))
05370         e->accept();
05371     else
05372         return KEdit::dragMoveEvent(e);
05373 }
05374 
05375 void KMEdit::keyPressEvent( QKeyEvent* e )
05376 {
05377     if( e->key() == Key_Return ) {
05378         int line, col;
05379         getCursorPosition( &line, &col );
05380         QString lineText = text( line );
05381         // returns line with additional trailing space (bug in Qt?), cut it off
05382         lineText.truncate( lineText.length() - 1 );
05383         // special treatment of quoted lines only if the cursor is neither at
05384         // the begin nor at the end of the line
05385         if( ( col > 0 ) && ( col < int( lineText.length() ) ) ) {
05386             bool isQuotedLine = false;
05387             uint bot = 0; // bot = begin of text after quote indicators
05388             while( bot < lineText.length() ) {
05389                 if( ( lineText[bot] == '>' ) || ( lineText[bot] == '|' ) ) {
05390                     isQuotedLine = true;
05391                     ++bot;
05392                 }
05393                 else if( lineText[bot].isSpace() ) {
05394                     ++bot;
05395                 }
05396                 else {
05397                     break;
05398                 }
05399             }
05400 
05401             KEdit::keyPressEvent( e );
05402 
05403             // duplicate quote indicators of the previous line before the new
05404             // line if the line actually contained text (apart from the quote
05405             // indicators) and the cursor is behind the quote indicators
05406             if( isQuotedLine
05407                 && ( bot != lineText.length() )
05408                 && ( col >= int( bot ) ) ) {
05409                 QString newLine = text( line + 1 );
05410                 // remove leading white space from the new line and instead
05411                 // add the quote indicators of the previous line
05412                 unsigned int leadingWhiteSpaceCount = 0;
05413                 while( ( leadingWhiteSpaceCount < newLine.length() )
05414                        && newLine[leadingWhiteSpaceCount].isSpace() ) {
05415                     ++leadingWhiteSpaceCount;
05416                 }
05417                 newLine = newLine.replace( 0, leadingWhiteSpaceCount,
05418                                            lineText.left( bot ) );
05419                 removeParagraph( line + 1 );
05420                 insertParagraph( newLine, line + 1 );
05421                 // place the cursor at the begin of the new line since
05422                 // we assume that the user split the quoted line in order
05423                 // to add a comment to the first part of the quoted line
05424                 setCursorPosition( line + 1 , 0 );
05425             }
05426         }
05427         else
05428             KEdit::keyPressEvent( e );
05429     }
05430     else
05431         KEdit::keyPressEvent( e );
05432 }
05433 
05434 void KMEdit::contentsDropEvent(QDropEvent *e)
05435 {
05436     if (e->provides(MailListDrag::format())) {
05437         // Decode the list of serial numbers stored as the drag data
05438         QByteArray serNums;
05439         MailListDrag::decode( e, serNums );
05440         QBuffer serNumBuffer(serNums);
05441         serNumBuffer.open(IO_ReadOnly);
05442         QDataStream serNumStream(&serNumBuffer);
05443         unsigned long serNum;
05444         KMFolder *folder = 0;
05445         int idx;
05446         QPtrList<KMMsgBase> messageList;
05447         while (!serNumStream.atEnd()) {
05448             KMMsgBase *msgBase = 0;
05449             serNumStream >> serNum;
05450             kmkernel->msgDict()->getLocation(serNum, &folder, &idx);
05451             if (folder)
05452                 msgBase = folder->getMsgBase(idx);
05453             if (msgBase)
05454                 messageList.append( msgBase );
05455         }
05456         serNumBuffer.close();
05457         uint identity = folder ? folder->identity() : 0;
05458         KMCommand *command =
05459             new KMForwardAttachedCommand(mComposer, messageList,
05460                                          identity, mComposer);
05461         command->start();
05462     }
05463     else if( KURLDrag::canDecode( e ) ) {
05464         KURL::List urlList;
05465         if( KURLDrag::decode( e, urlList ) ) {
05466             for( KURL::List::Iterator it = urlList.begin();
05467                  it != urlList.end(); ++it ) {
05468                 mComposer->addAttach( *it );
05469             }
05470         }
05471     }
05472     else {
05473         return KEdit::dropEvent(e);
05474     }
05475 }
05476 
05477 //=============================================================================
05478 //
05479 //   Class  KMAtmListViewItem
05480 //
05481 //=============================================================================
05482 
05483 KMAtmListViewItem::KMAtmListViewItem(QListView *parent) :
05484   QObject(), QListViewItem( parent )
05485 {
05486 
05487   mCBSignEnabled = false;
05488   mCBEncryptEnabled = false;
05489 
05490   mListview = parent;
05491   mCBEncrypt = new QCheckBox(mListview->viewport());
05492   mCBSign = new QCheckBox(mListview->viewport());
05493 
05494   mCBEncrypt->hide();
05495   mCBSign->hide();
05496 }
05497 
05498 KMAtmListViewItem::~KMAtmListViewItem()
05499 {
05500 }
05501 
05502 void KMAtmListViewItem::paintCell( QPainter * p, const QColorGroup & cg,
05503                                   int column, int width, int align )
05504 {
05505   // this is also called for the encrypt/sign columns to assure that the
05506   // background is cleared
05507   QListViewItem::paintCell( p, cg, column, width, align );
05508   if( 4 == column || 5 == column ) {
05509     QRect r = mListview->itemRect( this );
05510     if ( !r.size().isValid() ) {
05511         mListview->ensureItemVisible( this );
05512         mListview->repaintContents( FALSE );
05513         r = mListview->itemRect( this );
05514     }
05515     int colWidth = mListview->header()->sectionSize( column );
05516     r.setX( mListview->header()->sectionPos( column )
05517             + colWidth / 2
05518             - r.height() / 2
05519             - 1 );
05520     r.setY( r.y() + 1 );
05521     r.setWidth(  r.height() - 2 );
05522     r.setHeight( r.height() - 2 );
05523     r = QRect( mListview->viewportToContents( r.topLeft() ), r.size() );
05524 
05525     QCheckBox* cb = (4 == column) ? mCBEncrypt : mCBSign;
05526     cb->resize( r.size() );
05527     mListview->moveChild( cb, r.x(), r.y() );
05528 
05529     QColor bg;
05530     if (isSelected())
05531       bg = cg.highlight();
05532     else
05533       bg = cg.base();
05534 
05535     bool enabled = (4 == column) ? mCBEncryptEnabled : mCBSignEnabled;
05536     cb->setPaletteBackgroundColor(bg);
05537     if (enabled) cb->show();
05538   }
05539 }
05540 
05541 void KMAtmListViewItem::enableCryptoCBs(bool on)
05542 {
05543   if( mCBEncrypt ) {
05544     mCBEncryptEnabled = on;
05545     mCBEncrypt->setEnabled( on );
05546   }
05547   if( mCBSign ) {
05548     mCBSignEnabled = on;
05549     mCBSign->setEnabled( on );
05550   }
05551 }
05552 
05553 void KMAtmListViewItem::setEncrypt(bool on)
05554 {
05555   if( mCBEncrypt )
05556     mCBEncrypt->setChecked( on );
05557 }
05558 
05559 bool KMAtmListViewItem::isEncrypt()
05560 {
05561   if( mCBEncrypt )
05562     return mCBEncrypt->isChecked();
05563   else
05564     return false;
05565 }
05566 
05567 void KMAtmListViewItem::setSign(bool on)
05568 {
05569   if( mCBSign )
05570     mCBSign->setChecked( on );
05571 }
05572 
05573 bool KMAtmListViewItem::isSign()
05574 {
05575   if( mCBSign )
05576     return mCBSign->isChecked();
05577   else
05578     return false;
05579 }
05580 
05581 
05582 
05583 //=============================================================================
05584 //
05585 //   Class  KMLineEdit
05586 //
05587 //=============================================================================
05588 
05589 KMLineEdit::KMLineEdit(KMComposeWin* composer, bool useCompletion,
05590                        QWidget *parent, const char *name)
05591     : AddressLineEdit(parent,useCompletion,name), mComposer(composer)
05592 {
05593 }
05594 
05595 
05596 //-----------------------------------------------------------------------------
05597 void KMLineEdit::keyPressEvent(QKeyEvent *e)
05598 {
05599     // ---sven's Return is same Tab and arrow key navigation start ---
05600     if ((e->key() == Key_Enter || e->key() == Key_Return) &&
05601         !completionBox()->isVisible())
05602     {
05603       mComposer->focusNextPrevEdit(this,TRUE);
05604       return;
05605     }
05606     if (e->key() == Key_Up)
05607     {
05608       mComposer->focusNextPrevEdit(this,FALSE); // Go up
05609       return;
05610     }
05611     if (e->key() == Key_Down)
05612     {
05613       mComposer->focusNextPrevEdit(this,TRUE); // Go down
05614       return;
05615     }
05616     // ---sven's Return is same Tab and arrow key navigation end ---
05617   AddressLineEdit::keyPressEvent(e);
05618 }
05619 
05620 #if 0
05621 //-----------------------------------------------------------------------------
05622 void KMLineEdit::dropEvent(QDropEvent *e)
05623 {
05624   KURL::List uriList;
05625   if(KURLDrag::decode( e, uriList ))
05626   {
05627     for (KURL::List::ConstIterator it = uriList.begin(); it != uriList.end(); ++it)
05628     {
05629       smartInsert( (*it).url() );
05630     }
05631   }
05632   else {
05633     if (m_useCompletion)
05634        m_smartPaste = true;
05635     QLineEdit::dropEvent(e);
05636     m_smartPaste = false;
05637   }
05638 }
05639 
05640 void KMLineEdit::smartInsert( const QString &str, int pos /* = -1 */ )
05641 {
05642     QString newText = str.stripWhiteSpace();
05643     if (newText.isEmpty())
05644         return;
05645 
05646     // remove newlines in the to-be-pasted string:
05647     newText.replace( QRegExp("\r?\n"), " " );
05648 
05649     QString contents = text();
05650     // determine the position where to insert the to-be-pasted string
05651     if( ( pos < 0 ) || ( pos > (int) contents.length() ) )
05652         pos = contents.length();
05653     int start_sel = 0;
05654     int end_sel = 0;
05655     if (getSelection(&start_sel, &end_sel))
05656     {
05657         // Cut away the selection.
05658         if (pos > end_sel)
05659             pos -= (end_sel - start_sel);
05660         else if (pos > start_sel)
05661             pos = start_sel;
05662         contents = contents.left(start_sel) + contents.right(end_sel+1);
05663     }
05664 
05665     int eot = contents.length();
05666     // remove trailing whitespace from the contents of the line edit
05667     while ((eot > 0) && contents[eot-1].isSpace()) eot--;
05668     if (eot == 0)
05669      {
05670          contents = QString::null;
05671      }
05672     else if (pos >= eot)
05673     {
05674         if (contents[eot-1] == ',')
05675             eot--;
05676         contents.truncate(eot);
05677         contents += ", ";
05678         pos = eot+2;
05679     }
05680 
05681     if (newText.startsWith("mailto:"))
05682     {
05683         kdDebug(5006) << "Pasting '" << newText << "'" << endl;
05684         KURL u(newText);
05685         newText = u.path();
05686         kdDebug(5006) << "path of mailto URL: '" << newText << "'" << endl;
05687         // Is the mailto URL RFC 2047 encoded (cf. RFC 2368)?
05688         if (-1 != newText.find( QRegExp("=\\?.*\\?[bq]\\?.*\\?=") ) )
05689             newText = KMMsgBase::decodeRFC2047String( newText.latin1() );
05690     }
05691     else if (-1 != newText.find(" at "))
05692     {
05693         // Anti-spam stuff
05694         newText.replace( " at ", "@" );
        newText.replace( " dot ", "." );
05695     }
05696     else if (newText.contains("(at)"))
05697     {
05698         newText.replace( QRegExp("\\s*\\(at\\)\\s*"), "@" );
    }
    contents = contents.left(pos)+newText+contents.mid(pos);
    setText(contents);
    setEdited( true );
    setCursorPosition(pos+newText.length());
}
#endif

//-----------------------------------------------------------------------------
void KMLineEdit::loadAddresses()
{
    AddressLineEdit::loadAddresses();

    QStringList recent = RecentAddresses::self(KMKernel::config())->addresses();
    QStringList::Iterator it = recent.begin();
    for ( ; it != recent.end(); ++it )
        addAddress( *it );
}


KMLineEditSpell::KMLineEditSpell(KMComposeWin* composer, bool useCompletion,
                       QWidget *parent, const char *name)
    : KMLineEdit(composer,useCompletion,parent,name)
{
}


void KMLineEditSpell::highLightWord( unsigned int length, unsigned int pos )
{
    setSelection ( pos, length );
}

void KMLineEditSpell::spellCheckDone( const QString &s )
{
    if( s != text() )
        setText( s );
}

void KMLineEditSpell::spellCheckerMisspelling( const QString &_text, const QStringList&, unsigned int pos)
{
     highLightWord( _text.length(),pos );
}

void KMLineEditSpell::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos)
{
    if( old!= corr )
    {
        setSelection ( pos, old.length() );
        insert( corr );
        setSelection ( pos, corr.length() );
    }
}


//=============================================================================
//
//   Class  KMEdit
//
//=============================================================================
KMEdit::KMEdit(QWidget *parent, KMComposeWin* composer,
               KSpellConfig* autoSpellConfig,
               const char *name)
  : KEdit( parent, name ),
    mComposer( composer ),
    mKSpell( 0 ),
    mSpellingFilter( 0 ),
    mExtEditorTempFile( 0 ),
    mExtEditorTempFileWatcher( 0 ),
    mExtEditorProcess( 0 ),
    mUseExtEditor( false ),
    mWasModifiedBeforeSpellCheck( false ),
    mSpellChecker( 0 ),
    mSpellLineEdit( false )
{
  installEventFilter(this);
  KCursor::setAutoHideCursor( this, true, true );

  initializeAutoSpellChecking( autoSpellConfig );
}

//-----------------------------------------------------------------------------
void KMEdit::initializeAutoSpellChecking( KSpellConfig* autoSpellConfig )
{
  KConfigGroup readerConfig( KMKernel::config(), "Reader" );
05699   QColor defaultColor1( 0x00, 0x80, 0x00 ); // defaults from kmreaderwin.cpp
05700   QColor defaultColor2( 0x00, 0x70, 0x00 );
05701   QColor defaultColor3( 0x00, 0x60, 0x00 );
05702   QColor defaultForeground( kapp->palette().active().text() );
05703   QColor col1 = readerConfig.readColorEntry( "ForegroundColor", &defaultForeground );
05704   QColor col2 = readerConfig.readColorEntry( "QuotedText3", &defaultColor3 );
05705   QColor col3 = readerConfig.readColorEntry( "QuotedText2", &defaultColor2 );
05706   QColor col4 = readerConfig.readColorEntry( "QuotedText1", &defaultColor1 );
05707   QColor c = Qt::red;
05708   QColor misspelled = readerConfig.readColorEntry( "MisspelledColor", &c );
05709 
05710   mSpellChecker = new KDictSpellingHighlighter( this, /*active*/ true,
05711                                                 /*autoEnabled*/ false,
05712                                                 /*spellColor*/ misspelled,
05713                                                 /*colorQuoting*/ true,
05714                                                 col1, col2, col3, col4,
05715                                                 autoSpellConfig );
05716   connect( mSpellChecker, SIGNAL(activeChanged(const QString &)),
05717            mComposer, SLOT(slotStatusMessage(const QString &)));
05718   connect( mSpellChecker, SIGNAL(newSuggestions(const QString&, const QStringList&, unsigned int)),
05719            this, SLOT(addSuggestion(const QString&, const QStringList&, unsigned int)) );
05720 }
05721 
05722 //-----------------------------------------------------------------------------
05723 void KMEdit::addSuggestion(const QString& text, const QStringList& lst, unsigned int )
05724 {
05725   mReplacements[text] = lst;
05726 }
05727 
05728 //-----------------------------------------------------------------------------
05729 KMEdit::~KMEdit()
05730 {
05731   removeEventFilter(this);
05732 
05733   delete mKSpell;
05734   delete mSpellChecker;
05735 }
05736 
05737 
05738 //-----------------------------------------------------------------------------
05739 QString KMEdit::brokenText()
05740 {
05741   QString temp, line;
05742 
05743   int num_lines = numLines();
05744   for (int i = 0; i < num_lines; ++i)
05745   {
05746     int lastLine = 0;
05747     line = textLine(i);
05748     for (int j = 0; j < (int)line.length(); ++j)
05749     {
05750       if (lineOfChar(i, j) > lastLine)
05751       {
05752         lastLine = lineOfChar(i, j);
05753         temp += '\n';
05754       }
05755       temp += line[j];
05756     }
05757     if (i + 1 < num_lines) temp += '\n';
05758   }
05759 
05760   return temp;
05761 }
05762 
05763 //-----------------------------------------------------------------------------
05764 bool KMEdit::eventFilter(QObject*o, QEvent* e)
05765 {
05766   if (o == this)
05767     KCursor::autoHideEventFilter(o, e);
05768 
05769   if (e->type() == QEvent::KeyPress)
05770   {
05771     QKeyEvent *k = (QKeyEvent*)e;
05772 
05773     if (mUseExtEditor) {
05774       if (k->key() == Key_Up)
05775       {
05776         mComposer->focusNextPrevEdit(0, false); //take me up
05777         return TRUE;
05778       }
05779 
05780       // ignore modifier keys (cf. bug 48841)
05781       if ( (k->key() == Key_Shift) || (k->key() == Key_Control) ||
05782            (k->key() == Key_Meta) || (k->key() == Key_Alt) )
05783         return true;
05784       if (mExtEditorTempFile) return TRUE;
05785       QString sysLine = mExtEditor;
05786       mExtEditorTempFile = new KTempFile();
05787 
05788       mExtEditorTempFile->setAutoDelete(true);
05789 
05790       (*mExtEditorTempFile->textStream()) << text();
05791 
05792       mExtEditorTempFile->close();
05793       // replace %f in the system line
05794       sysLine.replace( "%f", mExtEditorTempFile->name() );
05795       mExtEditorProcess = new KProcess();
05796       sysLine += " ";
05797       while (!sysLine.isEmpty())
05798       {
05799         *mExtEditorProcess << sysLine.left(sysLine.find(" ")).local8Bit();
05800         sysLine.remove(0, sysLine.find(" ") + 1);
05801       }
05802       connect(mExtEditorProcess, SIGNAL(processExited(KProcess*)),
05803               SLOT(slotExternalEditorDone(KProcess*)));
05804       if (!mExtEditorProcess->start())
05805       {
05806         KMessageBox::error( topLevelWidget(),
05807                             i18n("Unable to start external editor.") );
05808         killExternalEditor();
05809       } else {
05810         mExtEditorTempFileWatcher = new KDirWatch( this, "mExtEditorTempFileWatcher" );
05811         connect( mExtEditorTempFileWatcher, SIGNAL(dirty(const QString&)),
05812                  SLOT(slotExternalEditorTempFileChanged(const QString&)) );
05813         mExtEditorTempFileWatcher->addFile( mExtEditorTempFile->name() );
05814       }
05815       return TRUE;
05816     } else {
05817     // ---sven's Arrow key navigation start ---
05818     // Key Up in first line takes you to Subject line.
05819     if (k->key() == Key_Up && k->state() != ShiftButton && currentLine() == 0
05820       && lineOfChar(0, currentColumn()) == 0)
05821     {
05822       deselect();
05823       mComposer->focusNextPrevEdit(0, false); //take me up
05824       return TRUE;
05825     }
05826     // ---sven's Arrow key navigation end ---
05827 
05828     if (k->key() == Key_Backtab && k->state() == ShiftButton)
05829     {
05830       deselect();
05831       mComposer->focusNextPrevEdit(0, false);
05832       return TRUE;
05833     }
05834 
05835     }
05836   } else if ( e->type() == QEvent::ContextMenu ) {
05837     QContextMenuEvent *event = (QContextMenuEvent*) e;
05838 
05839     int para = 1, charPos, firstSpace, lastSpace;
05840 
05841     //Get the character at the position of the click
05842     charPos = charAt( viewportToContents(event->pos()), &para );
05843     QString paraText = text( para );
05844 
05845     if( !paraText.at(charPos).isSpace() )
05846     {
05847       //Get word right clicked on
05848       const QRegExp wordBoundary( "[\\s\\W]" );
05849       firstSpace = paraText.findRev( wordBoundary, charPos ) + 1;
05850       lastSpace = paraText.find( wordBoundary, charPos );
05851       if( lastSpace == -1 )
05852         lastSpace = paraText.length();
05853       QString word = paraText.mid( firstSpace, lastSpace - firstSpace );
05854       //Continue if this word was misspelled
05855       if( !word.isEmpty() && mReplacements.contains( word ) )
05856       {
05857         KPopupMenu p;
05858         p.insertTitle( i18n("Suggestions") );
05859 
05860         //Add the suggestions to the popup menu
05861         QStringList reps = mReplacements[word];
05862         if( reps.count() > 0 )
05863         {
05864           int listPos = 0;
05865           for ( QStringList::Iterator it = reps.begin(); it != reps.end(); ++it ) {
05866             p.insertItem( *it, listPos );
05867             listPos++;
05868           }
05869         }
05870         else
05871         {
05872           p.insertItem( QString::fromLatin1("No Suggestions"), -2 );
05873         }
05874 
05875         //Execute the popup inline
05876         int id = p.exec( mapToGlobal( event->pos() ) );
05877 
05878         if( id > -1 )
05879         {
05880           //Save the cursor position
05881           int parIdx = 1, txtIdx = 1;
05882           getCursorPosition(&parIdx, &txtIdx);
05883           setSelection(para, firstSpace, para, lastSpace);
05884           insert(mReplacements[word][id]);
05885           // Restore the cursor position; if the cursor was behind the
05886           // misspelled word then adjust the cursor position
05887           if ( para == parIdx && txtIdx >= lastSpace )
05888             txtIdx += mReplacements[word][id].length() - word.length();
05889           setCursorPosition(parIdx, txtIdx);
05890         }
05891         //Cancel original event
05892         return true;
05893       }
05894     }
05895   }
05896 
05897   return KEdit::eventFilter(o, e);
05898 }
05899 
05900 
05901 //-----------------------------------------------------------------------------
05902 void KMEdit::slotAutoSpellCheckingToggled( bool on )
05903 {
05904   // don't autoEnable spell checking if the user turned spell checking off
05905   mSpellChecker->setAutomatic( on );
05906   mSpellChecker->setActive( on );
05907 }
05908 
05909 
05910 //-----------------------------------------------------------------------------
05911 void KMEdit::slotExternalEditorTempFileChanged( const QString & fileName ) {
05912   if ( !mExtEditorTempFile )
05913     return;
05914   if ( fileName != mExtEditorTempFile->name() )
05915     return;
05916   // read data back in from file
05917   setAutoUpdate(false);
05918   clear();
05919 
05920   insertLine(QString::fromLocal8Bit(kFileToString( fileName, true, false )), -1);
05921   setAutoUpdate(true);
05922   repaint();
05923 }
05924 
05925 void KMEdit::slotExternalEditorDone( KProcess * proc ) {
05926   assert(proc == mExtEditorProcess);
05927   // make sure, we update even when KDirWatcher is too slow:
05928   slotExternalEditorTempFileChanged( mExtEditorTempFile->name() );
05929   killExternalEditor();
05930 }
05931 
05932 void KMEdit::killExternalEditor() {
05933   delete mExtEditorTempFileWatcher; mExtEditorTempFileWatcher = 0;
05934   delete mExtEditorTempFile; mExtEditorTempFile = 0;
05935   delete mExtEditorProcess; mExtEditorProcess = 0;
05936 }
05937 
05938 
05939 bool KMEdit::checkExternalEditorFinished() {
05940   if ( !mExtEditorProcess )
05941     return true;
05942   switch ( KMessageBox::warningYesNoCancel( topLevelWidget(),
05943            i18n("The external editor is still running.\n"
05944                 "Abort the external editor or leave it open?"),
05945            i18n("External Editor"),
05946            i18n("Abort Editor"), i18n("Leave Editor Open") ) ) {
05947   case KMessageBox::Yes:
05948     killExternalEditor();
05949     return true;
05950   case KMessageBox::No:
05951     return true;
05952   default:
05953     return false;
05954   }
05955 }
05956 
05957 //-----------------------------------------------------------------------------
05958 void KMEdit::spellcheck()
05959 {
05960   if ( mKSpell )
05961     return;
05962   mWasModifiedBeforeSpellCheck = isModified();
05963   mSpellLineEdit = !mSpellLineEdit;
05964   mKSpell = new KSpell(this, i18n("Spellcheck - KMail"), this,
05965                        SLOT(slotSpellcheck2(KSpell*)));
05966   QStringList l = KSpellingHighlighter::personalWords();
05967   for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
05968       mKSpell->addPersonal( *it );
05969   }
05970   connect (mKSpell, SIGNAL( death()),
05971           this, SLOT (slotSpellDone()));
05972   connect (mKSpell, SIGNAL (misspelling (const QString &, const QStringList &, unsigned int)),
05973           this, SLOT (slotMisspelling (const QString &, const QStringList &, unsigned int)));
05974   connect (mKSpell, SIGNAL (corrected (const QString &, const QString &, unsigned int)),
05975           this, SLOT (slotCorrected (const QString &, const QString &, unsigned int)));
05976   connect (mKSpell, SIGNAL (done(const QString &)),
05977           this, SLOT (slotSpellResult (const QString&)));
05978 }
05979 
05980 #if KDE_IS_VERSION( 3, 1, 92 )
05981 void KMEdit::cut()
05982 {
05983     KEdit::cut();
05984     mSpellChecker->restartBackgroundSpellCheck();
05985 }
05986 
05987 void KMEdit::clear()
05988 {
05989     KEdit::clear();
05990     mSpellChecker->restartBackgroundSpellCheck();
05991 }
05992 
05993 void KMEdit::del()
05994 {
05995     KEdit::del();
05996     mSpellChecker->restartBackgroundSpellCheck();
05997 }
05998 #else
05999 // can't #ifdef slots :-(
06000 void KMEdit::cut() { KEdit::cut(); }
06001 void KMEdit::clear() { KEdit::clear(); }
06002 void KMEdit::del() { KEdit::del(); }
06003 #endif
06004 
06005 
06006 void KMEdit::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos)
06007 {
06008     kdDebug()<<"void KMEdit::slotMisspelling(const QString &text, const QStringList &lst, unsigned int pos) : "<<text <<endl;
06009     if( mSpellLineEdit )
06010         mComposer->sujectLineWidget()->spellCheckerMisspelling( text, lst, pos);
06011     else
06012         misspelling(text, lst, pos);
06013 }
06014 
06015 void KMEdit::slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos)
06016 {
06017     kdDebug()<<"slotCorrected (const QString &oldWord, const QString &newWord, unsigned int pos) : "<<oldWord<<endl;
06018     if( mSpellLineEdit )
06019         mComposer->sujectLineWidget()->spellCheckerCorrected( oldWord, newWord, pos);
06020      else
06021          corrected(oldWord, newWord, pos);
06022 }
06023 
06024 //-----------------------------------------------------------------------------
06025 void KMEdit::slotSpellcheck2(KSpell*)
06026 {
06027     if( !mSpellLineEdit)
06028     {
06029         spellcheck_start();
06030 
06031         QString quotePrefix;
06032         if(mComposer && mComposer->msg())
06033         {
06034             // read the quote indicator from the preferences
06035             KConfig *config=KMKernel::config();
06036             KConfigGroupSaver saver(config, "General");
06037 
06038             int languageNr = config->readNumEntry("reply-current-language",0);
06039             config->setGroup( QString("KMMessage #%1").arg(languageNr) );
06040 
06041             quotePrefix = config->readEntry("indent-prefix", ">%_");
06042             quotePrefix = mComposer->msg()->formatString(quotePrefix);
06043         }
06044 
06045         kdDebug(5006) << "spelling: new SpellingFilter with prefix=\"" << quotePrefix << "\"" << endl;
06046         mSpellingFilter = new SpellingFilter(text(), quotePrefix, SpellingFilter::FilterUrls,
06047                                              SpellingFilter::FilterEmailAddresses);
06048 
06049         mKSpell->check(mSpellingFilter->filteredText());
06050     }
06051     else if( mComposer )
06052         mKSpell->check( mComposer->sujectLineWidget()->text());
06053 }
06054 
06055 //-----------------------------------------------------------------------------
06056 void KMEdit::slotSpellResult(const QString &s)
06057 {
06058     if( !mSpellLineEdit)
06059         spellcheck_stop();
06060 
06061   int dlgResult = mKSpell->dlgResult();
06062   if ( dlgResult == KS_CANCEL )
06063   {
06064       if( mSpellLineEdit)
06065       {
06066           //stop spell check
06067           mSpellLineEdit = false;
06068           QString tmpText( s );
06069           tmpText =  tmpText.remove('\n');
06070 
06071           if( tmpText != mComposer->sujectLineWidget()->text() )
06072               mComposer->sujectLineWidget()->setText( tmpText );
06073       }
06074       else
06075       {
06076           kdDebug(5006) << "spelling: canceled - restoring text from SpellingFilter" << endl;
06077           setText(mSpellingFilter->originalText());
06078           setModified(mWasModifiedBeforeSpellCheck);
06079       }
06080   }
06081   mKSpell->cleanUp();
06082   KDictSpellingHighlighter::dictionaryChanged();
06083 
06084   emit spellcheck_done( dlgResult );
06085 }
06086 
06087 //-----------------------------------------------------------------------------
06088 void KMEdit::slotSpellDone()
06089 {
06090     kdDebug()<<" void KMEdit::slotSpellDone()**********************************************\n";
06091   KSpell::spellStatus status = mKSpell->status();
06092   delete mKSpell;
06093   mKSpell = 0;
06094 
06095   kdDebug() << "spelling: delete SpellingFilter" << endl;
06096   delete mSpellingFilter;
06097   mSpellingFilter = 0;
06098   mComposer->sujectLineWidget()->deselect();
06099   if (status == KSpell::Error)
06100   {
06101      KMessageBox::sorry( topLevelWidget(),
06102                          i18n("ISpell/Aspell could not be started. Please "
06103                               "make sure you have ISpell or Aspell properly "
06104                               "configured and in your PATH.") );
06105      emit spellcheck_done( KS_CANCEL );
06106   }
06107   else if (status == KSpell::Crashed)
06108   {
06109      spellcheck_stop();
06110      KMessageBox::sorry( topLevelWidget(),
06111                          i18n("ISpell/Aspell seems to have crashed.") );
06112      emit spellcheck_done( KS_CANCEL );
06113   }
06114   else
06115   {
06116       if( mSpellLineEdit )
06117           spellcheck();
06118 #if KDE_IS_VERSION( 3, 1, 90 )
06119       else if( status == KSpell::FinishedNoMisspellingsEncountered )
06120           KMessageBox::information( topLevelWidget(),
06121                                     i18n("No misspellings encountered.") );
06122 #endif
06123   }
06124 }
06125 
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Apr 26 23:23:21 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003