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 | |
41 | static const char appName[] = "kioclient" ; |
42 | static const char programName[] = I18N_NOOP("KIO Client" ); |
43 | static const char description[] = I18N_NOOP("Command-line tool for network-transparent operations" ); |
44 | static const char version[] = "2.0" ; |
45 | |
46 | bool ClientApp::m_ok = true; |
47 | static bool s_interactive = true; |
48 | static KIO::JobFlags s_jobFlags = KIO::DefaultFlags; |
49 | |
50 | #ifdef KIOCLIENT_AS_KIOCLIENT |
51 | static 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 | |
108 | int 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 | |
148 | bool krun_has_error = false; |
149 | |
150 | void 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 | |
160 | static 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 | |
174 | bool 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 | |
195 | bool 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 | |
210 | void 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 | |
221 | bool 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 | |
235 | bool 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 | |
251 | bool 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 | |
266 | bool 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 | |
400 | void 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 | |
408 | void ClientApp::slotDialogCanceled() |
409 | { |
410 | m_ok = false; |
411 | quit(); |
412 | } |
413 | |
414 | void ClientApp::deref() |
415 | { |
416 | KGlobal::deref(); |
417 | } |
418 | |
419 | void ClientApp::slotPrintData(KIO::Job*, const QByteArray &data) |
420 | { |
421 | if (!data.isEmpty()) |
422 | std::cout.write(data.constData(), data.size()); |
423 | } |
424 | |
425 | ClientApp::ClientApp(int &argc, char **argv ) |
426 | : QApplication( argc, argv, s_interactive /*-> bool GUIenabled*/ ) |
427 | { |
428 | } |
429 | |
430 | #include "kioclient.moc" |
431 | |