KDECore
ktoolinvocation_x11.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (c) 1997,1998 Matthias Kalle Dalheimer <kalle@kde.org> 00003 Copyright (c) 1999 Espen Sand <espen@kde.org> 00004 Copyright (c) 2000-2004 Frerich Raabe <raabe@kde.org> 00005 Copyright (c) 2003,2004 Oswald Buddenhagen <ossi@kde.org> 00006 Copyright (c) 2006 Thiago Macieira <thiago@kde.org> 00007 Copyright (C) 2008 Aaron Seigo <aseigo@kde.org> 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Library General Public 00011 License as published by the Free Software Foundation; either 00012 version 2 of the License, or (at your option) any later version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 Library General Public License for more details. 00018 00019 You should have received a copy of the GNU Library General Public License 00020 along with this library; see the file COPYING.LIB. If not, write to 00021 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00022 Boston, MA 02110-1301, USA. 00023 */ 00024 00025 #include <config.h> 00026 00027 #include "ktoolinvocation.h" 00028 00029 #include <kconfiggroup.h> 00030 #include <kmimetypetrader.h> 00031 00032 #include "kcmdlineargs.h" 00033 #include "kconfig.h" 00034 #include "kcodecs.h" 00035 #include "kdebug.h" 00036 #include "kglobal.h" 00037 #include "kshell.h" 00038 #include "kmacroexpander.h" 00039 #include "klocale.h" 00040 #include "kstandarddirs.h" 00041 #include "kmessage.h" 00042 #include "kservice.h" 00043 00044 #include <QtCore/QCoreApplication> 00045 #include <QtCore/QProcess> 00046 #include <QtCore/QHash> 00047 #include <QtCore/QDebug> 00048 #include <QtCore/QBool> 00049 #include <QtCore/QFile> 00050 #include <QtDBus/QtDBus> 00051 00052 static QStringList splitEmailAddressList( const QString & aStr ) 00053 { 00054 // This is a copy of KPIM::splitEmailAddrList(). 00055 // Features: 00056 // - always ignores quoted characters 00057 // - ignores everything (including parentheses and commas) 00058 // inside quoted strings 00059 // - supports nested comments 00060 // - ignores everything (including double quotes and commas) 00061 // inside comments 00062 00063 QStringList list; 00064 00065 if (aStr.isEmpty()) 00066 return list; 00067 00068 QString addr; 00069 uint addrstart = 0; 00070 int commentlevel = 0; 00071 bool insidequote = false; 00072 00073 for (int index=0; index<aStr.length(); index++) { 00074 // the following conversion to latin1 is o.k. because 00075 // we can safely ignore all non-latin1 characters 00076 switch (aStr[index].toLatin1()) { 00077 case '"' : // start or end of quoted string 00078 if (commentlevel == 0) 00079 insidequote = !insidequote; 00080 break; 00081 case '(' : // start of comment 00082 if (!insidequote) 00083 commentlevel++; 00084 break; 00085 case ')' : // end of comment 00086 if (!insidequote) { 00087 if (commentlevel > 0) 00088 commentlevel--; 00089 else { 00090 //kDebug() << "Error in address splitting: Unmatched ')'" 00091 // << endl; 00092 return list; 00093 } 00094 } 00095 break; 00096 case '\\' : // quoted character 00097 index++; // ignore the quoted character 00098 break; 00099 case ',' : 00100 if (!insidequote && (commentlevel == 0)) { 00101 addr = aStr.mid(addrstart, index-addrstart); 00102 if (!addr.isEmpty()) 00103 list += addr.simplified(); 00104 addrstart = index+1; 00105 } 00106 break; 00107 } 00108 } 00109 // append the last address to the list 00110 if (!insidequote && (commentlevel == 0)) { 00111 addr = aStr.mid(addrstart, aStr.length()-addrstart); 00112 if (!addr.isEmpty()) 00113 list += addr.simplified(); 00114 } 00115 //else 00116 // kDebug() << "Error in address splitting: " 00117 // << "Unexpected end of address list" 00118 // << endl; 00119 00120 return list; 00121 } 00122 00123 #ifdef Q_WS_MAEMO_5 00124 // taken from QDesktopServices, which we cannot use here due to it being in QtGui 00125 inline static bool maemo5Launch(const QUrl &url) 00126 { 00127 typedef bool (*Ptr_hildon_uri_open)(const char *, void *, void **); 00128 static Ptr_hildon_uri_open hildon_uri_open = 0; 00129 00130 if (!hildon_uri_open) { 00131 QLibrary lib(QLatin1String("libhildonmime"), 0, 0); 00132 hildon_uri_open = (Ptr_hildon_uri_open)lib.resolve("hildon_uri_open"); 00133 } 00134 if (hildon_uri_open) 00135 return hildon_uri_open(url.toEncoded().constData(), 0, 0); 00136 return false; 00137 } 00138 #endif 00139 00140 void KToolInvocation::invokeMailer(const QString &_to, const QString &_cc, const QString &_bcc, 00141 const QString &subject, const QString &body, 00142 const QString & /*messageFile TODO*/, const QStringList &attachURLs, 00143 const QByteArray& startup_id ) 00144 { 00145 if (!isMainThreadActive()) 00146 return; 00147 00148 KConfig config(QString::fromLatin1("emaildefaults")); 00149 KConfigGroup defaultsGrp(&config, "Defaults"); 00150 00151 QString group = defaultsGrp.readEntry("Profile","Default"); 00152 00153 KConfigGroup profileGrp(&config, QString::fromLatin1("PROFILE_%1").arg(group) ); 00154 QString command = profileGrp.readPathEntry("EmailClient", QString()); 00155 00156 QString to, cc, bcc; 00157 if (command.isEmpty() || command == QLatin1String("kmail") 00158 || command.endsWith(QLatin1String("/kmail"))) 00159 { 00160 command = QLatin1String("kmail --composer -s %s -c %c -b %b --body %B --attach %A -- %t"); 00161 if ( !_to.isEmpty() ) 00162 { 00163 KUrl url; 00164 url.setProtocol(QLatin1String("mailto")); 00165 url.setPath(_to); 00166 to = QString::fromLatin1(url.toEncoded()); 00167 } 00168 if ( !_cc.isEmpty() ) 00169 { 00170 KUrl url; 00171 url.setProtocol(QLatin1String("mailto")); 00172 url.setPath(_cc); 00173 cc = QString::fromLatin1(url.toEncoded()); 00174 } 00175 if ( !_bcc.isEmpty() ) 00176 { 00177 KUrl url; 00178 url.setProtocol(QLatin1String("mailto")); 00179 url.setPath(_bcc); 00180 bcc = QString::fromLatin1(url.toEncoded()); 00181 } 00182 } else { 00183 to = _to; 00184 cc = _cc; 00185 bcc = _bcc; 00186 if( !command.contains( QLatin1Char('%') )) 00187 command += QLatin1String(" %u"); 00188 } 00189 00190 if (profileGrp.readEntry("TerminalClient", false)) 00191 { 00192 KConfigGroup confGroup( KGlobal::config(), "General" ); 00193 QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00194 command = preferredTerminal + QString::fromLatin1(" -e ") + command; 00195 } 00196 00197 QStringList cmdTokens = KShell::splitArgs(command); 00198 QString cmd = cmdTokens.takeFirst(); 00199 00200 KUrl url; 00201 //QStringList qry; 00202 if (!to.isEmpty()) 00203 { 00204 QStringList tos = splitEmailAddressList( to ); 00205 url.setPath( tos.first() ); 00206 tos.erase( tos.begin() ); 00207 for (QStringList::ConstIterator it = tos.constBegin(); it != tos.constEnd(); ++it) 00208 url.addQueryItem(QString::fromLatin1("to"), *it); 00209 //qry.append( "to=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00210 } 00211 const QStringList ccs = splitEmailAddressList( cc ); 00212 for (QStringList::ConstIterator it = ccs.constBegin(); it != ccs.constEnd(); ++it) 00213 url.addQueryItem(QString::fromLatin1("cc"), *it); 00214 //qry.append( "cc=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00215 const QStringList bccs = splitEmailAddressList( bcc ); 00216 for (QStringList::ConstIterator it = bccs.constBegin(); it != bccs.constEnd(); ++it) 00217 url.addQueryItem(QString::fromLatin1("bcc"), *it); 00218 //qry.append( "bcc=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00219 for (QStringList::ConstIterator it = attachURLs.constBegin(); it != attachURLs.constEnd(); ++it) 00220 url.addQueryItem(QString::fromLatin1("attach"), *it); 00221 //qry.append( "attach=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00222 if (!subject.isEmpty()) 00223 url.addQueryItem(QString::fromLatin1("subject"), subject); 00224 //qry.append( "subject=" + QLatin1String(KUrl::toPercentEncoding( subject ) )); 00225 if (!body.isEmpty()) 00226 url.addQueryItem(QString::fromLatin1("body"), body); 00227 //qry.append( "body=" + QLatin1String(KUrl::toPercentEncoding( body ) )); 00228 //url.setQuery( qry.join( "&" ) ); 00229 00230 if ( ! (to.isEmpty() && (!url.hasQuery())) ) 00231 url.setProtocol(QString::fromLatin1("mailto")); 00232 00233 QHash<QChar, QString> keyMap; 00234 keyMap.insert(QLatin1Char('t'), to); 00235 keyMap.insert(QLatin1Char('s'), subject); 00236 keyMap.insert(QLatin1Char('c'), cc); 00237 keyMap.insert(QLatin1Char('b'), bcc); 00238 keyMap.insert(QLatin1Char('B'), body); 00239 keyMap.insert(QLatin1Char('u'), url.url()); 00240 00241 QString attachlist = attachURLs.join(QString::fromLatin1(",")); 00242 attachlist.prepend(QLatin1Char('\'')); 00243 attachlist.append(QLatin1Char('\'')); 00244 keyMap.insert(QLatin1Char('A'), attachlist); 00245 00246 for (QStringList::Iterator it = cmdTokens.begin(); it != cmdTokens.end(); ) 00247 { 00248 if (*it == QLatin1String("%A")) 00249 { 00250 if (it == cmdTokens.begin()) // better safe than sorry ... 00251 continue; 00252 QStringList::ConstIterator urlit = attachURLs.begin(); 00253 QStringList::ConstIterator urlend = attachURLs.end(); 00254 if ( urlit != urlend ) 00255 { 00256 QStringList::Iterator previt = it; 00257 --previt; 00258 *it = *urlit; 00259 ++it; 00260 while ( ++urlit != urlend ) 00261 { 00262 cmdTokens.insert( it, *previt ); 00263 cmdTokens.insert( it, *urlit ); 00264 } 00265 } else { 00266 --it; 00267 it = cmdTokens.erase( cmdTokens.erase( it ) ); 00268 } 00269 } else { 00270 *it = KMacroExpander::expandMacros(*it, keyMap); 00271 ++it; 00272 } 00273 } 00274 00275 QString error; 00276 // TODO this should check if cmd has a .desktop file, and use data from it, together 00277 // with sending more ASN data 00278 if (kdeinitExec(cmd, cmdTokens, &error, NULL, startup_id )) 00279 { 00280 KMessage::message(KMessage::Error, 00281 i18n("Could not launch the mail client:\n\n%1", error), 00282 i18n("Could not launch Mail Client")); 00283 } 00284 } 00285 00286 void KToolInvocation::invokeBrowser( const QString &url, const QByteArray& startup_id ) 00287 { 00288 if (!isMainThreadActive()) 00289 return; 00290 00291 #ifdef Q_WS_MAEMO_5 00292 if (maemo5Launch(url)) 00293 return; 00294 #endif 00295 QStringList args; 00296 args << url; 00297 QString error; 00298 00299 // This method should launch a webbrowser, preferably without doing a mimetype 00300 // check first, like KRun (i.e. kde-open) would do. 00301 00302 // In a KDE session, honour BrowserApplication if set, otherwise use preferred app for text/html if any, 00303 // otherwise xdg-open, otherwise kde-open (which does a mimetype check first though). 00304 00305 // Outside KDE, call xdg-open if present, otherwise fallback to the above logic. 00306 00307 QString exe; // the binary we are going to launch. 00308 00309 const QString xdg_open = KStandardDirs::findExe(QString::fromLatin1("xdg-open")); 00310 if (qgetenv("KDE_FULL_SESSION").isEmpty()) { 00311 exe = xdg_open; 00312 } 00313 00314 if (exe.isEmpty()) { 00315 // We're in a KDE session (or there's no xdg-open installed) 00316 KConfigGroup config(KGlobal::config(), "General"); 00317 const QString browserApp = config.readPathEntry("BrowserApplication", QString()); 00318 if (!browserApp.isEmpty()) { 00319 exe = browserApp; 00320 if (exe.startsWith(QLatin1Char('!'))) { 00321 exe = exe.mid(1); // Literal command 00322 QStringList cmdTokens = KShell::splitArgs(exe); 00323 exe = cmdTokens.takeFirst(); 00324 args = cmdTokens + args; 00325 } else { 00326 // desktop file ID 00327 KService::Ptr service = KService::serviceByStorageId(exe); 00328 if (service) { 00329 kDebug() << "Starting service" << service->entryPath(); 00330 if (startServiceByDesktopPath(service->entryPath(), args, 00331 &error, 0, 0, startup_id)) { 00332 KMessage::message(KMessage::Error, 00333 // TODO: i18n("Could not launch %1:\n\n%2", exe, error), 00334 i18n("Could not launch the browser:\n\n%1", error), 00335 i18n("Could not launch Browser")); 00336 } 00337 return; 00338 } 00339 } 00340 } else { 00341 const KService::Ptr htmlApp = KMimeTypeTrader::self()->preferredService(QLatin1String("text/html")); 00342 if (htmlApp) { 00343 // WORKAROUND: For bugs 264562 and 265474: 00344 // In order to correctly handle non-HTML urls we change the service 00345 // desktop file name to "kfmclient.desktop" whenever the above query 00346 // returns "kfmclient_html.desktop".Otherwise, the hard coded mime-type 00347 // "text/html" mime-type parameter in the kfmclient_html will cause all 00348 // URLs to be treated as if they are HTML page. 00349 QString entryPath = htmlApp->entryPath(); 00350 if (entryPath.endsWith(QLatin1String("kfmclient_html.desktop"))) { 00351 entryPath.remove(entryPath.length()-13, 5); 00352 } 00353 QString error; 00354 int pid = 0; 00355 int err = startServiceByDesktopPath(entryPath, url, &error, 0, &pid, startup_id); 00356 if (err != 0) { 00357 KMessage::message(KMessage::Error, 00358 // TODO: i18n("Could not launch %1:\n\n%2", htmlApp->exec(), error), 00359 i18n("Could not launch the browser:\n\n%1", error), 00360 i18n("Could not launch Browser")); 00361 } else { // success 00362 return; 00363 } 00364 } else { 00365 exe = xdg_open; 00366 } 00367 } 00368 } 00369 00370 if (exe.isEmpty()) { 00371 exe = QString::fromLatin1("kde-open"); // it's from kdebase-runtime, it has to be there. 00372 } 00373 00374 kDebug(180) << "Using" << exe << "to open" << url; 00375 if (kdeinitExec(exe, args, &error, NULL, startup_id )) 00376 { 00377 KMessage::message(KMessage::Error, 00378 // TODO: i18n("Could not launch %1:\n\n%2", exe, error), 00379 i18n("Could not launch the browser:\n\n%1", error), 00380 i18n("Could not launch Browser")); 00381 } 00382 } 00383 00384 void KToolInvocation::invokeTerminal(const QString &command, 00385 const QString &workdir, 00386 const QByteArray &startup_id) 00387 { 00388 if (!isMainThreadActive()) { 00389 return; 00390 } 00391 00392 KConfigGroup confGroup( KGlobal::config(), "General" ); 00393 QString exec = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00394 00395 if (!command.isEmpty()) { 00396 if (exec == QLatin1String("konsole")) { 00397 exec += QString::fromLatin1(" --noclose"); 00398 } else if (exec == QLatin1String("xterm")) { 00399 exec += QString::fromLatin1(" -hold"); 00400 } 00401 00402 exec += QString::fromLatin1(" -e ") + command; 00403 } 00404 00405 QStringList cmdTokens = KShell::splitArgs(exec); 00406 QString cmd = cmdTokens.takeFirst(); 00407 00408 if (exec == QLatin1String("konsole") && !workdir.isEmpty()) { 00409 cmdTokens << QString::fromLatin1("--workdir"); 00410 cmdTokens << workdir; 00411 // For other terminals like xterm, we'll simply change the working 00412 // directory before launching them, see below. 00413 } 00414 00415 QString error; 00416 if (self()->startServiceInternal("kdeinit_exec_with_workdir", 00417 cmd, cmdTokens, &error, 0, NULL, startup_id, false, workdir)) { 00418 KMessage::message(KMessage::Error, 00419 i18n("Could not launch the terminal client:\n\n%1", error), 00420 i18n("Could not launch Terminal Client")); 00421 } 00422 }
KDE 4.7 API Reference