1 /* This file is part of the KDE project
2 SPDX-FileCopyrightText: 1999-2006 David Faure <faure@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-only
5 */
6
7 #include "kioclient.h"
8 #include "kio_version.h"
9 #include "urlinfo.h"
10
11 #include <KIO/CopyJob>
12 #include <KIO/DeleteJob>
13 #include <kio/listjob.h>
14 #include <kio/transferjob.h>
15 #ifndef KIOCORE_ONLY
16 #include <KIO/ApplicationLauncherJob>
17 #include <KIO/JobUiDelegate>
18 #include <KIO/OpenUrlJob>
19 #include <KIO/StatJob>
20 #include <KIO/UDSEntry>
21 #include <KPropertiesDialog>
22 #include <KService>
23 #endif
24 #include <KAboutData>
25 #include <KLocalizedString>
26
27 #include <QCommandLineParser>
28 #include <QDBusConnection>
29 #include <QDebug>
30 #include <QFileDialog>
31 #include <QUrlQuery>
32 #include <iostream>
33
34 bool ClientApp::m_ok = true;
35 static bool s_interactive = false;
36 static KIO::JobFlags s_jobFlags = KIO::DefaultFlags;
37
makeURL(const QString & urlArg)38 static QUrl makeURL(const QString &urlArg)
39 {
40 return QUrl::fromUserInput(urlArg, QDir::currentPath());
41 }
42
makeUrls(const QStringList & urlArgs)43 static QList<QUrl> makeUrls(const QStringList &urlArgs)
44 {
45 QList<QUrl> ret;
46 for (const QString &url : urlArgs) {
47 ret += makeURL(url);
48 }
49 return ret;
50 }
51
52 #ifdef KIOCLIENT_AS_KIOCLIENT5
usage()53 static void usage()
54 {
55 puts(i18n("\nSyntax:\n").toLocal8Bit().constData());
56 puts(i18n(" kioclient5 openProperties 'url'\n"
57 " # Opens a properties menu\n\n")
58 .toLocal8Bit()
59 .constData());
60 puts(i18n(" kioclient5 exec 'url' ['mimetype']\n"
61 " # Tries to open the document pointed to by 'url', in the application\n"
62 " # associated with it in KDE. You may omit 'mimetype'.\n"
63 " # In this case the mimetype is determined\n"
64 " # automatically. Of course URL may be the URL of a\n"
65 " # document, or it may be a *.desktop file.\n"
66 " # 'url' can be an executable, too.\n")
67 .toLocal8Bit()
68 .constData());
69 puts(i18n(" kioclient5 move 'src' 'dest'\n"
70 " # Moves the URL 'src' to 'dest'.\n"
71 " # 'src' may be a list of URLs.\n")
72 .toLocal8Bit()
73 .constData());
74 puts(i18n(" # 'dest' may be \"trash:/\" to move the files\n"
75 " # to the trash.\n")
76 .toLocal8Bit()
77 .constData());
78 puts(i18n(" # the short version kioclient5 mv\n"
79 " # is also available.\n\n")
80 .toLocal8Bit()
81 .constData());
82 puts(i18n(" kioclient5 download ['src']\n"
83 " # Copies the URL 'src' to a user-specified location'.\n"
84 " # 'src' may be a list of URLs, if not present then\n"
85 " # a URL will be requested.\n\n")
86 .toLocal8Bit()
87 .constData());
88 puts(i18n(" kioclient5 copy 'src' 'dest'\n"
89 " # Copies the URL 'src' to 'dest'.\n"
90 " # 'src' may be a list of URLs.\n")
91 .toLocal8Bit()
92 .constData());
93 puts(i18n(" # the short version kioclient5 cp\n"
94 " # is also available.\n\n")
95 .toLocal8Bit()
96 .constData());
97 puts(i18n(" kioclient5 cat 'url'\n"
98 " # Writes out the contents of 'url' to stdout\n\n")
99 .toLocal8Bit()
100 .constData());
101 puts(i18n(" kioclient5 ls 'url'\n"
102 " # Lists the contents of the directory 'url' to stdout\n\n")
103 .toLocal8Bit()
104 .constData());
105 puts(i18n(" kioclient5 remove 'url'\n"
106 " # Removes the URL\n"
107 " # 'url' may be a list of URLs.\n")
108 .toLocal8Bit()
109 .constData());
110 puts(i18n(" # the short version kioclient5 rm\n"
111 " # is also available.\n\n")
112 .toLocal8Bit()
113 .constData());
114 puts(i18n(" kioclient5 stat 'url'\n"
115 " # Shows all of the available information for 'url'\n\n")
116 .toLocal8Bit()
117 .constData());
118 puts(i18n(" kioclient5 appmenu\n"
119 " # Opens a basic application launcher.\n\n")
120 .toLocal8Bit()
121 .constData());
122
123 puts(i18n("*** Examples:\n").toLocal8Bit().constData());
124 puts(i18n(" kioclient5 exec file:/home/weis/data/test.html\n"
125 " // Opens the file with default binding\n\n")
126 .toLocal8Bit()
127 .constData());
128 puts(i18n(" kioclient5 exec ftp://localhost/\n"
129 " // Opens new window with URL\n\n")
130 .toLocal8Bit()
131 .constData());
132 puts(i18n(" kioclient5 exec file:/root/Desktop/emacs.desktop\n"
133 " // Starts emacs\n\n")
134 .toLocal8Bit()
135 .constData());
136 puts(i18n(" kioclient5 exec .\n"
137 " // Opens the current directory. Very convenient.\n\n")
138 .toLocal8Bit()
139 .constData());
140 }
141 #endif
142
main(int argc,char ** argv)143 int main(int argc, char **argv)
144 {
145 #ifdef KIOCORE_ONLY
146 QCoreApplication app(argc, argv);
147 #else
148 QApplication app(argc, argv);
149 #endif
150
151 KLocalizedString::setApplicationDomain("kioclient5");
152
153 QString appName = QStringLiteral("kioclient");
154 QString programName = i18n("KIO Client");
155 QString description = i18n("Command-line tool for network-transparent operations");
156 QString version = QLatin1String(PROJECT_VERSION);
157 KAboutData data(appName, programName, version, description, KAboutLicense::LGPL_V2);
158 KAboutData::setApplicationData(data);
159
160 QCommandLineParser parser;
161 data.setupCommandLine(&parser);
162 parser.addOption(QCommandLineOption(QStringLiteral("interactive"), i18n("Use message boxes and other native notifications")));
163
164 parser.addOption(QCommandLineOption(QStringLiteral("noninteractive"),
165 i18n("Non-interactive use: no message boxes. If you don't want a "
166 "graphical connection, use --platform offscreen")));
167
168 #if !defined(KIOCLIENT_AS_KDEOPEN)
169 parser.addOption(QCommandLineOption(QStringLiteral("overwrite"), i18n("Overwrite destination if it exists (for copy and move)")));
170 #endif
171
172 #if defined(KIOCLIENT_AS_KDEOPEN)
173 parser.addPositionalArgument(QStringLiteral("url"), i18n("file or URL"), i18n("urls..."));
174 #elif defined(KIOCLIENT_AS_KDECP5)
175 parser.addPositionalArgument(QStringLiteral("src"), i18n("Source URL or URLs"), i18n("urls..."));
176 parser.addPositionalArgument(QStringLiteral("dest"), i18n("Destination URL"), i18n("url"));
177 #elif defined(KIOCLIENT_AS_KDEMV5)
178 parser.addPositionalArgument(QStringLiteral("src"), i18n("Source URL or URLs"), i18n("urls..."));
179 parser.addPositionalArgument(QStringLiteral("dest"), i18n("Destination URL"), i18n("url"));
180 #elif defined(KIOCLIENT_AS_KIOCLIENT5)
181 parser.addOption(QCommandLineOption(QStringLiteral("commands"), i18n("Show available commands")));
182 parser.addPositionalArgument(QStringLiteral("command"), i18n("Command (see --commands)"), i18n("command"));
183 parser.addPositionalArgument(QStringLiteral("URLs"), i18n("Arguments for command"), i18n("urls..."));
184 #endif
185
186 // KCmdLineArgs::addTempFileOption();
187
188 parser.process(app);
189 data.processCommandLine(&parser);
190
191 #ifdef KIOCLIENT_AS_KIOCLIENT5
192 if (argc == 1 || parser.isSet(QStringLiteral("commands"))) {
193 puts(parser.helpText().toLocal8Bit().constData());
194 puts("\n\n");
195 usage();
196 return 0;
197 }
198 #endif
199
200 ClientApp client;
201 return client.doIt(parser) ? 0 /*no error*/ : 1 /*error*/;
202 }
203
checkArgumentCount(int count,int min,int max)204 static void checkArgumentCount(int count, int min, int max)
205 {
206 if (count < min) {
207 fputs(i18nc("@info:shell", "%1: Syntax error, not enough arguments\n", qAppName()).toLocal8Bit().constData(), stderr);
208 ::exit(1);
209 }
210 if (max && (count > max)) {
211 fputs(i18nc("@info:shell", "%1: Syntax error, too many arguments\n", qAppName()).toLocal8Bit().constData(), stderr);
212 ::exit(1);
213 }
214 }
215
216 #ifndef KIOCORE_ONLY
kde_open(const QString & url,const QString & mimeType,bool allowExec)217 bool ClientApp::kde_open(const QString &url, const QString &mimeType, bool allowExec)
218 {
219 UrlInfo info(url);
220
221 if (!info.atStart()) {
222 QUrlQuery q;
223 q.addQueryItem(QStringLiteral("line"), QString::number(info.line));
224 q.addQueryItem(QStringLiteral("column"), QString::number(info.column));
225 info.url.setQuery(q);
226 }
227
228 auto *job = new KIO::OpenUrlJob(info.url, mimeType);
229 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, nullptr));
230 job->setRunExecutables(allowExec);
231 job->setFollowRedirections(false);
232 bool job_had_error = false;
233 QObject::connect(job, &KJob::result, this, [&](KJob *job) {
234 if (job->error()) {
235 job_had_error = true;
236 }
237 });
238 job->start();
239 qApp->exec();
240 return !job_had_error;
241 }
242 #endif
243
doCopy(const QStringList & urls)244 bool ClientApp::doCopy(const QStringList &urls)
245 {
246 QList<QUrl> srcLst(makeUrls(urls));
247 QUrl dest = srcLst.takeLast();
248 KIO::Job *job = KIO::copy(srcLst, dest, s_jobFlags);
249 if (!s_interactive) {
250 job->setUiDelegate(nullptr);
251 job->setUiDelegateExtension(nullptr);
252 }
253 connect(job, &KJob::result, this, &ClientApp::slotResult);
254 qApp->exec();
255 return m_ok;
256 }
257
slotEntries(KIO::Job *,const KIO::UDSEntryList & list)258 void ClientApp::slotEntries(KIO::Job *, const KIO::UDSEntryList &list)
259 {
260 for (const auto &entry : list) {
261 // For each file...
262 std::cout << qPrintable(entry.stringValue(KIO::UDSEntry::UDS_NAME)) << '\n';
263 }
264
265 std::cout << std::endl;
266 }
267
doList(const QStringList & urls)268 bool ClientApp::doList(const QStringList &urls)
269 {
270 const QUrl dir = makeURL(urls.at(0));
271
272 KIO::ListJob *job = KIO::listDir(dir, KIO::HideProgressInfo);
273 if (!s_interactive) {
274 job->setUiDelegate(nullptr);
275 job->setUiDelegateExtension(nullptr);
276 }
277
278 connect(job, &KIO::ListJob::entries, this, &ClientApp::slotEntries);
279 connect(job, &KJob::result, this, &ClientApp::slotResult);
280
281 qApp->exec();
282 return m_ok;
283 }
284
doMove(const QStringList & urls)285 bool ClientApp::doMove(const QStringList &urls)
286 {
287 QList<QUrl> srcLst(makeUrls(urls));
288 const QUrl dest = srcLst.takeLast();
289
290 KIO::Job *job = KIO::move(srcLst, dest, s_jobFlags);
291 if (!s_interactive) {
292 job->setUiDelegate(nullptr);
293 job->setUiDelegateExtension(nullptr);
294 }
295
296 connect(job, &KJob::result, this, &ClientApp::slotResult);
297
298 qApp->exec();
299 return m_ok;
300 }
301
doRemove(const QStringList & urls)302 bool ClientApp::doRemove(const QStringList &urls)
303 {
304 KIO::Job *job = KIO::del(makeUrls(urls), s_jobFlags);
305 if (!s_interactive) {
306 job->setUiDelegate(nullptr);
307 job->setUiDelegateExtension(nullptr);
308 }
309
310 connect(job, &KJob::result, this, &ClientApp::slotResult);
311
312 qApp->exec();
313 return m_ok;
314 }
315
doStat(const QStringList & urls)316 bool ClientApp::doStat(const QStringList &urls)
317 {
318 KIO::Job *job = KIO::statDetails(makeURL(urls.first()),
319 KIO::StatJob::SourceSide,
320 (KIO::StatBasic | KIO::StatUser | KIO::StatTime | KIO::StatInode | KIO::StatMimeType | KIO::StatAcl),
321 s_jobFlags);
322 if (!s_interactive) {
323 job->setUiDelegate(nullptr);
324 job->setUiDelegateExtension(nullptr);
325 }
326
327 connect(job, &KJob::result, this, &ClientApp::slotStatResult);
328
329 qApp->exec();
330 return m_ok;
331 }
332
doIt(const QCommandLineParser & parser)333 bool ClientApp::doIt(const QCommandLineParser &parser)
334 {
335 const int argc = parser.positionalArguments().count();
336 checkArgumentCount(argc, 1, 0);
337
338 if (parser.isSet(QStringLiteral("interactive"))) {
339 s_interactive = true;
340 } else {
341 // "noninteractive" is currently the default mode, so we don't check.
342 // The argument still needs to exist for compatibility
343 s_interactive = false;
344 s_jobFlags = KIO::HideProgressInfo;
345 }
346 #if !defined(KIOCLIENT_AS_KDEOPEN)
347 if (parser.isSet(QStringLiteral("overwrite"))) {
348 s_jobFlags |= KIO::Overwrite;
349 }
350 #endif
351
352 #ifdef KIOCLIENT_AS_KDEOPEN
353 return kde_open(parser.positionalArguments().at(0), QString(), false);
354 #elif defined(KIOCLIENT_AS_KDECP5)
355 checkArgumentCount(argc, 2, 0);
356 return doCopy(parser.positionalArguments());
357 #elif defined(KIOCLIENT_AS_KDEMV5)
358 checkArgumentCount(argc, 2, 0);
359 return doMove(parser.positionalArguments());
360 #else
361 // Normal kioclient mode
362 const QString command = parser.positionalArguments().at(0);
363 #ifndef KIOCORE_ONLY
364 if (command == QLatin1String("openProperties")) {
365 checkArgumentCount(argc, 2, 2); // openProperties <url>
366 const QUrl url = makeURL(parser.positionalArguments().constLast());
367
368 KPropertiesDialog *dlg = new KPropertiesDialog(url, nullptr /*no parent*/);
369 QObject::connect(dlg, &QObject::destroyed, qApp, &QCoreApplication::quit);
370 QObject::connect(dlg, &KPropertiesDialog::canceled, this, &ClientApp::slotDialogCanceled);
371 dlg->show();
372
373 qApp->exec();
374 return m_ok;
375 } else
376 #endif
377 if (command == QLatin1String("cat")) {
378 checkArgumentCount(argc, 2, 2); // cat <url>
379 const QUrl url = makeURL(parser.positionalArguments().constLast());
380
381 KIO::TransferJob *job = KIO::get(url, KIO::NoReload, s_jobFlags);
382 if (!s_interactive) {
383 job->setUiDelegate(nullptr);
384 job->setUiDelegateExtension(nullptr);
385 }
386 connect(job, &KIO::TransferJob::data, this, &ClientApp::slotPrintData);
387 connect(job, &KJob::result, this, &ClientApp::slotResult);
388
389 qApp->exec();
390 return m_ok;
391 }
392 #ifndef KIOCORE_ONLY
393 else if (command == QLatin1String("exec")) {
394 checkArgumentCount(argc, 2, 3);
395 return kde_open(parser.positionalArguments().at(1), argc == 3 ? parser.positionalArguments().constLast() : QString(), true);
396 } else if (command == QLatin1String("appmenu")) {
397 auto *job = new KIO::ApplicationLauncherJob();
398 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, nullptr));
399 connect(job, &KJob::result, this, &ClientApp::slotResult);
400 job->start();
401
402 qApp->exec();
403 return m_ok;
404 }
405 #endif
406 else if (command == QLatin1String("download")) {
407 checkArgumentCount(argc, 0, 0);
408 QStringList args = parser.positionalArguments();
409 args.removeFirst();
410 const QList<QUrl> srcLst = makeUrls(args);
411
412 if (srcLst.isEmpty()) {
413 return m_ok;
414 }
415
416 const QUrl dsturl = QFileDialog::getSaveFileUrl(nullptr, i18n("Destination where to download the files"), srcLst.at(0));
417
418 if (dsturl.isEmpty()) { // canceled
419 return m_ok; // AK - really okay?
420 }
421
422 KIO::Job *job = KIO::copy(srcLst, dsturl, s_jobFlags);
423 if (!s_interactive) {
424 job->setUiDelegate(nullptr);
425 job->setUiDelegateExtension(nullptr);
426 }
427
428 connect(job, &KJob::result, this, &ClientApp::slotResult);
429
430 qApp->exec();
431 return m_ok;
432 } else if (command == QLatin1String("copy") || command == QLatin1String("cp")) {
433 checkArgumentCount(argc, 3, 0); // cp <src> <dest>
434 QStringList args = parser.positionalArguments();
435 args.removeFirst();
436 return doCopy(args);
437 } else if (command == QLatin1String("move") || command == QLatin1String("mv")) {
438 checkArgumentCount(argc, 3, 0); // mv <src> <dest>
439 QStringList args = parser.positionalArguments();
440 args.removeFirst();
441 return doMove(args);
442 } else if (command == QLatin1String("list") || command == QLatin1String("ls")) {
443 checkArgumentCount(argc, 2, 2); // ls <url>
444 QStringList args = parser.positionalArguments();
445 args.removeFirst();
446 return doList(args);
447 } else if (command == QLatin1String("remove") || command == QLatin1String("rm")) {
448 checkArgumentCount(argc, 2, 0); // rm <url>
449 QStringList args = parser.positionalArguments();
450 args.removeFirst();
451 return doRemove(args);
452 } else if (command == QLatin1String("stat")) {
453 checkArgumentCount(argc, 2, 2); // stat <url>
454 QStringList args = parser.positionalArguments();
455 args.removeFirst();
456 return doStat(args);
457 } else {
458 fputs(i18nc("@info:shell", "%1: Syntax error, unknown command '%2'\n", qAppName(), command).toLocal8Bit().data(), stderr);
459 return false;
460 }
461 Q_UNREACHABLE();
462 #endif
463 }
464
slotResult(KJob * job)465 void ClientApp::slotResult(KJob *job)
466 {
467 if (job->error()) {
468 #ifndef KIOCORE_ONLY
469 if (s_interactive) {
470 static_cast<KIO::Job *>(job)->uiDelegate()->showErrorMessage();
471 } else
472 #endif
473 {
474 fputs(qPrintable(i18nc("@info:shell", "%1: %2\n", qAppName(), job->errorString())), stderr);
475 }
476 }
477 m_ok = !job->error();
478 if (qApp->topLevelWindows().isEmpty()) {
479 qApp->quit();
480 } else {
481 qApp->setQuitOnLastWindowClosed(true);
482 }
483 }
484
slotDialogCanceled()485 void ClientApp::slotDialogCanceled()
486 {
487 m_ok = false;
488 qApp->quit();
489 }
490
slotPrintData(KIO::Job *,const QByteArray & data)491 void ClientApp::slotPrintData(KIO::Job *, const QByteArray &data)
492 {
493 if (!data.isEmpty()) {
494 std::cout.write(data.constData(), data.size());
495 }
496 }
497
showStatField(const KIO::UDSEntry & entry,uint field,const char * name)498 static void showStatField(const KIO::UDSEntry &entry, uint field, const char *name)
499 {
500 if (!entry.contains(field))
501 return;
502 std::cout << qPrintable(QString::fromLocal8Bit(name).leftJustified(20, ' ')) << " ";
503
504 if (field == KIO::UDSEntry::UDS_ACCESS) {
505 std::cout << qPrintable(QString("0%1").arg(entry.numberValue(field), 3, 8, QLatin1Char('0')));
506 } else if (field == KIO::UDSEntry::UDS_FILE_TYPE) {
507 std::cout << qPrintable(QString("0%1").arg((entry.numberValue(field) & S_IFMT), 6, 8, QLatin1Char('0')));
508 } else if (field & KIO::UDSEntry::UDS_STRING) {
509 std::cout << qPrintable(entry.stringValue(field));
510 } else if ((field & KIO::UDSEntry::UDS_TIME) == KIO::UDSEntry::UDS_TIME) {
511 // The previous comparison is necessary because the value
512 // of UDS_TIME is 0x04000000|UDS_NUMBER which is 0x06000000.
513 // So simply testing with (field & KIO::UDSEntry::UDS_TIME)
514 // would be true for both UDS_TIME and UDS_NUMBER fields.
515 // The same would happen if UDS_NUMBER were tested first.
516 const QDateTime dt = QDateTime::fromSecsSinceEpoch(entry.numberValue(field));
517 if (dt.isValid())
518 std::cout << qPrintable(dt.toString(Qt::TextDate));
519 } else if (field & KIO::UDSEntry::UDS_NUMBER) {
520 std::cout << entry.numberValue(field);
521 }
522 std::cout << std::endl;
523 }
524
slotStatResult(KJob * job)525 void ClientApp::slotStatResult(KJob *job)
526 {
527 if (!job->error()) {
528 KIO::StatJob *statJob = qobject_cast<KIO::StatJob *>(job);
529 Q_ASSERT(statJob != nullptr);
530 const KIO::UDSEntry &result = statJob->statResult();
531
532 showStatField(result, KIO::UDSEntry::UDS_NAME, "NAME");
533 showStatField(result, KIO::UDSEntry::UDS_DISPLAY_NAME, "DISPLAY_NAME");
534 showStatField(result, KIO::UDSEntry::UDS_COMMENT, "COMMENT");
535 showStatField(result, KIO::UDSEntry::UDS_SIZE, "SIZE");
536 // This is not requested for the StatJob, so should never be seen
537 showStatField(result, KIO::UDSEntry::UDS_RECURSIVE_SIZE, "RECURSIVE_SIZE");
538
539 showStatField(result, KIO::UDSEntry::UDS_FILE_TYPE, "FILE_TYPE");
540 showStatField(result, KIO::UDSEntry::UDS_USER, "USER");
541 showStatField(result, KIO::UDSEntry::UDS_GROUP, "GROUP");
542 showStatField(result, KIO::UDSEntry::UDS_HIDDEN, "HIDDEN");
543 showStatField(result, KIO::UDSEntry::UDS_DEVICE_ID, "DEVICE_ID");
544 showStatField(result, KIO::UDSEntry::UDS_INODE, "INODE");
545
546 showStatField(result, KIO::UDSEntry::UDS_LINK_DEST, "LINK_DEST");
547 showStatField(result, KIO::UDSEntry::UDS_URL, "URL");
548 showStatField(result, KIO::UDSEntry::UDS_LOCAL_PATH, "LOCAL_PATH");
549 showStatField(result, KIO::UDSEntry::UDS_TARGET_URL, "TARGET_URL");
550
551 showStatField(result, KIO::UDSEntry::UDS_MIME_TYPE, "MIME_TYPE");
552 showStatField(result, KIO::UDSEntry::UDS_GUESSED_MIME_TYPE, "GUESSED_MIME_TYPE");
553
554 showStatField(result, KIO::UDSEntry::UDS_ICON_NAME, "ICON_NAME");
555 showStatField(result, KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES, "ICON_OVERLAY_NAMES");
556
557 showStatField(result, KIO::UDSEntry::UDS_ACCESS, "ACCESS");
558 showStatField(result, KIO::UDSEntry::UDS_EXTENDED_ACL, "EXTENDED_ACL");
559 showStatField(result, KIO::UDSEntry::UDS_ACL_STRING, "ACL_STRING");
560 showStatField(result, KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, "DEFAULT_ACL_STRING");
561
562 showStatField(result, KIO::UDSEntry::UDS_MODIFICATION_TIME, "MODIFICATION_TIME");
563 showStatField(result, KIO::UDSEntry::UDS_ACCESS_TIME, "ACCESS_TIME");
564 showStatField(result, KIO::UDSEntry::UDS_CREATION_TIME, "CREATION_TIME");
565
566 showStatField(result, KIO::UDSEntry::UDS_XML_PROPERTIES, "XML_PROPERTIES");
567 showStatField(result, KIO::UDSEntry::UDS_DISPLAY_TYPE, "DISPLAY_TYPE");
568 }
569
570 slotResult(job);
571 }
572
ClientApp()573 ClientApp::ClientApp()
574 : QObject()
575 {
576 }
577