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