[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    [PATCH] Fix KMail mailto url handling / new KURL method
From:       Marc Mutz <mutz () kde ! org>
Date:       2002-12-21 17:07:13
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


Hi!

I've attached two patches. One for kdecore/kurl.* implementing
  QMap< QString, QString >
  KURL::queryItems(bool caseInsensitiveKeys=false) const;
and the second for fixing mailto URL handling in KMMailtoComposeCommand 
and KMUrlClickedCommand.

The details:
- kdecore:
  - KURL has a new method queryItems that returns the query parsed into
    key/value pairs, optionally normalizing the keys to lowercase.
- kmail:
  - new method KMMessage::setFieldsAndBodyFromMailtoUrl() which sets the
    information encoded in the mailto url. Since it uses
    KURL::queryItems(true), it is robust against case in header names
    (as reported on ietf-822@imc.org).

    Expl.: mailto:mutz@kde.org?Subject=is+not+shown

  - new config key [General]HeadersAllowedForMailtoUrls that contains a
    list of header fields that are interpreted in mailto urls (in
    addition to To and Body, both of which are special).

  - KMMainWin::slotUrlClicked() dispatches mailto urls to
    KMMailtoComposeCommand instead of KMUrlClickedCommand, thus
    eleminating a bit of code duplication.

I'd like to commit both to KDE_3_1_BRANCH. For reference, the reported 
URL was:

mailto:%3D%3fiso-8859-1%3FQ%3fJ%3dFCrgen%3F%3d%20%3cj@foo.com%3E?Subject=list

Marc

-- 
"Similia similibus curentur"
           -- Bush's new motto in fighting terrorism.

["kurl.diff" (text/x-diff)]

Index: kurl.h
===================================================================
RCS file: /home/kde/kdelibs/kdecore/kurl.h,v
retrieving revision 1.102
diff -u -3 -p -r1.102 kurl.h
--- kurl.h	22 Oct 2002 17:11:40 -0000	1.102
+++ kurl.h	21 Dec 2002 17:04:35 -0000
@@ -25,6 +25,7 @@
 
 class QUrl;
 class QStringList;
+template <typename K, typename V> class QMap;
 
 class KURLPrivate;
 /**
@@ -473,6 +474,18 @@ public:
    * specified item does not exist.
    */
   QString queryItem( const QString& _item ) const;
+
+  /**
+   * Returns the list of query items as a map mapping keys to values.
+   *
+   * @param caseInsensitiveKeys whether to normalize query keys to lowercase
+   *
+   * @return the map of query items or the empty map if the url has no
+   * query items.
+   *
+   * @since 3.1
+   */
+  QMap< QString, QString > queryItems( bool caseInsensitiveKeys=false ) const;
 
   /**
    * Add an additional query item.
Index: kurl.cpp
===================================================================
RCS file: /home/kde/kdelibs/kdecore/kurl.cpp,v
retrieving revision 1.223
diff -u -3 -p -r1.223 kurl.cpp
--- kurl.cpp	31 Oct 2002 17:01:01 -0000	1.223
+++ kurl.cpp	21 Dec 2002 17:04:37 -0000
@@ -34,7 +34,7 @@
 #include <qstringlist.h>
 #include <qregexp.h>
 #include <qstylesheet.h>
-
+#include <qmap.h>
 #include <qtextcodec.h>
 
 static QTextCodec * codecForHint( int encoding_hint /* not 0 ! */ )
@@ -1752,6 +1752,37 @@ bool urlcmp( const QString& _url1, const
       return false;
 
   return true;
+}
+
+QMap< QString, QString > KURL::queryItems( bool cisKeys ) const {
+  if ( m_strQuery_encoded.isEmpty() )
+    return QMap<QString,QString>();
+
+  QMap< QString, QString > result;
+  QStringList items = QStringList::split( '&', m_strQuery_encoded );
+  for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) \
{ +    int equal_pos = (*it).find( '=' );
+    if ( equal_pos > 0 ) { // = is not the first char...
+      QString name = (*it).left( equal_pos );
+      if ( cisKeys )
+	name = name.lower();
+      QString value = (*it).mid( equal_pos + 1 );
+      if ( value.isEmpty() )
+	result.insert( name, QString::fromLatin1("") );
+      else {
+	// ### why is decoding name not neccessary?
+	value.replace( '+', ' ' ); // + in queries means space
+	result.insert( name, decode_string( value ) );
+      }
+    } else if ( equal_pos < 0 ) { // no =
+      QString name = (*it);
+      if ( cisKeys )
+	name = name.lower();
+      result.insert( name, QString::null );
+    }
+  }
+
+  return result;
 }
 
 QString KURL::queryItem( const QString& _item ) const
Index: tests/kurltest.cpp
===================================================================
RCS file: /home/kde/kdelibs/kdecore/tests/kurltest.cpp,v
retrieving revision 1.64
diff -u -3 -p -r1.64 kurltest.cpp
--- tests/kurltest.cpp	31 Oct 2002 17:18:20 -0000	1.64
+++ tests/kurltest.cpp	21 Dec 2002 17:04:37 -0000
@@ -518,6 +518,8 @@ int main(int argc, char *argv[])
   check("queryItem (invalid item)", theKow.queryItem("InterstellarCounselor"), \
QString::null);  check("queryItem (empty item)", theKow.queryItem("empty"), "");
   check("queryItem (item with encoded chars)", theKow.queryItem("test"), "+ :%");
