kmail Library API Documentation

kmcomposewin.cpp

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