1 /*
2     This file is part of the KDE project
3     SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
4     SPDX-FileCopyrightText: 1999-2006 David Faure <faure@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #ifndef KSERVICE_H
10 #define KSERVICE_H
11 
12 #include "kserviceaction.h"
13 #include <KPluginFactory>
14 #include <KPluginLoader>
15 #include <QCoreApplication>
16 #include <QStringList>
17 #include <QVariant>
18 #include <ksycocaentry.h>
19 
20 class KServiceType;
21 class QDataStream;
22 class KDesktopFile;
23 class QWidget;
24 
25 class KServicePrivate;
26 
27 /**
28  * @class KService kservice.h <KService>
29  *
30  * Represent a service, like an application or plugin
31  * bound to one or several MIME types (or servicetypes) as written
32  * in its desktop entry file.
33  *
34  * The starting point you need is often the static methods, like createInstance().
35  * The types of service a plugin provides is taken from the accompanying desktop file
36  * where the 'X-KDE-ServiceTypes=' field is used.
37  *
38  * For a tutorial on how to build a plugin-loading mechanism and how to write plugins
39  * in general, see http://techbase.kde.org/Development/Tutorials#Services:_Applications_and_Plugins
40  *
41  * @see KServiceType
42  * @see KServiceGroup
43  * @author Torben Weis
44  */
45 class KSERVICE_EXPORT KService : public KSycocaEntry
46 {
47 public:
48     /**
49      * A shared data pointer for KService.
50      */
51     typedef QExplicitlySharedDataPointer<KService> Ptr;
52     /**
53      * A list of shared data pointers for KService.
54      */
55     typedef QList<Ptr> List;
56 
57     /**
58      * Construct a temporary service with a given name, exec-line and icon.
59      * @param name the name of the service
60      * @param exec the executable
61      * @param icon the name of the icon
62      */
63     KService(const QString &name, const QString &exec, const QString &icon);
64 
65     /**
66      * Construct a service and take all information from a config file.
67      *
68      * @param fullpath Full path to the config file.
69      */
70     explicit KService(const QString &fullpath);
71 
72     /**
73      * Construct a service and take all information from a desktop file.
74      * @param config the desktop file to read
75      * @param optional relative path to store for findByName
76      */
77     explicit KService(const KDesktopFile *config, const QString &entryPath = QString());
78 
79     KService(const KService &other);
80 
81     ~KService() override;
82 
83     /**
84      * Services are either applications (executables) or dlopened libraries (plugins).
85      * @return true if this service is an application, i.e. it has Type=Application in its
86      * .desktop file and exec() will not be empty.
87      */
88     bool isApplication() const;
89 
90     /**
91      * Returns the executable.
92      * @return the command that the service executes,
93      *         or QString() if not set
94      */
95     QString exec() const;
96     /**
97      * Returns the name of the service's library.
98      * @return the name of the library that contains the service's
99      *         implementation, or QString() if not set
100      */
101     QString library() const;
102 
103     /**
104      * Returns the name of the icon.
105      * @return the icon associated with the service,
106      *         or QString() if not set
107      */
108     QString icon() const;
109     /**
110      * Checks whether the service should be run in a terminal.
111      * @return true if the service is to be run in a terminal.
112      */
113     bool terminal() const;
114 
115     /**
116      * Returns any options associated with the terminal the service
117      * runs in, if it requires a terminal.
118      *
119      * The service must be a tty-oriented program.
120      * @return the terminal options,
121      *         or QString() if not set
122      */
123     QString terminalOptions() const;
124 
125     /**
126      * Returns @c true if the service inidicates that it's preferred to run
127      * the application on a discrete graphics card, otherwise return @c false.
128      *
129      * In releases older than 5.86 this methoed checked for the @c X-KDE-RunOnDiscreteGpu
130      * key in the .desktop file represented by this service; starting from 5.86 this method
131      * now also checks for @c PrefersNonDefaultGPU key (added to the Freedesktop.org desktop
132      * entry spec in version 1.4 of the spec).
133      *
134      * @since 5.30
135      */
136     bool runOnDiscreteGpu() const;
137 
138     /**
139      * Checks whether the service runs with a different user id.
140      * @return true if the service has to be run under a different uid.
141      * @see username()
142      */
143     bool substituteUid() const;
144     /**
145      * Returns the user name, if the service runs with a
146      * different user id.
147      * @return the username under which the service has to be run,
148      *         or QString() if not set
149      * @see substituteUid()
150      */
151     QString username() const;
152 
153     /**
154      * Returns the filename of the service desktop entry without any
155      * extension. E.g. "kppp"
156      * @return the name of the desktop entry without path or extension,
157      *         or QString() if not set
158      */
159     QString desktopEntryName() const;
160 
161     /**
162      * Returns the menu ID of the service desktop entry.
163      * The menu ID is used to add or remove the entry to a menu.
164      * @return the menu ID
165      */
166     QString menuId() const;
167 
168     /**
169      * Returns a normalized ID suitable for storing in configuration files.
170      * It will be based on the menu-id when available and otherwise falls
171      * back to entryPath()
172      * @return the storage ID
173      */
174     QString storageId() const;
175 
176     /**
177      * Describes the D-Bus Startup type of the service.
178      * @li None - This service has no D-Bus support
179      * @li Unique - This service provides a unique D-Bus service.
180      *              The service name is equal to the desktopEntryName.
181      * @li Multi - This service provides a D-Bus service which can be run
182      *             with multiple instances in parallel. The service name of
183      *             an instance is equal to the desktopEntryName + "-" +
184      *             the PID of the process.
185      */
186     enum DBusStartupType { DBusNone = 0, DBusUnique, DBusMulti };
187 
188     /**
189      * Returns the DBUSStartupType supported by this service.
190      * @return the DBUSStartupType supported by this service
191      */
192     DBusStartupType dbusStartupType() const;
193 
194 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 63)
195     /**
196      * @return the working directory to run the program in,
197      *         or QString() if not set
198      * @deprecated since 5.63, use workingDirectory() instead
199      */
200     KSERVICE_DEPRECATED_VERSION(5, 63, "Use KService::workingDirectory()")
201     QString path() const;
202 #endif
203 
204     /**
205      * @return the working directory to run the program in,
206      *         or QString() if not set
207      * @since 5.63
208      */
209     QString workingDirectory() const;
210 
211     /**
212      * Returns the descriptive comment for the service, if there is one.
213      * @return the descriptive comment for the service, or QString()
214      *         if not set
215      */
216     QString comment() const;
217 
218     /**
219      * Returns the generic name for the service, if there is one
220      * (e.g. "Mail Client").
221      * @return the generic name,
222      *         or QString() if not set
223      */
224     QString genericName() const;
225 
226     /**
227      * Returns the untranslated (US English) generic name
228      * for the service, if there is one
229      * (e.g. "Mail Client").
230      * @return the generic name,
231      *         or QString() if not set
232      */
233     QString untranslatedGenericName() const;
234 
235     /**
236      * Returns a list of descriptive keywords the service, if there are any.
237      * @return the list of keywords
238      */
239     QStringList keywords() const;
240 
241     /**
242      * Returns a list of VFolder categories.
243      * @return the list of VFolder categories
244      */
245     QStringList categories() const;
246 
247     /**
248      * Returns the list of MIME types that this service supports.
249      * Note that this doesn't include inherited MIME types,
250      * only the MIME types types listed in the .desktop file.
251      * @since 4.8.3
252      */
253     QStringList mimeTypes() const;
254 
255     /**
256      * Returns the service types that this service supports.
257      * @return the list of service types that are supported
258      * Note that this doesn't include inherited servicetypes or MIME types,
259      * only the service types listed in the .desktop file.
260      */
261     QStringList serviceTypes() const;
262 
263     /**
264      * Checks whether the service supports this service type
265      * @param serviceTypePtr The name of the service type you are
266      *        interested in determining whether this service supports.
267      *
268      * @return true if the service type you specified is supported, otherwise false.
269      */
270     bool hasServiceType(const QString &serviceTypePtr) const;
271 
272     /**
273      * Checks whether the service supports this MIME type
274      * @param mimeType The name of the MIME type you are
275      *        interested in determining whether this service supports.
276      * @since 4.6
277      */
278     bool hasMimeType(const QString &mimeType) const;
279 
280 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 67)
281     /**
282      * Set to true if it is allowed to use this service as the default (main)
283      * action for the files it supports (e.g. Left Click in a file manager, or KRun in general).
284      *
285      * If not, then this service is only available in RMB popups, so it must
286      * be selected explicitly by the user in order to be used.
287      * Note that servicemenus supersede this functionality though, at least in konqueror.
288      *
289      * @return true if the service may be used as the default (main) handler
290      * @deprecated since 5.67 due to no known use case
291      */
292     KSERVICE_DEPRECATED_VERSION(5, 67, "No known use case")
293     bool allowAsDefault() const;
294 #endif
295 
296     /**
297      * Returns the actions defined in this desktop file
298      */
299     QList<KServiceAction> actions() const;
300 
301     /**
302      * Checks whether this service can handle several files as
303      * startup arguments.
304      * @return true if multiple files may be passed to this service at
305      * startup. False if only one file at a time may be passed.
306      */
307     bool allowMultipleFiles() const;
308 
309     /**
310      * What preference to associate with this service initially (before
311      * the user has had any chance to define a profile for it).
312      * The bigger the value, the most preferred the service is.
313      * @return the service preference level of the service
314      */
315     int initialPreference() const;
316 
317     /**
318      * Whether the entry should be suppressed in the K menu.
319      * @return true to suppress this service
320      *
321      * Such services still appear in trader queries, i.e. in
322      * "Open With" popup menus for instance.
323      */
324     bool noDisplay() const;
325 
326 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 0)
327     /**
328      * Whether the service should be shown in KDE at all
329      * (including in context menus).
330      * @return true if the service should be shown.
331      *
332      * KMimeTypeTrader honors this and removes such services
333      * from its results.
334      *
335      * @since 4.5
336      * @deprecated since 5.0, use showInCurrentDesktop()
337      */
338     KSERVICE_DEPRECATED_VERSION(5, 0, "Use KService::showInCurrentDesktop()")
showInKDE()339     bool showInKDE() const
340     {
341         return showInCurrentDesktop();
342     }
343 #endif
344 
345     /**
346      * Whether the service should be shown in the current desktop
347      * (including in context menus).
348      * @return true if the service should be shown.
349      *
350      * KApplicationTrader honors this and removes such services
351      * from its results.
352      *
353      * @since 5.0
354      */
355     bool showInCurrentDesktop() const;
356 
357     /**
358      * Whether the service should be shown on the current
359      * platform (e.g. on xcb or on wayland).
360      * @return true if the service should be shown
361      *
362      * @since 5.0
363      */
364     bool showOnCurrentPlatform() const;
365 
366 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 87)
367     /**
368      * Name of the application this service belongs to.
369      * (Useful for e.g. plugins)
370      * @return the parent application, or QString() if not set
371      * @deprecated Since 5.87, the concept of parent apps is deprecated. Plugins should use KPluginMetaData instead of
372      * KService and a dedicated namespace if the plugins are only for one app relevant.
373      */
374     KSERVICE_DEPRECATED_VERSION(5, 87, "See API docs")
375     QString parentApp() const;
376 #endif
377 
378 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 87)
379     /**
380      * The keyword to be used when constructing the plugin using KPluginFactory. The keyword is
381      * defined with X-KDE-PluginKeyword in the .desktop file and with registerPlugin<T>(keyword)
382      * in the K_PLUGIN_FACTORY macro when implementing the plugin.
383      * @deprecated Since 5.87, the metadata should be embedded in the actual plugin. Consequently
384      * this property is obsolete. In case there is only one plugin in the library the usage is not needed.
385      * In case there are different base classes registered the keyword is not needed too.
386      * If there are multiple classes of a common base class registered they should be split up
387      * in separate libs.
388      */
389     KSERVICE_DEPRECATED_VERSION(5, 87, "See API docs")
390     QString pluginKeyword() const;
391 #endif
392 
393     /**
394      * The path to the documentation for this service.
395      * @since 4.2
396      * @return the documentation path, or QString() if not set
397      */
398     QString docPath() const;
399 
400     /**
401      * Returns the requested property.
402      *
403      * @param _name the name of the property
404      * @param t the assumed type of the property
405      * @return the property, or invalid if not found
406      * @see KServiceType
407      */
408     QVariant property(const QString &_name, QVariant::Type t) const;
409 
410     using KSycocaEntry::property;
411 
412     /**
413      * Returns a path that can be used for saving changes to this
414      * service
415      * @return path that can be used for saving changes to this service
416      */
417     QString locateLocal() const;
418 
419     /**
420      * @internal
421      * Set the menu id
422      */
423     void setMenuId(const QString &menuId);
424     /**
425      * @internal
426      * Sets whether to use a terminal or not
427      */
428     void setTerminal(bool b);
429     /**
430      * @internal
431      * Sets the terminal options to use
432      */
433     void setTerminalOptions(const QString &options);
434 
435     /**
436      * Overrides the "Exec=" line of the service.
437      *
438      * If @ref exec is not empty, its value will override the one
439      * the one set when this service was created.
440      *
441      * Please note that @ref entryPath is also cleared so the service
442      * will no longer be associated with a specific config file.
443      *
444      * @internal
445      * @since 4.11
446      */
447     void setExec(const QString &exec);
448 
449     /**
450      * Overrides the "Path=" line of the service.
451      *
452      * If @ref workingDir is not empty, its value will override the one
453      * the one set when this service was created.
454      *
455      * Please note that @ref entryPath is also cleared so the service
456      * will no longer be associated with a specific config file.
457      *
458      * @internal
459      * @param workingDir
460      * @since 5.79
461      */
462     void setWorkingDirectory(const QString &workingDir);
463 
464     /**
465      * Find a service based on its path as returned by entryPath().
466      * It's usually better to use serviceByStorageId() instead.
467      *
468      * @param _path the path of the configuration file
469      * @return a pointer to the requested service or @c nullptr if the service is
470      *         unknown.
471      * @em Very @em important: Don't store the result in a KService* !
472      */
473     static Ptr serviceByDesktopPath(const QString &_path);
474 
475     /**
476      * Find a service by the name of its desktop file, not depending on
477      * its actual location (as long as it's under the applications or service
478      * directories). For instance "konqbrowser" or "kcookiejar". Note that
479      * the ".desktop" extension is implicit.
480      *
481      * This is the recommended method (safe even if the user moves stuff)
482      * but note that it assumes that no two entries have the same filename.
483      *
484      * @param _name the name of the configuration file
485      * @return a pointer to the requested service or @c nullptr if the service is
486      *         unknown.
487      * @em Very @em important: Don't store the result in a KService* !
488      */
489     static Ptr serviceByDesktopName(const QString &_name);
490 
491     /**
492      * Find a service by its menu-id
493      *
494      * @param _menuId the menu id of the service
495      * @return a pointer to the requested service or @c nullptr if the service is
496      *         unknown.
497      * @em Very @em important: Don't store the result in a KService* !
498      */
499     static Ptr serviceByMenuId(const QString &_menuId);
500 
501     /**
502      * Find a service by its storage-id or desktop-file path. This
503      * function will try very hard to find a matching service.
504      *
505      * @param _storageId the storage id or desktop-file path of the service
506      * @return a pointer to the requested service or @c nullptr if the service is
507      *         unknown.
508      * @em Very @em important: Don't store the result in a KService* !
509      */
510     static Ptr serviceByStorageId(const QString &_storageId);
511 
512     /**
513      * Returns the whole list of services.
514      *
515      *  Useful for being able to
516      * to display them in a list box, for example.
517      * More memory consuming than the ones above, don't use unless
518      * really necessary.
519      * @return the list of all services
520      */
521     static List allServices();
522 
523     /**
524      * Returns a path that can be used to create a new KService based
525      * on @p suggestedName.
526      * @param showInMenu true, if the service should be shown in the KDE menu
527      *        false, if the service should be hidden from the menu
528      *        This argument isn't used anymore, use NoDisplay=true to hide the service.
529      * @param suggestedName name to base the file on, if a service with such a
530      *        name already exists, a suffix will be added to make it unique
531      *        (e.g. foo.desktop, foo-1.desktop, foo-2.desktop).
532      * @param menuId If provided, menuId will be set to the menu id to use for
533      *        the KService
534      * @param reservedMenuIds If provided, the path and menu id will be chosen
535      *        in such a way that the new menu id does not conflict with any
536      *        of the reservedMenuIds
537      * @return The path to use for the new KService.
538      */
539     static QString newServicePath(bool showInMenu, const QString &suggestedName, QString *menuId = nullptr, const QStringList *reservedMenuIds = nullptr);
540 
541 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 86)
542 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 86)
543     /**
544      * This template allows to load the library for the specified service and ask the
545      * factory to create an instance of the given template type.
546      *
547      * @param parent The parent object (see QObject constructor)
548      * @param args A list of arguments, passed to the factory and possibly
549      *             to the component (see KPluginFactory)
550      * @param error A pointer to QString which should receive the error description or @c nullptr
551      *
552      * @return A pointer to the newly created object or a null pointer if the
553      *         factory was unable to create an object of the given type.
554      *
555      * @see KPluginFactory::instantiatePlugin
556      * @deprecated Since 5.86, Use KPluginMetaData/KPluginFactory or QPluginloader instead
557      */
558     template<class T>
559     KSERVICE_DEPRECATED_VERSION(5, 86, "Use KPluginMetaData/KPluginFactory or QPluginloader instead")
560     T *createInstance(QObject *parent = nullptr, const QVariantList &args = QVariantList(), QString *error = nullptr) const
561     {
562         return createInstance<T>(nullptr, parent, args, error);
563     }
564 #endif
565 #endif
566 
567 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 86)
568 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 86)
569     /**
570      * This template allows to load the library for the specified service and ask the
571      * factory to create an instance of the given template type.
572      *
573      * @param parentWidget A parent widget for the service. This is used e. g. for parts
574      * @param parent The parent object (see QObject constructor)
575      * @param args A list of arguments, passed to the factory and possibly
576      *             to the component (see KPluginFactory)
577      * @param error A pointer to QString which should receive the error description or @c nullptr
578      *
579      * @return A pointer to the newly created object or a null pointer if the
580      *         factory was unable to create an object of the given type.
581      *
582      * @see KPluginFactory::instantiatePlugin
583      * @deprecated Since 5.86, Use KPluginMetaData/KPluginFactory or QPluginloader instead
584      */
585     template<class T>
586     KSERVICE_DEPRECATED_VERSION(5, 86, "Use KPluginMetaData/KPluginFactory or QPluginloader instead")
587     T *createInstance(QWidget *parentWidget, QObject *parent, const QVariantList &args = QVariantList(), QString *error = nullptr) const
588     {
589         QT_WARNING_PUSH
590         QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
591         QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
592         KPluginLoader pluginLoader(*this);
593         KPluginFactory *factory = pluginLoader.factory();
594         if (factory) {
595             QVariantList argsWithMetaData = args;
596             argsWithMetaData << pluginLoader.metaData().toVariantMap();
597             T *o = factory->template create<T>(parentWidget, parent, pluginKeyword(), argsWithMetaData);
598             if (!o && error)
599                 *error = QCoreApplication::translate("", "The service '%1' does not provide an interface '%2' with keyword '%3'")
600                              .arg(name(), QString::fromLatin1(T::staticMetaObject.className()), pluginKeyword());
601             return o;
602         } else if (error) {
603             *error = pluginLoader.errorString();
604             pluginLoader.unload();
605         }
606         QT_WARNING_POP
607         return nullptr;
608     }
609 #endif
610 #endif
611 
612 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 86)
613 #if KSERVICE_ENABLE_DEPRECATED_SINCE(5, 86)
614     /**
615      * Convert this KService to a KPluginName.
616      *
617      * This allows expressions like
618      * @code
619      * KPluginLoader(service);
620      * @endcode
621      * which will use library() as the name of the plugin.  If the service
622      * is not valid or does not have a library, KPluginLoader::errorString()
623      * will be set appropriately.
624      */
625     operator KPluginName() const;
626 #endif
627 #endif
628 
629 private:
630     friend class KBuildServiceFactory;
631 
632     /// @internal for KBuildSycoca only
633     struct ServiceTypeAndPreference {
ServiceTypeAndPreferenceServiceTypeAndPreference634         ServiceTypeAndPreference()
635             : preference(-1)
636             , serviceType()
637         {
638         }
ServiceTypeAndPreferenceServiceTypeAndPreference639         ServiceTypeAndPreference(int pref, const QString &servType)
640             : preference(pref)
641             , serviceType(servType)
642         {
643         }
644         int preference;
645         QString serviceType; // or MIME type
646     };
647     /// @internal for KBuildSycoca only
648     QVector<ServiceTypeAndPreference> &_k_accessServiceTypes();
649 
650     friend QDataStream &operator>>(QDataStream &, ServiceTypeAndPreference &);
651     friend QDataStream &operator<<(QDataStream &, const ServiceTypeAndPreference &);
652 
653     Q_DECLARE_PRIVATE(KService)
654 
655     friend class KServiceFactory;
656 
657     /**
658      * @internal
659      * Construct a service from a stream.
660      * The stream must already be positioned at the correct offset.
661      */
662     KService(QDataStream &str, int offset);
663 };
664 #endif
665