+  check("queryItems (keys)", QStringList(theKow.queryItems().keys()).join(", "), \
"empty, hl, hlx, lr, q, test" ); +  check("queryItems (values)", \
QStringList(theKow.queryItems().values()).join(", "), ", de, xx, lang de, frerich, + \
:%" );  
   KURL umlaut1("http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel");
   check("umlaut1.url()", umlaut1.url(), \
"http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel");


["kmail-mailto-url-handling.diff" (text/x-diff)]

Index: kmcommands.cpp
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmcommands.cpp,v
retrieving revision 1.8.2.6
diff -u -3 -p -r1.8.2.6 kmcommands.cpp
--- kmcommands.cpp	26 Nov 2002 23:37:01 -0000	1.8.2.6
+++ kmcommands.cpp	21 Dec 2002 17:08:29 -0000
@@ -283,7 +283,8 @@ void KMMailtoComposeCommand::execute()
 
   msg->initHeader(id);
   msg->setCharset("utf-8");
-  msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
+
+  msg->setFieldsAndBodyFromMailtoUrl( mUrl );
 
   win = new KMComposeWin(msg, id);
   win->setCharset("", TRUE);
@@ -1182,48 +1183,13 @@ KMUrlClickedCommand::KMUrlClickedCommand
 
 void KMUrlClickedCommand::execute()
 {
-  KMComposeWin *win;
-  KMMessage* msg;
-
-  if (mUrl.protocol() == "mailto")
-  {
-    uint id = 0;
-    if ( mFolder )
-      id = mFolder->identity();
-
-    msg = new KMMessage;
-    msg->initHeader(id);
-    msg->setCharset("utf-8");
-    msg->setTo( KMMessage::decodeMailtoUrl( mUrl.path() ) );
-    QString query=mUrl.query();
-    while (!query.isEmpty()) {
-      QString queryPart;
-      int secondQuery = query.find('?',1);
-      if (secondQuery != -1)
-	queryPart = query.left(secondQuery);
-      else
-	queryPart = query;
-      query = query.mid(queryPart.length());
-
-      if (queryPart.left(9) == "?subject=")
-	msg->setSubject( KURL::decode_string(queryPart.mid(9)) );
-      else if (queryPart.left(6) == "?body=")
-	// It is correct to convert to latin1() as URL should not contain
-	// anything except ascii.
-	msg->setBody( KURL::decode_string(queryPart.mid(6)).latin1() );
-      else if (queryPart.left(4) == "?cc=")
-	msg->setCc( KURL::decode_string(queryPart.mid(4)) );
-    }
-
-    win = new KMComposeWin(msg, id);
-    win->setCharset("", TRUE);
-    win->show();
-  }
-  else if ((mUrl.protocol() == "http") || (mUrl.protocol() == "https") ||
-           (mUrl.protocol() == "ftp") || (mUrl.protocol() == "file") ||
-           (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
-           (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") ||
-           (mUrl.protocol() == "smb"))
+  kdWarning( mUrl.protocol() != "mailto", 5006 )
+    << "KMUrlClickedCommand::execute(): mailto URLs should be handled by \
KMMailtoComposeCommand" << endl; +  if ((mUrl.protocol() == "http") || \
(mUrl.protocol() == "https") || +      (mUrl.protocol() == "ftp") || (mUrl.protocol() \
== "file") || +      (mUrl.protocol() == "ftps") || (mUrl.protocol() == "sftp" ) ||
+      (mUrl.protocol() == "help") || (mUrl.protocol() == "vnc") ||
+      (mUrl.protocol() == "smb"))
   {
     if (mMainWin)
       mMainWin->statusMsg(i18n("Opening URL..."));
Index: kmmainwin.cpp
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmmainwin.cpp,v
retrieving revision 1.525.2.41
diff -u -3 -p -r1.525.2.41 kmmainwin.cpp
--- kmmainwin.cpp	2 Dec 2002 14:57:57 -0000	1.525.2.41
+++ kmmainwin.cpp	21 Dec 2002 17:08:33 -0000
@@ -2066,9 +2066,13 @@ void KMMainWin::slotSelectText() {
 //-----------------------------------------------------------------------------
 void KMMainWin::slotUrlClicked(const KURL &aUrl, int)
 {
-  KMCommand *command = new KMUrlClickedCommand( aUrl, mFolder, mMsgView,
-                                               mFolderHtmlPref, this );
-  command->start();
+  if ( aUrl.protocol() == "mailto" )
+    (new KMMailtoComposeCommand( aUrl, mHeaders->currentMsg() ))->start();
+  else {
+    KMCommand *command = new KMUrlClickedCommand( aUrl, mFolder, mMsgView,
+						  mFolderHtmlPref, this );
+    command->start();
+  }
 }
 
 
Index: kmmessage.cpp
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmmessage.cpp,v
retrieving revision 1.318.2.41
diff -u -3 -p -r1.318.2.41 kmmessage.cpp
--- kmmessage.cpp	30 Nov 2002 16:34:18 -0000	1.318.2.41
+++ kmmessage.cpp	21 Dec 2002 17:08:36 -0000
@@ -19,6 +19,7 @@
 #include "kmversion.h"
 #include "kmidentity.h"
 #include "kmkernel.h"
+#include "kmsender.h"
 #include "identitymanager.h"
 
 #include <kapplication.h>
@@ -37,6 +38,7 @@
 #include <qstrlist.h>
 #include <qmessagebox.h>
 #include <qregexp.h>
+#include <qmap.h>
 
 #include <kmime_util.h>
 #include <kmime_charfreq.h>
@@ -68,7 +70,7 @@ static QString sReplyLanguage, sReplyStr
 static bool sSmartQuote, sReplaceSubjPrefix, sReplaceForwSubjPrefix;
 static int sWrapCol;
 static QStringList sReplySubjPrefixes, sForwardSubjPrefixes;
-static QStringList sPrefCharsets;
+static QStringList sPrefCharsets, sAllowedHeadersForMailtoUrls;
 
 QString KMMessage::sForwardStr = "";
 int KMMessage::sHdrStyle = KMReaderWin::HdrFancy;
@@ -2601,6 +2603,44 @@ void KMMessage::setBody(const QCString& 
   mNeedsAssembly = TRUE;
 }
 
+void KMMessage::setFieldsAndBodyFromMailtoUrl( const KURL & url ) {
+
+  setCharset( "utf-8" );
+
+  // handle "to", which can be specified implicitly in the "path",
+  // explicitely in the query, or both, and needs to be concatenated
+  // from both:
+  QString to = KMMessage::decodeMailtoUrl( url.path() ); // implicit
+  QMap< QString, QString > query = url.queryItems( true /*case ins. keys*/ );
+  if ( query.contains( "to" ) ) { // explicit
+    QString queryTo = KMMsgBase::decodeRFC2047String( query["to"].latin1() );
+    if ( to.isEmpty() )
+      to = queryTo;
+    else if ( !queryTo.isEmpty() )
+      to += ", " + queryTo;
+    query.erase( "to" );
+  }
+  setTo( to );
+
+  // handle "body"
+  if ( query.contains( "body" ) ) {
+    QString body = query["body"];
+    if ( !body.isEmpty() ) {
+      QValueList<int> dummy;
+      setBodyAndGuessCte( body.utf8(), dummy,
+			  !kernel->msgSender()->sendQuotedPrintable() );
+    }
+    query.erase( "body" );
+  }
+
+  // handle the rest of the headers that can be specified in the query:
+  for ( QMap< QString, QString >::const_iterator it = query.begin() ; it != \
query.end() ; ++it ) { +    if ( sAllowedHeadersForMailtoUrls.contains( it.key() ) )
+      setHeaderField( it.key().latin1(),
+		      KMMsgBase::decodeRFC2047String( it.data().latin1() ) );
+  }
+
+}
 
 // Patched by Daniel Moisset <dmoisset@grulic.org.ar>
 // modified numbodyparts, bodypart to take nested body parts as
@@ -3439,6 +3479,19 @@ void KMMessage::readConfig(void)
   KConfigGroupSaver saver(config, "General");
 
   config->setGroup("General");
+
+  if ( config->hasKey( "HeadersAllowedForMailtoUrls" ) ) {
+    sAllowedHeadersForMailtoUrls =
+      config->readListEntry( "HeadersAllowedForMailtoUrls" );
+    for ( QStringList::iterator it = sAllowedHeadersForMailtoUrls.begin() ;
+	  it != sAllowedHeadersForMailtoUrls.end() ; ++it )
+      *it = (*it).lower();
+  } else {
+    sAllowedHeadersForMailtoUrls.clear();
+    sAllowedHeadersForMailtoUrls << "cc" << "bcc"
+				 << "subject" << "keywords"
+				 << "in-reply-to" << "references";
+  }
 
   int languageNr = config->readNumEntry("reply-current-language",0);
 
Index: kmmessage.h
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmmessage.h,v
retrieving revision 1.102.2.16
diff -u -3 -p -r1.102.2.16 kmmessage.h
--- kmmessage.h	30 Nov 2002 15:39:20 -0000	1.102.2.16
+++ kmmessage.h	21 Dec 2002 17:08:37 -0000
@@ -13,6 +13,8 @@
 
 #include <kmime_mdn.h>
 
+class KURL;
+
 template <typename T>
 class QValueList;
 
@@ -454,6 +456,10 @@ public:
       method is meant for binary data, not null is appended */
   virtual QCString bodyDecoded(void) const;
   virtual QByteArray bodyDecodedBinary(void) const;
+
+  /** Sets the message body and headers from a given mailto: URL.
+   **/
+  void setFieldsAndBodyFromMailtoUrl( const KURL & url );
 
   /** Number of body parts the message has. This is one for plain messages
       without any attachment. */


[Attachment #7 (application/pgp-signature)]

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic