kmail

urlhandlermanager.cpp

Go to the documentation of this file.
00001 /*  -*- c++ -*-
00002     urlhandlermanager.cpp
00003 
00004     This file is part of KMail, the KDE mail client.
00005     Copyright (c) 2002-2003 Klar�vdalens Datakonsult AB
00006     Copyright (c) 2003      Marc Mutz <mutz@kde.org>
00007 
00008     KMail is free software; you can redistribute it and/or modify it
00009     under the terms of the GNU General Public License, version 2, as
00010     published by the Free Software Foundation.
00011 
00012     KMail is distributed in the hope that it will be useful, but
00013     WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021     In addition, as a special exception, the copyright holders give
00022     permission to link the code of this program with any edition of
00023     the Qt library by Trolltech AS, Norway (or with modified versions
00024     of Qt that use the same license as Qt), and distribute linked
00025     combinations including the two.  You must obey the GNU General
00026     Public License in all respects for all of the code used other than
00027     Qt.  If you modify this file, you may extend this exception to
00028     your version of the file, but you are not obligated to do so.  If
00029     you do not wish to do so, delete this exception statement from
00030     your version.
00031 */
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #include <config.h>
00035 #endif
00036 
00037 #include "urlhandlermanager.h"
00038 
00039 #include "interfaces/urlhandler.h"
00040 #include "interfaces/bodyparturlhandler.h"
00041 #include "partNode.h"
00042 #include "partnodebodypart.h"
00043 #include "kmreaderwin.h"
00044 #include "kmkernel.h"
00045 #include "callback.h"
00046 
00047 #include <kimproxy.h>
00048 #include "stl_util.h"
00049 #include <kurl.h>
00050 
00051 #include <algorithm>
00052 using std::for_each;
00053 using std::remove;
00054 using std::find;
00055 
00056 KMail::URLHandlerManager * KMail::URLHandlerManager::self = 0;
00057 
00058 namespace {
00059   class KMailProtocolURLHandler : public KMail::URLHandler {
00060   public:
00061     KMailProtocolURLHandler() : KMail::URLHandler() {}
00062     ~KMailProtocolURLHandler() {}
00063 
00064     bool handleClick( const KURL &, KMReaderWin * ) const;
00065     bool handleContextMenuRequest( const KURL & url, const QPoint &, KMReaderWin * ) const {
00066       return url.protocol() == "kmail";
00067     }
00068     QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00069   };
00070 
00071   class ExpandCollapseQuoteURLManager : public KMail::URLHandler {
00072   public:
00073     ExpandCollapseQuoteURLManager() : KMail::URLHandler() {}
00074     ~ExpandCollapseQuoteURLManager() {}
00075 
00076     bool handleClick( const KURL &, KMReaderWin * ) const;
00077     bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const {
00078       return false;
00079     }
00080     QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00081 
00082   };
00083 
00084   class SMimeURLHandler : public KMail::URLHandler {
00085   public:
00086     SMimeURLHandler() : KMail::URLHandler() {}
00087     ~SMimeURLHandler() {}
00088 
00089     bool handleClick( const KURL &, KMReaderWin * ) const;
00090     bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const {
00091       return false;
00092     }
00093     QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00094   };
00095 
00096   class MailToURLHandler : public KMail::URLHandler {
00097   public:
00098     MailToURLHandler() : KMail::URLHandler() {}
00099     ~MailToURLHandler() {}
00100 
00101     bool handleClick( const KURL &, KMReaderWin * ) const { return false; }
00102     bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const {
00103       return false;
00104     }
00105     QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00106   };
00107 
00108   class HtmlAnchorHandler : public KMail::URLHandler {
00109   public:
00110     HtmlAnchorHandler() : KMail::URLHandler() {}
00111     ~HtmlAnchorHandler() {}
00112 
00113     bool handleClick( const KURL &, KMReaderWin * ) const;
00114     bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const {
00115       return false;
00116     }
00117     QString statusBarMessage( const KURL &, KMReaderWin * ) const { return QString::null; }
00118   };
00119 
00120   class AttachmentURLHandler : public KMail::URLHandler {
00121   public:
00122     AttachmentURLHandler() : KMail::URLHandler() {}
00123     ~AttachmentURLHandler() {}
00124 
00125     bool handleClick( const KURL &, KMReaderWin * ) const;
00126     bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const;
00127     QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00128   };
00129 
00130   class ShowAuditLogURLHandler : public KMail::URLHandler {
00131   public:
00132       ShowAuditLogURLHandler() : KMail::URLHandler() {}
00133       ~ShowAuditLogURLHandler() {}
00134 
00135       bool handleClick( const KURL &, KMReaderWin * ) const;
00136       bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const;
00137       QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00138   };
00139 
00140   class FallBackURLHandler : public KMail::URLHandler {
00141   public:
00142     FallBackURLHandler() : KMail::URLHandler() {}
00143     ~FallBackURLHandler() {}
00144 
00145     bool handleClick( const KURL &, KMReaderWin * ) const;
00146     bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const;
00147     QString statusBarMessage( const KURL & url, KMReaderWin * ) const {
00148       return url.prettyURL();
00149     }
00150   };
00151 
00152 } // anon namespace
00153 
00154 
00155 //
00156 //
00157 // BodyPartURLHandlerManager
00158 //
00159 //
00160 
00161 class KMail::URLHandlerManager::BodyPartURLHandlerManager : public KMail::URLHandler {
00162 public:
00163   BodyPartURLHandlerManager() : KMail::URLHandler() {}
00164   ~BodyPartURLHandlerManager();
00165 
00166   bool handleClick( const KURL &, KMReaderWin * ) const;
00167   bool handleContextMenuRequest( const KURL &, const QPoint &, KMReaderWin * ) const;
00168   QString statusBarMessage( const KURL &, KMReaderWin * ) const;
00169 
00170   void registerHandler( const Interface::BodyPartURLHandler * handler );
00171   void unregisterHandler( const Interface::BodyPartURLHandler * handler );
00172 
00173 private:
00174   typedef QValueVector<const Interface::BodyPartURLHandler*> BodyPartHandlerList;
00175   BodyPartHandlerList mHandlers;
00176 };
00177 
00178 KMail::URLHandlerManager::BodyPartURLHandlerManager::~BodyPartURLHandlerManager() {
00179   for_each( mHandlers.begin(), mHandlers.end(),
00180         DeleteAndSetToZero<Interface::BodyPartURLHandler>() );
00181 }
00182 
00183 void KMail::URLHandlerManager::BodyPartURLHandlerManager::registerHandler( const Interface::BodyPartURLHandler * handler ) {
00184   if ( !handler )
00185     return;
00186   unregisterHandler( handler ); // don't produce duplicates
00187   mHandlers.push_back( handler );
00188 }
00189 
00190 void KMail::URLHandlerManager::BodyPartURLHandlerManager::unregisterHandler( const Interface::BodyPartURLHandler * handler ) {
00191   // don't delete them, only remove them from the list!
00192   mHandlers.erase( remove( mHandlers.begin(), mHandlers.end(), handler ), mHandlers.end() );
00193 }
00194 
00195 static partNode * partNodeFromXKMailUrl( const KURL & url, KMReaderWin * w, QString * path ) {
00196   assert( path );
00197 
00198   if ( !w || url.protocol() != "x-kmail" )
00199     return 0;
00200   const QString urlPath = url.path();
00201 
00202   // urlPath format is: /bodypart/<random number>/<part id>/<path>
00203 
00204   kdDebug( 5006 ) << "BodyPartURLHandler: urlPath == \"" << urlPath << "\"" << endl;
00205   if ( !urlPath.startsWith( "/bodypart/" ) )
00206     return 0;
00207 
00208   const QStringList urlParts = QStringList::split( '/', urlPath.mid( 10 ), true );
00209   if ( urlParts.size() != 3 )
00210     return 0;
00211   bool ok = false;
00212   const int part_id = urlParts[1].toInt( &ok );
00213   if ( !ok )
00214     return 0;
00215   *path = KURL::decode_string( urlParts[2], 106 );
00216   return w->partNodeForId( part_id );
00217 }
00218 
00219 bool KMail::URLHandlerManager::BodyPartURLHandlerManager::handleClick( const KURL & url, KMReaderWin * w ) const {
00220   QString path;
00221   partNode * node = partNodeFromXKMailUrl( url, w, &path );
00222   if ( !node )
00223     return false;
00224   KMMessage *msg = w->message();
00225   if ( !msg ) return false;
00226   Callback callback( msg, w );
00227   KMail::PartNodeBodyPart part( *node, w->overrideCodec() );
00228   for ( BodyPartHandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it )
00229     if ( (*it)->handleClick( &part, path, callback ) )
00230       return true;
00231   return false;
00232 }
00233 
00234 bool KMail::URLHandlerManager::BodyPartURLHandlerManager::handleContextMenuRequest( const KURL & url, const QPoint & p, KMReaderWin * w ) const {
00235   QString path;
00236   partNode * node = partNodeFromXKMailUrl( url, w, &path );
00237   if ( !node )
00238     return false;
00239 
00240   KMail::PartNodeBodyPart part( *node, w->overrideCodec() );
00241   for ( BodyPartHandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it )
00242     if ( (*it)->handleContextMenuRequest( &part, path, p ) )
00243       return true;
00244   return false;
00245 }
00246 
00247 QString KMail::URLHandlerManager::BodyPartURLHandlerManager::statusBarMessage( const KURL & url, KMReaderWin * w ) const {
00248   QString path;
00249   partNode * node = partNodeFromXKMailUrl( url, w, &path );
00250   if ( !node )
00251     return QString::null;
00252 
00253   KMail::PartNodeBodyPart part( *node, w->overrideCodec() );
00254   for ( BodyPartHandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it ) {
00255     const QString msg = (*it)->statusBarMessage( &part, path );
00256     if ( !msg.isEmpty() )
00257       return msg;
00258   }
00259   return QString::null;
00260 }
00261 
00262 //
00263 //
00264 // URLHandlerManager
00265 //
00266 //
00267 
00268 KMail::URLHandlerManager::URLHandlerManager() {
00269   registerHandler( new KMailProtocolURLHandler() );
00270   registerHandler( new ExpandCollapseQuoteURLManager() );
00271   registerHandler( new SMimeURLHandler() );
00272   registerHandler( new MailToURLHandler() );
00273   registerHandler( new HtmlAnchorHandler() );
00274   registerHandler( new AttachmentURLHandler() );
00275   registerHandler( mBodyPartURLHandlerManager = new BodyPartURLHandlerManager() );
00276   registerHandler( new ShowAuditLogURLHandler() );
00277   registerHandler( new FallBackURLHandler() );
00278 }
00279 
00280 KMail::URLHandlerManager::~URLHandlerManager() {
00281   for_each( mHandlers.begin(), mHandlers.end(),
00282         DeleteAndSetToZero<URLHandler>() );
00283 }
00284 
00285 void KMail::URLHandlerManager::registerHandler( const URLHandler * handler ) {
00286   if ( !handler )
00287     return;
00288   unregisterHandler( handler ); // don't produce duplicates
00289   mHandlers.push_back( handler );
00290 }
00291 
00292 void KMail::URLHandlerManager::unregisterHandler( const URLHandler * handler ) {
00293   // don't delete them, only remove them from the list!
00294   mHandlers.erase( remove( mHandlers.begin(), mHandlers.end(), handler ), mHandlers.end() );
00295 }
00296 
00297 void KMail::URLHandlerManager::registerHandler( const Interface::BodyPartURLHandler * handler ) {
00298   if ( mBodyPartURLHandlerManager )
00299     mBodyPartURLHandlerManager->registerHandler( handler );
00300 }
00301 
00302 void KMail::URLHandlerManager::unregisterHandler( const Interface::BodyPartURLHandler * handler ) {
00303   if ( mBodyPartURLHandlerManager )
00304     mBodyPartURLHandlerManager->unregisterHandler( handler );
00305 }
00306 
00307 bool KMail::URLHandlerManager::handleClick( const KURL & url, KMReaderWin * w ) const {
00308   for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it )
00309     if ( (*it)->handleClick( url, w ) )
00310       return true;
00311   return false;
00312 }
00313 
00314 bool KMail::URLHandlerManager::handleContextMenuRequest( const KURL & url, const QPoint & p, KMReaderWin * w ) const {
00315   for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it )
00316     if ( (*it)->handleContextMenuRequest( url, p, w ) )
00317       return true;
00318   return false;
00319 }
00320 
00321 QString KMail::URLHandlerManager::statusBarMessage( const KURL & url, KMReaderWin * w ) const {
00322   for ( HandlerList::const_iterator it = mHandlers.begin() ; it != mHandlers.end() ; ++it ) {
00323     const QString msg = (*it)->statusBarMessage( url, w );
00324     if ( !msg.isEmpty() )
00325       return msg;
00326   }
00327   return QString::null;
00328 }
00329 
00330 
00331 //
00332 //
00333 // URLHandler
00334 //
00335 //
00336 
00337 // these includes are temporary and should not be needed for the code
00338 // above this line, so they appear only here:
00339 #include "kmmessage.h"
00340 #include "kmreaderwin.h"
00341 #include "partNode.h"
00342 #include "kmmsgpart.h"
00343 
00344 #include <ui/messagebox.h>
00345 
00346 #include <klocale.h>
00347 #include <kprocess.h>
00348 #include <kmessagebox.h>
00349 #include <khtml_part.h>
00350 
00351 #include <qstring.h>
00352 
00353 namespace {
00354   bool KMailProtocolURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00355     if ( url.protocol() == "kmail" ) {
00356       if ( !w )
00357         return false;
00358 
00359       if ( url.path() == "showHTML" ) {
00360         w->setHtmlOverride( !w->htmlOverride() );
00361         w->update( true );
00362         return true;
00363       }
00364 
00365       if ( url.path() == "loadExternal" ) {
00366         w->setHtmlLoadExtOverride( !w->htmlLoadExtOverride() );
00367         w->update( true );
00368         return true;
00369       }
00370 
00371       if ( url.path() == "goOnline" ) {
00372         kmkernel->resumeNetworkJobs();
00373         return true;
00374       }
00375 
00376       if ( url.path() == "decryptMessage" ) {
00377         w->setDecryptMessageOverwrite( true );
00378         w->update( true );
00379         return true;
00380       }
00381 
00382       if ( url.path() == "showSignatureDetails" ) {
00383         w->setShowSignatureDetails( true );
00384         w->update( true );
00385         return true;
00386       }
00387 
00388       if ( url.path() == "hideSignatureDetails" ) {
00389         w->setShowSignatureDetails( false );
00390         w->update( true );
00391         return true;
00392       }
00393 
00394       if ( url.path() == "showAttachmentQuicklist" ) {
00395       w->saveRelativePosition();
00396       w->setShowAttachmentQuicklist( true );
00397       w->update( true );
00398       return true;
00399       }
00400 
00401       if ( url.path() == "hideAttachmentQuicklist" ) {
00402       w->saveRelativePosition();
00403       w->setShowAttachmentQuicklist( false );
00404       w->update( true );
00405       return true;
00406       }
00407 
00408 //       if ( url.path() == "startIMApp" )
00409 //       {
00410 //         kmkernel->imProxy()->startPreferredApp();
00411 //         return true;
00412 //       }
00413 //       //FIXME: handle startIMApp urls in their own handler, or rename this one
00414     }
00415     return false;
00416   }
00417 
00418   QString KMailProtocolURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00419     if ( url.protocol() == "kmail" )
00420     {
00421       if ( url.path() == "showHTML" )
00422         return i18n("Turn on HTML rendering for this message.");
00423       if ( url.path() == "loadExternal" )
00424         return i18n("Load external references from the Internet for this message.");
00425       if ( url.path() == "goOnline" )
00426         return i18n("Work online.");
00427       if ( url.path() == "decryptMessage" )
00428         return i18n("Decrypt message.");
00429       if ( url.path() == "showSignatureDetails" )
00430         return i18n("Show signature details.");
00431       if ( url.path() == "hideSignatureDetails" )
00432         return i18n("Hide signature details.");
00433     }
00434     return QString::null ;
00435   }
00436 }
00437 
00438 namespace {
00439 
00440   bool ExpandCollapseQuoteURLManager::handleClick(
00441       const KURL & url, KMReaderWin * w ) const
00442   {
00443     //  kmail:levelquote/?num      -> the level quote to collapse.
00444     //  kmail:levelquote/?-num      -> expand all levels quote.
00445     if ( url.protocol() == "kmail" && url.path()=="levelquote" )
00446     {
00447       QString levelStr= url.query().mid( 1,url.query().length() );
00448       bool isNumber;
00449       int levelQuote= levelStr.toInt(&isNumber);
00450       if ( isNumber )
00451         w->slotLevelQuote( levelQuote );
00452       return true;
00453     }
00454     return false;
00455   }
00456   QString ExpandCollapseQuoteURLManager::statusBarMessage(
00457       const KURL & url, KMReaderWin * ) const
00458   {
00459       if ( url.protocol() == "kmail" && url.path() == "levelquote" )
00460       {
00461         QString query= url.query();
00462         if ( query.length()>=2 )
00463           if ( query[ 1 ] =='-'  )
00464             return i18n("Expand all quoted text.");
00465           else
00466             return i18n("Collapse quoted text.");
00467       }
00468       return QString::null ;
00469   }
00470 
00471 }
00472 
00473 // defined in kmreaderwin.cpp...
00474 extern bool foundSMIMEData( const QString aUrl, QString & displayName,
00475                 QString & libName, QString & keyId );
00476 
00477 namespace {
00478   bool SMimeURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00479     if ( !url.hasRef() )
00480       return false;
00481     QString displayName, libName, keyId;
00482     if ( !foundSMIMEData( url.path() + '#' + url.ref(), displayName, libName, keyId ) )
00483       return false;
00484     KProcess cmp;
00485     cmp << "kleopatra" << "-query" << keyId;
00486     if ( !cmp.start( KProcess::DontCare ) )
00487       KMessageBox::error( w, i18n("Could not start certificate manager. "
00488                   "Please check your installation."),
00489               i18n("KMail Error") );
00490     return true;
00491   }
00492 
00493   QString SMimeURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00494     QString displayName, libName, keyId;
00495     if ( !foundSMIMEData( url.path() + '#' + url.ref(), displayName, libName, keyId ) )
00496       return QString::null;
00497     return i18n("Show certificate 0x%1").arg( keyId );
00498   }
00499 }
00500 
00501 namespace {
00502   bool HtmlAnchorHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00503     if ( url.hasHost() || url.path() != "/" || !url.hasRef() )
00504       return false;
00505     if ( w && !w->htmlPart()->gotoAnchor( url.ref() ) )
00506       static_cast<QScrollView*>( w->htmlPart()->widget() )->ensureVisible( 0, 0 );
00507     return true;
00508   }
00509 }
00510 
00511 namespace {
00512   QString MailToURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00513     if ( url.protocol() != "mailto" )
00514       return QString::null;
00515     return KMMessage::decodeMailtoUrl( url.url() );
00516   }
00517 }
00518 
00519 namespace {
00520   bool AttachmentURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00521     if ( !w || !w->message() )
00522       return false;
00523     const int id = KMReaderWin::msgPartFromUrl( url );
00524     if ( id <= 0 )
00525       return false;
00526     w->openAttachment( id, url.path() );
00527     return true;
00528   }
00529 
00530   bool AttachmentURLHandler::handleContextMenuRequest( const KURL & url, const QPoint & p, KMReaderWin * w ) const {
00531     if ( !w || !w->message() )
00532       return false;
00533     const int id = KMReaderWin::msgPartFromUrl( url );
00534     if ( id <= 0 )
00535       return false;
00536     w->showAttachmentPopup( id, url.path(), p );
00537     return true;
00538   }
00539 
00540   QString AttachmentURLHandler::statusBarMessage( const KURL & url, KMReaderWin * w ) const {
00541     if ( !w || !w->message() )
00542       return QString::null;
00543     const partNode * node = w->partNodeFromUrl( url );
00544     if ( !node )
00545       return QString::null;
00546     const KMMessagePart & msgPart = node->msgPart();
00547     QString name = msgPart.fileName();
00548     if ( name.isEmpty() )
00549       name = msgPart.name();
00550     if ( !name.isEmpty() )
00551       return i18n( "Attachment: %1" ).arg( name );
00552     return i18n( "Attachment #%1 (unnamed)" ).arg( KMReaderWin::msgPartFromUrl( url ) );
00553   }
00554 }
00555 
00556 namespace {
00557   static QString extractAuditLog( const KURL & url ) {
00558     if ( url.protocol() != "kmail" || url.path() != "showAuditLog" )
00559       return QString();
00560     assert( !url.queryItem( "log" ).isEmpty() );
00561     return url.queryItem( "log" );
00562   }
00563 
00564   bool ShowAuditLogURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00565     const QString auditLog = extractAuditLog( url );
00566     if ( auditLog.isEmpty() )
00567         return false;
00568     Kleo::MessageBox::auditLog( w, auditLog );
00569     return true;
00570   }
00571 
00572   bool ShowAuditLogURLHandler::handleContextMenuRequest( const KURL & url, const QPoint &, KMReaderWin * w ) const {
00573     // disable RMB for my own links:
00574     return !extractAuditLog( url ).isEmpty();
00575   }
00576 
00577   QString ShowAuditLogURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00578     if ( extractAuditLog( url ).isEmpty() )
00579       return QString();
00580     else
00581       return i18n("Show GnuPG Audit Log for this operation");
00582   }
00583 }
00584 
00585 namespace {
00586   bool FallBackURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00587     if ( w )
00588       w->emitUrlClicked( url, Qt::LeftButton );
00589     return true;
00590   }
00591 
00592   bool FallBackURLHandler::handleContextMenuRequest( const KURL & url, const QPoint & p, KMReaderWin * w ) const {
00593     if ( w )
00594       w->emitPopupMenu( url, p );
00595     return true;
00596   }
00597 }