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