QDir getExecutableDirectory() { QDir packageDir(QCoreApplication::applicationDirPath()); #ifdef Q_OS_MAC // On macs packageDir points to a location inside the app bundle. // Going up 3 levels gets the dir we want. for(int i = 0; i < 3; ++i) packageDir.cdUp(); #endif return packageDir; }
RemoteInstaller::RemoteInstaller(const QString& username, const QString& hostname, const QString& source, QWidget* parent) : QObject(parent), m_widget(parent) { //like fish://[email protected] m_execUrl.setUrl("fish://" + hostname); m_execUrl.setUserName(username); //this will be out temp directory. First we need to create a temporary file, and then find its //absolute path. //Q: Why don't we use the something like //const QString temporaryDirectory(KStandardDirs::locate("tmp", "") + "plasmaremoteinstaller/"); //A: The following code is running in our system. That means that the temporary directory will be something //like /tmp/kde-$username. If my user is named foo and the other user is named bar //then there is no user foo on the other system. So the /tmp/kde-foo doesn't exist. QString temporaryDirectory = QDir::tempPath(); temporaryDirectory.append("/plasmaremoteinstaller"); QUrl tmpUrl; tmpUrl.setPath(m_execUrl.path() + temporaryDirectory); if (!KIO::NetAccess::exists(tmpUrl, KIO::NetAccess::DestinationSide, m_widget)) { const bool ok = KIO::NetAccess::mkdir(tmpUrl, m_widget); if (!ok) { QString text = i18n("Plasma Remote Installer was not able to create a temporary directory in " "%1, please check the permissions of %1", temporaryDirectory); KMessageBox::error(m_widget, text); return; } } //our destination path m_destinationPath = m_execUrl; m_destinationPath.setPath(temporaryDirectory); //we need our source path m_sourcePath = source; //we will need the dirname of the source QDir packageDir(source); m_plasmaPkgUrl.append("plasmapkg -u " + temporaryDirectory + packageDir.dirName() + '/'); //now do the installation doInstall(); }
void WorkerThread::_PerformInstall(BMenu* srcMenu, BMenu* targetMenu) { CALLED(); BPath targetDirectory; BPath srcDirectory; BPath trashPath; BPath testPath; BDirectory targetDir; BDiskDevice device; BPartition* partition; BVolume targetVolume; status_t err = B_OK; int32 entries = 0; entry_ref testRef; const char* mountError = B_TRANSLATE("The disk can't be mounted. Please " "choose a different disk."); BMessenger messenger(fWindow); ProgressReporter reporter(messenger, new BMessage(MSG_STATUS_MESSAGE)); CopyEngine engine(&reporter); BList unzipEngines; PartitionMenuItem* targetItem = (PartitionMenuItem*)targetMenu->FindMarked(); PartitionMenuItem* srcItem = (PartitionMenuItem*)srcMenu->FindMarked(); if (!srcItem || !targetItem) { ERR("bad menu items\n"); goto error; } // check if target is initialized // ask if init or mount as is if (fDDRoster.GetPartitionWithID(targetItem->ID(), &device, &partition) == B_OK) { if (!partition->IsMounted()) { if ((err = partition->Mount()) < B_OK) { _SetStatusMessage(mountError); ERR("BPartition::Mount"); goto error; } } if ((err = partition->GetVolume(&targetVolume)) != B_OK) { ERR("BPartition::GetVolume"); goto error; } if ((err = partition->GetMountPoint(&targetDirectory)) != B_OK) { ERR("BPartition::GetMountPoint"); goto error; } } else if (fDDRoster.GetDeviceWithID(targetItem->ID(), &device) == B_OK) { if (!device.IsMounted()) { if ((err = device.Mount()) < B_OK) { _SetStatusMessage(mountError); ERR("BDiskDevice::Mount"); goto error; } } if ((err = device.GetVolume(&targetVolume)) != B_OK) { ERR("BDiskDevice::GetVolume"); goto error; } if ((err = device.GetMountPoint(&targetDirectory)) != B_OK) { ERR("BDiskDevice::GetMountPoint"); goto error; } } else goto error; // shouldn't happen // check if target has enough space if ((fSpaceRequired > 0 && targetVolume.FreeBytes() < fSpaceRequired) && ((new BAlert("", B_TRANSLATE("The destination disk may not have " "enough space. Try choosing a different disk or choose to not " "install optional items."), B_TRANSLATE("Try installing anyway"), B_TRANSLATE("Cancel"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { goto error; } if (fDDRoster.GetPartitionWithID(srcItem->ID(), &device, &partition) == B_OK) { if ((err = partition->GetMountPoint(&srcDirectory)) != B_OK) { ERR("BPartition::GetMountPoint"); goto error; } } else if (fDDRoster.GetDeviceWithID(srcItem->ID(), &device) == B_OK) { if ((err = device.GetMountPoint(&srcDirectory)) != B_OK) { ERR("BDiskDevice::GetMountPoint"); goto error; } } else goto error; // shouldn't happen // check not installing on itself if (strcmp(srcDirectory.Path(), targetDirectory.Path()) == 0) { _SetStatusMessage(B_TRANSLATE("You can't install the contents of a " "disk onto itself. Please choose a different disk.")); goto error; } // check not installing on boot volume if ((strncmp(BOOT_PATH, targetDirectory.Path(), strlen(BOOT_PATH)) == 0) && ((new BAlert("", B_TRANSLATE("Are you sure you want to install " "onto the current boot disk? The Installer will have to reboot " "your machine if you proceed."), B_TRANSLATE("OK"), B_TRANSLATE("Cancel"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { _SetStatusMessage("Installation stopped."); goto error; } // check if target volume's trash dir has anything in it // (target volume w/ only an empty trash dir is considered // an empty volume) if (find_directory(B_TRASH_DIRECTORY, &trashPath, false, &targetVolume) == B_OK && targetDir.SetTo(trashPath.Path()) == B_OK) { while (targetDir.GetNextRef(&testRef) == B_OK) { // Something in the Trash entries++; break; } } targetDir.SetTo(targetDirectory.Path()); // check if target volume otherwise has any entries while (entries == 0 && targetDir.GetNextRef(&testRef) == B_OK) { if (testPath.SetTo(&testRef) == B_OK && testPath != trashPath) entries++; } if (entries != 0 && ((new BAlert("", B_TRANSLATE("The target volume is not empty. Are " "you sure you want to install anyway?\n\nNote: The 'system' folder " "will be a clean copy from the source volume, all other folders " "will be merged, whereas files and links that exist on both the " "source and target volume will be overwritten with the source " "volume version."), B_TRANSLATE("Install anyway"), B_TRANSLATE("Cancel"), 0, B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0)) { // TODO: Would be cool to offer the option here to clean additional // folders at the user's choice (like /boot/common and /boot/develop). err = B_CANCELED; goto error; } // Begin actual installation _LaunchInitScript(targetDirectory); // Create the default indices which should always be present on a proper // boot volume. We don't care if the source volume does not have them. // After all, the user might be re-installing to another drive and may // want problems fixed along the way... err = _CreateDefaultIndices(targetDirectory); if (err != B_OK) goto error; // Mirror all the indices which are present on the source volume onto // the target volume. err = _MirrorIndices(srcDirectory, targetDirectory); if (err != B_OK) goto error; // Let the engine collect information for the progress bar later on engine.ResetTargets(srcDirectory.Path()); err = engine.CollectTargets(srcDirectory.Path(), fCancelSemaphore); if (err != B_OK) goto error; // Collect selected packages also if (fPackages) { BPath pkgRootDir(srcDirectory.Path(), PACKAGES_DIRECTORY); int32 count = fPackages->CountItems(); for (int32 i = 0; i < count; i++) { Package *p = static_cast<Package*>(fPackages->ItemAt(i)); BPath packageDir(pkgRootDir.Path(), p->Folder()); err = engine.CollectTargets(packageDir.Path(), fCancelSemaphore); if (err != B_OK) goto error; } } // collect information about all zip packages err = _ProcessZipPackages(srcDirectory.Path(), targetDirectory.Path(), &reporter, unzipEngines); if (err != B_OK) goto error; reporter.StartTimer(); // copy source volume err = engine.CopyFolder(srcDirectory.Path(), targetDirectory.Path(), fCancelSemaphore); if (err != B_OK) goto error; // copy selected packages if (fPackages) { BPath pkgRootDir(srcDirectory.Path(), PACKAGES_DIRECTORY); int32 count = fPackages->CountItems(); for (int32 i = 0; i < count; i++) { Package *p = static_cast<Package*>(fPackages->ItemAt(i)); BPath packageDir(pkgRootDir.Path(), p->Folder()); err = engine.CopyFolder(packageDir.Path(), targetDirectory.Path(), fCancelSemaphore); if (err != B_OK) goto error; } } // Extract all zip packages. If an error occured, delete the rest of // the engines, but stop extracting. for (int32 i = 0; i < unzipEngines.CountItems(); i++) { UnzipEngine* engine = reinterpret_cast<UnzipEngine*>( unzipEngines.ItemAtFast(i)); if (err == B_OK) err = engine->UnzipPackage(); delete engine; } if (err != B_OK) goto error; _LaunchFinishScript(targetDirectory); BMessenger(fWindow).SendMessage(MSG_INSTALL_FINISHED); return; error: BMessage statusMessage(MSG_RESET); if (err == B_CANCELED) _SetStatusMessage(B_TRANSLATE("Installation canceled.")); else statusMessage.AddInt32("error", err); ERR("_PerformInstall failed"); BMessenger(fWindow).SendMessage(&statusMessage); }
status_t InstalledPackageInfo::SetTo(const char *packageName, const char *version, bool create) { _ClearItemList(); fCreate = create; fStatus = B_NO_INIT; fVersion = version; if (!packageName) return fStatus; BPath configPath; if (find_directory(B_USER_CONFIG_DIRECTORY, &configPath) != B_OK) { fStatus = B_ERROR; return fStatus; } if (fPathToInfo.SetTo(configPath.Path(), kPackagesDir) != B_OK) { fStatus = B_ERROR; return fStatus; } // Check whether the directory exists BDirectory packageDir(fPathToInfo.Path()); fStatus = packageDir.InitCheck(); if (fStatus == B_ENTRY_NOT_FOUND) { // If not, create it packageDir.SetTo(configPath.Path()); if (packageDir.CreateDirectory(kPackagesDir, &packageDir) != B_OK) { fStatus = B_ERROR; return fStatus; } } BString filename = packageName; filename << version << ".pdb"; if (fPathToInfo.Append(filename.String()) != B_OK) { fStatus = B_ERROR; return fStatus; } BFile package(fPathToInfo.Path(), B_READ_ONLY); fStatus = package.InitCheck(); if (fStatus == B_OK) { // The given package exists, so we can unflatten the data to a message // and then pass it further BMessage info; if (info.Unflatten(&package) != B_OK || info.what != P_PACKAGE_INFO) { fStatus = B_ERROR; return fStatus; } int32 count; fStatus = info.FindString("package_name", &fName); fStatus |= info.FindString("package_desc", &fDescription); fStatus |= info.FindString("package_version", &fVersion); int64 spaceNeeded = 0; fStatus |= info.FindInt64("package_size", &spaceNeeded); fSpaceNeeded = static_cast<uint64>(spaceNeeded); fStatus |= info.FindInt32("file_count", &count); if (fStatus != B_OK) { fStatus = B_ERROR; return fStatus; } int32 i; BString itemPath; for (i = 0; i < count; i++) { if (info.FindString("items", i, &itemPath) != B_OK) { fStatus = B_ERROR; return fStatus; } fInstalledItems.AddItem(new BString(itemPath)); // Or maybe BPath better? } fIsUpToDate = true; } else if (fStatus == B_ENTRY_NOT_FOUND) { if (create) { fStatus = B_OK; fIsUpToDate = false; } } return fStatus; }
bool PackageJobThread::installPackage(const QString &src, const QString &dest, OperationType operation) { QDir root(dest); if (!root.exists()) { QDir().mkpath(dest); if (!root.exists()) { d->errorMessage = i18n("Could not create package root directory: %1", dest); d->errorCode = Package::JobError::RootCreationError; //qWarning() << "Could not create package root directory: " << dest; return false; } } QFileInfo fileInfo(src); if (!fileInfo.exists()) { d->errorMessage = i18n("No such file: %1", src); d->errorCode = Package::JobError::PackageFileNotFoundError; return false; } QString path; QTemporaryDir tempdir; bool archivedPackage = false; if (fileInfo.isDir()) { // we have a directory, so let's just install what is in there path = src; // make sure we end in a slash! if (!path.endsWith('/')) { path.append('/'); } } else { KArchive *archive = 0; QMimeDatabase db; QMimeType mimetype = db.mimeTypeForFile(src); if (mimetype.inherits(QStringLiteral("application/zip"))) { archive = new KZip(src); } else if (mimetype.inherits(QStringLiteral("application/x-compressed-tar")) || mimetype.inherits(QStringLiteral("application/x-tar")) || mimetype.inherits(QStringLiteral("application/x-bzip-compressed-tar")) || mimetype.inherits(QStringLiteral("application/x-xz")) || mimetype.inherits(QStringLiteral("application/x-lzma"))) { archive = new KTar(src); } else { //qWarning() << "Could not open package file, unsupported archive format:" << src << mimetype.name(); d->errorMessage = i18n("Could not open package file, unsupported archive format: %1 %2", src, mimetype.name()); d->errorCode = Package::JobError::UnsupportedArchiveFormatError; return false; } if (!archive->open(QIODevice::ReadOnly)) { //qWarning() << "Could not open package file:" << src; delete archive; d->errorMessage = i18n("Could not open package file: %1", src); d->errorCode = Package::JobError::PackageOpenError; return false; } archivedPackage = true; path = tempdir.path() + '/'; d->installPath = path; const KArchiveDirectory *source = archive->directory(); source->copyTo(path); QStringList entries = source->entries(); if (entries.count() == 1) { const KArchiveEntry *entry = source->entry(entries[0]); if (entry->isDirectory()) { path.append(entry->name()).append("/"); } } delete archive; } QDir packageDir(path); QFileInfoList entries = packageDir.entryInfoList(*metaDataFiles); KPluginMetaData meta; if (!entries.isEmpty()) { const QString metadataFilePath = entries.first().filePath(); if (metadataFilePath.endsWith(QLatin1String(".desktop"))) meta = KPluginMetaData(metadataFilePath); else { QFile f(metadataFilePath); if(!f.open(QIODevice::ReadOnly)){ qWarning() << "Couldn't open metadata file" << src << path; d->errorMessage = i18n("Could not open metadata file: %1", src); d->errorCode = Package::JobError::MetadataFileMissingError; return false; } QJsonObject metadataObject = QJsonDocument::fromJson(f.readAll()).object(); meta = KPluginMetaData(metadataObject, QString(), metadataFilePath); } } if (!meta.isValid()) { qDebug() << "No metadata file in package" << src << path; d->errorMessage = i18n("No metadata file in package: %1", src); d->errorCode = Package::JobError::MetadataFileMissingError; return false; } QString pluginName = meta.pluginId(); qDebug() << "pluginname: " << meta.pluginId(); if (pluginName.isEmpty()) { //qWarning() << "Package plugin name not specified"; d->errorMessage = i18n("Package plugin name not specified: %1", src); d->errorCode = Package::JobError::PluginNameMissingError; return false; } // Ensure that package names are safe so package uninstall can't inject // bad characters into the paths used for removal. QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period. if (!validatePluginName.exactMatch(pluginName)) { //qDebug() << "Package plugin name " << pluginName << "contains invalid characters"; d->errorMessage = i18n("Package plugin name %1 contains invalid characters", pluginName); d->errorCode = Package::JobError::PluginNameInvalidError; return false; } QString targetName = dest; if (targetName[targetName.size() - 1] != '/') { targetName.append('/'); } targetName.append(pluginName); if (QFile::exists(targetName)) { if (operation == Update) { KPluginMetaData oldMeta(targetName + QLatin1String("/metadata.desktop")); if (oldMeta.serviceTypes() != meta.serviceTypes()) { d->errorMessage = i18n("The new package has a different type from the old version already installed.", meta.version(), meta.pluginId(), oldMeta.version()); d->errorCode = Package::JobError::UpdatePackageTypeMismatchError; } else if (isVersionNewer(oldMeta.version(), meta.version())) { const bool ok = uninstallPackage(targetName); if (!ok) { d->errorMessage = i18n("Impossible to remove the old installation of %1 located at %2. error: %3", pluginName, targetName, d->errorMessage); d->errorCode = Package::JobError::OldVersionRemovalError; } } else { d->errorMessage = i18n("Not installing version %1 of %2. Version %3 already installed.", meta.version(), meta.pluginId(), oldMeta.version()); d->errorCode = Package::JobError::NewerVersionAlreadyInstalledError; } } else { d->errorMessage = i18n("%1 already exists", targetName); d->errorCode = Package::JobError::PackageAlreadyInstalledError; } if (d->errorCode != KJob::NoError) { d->installPath = targetName; return false; } } //install dependencies const QStringList dependencies = KPluginMetaData::readStringList(meta.rawData(), QStringLiteral("X-KPackage-Dependencies")); for(const QString &dep : dependencies) { QUrl depUrl(dep); if (!installDependency(depUrl)) { d->errorMessage = i18n("Could not install dependency: %1", dep); d->errorCode = Package::JobError::PackageCopyError; return false; } } if (archivedPackage) { // it's in a temp dir, so just move it over. const bool ok = copyFolder(path, targetName); removeFolder(path); if (!ok) { //qWarning() << "Could not move package to destination:" << targetName; d->errorMessage = i18n("Could not move package to destination: %1", targetName); d->errorCode = Package::JobError::PackageMoveError; return false; } } else { // it's a directory containing the stuff, so copy the contents rather // than move them const bool ok = copyFolder(path, targetName); if (!ok) { //qWarning() << "Could not copy package to destination:" << targetName; d->errorMessage = i18n("Could not copy package to destination: %1", targetName); d->errorCode = Package::JobError::PackageCopyError; return false; } } if (archivedPackage) { // no need to remove the temp dir (which has been successfully moved if it's an archive) tempdir.setAutoRemove(false); } indexDirectory(dest, QStringLiteral("kpluginindex.json")); d->installPath = targetName; //qWarning() << "Not updating kbuildsycoca4, since that will go away. Do it yourself for now if needed."; return true; }