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

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

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


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


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

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

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

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


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

  initializeAutoSpellChecking( autoSpellConfig );
}

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