1/* This file is part of the KDE project
2 Copyright (C) 1999-2006 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17*/
18
19#include "kioclient.h"
20
21#include <kio/job.h>
22#include <kio/copyjob.h>
23#include <kio/deletejob.h>
24#include <kio/jobuidelegate.h>
25#include <kcmdlineargs.h>
26#include <kpropertiesdialog.h>
27#include <klocale.h>
28#include <kstandarddirs.h>
29#include <kurlrequesterdialog.h>
30#include <kmessagebox.h>
31#include <kmimetypetrader.h>
32#include <kfiledialog.h>
33#include <kdebug.h>
34#include <kservice.h>
35#include <QTimer>
36#include <krun.h>
37#include <QtDBus/QtDBus>
38#include <kcomponentdata.h>
39#include <iostream>
40
41static const char appName[] = "kioclient";
42static const char programName[] = I18N_NOOP("KIO Client");
43static const char description[] = I18N_NOOP("Command-line tool for network-transparent operations");
44static const char version[] = "2.0";
45
46bool ClientApp::m_ok = true;
47static bool s_interactive = true;
48static KIO::JobFlags s_jobFlags = KIO::DefaultFlags;
49
50#ifdef KIOCLIENT_AS_KIOCLIENT
51static void usage()
52{
53 KCmdLineArgs::enable_i18n();
54 puts(i18n("\nSyntax:\n").toLocal8Bit());
55 puts(i18n(" kioclient openProperties 'url'\n"
56 " # Opens a properties menu\n\n").toLocal8Bit());
57 puts(i18n(" kioclient exec 'url' ['mimetype']\n"
58 " # Tries to open the document pointed to by 'url', in the application\n"
59 " # associated with it in KDE. You may omit 'mimetype'.\n"
60 " # In this case the mimetype is determined\n"
61 " # automatically. Of course URL may be the URL of a\n"
62 " # document, or it may be a *.desktop file.\n"
63 " # 'url' can be an executable, too.\n").toLocal8Bit());
64 puts(i18n(" kioclient move 'src' 'dest'\n"
65 " # Moves the URL 'src' to 'dest'.\n"
66 " # 'src' may be a list of URLs.\n").toLocal8Bit());
67 puts(i18n(" # 'dest' may be \"trash:/\" to move the files\n"
68 " # to the trash.\n").toLocal8Bit());
69 puts(i18n(" # the short version kioclient mv\n"
70 " # is also available.\n\n").toLocal8Bit());
71 puts(i18n(" kioclient download ['src']\n"
72 " # Copies the URL 'src' to a user-specified location'.\n"
73 " # 'src' may be a list of URLs, if not present then\n"
74 " # a URL will be requested.\n\n").toLocal8Bit());
75 puts(i18n(" kioclient copy 'src' 'dest'\n"
76 " # Copies the URL 'src' to 'dest'.\n"
77 " # 'src' may be a list of URLs.\n").toLocal8Bit());
78 puts(i18n(" # the short version kioclient cp\n"
79 " # is also available.\n\n").toLocal8Bit());
80 puts(i18n(" kioclient cat 'url'\n"
81 " # Writes out the contents of 'url' to stdout\n\n").toLocal8Bit());
82 puts(i18n(" kioclient ls 'url'\n"
83 " # Lists the contents of the directory 'url' to stdout\n\n").toLocal8Bit());
84 puts(i18n(" kioclient remove 'url'\n"
85 " # Removes the URL\n"
86 " # 'url' may be a list of URLs.\n").toLocal8Bit());
87 puts(i18n(" # the short version kioclient rm\n"
88 " # is also available.\n\n").toLocal8Bit());
89
90 puts(i18n("*** Examples:\n"
91 " kioclient exec file:/root/Desktop/cdrom.desktop \"Mount default\"\n"
92 " // Mounts the CDROM\n\n").toLocal8Bit());
93 puts(i18n(" kioclient exec file:/home/weis/data/test.html\n"
94 " // Opens the file with default binding\n\n").toLocal8Bit());
95 puts(i18n(" kioclient exec file:/home/weis/data/test.html Netscape\n"
96 " // Opens the file with netscape\n\n").toLocal8Bit());
97 puts(i18n(" kioclient exec ftp://localhost/\n"
98 " // Opens new window with URL\n\n").toLocal8Bit());
99 puts(i18n(" kioclient exec file:/root/Desktop/emacs.desktop\n"
100 " // Starts emacs\n\n").toLocal8Bit());
101 puts(i18n(" kioclient exec file:/root/Desktop/cdrom.desktop\n"
102 " // Opens the CDROM's mount directory\n\n").toLocal8Bit());
103 puts(i18n(" kioclient exec .\n"
104 " // Opens the current directory. Very convenient.\n\n").toLocal8Bit());
105}
106#endif
107
108int main( int argc, char **argv )
109{
110 KCmdLineArgs::init(argc, argv, appName, 0, ki18n(programName), version, ki18n(description), KCmdLineArgs::CmdLineArgNone);
111
112
113 KCmdLineOptions options;
114 options.add("noninteractive", ki18n("Non-interactive use: no message boxes"));
115 #if !defined(KIOCLIENT_AS_KDEOPEN)
116 options.add("overwrite", ki18n("Overwrite destination if it exists (for copy and move)"));
117 #endif
118 #if defined(KIOCLIENT_AS_KDEOPEN)
119 options.add("+url", ki18n("file or URL"));
120 #elif defined(KIOCLIENT_AS_KDECP)
121 options.add("+src", ki18n("Source URL or URLs"));
122 options.add("+dest", ki18n("Destination URL"));
123 #elif defined(KIOCLIENT_AS_KDEMV)
124 options.add("+src", ki18n("Source URL or URLs"));
125 options.add("+dest", ki18n("Destination URL"));
126 #elif defined(KIOCLIENT_AS_KIOCLIENT)
127 options.add("commands", ki18n("Show available commands"));
128 options.add("+command", ki18n("Command (see --commands)"));
129 options.add("+[URL(s)]", ki18n("Arguments for command"));
130 #endif
131
132 KCmdLineArgs::addCmdLineOptions( options );
133 KCmdLineArgs::addTempFileOption();
134
135#ifdef KIOCLIENT_AS_KIOCLIENT
136 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
137
138 if ( argc == 1 || args->isSet("commands") )
139 {
140 usage();
141 return 0;
142 }
143#endif
144
145 return ClientApp::doIt() ? 0 /*no error*/ : 1 /*error*/;
146}
147
148bool krun_has_error = false;
149
150void ClientApp::delayedQuit()
151{
152 // Quit in 2 seconds. This leaves time for KRun to pop up
153 // "app not found" in KProcessRunner, if that was the case.
154 QTimer::singleShot( 2000, this, SLOT(deref()) );
155 // don't access the KRun instance later, it will be deleted after calling slots
156 if( static_cast< const KRun* >( sender())->hasError())
157 krun_has_error = true;
158}
159
160static void checkArgumentCount(int count, int min, int max)
161{
162 if (count < min)
163 {
164 fputs( i18nc("@info:shell", "%1: Syntax error, not enough arguments\n", appName).toLocal8Bit(), stderr );
165 ::exit(1);
166 }
167 if (max && (count > max))
168 {
169 fputs( i18nc("@info:shell", "%1: Syntax error, too many arguments\n", appName).toLocal8Bit(), stderr );
170 ::exit(1);
171 }
172}
173
174bool ClientApp::kde_open(const KUrl& url, const QString& mimeType, bool allowExec)
175{
176 if ( mimeType.isEmpty() ) {
177 kDebug() << url;
178 KRun * run = new KRun( url, 0 );
179 run->setRunExecutables(allowExec);
180 QObject::connect( run, SIGNAL( finished() ), this, SLOT( delayedQuit() ));
181 QObject::connect( run, SIGNAL( error() ), this, SLOT( delayedQuit() ));
182 this->exec();
183 return !krun_has_error;
184 } else {
185 KUrl::List urls;
186 urls.append( url );
187 const KService::List offers = KMimeTypeTrader::self()->query(
188 mimeType, QLatin1String( "Application" ) );
189 if (offers.isEmpty()) return 1;
190 KService::Ptr serv = offers.first();
191 return KRun::run( *serv, urls, 0 );
192 }
193}
194
195bool ClientApp::doCopy( int firstArg )
196{
197 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
198 int argc = args->count();
199 KUrl::List srcLst;
200 for ( int i = firstArg; i <= argc - 2; i++ )
201 srcLst.append( args->url(i) );
202 KIO::Job * job = KIO::copy( srcLst, args->url(argc - 1), s_jobFlags );
203 if ( !s_interactive )
204 job->setUiDelegate( 0 );
205 connect( job, SIGNAL( result( KJob * ) ), this, SLOT( slotResult( KJob * ) ) );
206 this->exec();
207 return m_ok;
208}
209
210void ClientApp::slotEntries(KIO::Job* job, const KIO::UDSEntryList& list)
211{
212 KUrl url = static_cast<KIO::ListJob*>( job )->url();
213 KIO::UDSEntryList::ConstIterator it=list.begin();
214 for (; it != list.end(); ++it) {
215 // For each file...
216 QString name = (*it).stringValue( KIO::UDSEntry::UDS_NAME );
217 std::cout << qPrintable(name) << std::endl;
218 }
219}
220
221bool ClientApp::doList( int firstArg )
222{
223 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
224 KUrl dir = args->url(firstArg);
225 KIO::Job * job = KIO::listDir(dir, KIO::HideProgressInfo);
226 if ( !s_interactive )
227 job->setUiDelegate(0);
228 connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
229 SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList)));
230 connect(job, SIGNAL(result(KJob *)), this, SLOT(slotResult(KJob *)));
231 this->exec();
232 return m_ok;
233}
234
235bool ClientApp::doMove( int firstArg )
236{
237 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
238 int argc = args->count();
239 KUrl::List srcLst;
240 for ( int i = firstArg; i <= argc - 2; i++ )
241 srcLst.append( args->url(i) );
242
243 KIO::Job * job = KIO::move( srcLst, args->url(argc - 1), s_jobFlags );
244 if ( !s_interactive )
245 job->setUiDelegate( 0 );
246 connect( job, SIGNAL( result( KJob * ) ), this, SLOT( slotResult( KJob * ) ) );
247 this->exec();
248 return m_ok;
249}
250
251bool ClientApp::doRemove( int firstArg )
252{
253 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
254 int argc = args->count();
255 KUrl::List srcLst;
256 for ( int i = firstArg; i < argc; i++ )
257 srcLst.append( args->url(i) );
258 KIO::Job * job = KIO::del( srcLst, s_jobFlags );
259 if ( !s_interactive )
260 job->setUiDelegate( 0 );
261 connect( job, SIGNAL( result( KJob * ) ), this, SLOT( slotResult( KJob * ) ) );
262 this->exec();
263 return m_ok;
264}
265
266bool ClientApp::doIt()
267{
268 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
269 const int argc = args->count();
270 checkArgumentCount(argc, 1, 0);
271
272 if ( !args->isSet( "ninteractive" ) ) {
273 s_interactive = false;
274 s_jobFlags = KIO::HideProgressInfo;
275 }
276#if !defined(KIOCLIENT_AS_KDEOPEN)
277 if (args->isSet("overwrite")) {
278 s_jobFlags |= KIO::Overwrite;
279 }
280#endif
281
282 kDebug() << "Creating ClientApp";
283 int fake_argc = 0;
284 char** fake_argv = 0;
285 ClientApp app( fake_argc, fake_argv );
286 KComponentData componentData("kioclient"); // needed by KIO's internal use of KConfig
287 app.setApplicationName(componentData.componentName());
288 KGlobal::ref();
289 KGlobal::setAllowQuit(true);
290
291 // KIO needs dbus (for uiserver communication)
292 extern void qDBusBindToApplication();
293 qDBusBindToApplication();
294 if (!QDBusConnection::sessionBus().isConnected())
295 kFatal(101) << "Session bus not found" ;
296
297#ifdef KIOCLIENT_AS_KDEOPEN
298 return app.kde_open(args->url(0), QByteArray(), false);
299#elif defined(KIOCLIENT_AS_KDECP)
300 checkArgumentCount(argc, 2, 0);
301 return app.doCopy(0);
302#elif defined(KIOCLIENT_AS_KDEMV)
303 checkArgumentCount(argc, 2, 0);
304 return app.doMove(0);
305#else
306 // Normal kioclient mode
307 const QByteArray command = args->arg(0).toLocal8Bit();
308 if ( command == "openProperties" )
309 {
310 checkArgumentCount(argc, 2, 2); // openProperties <url>
311 KPropertiesDialog * p = new KPropertiesDialog( args->url(1), 0 /*no parent*/ );
312 QObject::connect( p, SIGNAL( destroyed() ), &app, SLOT( quit() ));
313 QObject::connect( p, SIGNAL( canceled() ), &app, SLOT( slotDialogCanceled() ));
314 p->show();
315 app.exec();
316 return m_ok;
317 }
318 else if ( command == "cat" )
319 {
320 checkArgumentCount(argc, 2, 2); // cat <url>
321 KIO::TransferJob* job = KIO::get(args->url(1), KIO::NoReload, s_jobFlags);
322 if ( !s_interactive )
323 job->setUiDelegate( 0 );
324 connect(job, SIGNAL(data(KIO::Job*,QByteArray) ), &app, SLOT(slotPrintData(KIO::Job*,QByteArray)));
325 connect( job, SIGNAL( result( KJob * ) ), &app, SLOT( slotResult( KJob * ) ) );
326 app.exec();
327 return m_ok;
328 }
329 else if ( command == "exec" )
330 {
331 checkArgumentCount(argc, 2, 3);
332 return app.kde_open( args->url( 1 ),
333 argc == 3 ? args->arg( 2 ) : QString(),
334 true );
335 }
336 else if ( command == "download" )
337 {
338 checkArgumentCount(argc, 0, 0);
339 KUrl::List srcLst;
340 if (argc == 1) {
341 while(true) {
342 KUrl src = KUrlRequesterDialog::getUrl();
343 if (!src.isEmpty()) {
344 if (!src.isValid()) {
345 KMessageBox::error(0, i18n("Unable to download from an invalid URL."));
346 continue;
347 }
348 srcLst.append(src);
349 }
350 break;
351 }
352 } else {
353 for ( int i = 1; i <= argc - 1; i++ )
354 srcLst.append( args->url(i) );
355 }
356 if (srcLst.count() == 0)
357 return m_ok;
358 QString dst =
359 KFileDialog::getSaveFileName( (argc<2) ? QString() : (args->url(1).fileName()) );
360 if (dst.isEmpty()) // canceled
361 return m_ok; // AK - really okay?
362 KUrl dsturl;
363 dsturl.setPath( dst );
364 KIO::Job * job = KIO::copy( srcLst, dsturl, s_jobFlags );
365 if ( !s_interactive )
366 job->setUiDelegate( 0 );
367 connect( job, SIGNAL( result( KJob * ) ), &app, SLOT( slotResult( KJob * ) ) );
368 app.exec();
369 return m_ok;
370 }
371 else if ( command == "copy" || command == "cp" )
372 {
373 checkArgumentCount(argc, 3, 0); // cp <src> <dest>
374 return app.doCopy( 1 );
375 }
376 else if ( command == "move" || command == "mv" )
377 {
378 checkArgumentCount(argc, 3, 0); // mv <src> <dest>
379 return app.doMove( 1 );
380 }
381 else if ( command == "list" || command == "ls" )
382 {
383 checkArgumentCount(argc, 2, 2); // ls <url>
384 return app.doList( 1 );
385 }
386 else if ( command == "remove" || command == "rm" )
387 {
388 checkArgumentCount(argc, 2, 0); // rm <url>
389 return app.doRemove( 1 );
390 }
391 else
392 {
393 fputs( i18nc("@info:shell", "%1: Syntax error, unknown command '%2'\n", appName, QString::fromLocal8Bit(command)).toLocal8Bit().data(), stderr );
394 return false;
395 }
396 return true;
397#endif
398}
399
400void ClientApp::slotResult( KJob * job )
401{
402 if (job->error() && s_interactive)
403 static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
404 m_ok = !job->error();
405 quit();
406}
407
408void ClientApp::slotDialogCanceled()
409{
410 m_ok = false;
411 quit();
412}
413
414void ClientApp::deref()
415{
416 KGlobal::deref();
417}
418
419void ClientApp::slotPrintData(KIO::Job*, const QByteArray &data)
420{
421 if (!data.isEmpty())
422 std::cout.write(data.constData(), data.size());
423}
424
425ClientApp::ClientApp(int &argc, char **argv )
426 : QApplication( argc, argv, s_interactive /*-> bool GUIenabled*/ )
427{
428}
429
430#include "kioclient.moc"
431