kmail

urlhandlermanager.cpp

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() == "startIMApp" )
00395 //       {
00396 //         kmkernel->imProxy()->startPreferredApp();
00397 //         return true;
00398 //       }
00399 //       //FIXME: handle startIMApp urls in their own handler, or rename this one
00400     }
00401     return false;
00402   }
00403 
00404   QString KMailProtocolURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00405     if ( url.protocol() == "kmail" )
00406     {
00407       if ( url.path() == "showHTML" )
00408         return i18n("Turn on HTML rendering for this message.");
00409       if ( url.path() == "loadExternal" )
00410         return i18n("Load external references from the Internet for this message.");
00411       if ( url.path() == "goOnline" )
00412         return i18n("Work online.");
00413       if ( url.path() == "decryptMessage" )
00414         return i18n("Decrypt message.");
00415       if ( url.path() == "showSignatureDetails" )
00416         return i18n("Show signature details.");
00417       if ( url.path() == "hideSignatureDetails" )
00418         return i18n("Hide signature details.");
00419     }
00420     return QString::null ;
00421   }
00422 }
00423 
00424 namespace {
00425 
00426   bool ExpandCollapseQuoteURLManager::handleClick(
00427       const KURL & url, KMReaderWin * w ) const
00428   {
00429     //  kmail:levelquote/?num      -> the level quote to collapse.
00430     //  kmail:levelquote/?-num      -> expand all levels quote.
00431     if ( url.protocol() == "kmail" && url.path()=="levelquote" )
00432     {
00433       QString levelStr= url.query().mid( 1,url.query().length() );
00434       bool isNumber;
00435       int levelQuote= levelStr.toInt(&isNumber);
00436       if ( isNumber )
00437         w->slotLevelQuote( levelQuote );
00438       return true;
00439     }
00440     return false;
00441   }
00442   QString ExpandCollapseQuoteURLManager::statusBarMessage(
00443       const KURL & url, KMReaderWin * ) const
00444   {
00445       if ( url.protocol() == "kmail" && url.path() == "levelquote" )
00446       {
00447         QString query= url.query();
00448         if ( query.length()>=2 )
00449           if ( query[ 1 ] =='-'  )
00450             return i18n("Expand all quoted text.");
00451           else
00452             return i18n("Collapse quoted text.");
00453       }
00454       return QString::null ;
00455   }
00456 
00457 }
00458 
00459 // defined in kmreaderwin.cpp...
00460 extern bool foundSMIMEData( const QString aUrl, QString & displayName,
00461                 QString & libName, QString & keyId );
00462 
00463 namespace {
00464   bool SMimeURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00465     if ( !url.hasRef() )
00466       return false;
00467     QString displayName, libName, keyId;
00468     if ( !foundSMIMEData( url.path() + '#' + url.ref(), displayName, libName, keyId ) )
00469       return false;
00470     KProcess cmp;
00471     cmp << "kleopatra" << "-query" << keyId;
00472     if ( !cmp.start( KProcess::DontCare ) )
00473       KMessageBox::error( w, i18n("Could not start certificate manager. "
00474                   "Please check your installation."),
00475               i18n("KMail Error") );
00476     return true;
00477   }
00478 
00479   QString SMimeURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00480     QString displayName, libName, keyId;
00481     if ( !foundSMIMEData( url.path() + '#' + url.ref(), displayName, libName, keyId ) )
00482       return QString::null;
00483     return i18n("Show certificate 0x%1").arg( keyId );
00484   }
00485 }
00486 
00487 namespace {
00488   bool HtmlAnchorHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00489     if ( url.hasHost() || url.path() != "/" || !url.hasRef() )
00490       return false;
00491     if ( w && !w->htmlPart()->gotoAnchor( url.ref() ) )
00492       static_cast<QScrollView*>( w->htmlPart()->widget() )->ensureVisible( 0, 0 );
00493     return true;
00494   }
00495 }
00496 
00497 namespace {
00498   QString MailToURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00499     if ( url.protocol() != "mailto" )
00500       return QString::null;
00501     return KMMessage::decodeMailtoUrl( url.url() );
00502   }
00503 }
00504 
00505 namespace {
00506   bool AttachmentURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00507     if ( !w || !w->message() )
00508       return false;
00509     const int id = KMReaderWin::msgPartFromUrl( url );
00510     if ( id <= 0 )
00511       return false;
00512     w->openAttachment( id, url.path() );
00513     return true;
00514   }
00515 
00516   bool AttachmentURLHandler::handleContextMenuRequest( const KURL & url, const QPoint & p, KMReaderWin * w ) const {
00517     if ( !w || !w->message() )
00518       return false;
00519     const int id = KMReaderWin::msgPartFromUrl( url );
00520     if ( id <= 0 )
00521       return false;
00522     w->showAttachmentPopup( id, url.path(), p );
00523     return true;
00524   }
00525 
00526   QString AttachmentURLHandler::statusBarMessage( const KURL & url, KMReaderWin * w ) const {
00527     if ( !w || !w->message() )
00528       return QString::null;
00529     const partNode * node = w->partNodeFromUrl( url );
00530     if ( !node )
00531       return QString::null;
00532     const KMMessagePart & msgPart = node->msgPart();
00533     QString name = msgPart.fileName();
00534     if ( name.isEmpty() )
00535       name = msgPart.name();
00536     if ( !name.isEmpty() )
00537       return i18n( "Attachment: %1" ).arg( name );
00538     return i18n( "Attachment #%1 (unnamed)" ).arg( KMReaderWin::msgPartFromUrl( url ) );
00539   }
00540 }
00541 
00542 namespace {
00543   static QString extractAuditLog( const KURL & url ) {
00544     if ( url.protocol() != "kmail" || url.path() != "showAuditLog" )
00545       return QString();
00546     assert( !url.queryItem( "log" ).isEmpty() );
00547     return url.queryItem( "log" );
00548   }
00549 
00550   bool ShowAuditLogURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00551     const QString auditLog = extractAuditLog( url );
00552     if ( auditLog.isEmpty() )
00553         return false;
00554     Kleo::MessageBox::auditLog( w, auditLog );
00555     return true;
00556   }
00557 
00558   bool ShowAuditLogURLHandler::handleContextMenuRequest( const KURL & url, const QPoint &, KMReaderWin * w ) const {
00559     // disable RMB for my own links:
00560     return !extractAuditLog( url ).isEmpty();
00561   }
00562 
00563   QString ShowAuditLogURLHandler::statusBarMessage( const KURL & url, KMReaderWin * ) const {
00564     if ( extractAuditLog( url ).isEmpty() )
00565       return QString();
00566     else
00567       return i18n("Show GnuPG Audit Log for this operation");
00568   }
00569 }
00570 
00571 namespace {
00572   bool FallBackURLHandler::handleClick( const KURL & url, KMReaderWin * w ) const {
00573     if ( w )
00574       w->emitUrlClicked( url, Qt::LeftButton );
00575     return true;
00576   }
00577 
00578   bool FallBackURLHandler::handleContextMenuRequest( const KURL & url, const QPoint & p, KMReaderWin * w ) const {
00579     if ( w )
00580       w->emitPopupMenu( url, p );
00581     return true;
00582   }
00583 }
KDE Home | KDE Accessibility Home | Description of Access Keys