File indexing completed on 2024-12-01 09:55:03

0001 /*
0002     SPDX-FileCopyrightText: 2007-2011 Aaron Seigo <aseigo@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KPACKAGE_PACKAGE_H
0008 #define KPACKAGE_PACKAGE_H
0009 
0010 #include <QCryptographicHash>
0011 #include <QMetaType>
0012 #include <QStringList>
0013 #include <QUrl>
0014 
0015 #include <KPluginMetaData>
0016 
0017 #include <kpackage/package_export.h>
0018 
0019 #include <KJob>
0020 
0021 namespace KPackage
0022 {
0023 /**
0024  * @class Package kpackage/package.h <KPackage/Package>
0025  *
0026  * @short object representing an installed package
0027  *
0028  * Package defines what is in a package and provides easy access to the contents.
0029  *
0030  * To define a package, one might write the following code:
0031  *
0032  @code
0033     Package package;
0034 
0035     package.addDirectoryDefinition("images", "pics/");
0036     QStringList mimeTypes;
0037     mimeTypes << "image/svg" << "image/png" << "image/jpeg";
0038     package.setMimeTypes("images", mimeTypes);
0039 
0040     package.addDirectoryDefinition("scripts", "code/");
0041     mimeTypes.clear();
0042     mimeTypes << "text/\*";
0043     package.setMimeTypes("scripts", mimeTypes);
0044 
0045     package.addFileDefinition("mainscript", "code/main.js");
0046     package.setRequired("mainscript", true);
0047  @endcode
0048  * One may also choose to create a subclass of PackageStructure and include the setup
0049  * in the constructor.
0050  *
0051  * Either way, Package creates a self-documenting contract between the packager and
0052  * the application without exposing package internals such as actual on-disk structure
0053  * of the package or requiring that all contents be explicitly known ahead of time.
0054  *
0055  * Subclassing PackageStructure does have provide a number of potential const benefits:
0056  *    * the package can be notified of path changes via the virtual pathChanged() method
0057  *    * the subclass may implement mechanisms to install and remove packages using the
0058  *      virtual install and uninstall methods
0059  *    * subclasses can be compiled as plugins for easy re-use
0060  **/
0061 // TODO: write documentation on USING a package
0062 
0063 class PackagePrivate;
0064 class PackageStructure;
0065 
0066 class KPACKAGE_EXPORT Package
0067 {
0068 public:
0069     /**
0070      * Error codes for the install/update/remove jobs
0071      * @since 5.17
0072      */
0073     enum JobError {
0074         RootCreationError = KJob::UserDefinedError + 1, /**< Cannot create package root directory */
0075         PackageFileNotFoundError, /**< The package file does not exist */
0076         UnsupportedArchiveFormatError, /**< The archive format of the package is not supported */
0077         PackageOpenError, /**< Can't open the package file for reading */
0078         MetadataFileMissingError, /**< The package doesn't have a metadata.desktop file */
0079         PluginNameMissingError, /**< The metadata.desktop file doesn't specify a plugin name */
0080         PluginNameInvalidError, /**< The plugin name contains characters different from letters, digits, dots and underscores */
0081         UpdatePackageTypeMismatchError, /**< A package with this plugin name was already installed, but has a different type in the metadata.desktop file */
0082         OldVersionRemovalError, /**< Failed to remove the old version of the package during an upgrade */
0083         NewerVersionAlreadyInstalledError, /**< We tried to update, but the same version or a newer one is already installed */
0084         PackageAlreadyInstalledError, /**< The package is already installed and a normal install (not update) was performed */
0085         PackageMoveError, /**< Failure to move a package from the system temporary folder to its final destination */
0086         PackageCopyError, /**< Failure to copy a package folder from somewhere in the filesystem to its final destination */
0087         PackageUninstallError, /**< Failure to uninstall a package */
0088     };
0089 
0090     /**
0091      * Default constructor
0092      *
0093      * @param structure if a null pointer is passed in, this will creates an empty (invalid) Package;
0094      * otherwise the structure is allowed to set up the Package's initial layout
0095      * @since 4.6
0096      */
0097     explicit Package(PackageStructure *structure = nullptr);
0098 
0099     /**
0100      * Copy constructor
0101      * @since 4.6
0102      */
0103     Package(const Package &other);
0104 
0105     virtual ~Package();
0106 
0107     /**
0108      * Assignment operator
0109      * @since 4.6
0110      */
0111     Package &operator=(const Package &rhs);
0112 
0113     /**
0114      * @return true if this package has a valid PackageStructure associatedw it with it.
0115      * A package may not be valid, but have a valid structure. Useful when dealing with
0116      * Package objects in a semi-initialized state (e.g. before calling setPath())
0117      * @since 5.1
0118      */
0119     bool hasValidStructure() const;
0120 
0121     /**
0122      * @return true if all the required components exist
0123      **/
0124     bool isValid() const;
0125 
0126     /**
0127      * Sets the path to the root of this package
0128      * @param path an absolute path, or a relative path to the default package root
0129      * @since 4.3
0130      */
0131     void setPath(const QString &path);
0132 
0133     /**
0134      * @return the path to the root of this particular package
0135      */
0136     const QString path() const;
0137 
0138     /**
0139      * Get the path to a given file based on the key and an optional filename.
0140      * Example: finding the main script in a scripting package:
0141      *      filePath("mainscript")
0142      *
0143      * Example: finding a specific image in the images directory:
0144      *      filePath("images", "myimage.png")
0145      *
0146      * @param key the key of the file type to look for,
0147      * @param filename optional name of the file to locate within the package
0148      * @return path to the file on disk. QString() if not found.
0149      **/
0150     QString filePath(const QByteArray &key, const QString &filename = QString()) const;
0151 
0152     /**
0153      * Get the url to a given file based on the key and an optional filename, is the file:// or qrc:// format
0154      * Example: finding the main script in a scripting package:
0155      *      filePath("mainscript")
0156      *
0157      * Example: finding a specific image in the images directory:
0158      *      filePath("images", "myimage.png")
0159      *
0160      * @param key the key of the file type to look for,
0161      * @param filename optional name of the file to locate within the package
0162      * @return path to the file on disk. QString() if not found.
0163      * @since 5.41
0164      **/
0165     QUrl fileUrl(const QByteArray &key, const QString &filename = QString()) const;
0166 
0167     /**
0168      * Get the list of files of a given type.
0169      *
0170      * @param fileType the type of file to look for, as defined in the
0171      *               package structure.
0172      * @return list of files by name, suitable for passing to filePath
0173      **/
0174     QStringList entryList(const QByteArray &key) const;
0175 
0176 #if KPACKAGE_ENABLE_DEPRECATED_SINCE(5, 106)
0177     /**
0178      * @return user visible name for the given entry
0179      * @deprecated Since 5.106, deprecated for lack of usage
0180      **/
0181     KPACKAGE_DEPRECATED_VERSION(5, 106, "deprecated for lack of usage")
0182     QString name(const QByteArray &key) const;
0183 #endif
0184 
0185     /**
0186      * @return true if the item at path exists and is required
0187      **/
0188     bool isRequired(const QByteArray &key) const;
0189 
0190     /**
0191      * @return the mimeTypes associated with the path, if any
0192      **/
0193     QStringList mimeTypes(const QByteArray &key) const;
0194 
0195     /**
0196      * @return the prefix paths inserted between the base path and content entries, in order of priority.
0197      *         When searching for a file, all paths will be tried in order.
0198      * @since 4.6
0199      */
0200     QStringList contentsPrefixPaths() const;
0201 
0202     /**
0203      * @return preferred package root. This defaults to kpackage/generic/
0204      */
0205     QString defaultPackageRoot() const;
0206 
0207     /**
0208      * @return true if paths/symlinks outside the package itself should be followed.
0209      * By default this is set to false for security reasons.
0210      */
0211     bool allowExternalPaths() const;
0212 
0213     /**
0214      * Sets the metadata for the KPackage. This overwrites the current metadata.
0215      * This should be used in case a kpackage gets loaded by name, based
0216      * on the path a C++ plugin which has embedded metadata.
0217      * @since 5.88
0218      */
0219     void setMetadata(const KPluginMetaData &data);
0220 
0221     /**
0222      * @return the package metadata object.
0223      */
0224     KPluginMetaData metadata() const;
0225 
0226 #if KPACKAGE_ENABLE_DEPRECATED_SINCE(5, 21)
0227     /**
0228      * @return a SHA1 hash digest of the contents of the package in hexadecimal form
0229      * @since 4.4
0230      * @deprecated Since 5.21 use cryptographicHash
0231      */
0232     KPACKAGE_DEPRECATED_VERSION(5, 21, "Use Package::cryptographicHash(QCryptographicHash::Algorithm)")
0233     QString contentsHash() const;
0234 #endif
0235 
0236     /**
0237      * @return a hash digest of the contents of the package in hexadecimal form
0238      * @since 5.21
0239      */
0240     QByteArray cryptographicHash(QCryptographicHash::Algorithm algorithm) const;
0241 
0242     /**
0243      * Adds a directory to the structure of the package. It is added as
0244      * a not-required element with no associated mimeTypes.
0245      *
0246      * Starting in 4.6, if an entry with the given key
0247      * already exists, the path is added to it as a search alternative.
0248      *
0249      * @param key  used as an internal label for this directory
0250      * @param path the path within the package for this directory
0251      * @param name the user visible (translated) name for the directory, since 5.106 this defaults to an empty string. In KF6, this parameter is removed
0252      **/
0253     void addDirectoryDefinition(const QByteArray &key, const QString &path, const QString &name = QString());
0254 
0255     /**
0256      * Adds a file to the structure of the package. It is added as
0257      * a not-required element with no associated mimeTypes.
0258      *
0259      * Starting in 4.6, if an entry with the given key
0260      * already exists, the path is added to it as a search alternative.
0261      *
0262      * @param key  used as an internal label for this file
0263      * @param path the path within the package for this file
0264      * @param name the user visible (translated) name for the file, since 5.106 this defaults to an empty string. In KF6, this parameter is removed
0265      **/
0266     void addFileDefinition(const QByteArray &key, const QString &path, const QString &name = QString());
0267 
0268     /**
0269      * Removes a definition from the structure of the package.
0270      * @since 4.6
0271      * @param key the internal label of the file or directory to remove
0272      */
0273     void removeDefinition(const QByteArray &key);
0274 
0275     /**
0276      * Sets whether or not a given part of the structure is required or not.
0277      * The path must already have been added using addDirectoryDefinition
0278      * or addFileDefinition.
0279      *
0280      * @param key the entry within the package
0281      * @param required true if this entry is required, false if not
0282      */
0283     void setRequired(const QByteArray &key, bool required);
0284 
0285     /**
0286      * Defines the default mimeTypes for any definitions that do not have
0287      * associated mimeTypes. Handy for packages with only one or predominantly
0288      * one file type.
0289      *
0290      * @param mimeTypes a list of mimeTypes
0291      **/
0292     void setDefaultMimeTypes(const QStringList &mimeTypes);
0293 
0294     /**
0295      * Define mimeTypes for a given part of the structure
0296      * The path must already have been added using addDirectoryDefinition
0297      * or addFileDefinition.
0298      *
0299      * @param key the entry within the package
0300      * @param mimeTypes a list of mimeTypes
0301      **/
0302     void setMimeTypes(const QByteArray &key, const QStringList &mimeTypes);
0303 
0304     /**
0305      * Sets the prefixes that all the contents in this package should
0306      * appear under. This defaults to "contents/" and is added automatically
0307      * between the base path and the entries as defined by the package
0308      * structure. Multiple entries can be added.
0309      * In this case each file request will be searched in all prefixes in order,
0310      * and the first found will be returned.
0311      *
0312      * @param prefix paths the directory prefix to use
0313      * @since 4.6
0314      */
0315     void setContentsPrefixPaths(const QStringList &prefixPaths);
0316 
0317     /**
0318      * Sets whether or not external paths/symlinks can be followed by a package
0319      * @param allow true if paths/symlinks outside of the package should be followed,
0320      *             false if they should be rejected.
0321      */
0322     void setAllowExternalPaths(bool allow);
0323 
0324     /**
0325      * Sets preferred package root.
0326      */
0327     void setDefaultPackageRoot(const QString &packageRoot);
0328 
0329     /**
0330      * Sets the fallback package root path
0331      * If a file won't be found in this package, it will search it in the package
0332      * with the same structure identified by path
0333      * It is intended to be used by the packageStructure
0334      * @param path package root path @see setPath
0335      */
0336     void setFallbackPackage(const KPackage::Package &package);
0337 
0338     /**
0339      * @return The fallback package root path
0340      */
0341     KPackage::Package fallbackPackage() const;
0342 
0343     // Content structure description methods
0344     /**
0345      * @return all directories registered as part of this Package's structure
0346      */
0347     QList<QByteArray> directories() const;
0348 
0349     /**
0350      * @return all directories registered as part of this Package's required structure
0351      */
0352     QList<QByteArray> requiredDirectories() const;
0353 
0354     /**
0355      * @return all files registered as part of this Package's structure
0356      */
0357     QList<QByteArray> files() const;
0358 
0359     /**
0360      * @return all files registered as part of this Package's required structure
0361      */
0362     QList<QByteArray> requiredFiles() const;
0363 
0364     /**
0365      * Installs a package matching this package structure. By default installs a
0366      * native KPackage::Package.
0367      * After the KJob will emitted finished(), if the install went well
0368      * the Package instance will be guaranteed to have loaded the package just
0369      * installed, and be valid and usable.
0370      *
0371      * @return KJob to track installation progress and result
0372      **/
0373     KJob *install(const QString &sourcePackage, const QString &packageRoot = QString());
0374 
0375     /**
0376      * Updates a package matching this package structure. By default installs a
0377      * native KPackage::Package. If an older version is already installed,
0378      * it removes the old one. If the installed one is newer,
0379      * an error will occur.
0380      * After the KJob will emitted finished(), if the install went well
0381      * the Package instance will be guaranteed to have loaded the package just
0382      * updated, and be valid and usable.
0383      *
0384      * @return KJob to track installation progress and result
0385      * @since 5.17
0386      **/
0387     KJob *update(const QString &sourcePackage, const QString &packageRoot = QString());
0388 
0389     /**
0390      * Uninstalls a package matching this package structure.
0391      *
0392      * @return KJob to track removal progress and result
0393      */
0394     KJob *uninstall(const QString &packageName, const QString &packageRoot);
0395 
0396 private:
0397     QExplicitlySharedDataPointer<PackagePrivate> d;
0398     friend class PackagePrivate;
0399 };
0400 
0401 }
0402 
0403 Q_DECLARE_METATYPE(KPackage::Package)
0404 #endif