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 00031 #include "kcmdlineargs.h" 00032 #include "kconfig.h" 00033 #include "kcodecs.h" 00034 #include "kdebug.h" 00035 #include "kglobal.h" 00036 #include "kshell.h" 00037 #include "kmacroexpander.h" 00038 #include "klocale.h" 00039 #include "kstandarddirs.h" 00040 #include "kmessage.h" 00041 #include "kservice.h" 00042 00043 #include <QtCore/QCoreApplication> 00044 #include <QtCore/QProcess> 00045 #include <QtCore/QHash> 00046 #include <QtCore/QDebug> 00047 #include <QtCore/QBool> 00048 #include <QtCore/QFile> 00049 #include <QtDBus/QtDBus> 00050 00051 static QStringList splitEmailAddressList( const QString & aStr ) 00052 { 00053 // This is a copy of KPIM::splitEmailAddrList(). 00054 // Features: 00055 // - always ignores quoted characters 00056 // - ignores everything (including parentheses and commas) 00057 // inside quoted strings 00058 // - supports nested comments 00059 // - ignores everything (including double quotes and commas) 00060 // inside comments 00061 00062 QStringList list; 00063 00064 if (aStr.isEmpty()) 00065 return list; 00066 00067 QString addr; 00068 uint addrstart = 0; 00069 int commentlevel = 0; 00070 bool insidequote = false; 00071 00072 for (int index=0; index<aStr.length(); index++) { 00073 // the following conversion to latin1 is o.k. because 00074 // we can safely ignore all non-latin1 characters 00075 switch (aStr[index].toLatin1()) { 00076 case '"' : // start or end of quoted string 00077 if (commentlevel == 0) 00078 insidequote = !insidequote; 00079 break; 00080 case '(' : // start of comment 00081 if (!insidequote) 00082 commentlevel++; 00083 break; 00084 case ')' : // end of comment 00085 if (!insidequote) { 00086 if (commentlevel > 0) 00087 commentlevel--; 00088 else { 00089 //kDebug() << "Error in address splitting: Unmatched ')'" 00090 // << endl; 00091 return list; 00092 } 00093 } 00094 break; 00095 case '\\' : // quoted character 00096 index++; // ignore the quoted character 00097 break; 00098 case ',' : 00099 if (!insidequote && (commentlevel == 0)) { 00100 addr = aStr.mid(addrstart, index-addrstart); 00101 if (!addr.isEmpty()) 00102 list += addr.simplified(); 00103 addrstart = index+1; 00104 } 00105 break; 00106 } 00107 } 00108 // append the last address to the list 00109 if (!insidequote && (commentlevel == 0)) { 00110 addr = aStr.mid(addrstart, aStr.length()-addrstart); 00111 if (!addr.isEmpty()) 00112 list += addr.simplified(); 00113 } 00114 //else 00115 // kDebug() << "Error in address splitting: " 00116 // << "Unexpected end of address list" 00117 // << endl; 00118 00119 return list; 00120 } 00121 00122 #ifdef Q_WS_MAEMO_5 00123 // taken from QDesktopServices, which we cannot use here due to it being in QtGui 00124 inline static bool maemo5Launch(const QUrl &url) 00125 { 00126 typedef bool (*Ptr_hildon_uri_open)(const char *, void *, void **); 00127 static Ptr_hildon_uri_open hildon_uri_open = 0; 00128 00129 if (!hildon_uri_open) { 00130 QLibrary lib(QLatin1String("libhildonmime"), 0, 0); 00131 hildon_uri_open = (Ptr_hildon_uri_open)lib.resolve("hildon_uri_open"); 00132 } 00133 if (hildon_uri_open) 00134 return hildon_uri_open(url.toEncoded().constData(), 0, 0); 00135 return false; 00136 } 00137 #endif 00138 00139 void KToolInvocation::invokeMailer(const QString &_to, const QString &_cc, const QString &_bcc, 00140 const QString &subject, const QString &body, 00141 const QString & /*messageFile TODO*/, const QStringList &attachURLs, 00142 const QByteArray& startup_id ) 00143 { 00144 if (!isMainThreadActive()) 00145 return; 00146 00147 KConfig config(QString::fromLatin1("emaildefaults")); 00148 KConfigGroup defaultsGrp(&config, "Defaults"); 00149 00150 QString group = defaultsGrp.readEntry("Profile","Default"); 00151 00152 KConfigGroup profileGrp(&config, QString::fromLatin1("PROFILE_%1").arg(group) ); 00153 QString command = profileGrp.readPathEntry("EmailClient", QString()); 00154 00155 QString to, cc, bcc; 00156 if (command.isEmpty() || command == QLatin1String("kmail") 00157 || command.endsWith(QLatin1String("/kmail"))) 00158 { 00159 command = QLatin1String("kmail --composer -s %s -c %c -b %b --body %B --attach %A -- %t"); 00160 if ( !_to.isEmpty() ) 00161 { 00162 KUrl url; 00163 url.setProtocol(QLatin1String("mailto")); 00164 url.setPath(_to); 00165 to = QString::fromLatin1(url.toEncoded()); 00166 } 00167 if ( !_cc.isEmpty() ) 00168 { 00169 KUrl url; 00170 url.setProtocol(QLatin1String("mailto")); 00171 url.setPath(_cc); 00172 cc = QString::fromLatin1(url.toEncoded()); 00173 } 00174 if ( !_bcc.isEmpty() ) 00175 { 00176 KUrl url; 00177 url.setProtocol(QLatin1String("mailto")); 00178 url.setPath(_bcc); 00179 bcc = QString::fromLatin1(url.toEncoded()); 00180 } 00181 } else { 00182 to = _to; 00183 cc = _cc; 00184 bcc = _bcc; 00185 if( !command.contains( QLatin1Char('%') )) 00186 command += QLatin1String(" %u"); 00187 } 00188 00189 if (profileGrp.readEntry("TerminalClient", false)) 00190 { 00191 KConfigGroup confGroup( KGlobal::config(), "General" ); 00192 QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00193 command = preferredTerminal + QString::fromLatin1(" -e ") + command; 00194 } 00195 00196 QStringList cmdTokens = KShell::splitArgs(command); 00197 QString cmd = cmdTokens.takeFirst(); 00198 00199 KUrl url; 00200 //QStringList qry; 00201 if (!to.isEmpty()) 00202 { 00203 QStringList tos = splitEmailAddressList( to ); 00204 url.setPath( tos.first() ); 00205 tos.erase( tos.begin() ); 00206 for (QStringList::ConstIterator it = tos.constBegin(); it != tos.constEnd(); ++it) 00207 url.addQueryItem(QString::fromLatin1("to"), *it); 00208 //qry.append( "to=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00209 } 00210 const QStringList ccs = splitEmailAddressList( cc ); 00211 for (QStringList::ConstIterator it = ccs.constBegin(); it != ccs.constEnd(); ++it) 00212 url.addQueryItem(QString::fromLatin1("cc"), *it); 00213 //qry.append( "cc=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00214 const QStringList bccs = splitEmailAddressList( bcc ); 00215 for (QStringList::ConstIterator it = bccs.constBegin(); it != bccs.constEnd(); ++it) 00216 url.addQueryItem(QString::fromLatin1("bcc"), *it); 00217 //qry.append( "bcc=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00218 for (QStringList::ConstIterator it = attachURLs.constBegin(); it != attachURLs.constEnd(); ++it) 00219 url.addQueryItem(QString::fromLatin1("attach"), *it); 00220 //qry.append( "attach=" + QLatin1String(KUrl::toPercentEncoding( *it ) )); 00221 if (!subject.isEmpty()) 00222 url.addQueryItem(QString::fromLatin1("subject"), subject); 00223 //qry.append( "subject=" + QLatin1String(KUrl::toPercentEncoding( subject ) )); 00224 if (!body.isEmpty()) 00225 url.addQueryItem(QString::fromLatin1("body"), body); 00226 //qry.append( "body=" + QLatin1String(KUrl::toPercentEncoding( body ) )); 00227 //url.setQuery( qry.join( "&" ) ); 00228 00229 if ( ! (to.isEmpty() && (!url.hasQuery())) ) 00230 url.setProtocol(QString::fromLatin1("mailto")); 00231 00232 QHash<QChar, QString> keyMap; 00233 keyMap.insert(QLatin1Char('t'), to); 00234 keyMap.insert(QLatin1Char('s'), subject); 00235 keyMap.insert(QLatin1Char('c'), cc); 00236 keyMap.insert(QLatin1Char('b'), bcc); 00237 keyMap.insert(QLatin1Char('B'), body); 00238 keyMap.insert(QLatin1Char('u'), url.url()); 00239 00240 QString attachlist = attachURLs.join(QString::fromLatin1(",")); 00241 attachlist.prepend(QLatin1Char('\'')); 00242 attachlist.append(QLatin1Char('\'')); 00243 keyMap.insert(QLatin1Char('A'), attachlist); 00244 00245 for (QStringList::Iterator it = cmdTokens.begin(); it != cmdTokens.end(); ) 00246 { 00247 if (*it == QLatin1String("%A")) 00248 { 00249 if (it == cmdTokens.begin()) // better safe than sorry ... 00250 continue; 00251 QStringList::ConstIterator urlit = attachURLs.begin(); 00252 QStringList::ConstIterator urlend = attachURLs.end(); 00253 if ( urlit != urlend ) 00254 { 00255 QStringList::Iterator previt = it; 00256 --previt; 00257 *it = *urlit; 00258 ++it; 00259 while ( ++urlit != urlend ) 00260 { 00261 cmdTokens.insert( it, *previt ); 00262 cmdTokens.insert( it, *urlit ); 00263 } 00264 } else { 00265 --it; 00266 it = cmdTokens.erase( cmdTokens.erase( it ) ); 00267 } 00268 } else { 00269 *it = KMacroExpander::expandMacros(*it, keyMap); 00270 ++it; 00271 } 00272 } 00273 00274 QString error; 00275 // TODO this should check if cmd has a .desktop file, and use data from it, together 00276 // with sending more ASN data 00277 if (kdeinitExec(cmd, cmdTokens, &error, NULL, startup_id )) 00278 { 00279 KMessage::message(KMessage::Error, 00280 i18n("Could not launch the mail client:\n\n%1", error), 00281 i18n("Could not launch Mail Client")); 00282 } 00283 } 00284 00285 void KToolInvocation::invokeBrowser( const QString &url, const QByteArray& startup_id ) 00286 { 00287 if (!isMainThreadActive()) 00288 return; 00289 00290 #ifdef Q_WS_MAEMO_5 00291 if (maemo5Launch(url)) 00292 return; 00293 #endif 00294 QStringList args; 00295 args << url; 00296 QString error; 00297 00298 // This method should launch a webbrowser, preferably without doing a mimetype 00299 // check first, like KRun (i.e. kde-open) would do. 00300 00301 // In a KDE session, honour BrowserApplication if set, otherwise call kfmclient if present, 00302 // otherwise xdg-open, otherwise kde-open (which does a mimetype check first though). 00303 00304 // Outside KDE, call xdg-open if present, otherwise fallback to the above logic. 00305 00306 QString exe; // the binary we are going to launch. 00307 00308 const QString xdg_open = KStandardDirs::findExe(QString::fromLatin1("xdg-open")); 00309 if (qgetenv("KDE_FULL_SESSION").isEmpty()) { 00310 exe = xdg_open; 00311 } 00312 00313 if (exe.isEmpty()) { 00314 // We're in a KDE session (or there's no xdg-open installed) 00315 KConfigGroup config(KGlobal::config(), "General"); 00316 const QString browserApp = config.readPathEntry("BrowserApplication", QString()); 00317 if (!browserApp.isEmpty()) { 00318 exe = browserApp; 00319 if (exe.startsWith(QLatin1Char('!'))) { 00320 exe = exe.mid(1); // Literal command 00321 QStringList cmdTokens = KShell::splitArgs(exe); 00322 exe = cmdTokens.takeFirst(); 00323 args = cmdTokens + args; 00324 } else { 00325 // desktop file ID 00326 KService::Ptr service = KService::serviceByStorageId(exe); 00327 if (service) { 00328 kDebug() << "Starting service" << service->entryPath(); 00329 if (startServiceByDesktopPath(service->entryPath(), args, 00330 &error, 0, 0, startup_id)) { 00331 KMessage::message(KMessage::Error, 00332 // TODO: i18n("Could not launch %1:\n\n%2", exe, error), 00333 i18n("Could not launch the browser:\n\n%1", error), 00334 i18n("Could not launch Browser")); 00335 } 00336 return; 00337 } 00338 } 00339 } else { 00340 const QString kfmclient = KStandardDirs::findExe(QString::fromLatin1("kfmclient")); 00341 if (!kfmclient.isEmpty()) { 00342 exe = kfmclient; 00343 args.prepend(QLatin1String("openURL")); 00344 } else { 00345 exe = xdg_open; 00346 } 00347 } 00348 } 00349 00350 if (exe.isEmpty()) { 00351 exe = QString::fromLatin1("kde-open"); // it's from kdebase-runtime, it has to be there. 00352 } 00353 00354 kDebug(180) << "Using" << exe << "to open" << url; 00355 if (kdeinitExec(exe, args, &error, NULL, startup_id )) 00356 { 00357 KMessage::message(KMessage::Error, 00358 // TODO: i18n("Could not launch %1:\n\n%2", exe, error), 00359 i18n("Could not launch the browser:\n\n%1", error), 00360 i18n("Could not launch Browser")); 00361 } 00362 } 00363 00364 void KToolInvocation::invokeTerminal(const QString &command, 00365 const QString &workdir, 00366 const QByteArray &startup_id) 00367 { 00368 if (!isMainThreadActive()) { 00369 return; 00370 } 00371 00372 KConfigGroup confGroup( KGlobal::config(), "General" ); 00373 QString exec = confGroup.readPathEntry("TerminalApplication", QString::fromLatin1("konsole")); 00374 00375 if (!command.isEmpty()) { 00376 if (exec == QLatin1String("konsole")) { 00377 exec += QString::fromLatin1(" --noclose"); 00378 } else if (exec == QLatin1String("xterm")) { 00379 exec += QString::fromLatin1(" -hold"); 00380 } 00381 00382 exec += QString::fromLatin1(" -e ") + command; 00383 } 00384 00385 QStringList cmdTokens = KShell::splitArgs(exec); 00386 QString cmd = cmdTokens.takeFirst(); 00387 00388 if (exec == QLatin1String("konsole") && !workdir.isEmpty()) { 00389 cmdTokens << QString::fromLatin1("--workdir"); 00390 cmdTokens << workdir; 00391 // For other terminals like xterm, we'll simply change the working 00392 // directory before launching them, see below. 00393 } 00394 00395 QString error; 00396 if (self()->startServiceInternal("kdeinit_exec_with_workdir", 00397 cmd, cmdTokens, &error, 0, NULL, startup_id, false, workdir)) { 00398 KMessage::message(KMessage::Error, 00399 i18n("Could not launch the terminal client:\n\n%1", error), 00400 i18n("Could not launch Terminal Client")); 00401 } 00402 }
KDE 4.6 API Reference