From 01c9c13d77aa99fa3797627e42938bd48d9f3b4d Mon Sep 17 00:00:00 2001 From: Andrew Lindesay Date: Thu, 18 Feb 2021 08:19:04 +1300 Subject: [PATCH] pickup icons from the hpkg data - closes #110 --- .../pkg/PkgImportServiceImplIT.java | 52 +++-- .../support/HpkgHelperTest.java | 73 +++++++ .../src/test/resources/README.TXT | 10 +- .../resources/tipster-1.1.1-1-x86_64.hpkg | Bin 0 -> 49334 bytes .../graphics/ImageHelper.java | 8 +- .../pkg/PkgIconServiceImpl.java | 26 +-- .../pkg/PkgImportServiceImpl.java | 186 +++++++++++++++--- .../pkg/model/PkgImportService.java | 6 +- .../job/RepositoryHpkrIngressJobRunner.java | 14 +- .../haikudepotserver/support/FileHelper.java | 2 +- .../haikudepotserver/support/HpkgHelper.java | 76 +++++++ .../support/URLHelperService.java | 24 ++- .../java/org/haiku/pkg/HpkgFileExtractor.java | 17 ++ .../org/haiku/pkg/heap/HpkHeapReader.java | 12 +- .../haiku/pkg/model/FileAttributesValues.java | 26 +++ .../pkg/HpkgFileExtractorAttributeTest.java | 35 +++- support/deployment/config.properties | 4 +- 17 files changed, 484 insertions(+), 87 deletions(-) create mode 100644 haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/support/HpkgHelperTest.java create mode 100644 haikudepotserver-core-test/src/test/resources/tipster-1.1.1-1-x86_64.hpkg create mode 100644 haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/support/HpkgHelper.java create mode 100644 haikudepotserver-packagefile/src/main/java/org/haiku/pkg/model/FileAttributesValues.java diff --git a/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImplIT.java b/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImplIT.java index 97b28158a..dd5bed374 100644 --- a/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImplIT.java +++ b/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImplIT.java @@ -1,10 +1,11 @@ /* - * Copyright 2018-2020, Andrew Lindesay + * Copyright 2018-2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ package org.haiku.haikudepotserver.pkg; +import com.google.common.collect.Iterables; import com.google.common.io.Files; import com.google.common.io.Resources; import org.apache.cayenne.ObjectContext; @@ -29,6 +30,7 @@ import javax.annotation.Resource; import java.io.File; import java.io.InputStream; +import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Random; @@ -36,6 +38,8 @@ @ContextConfiguration(classes = TestConfig.class) public class PkgImportServiceImplIT extends AbstractIntegrationTest { + private static final String RESOURCE_TEST = "tipster-1.1.1-1-x86_64.hpkg"; + @Resource private PkgImportService pkgImportService; @@ -51,19 +55,6 @@ public class PkgImportServiceImplIT extends AbstractIntegrationTest { @Resource private IntegrationTestSupportService integrationTestSupportService; - private Pkg createPkg(String minor) { - return new Pkg( - "testpkg", - new PkgVersion("1", minor, "3", "4", 5), - PkgArchitecture.X86_64, - null, - Collections.emptyList(), - Collections.emptyList(), - "test-summary-en", - "test-description-en", - null); - } - /** *

When a "_devel" package is imported there is a special behaviour that the localization and the * icons are copied from the main package over to the "_devel" package.

@@ -171,7 +162,7 @@ public void testImport_develPkgHandling() throws Exception { */ @Test - public void testImport_payloadLength() throws Exception { + public void testImport_payloadData() throws Exception { File repositoryDirectory = null; int expectedPayloadLength; @@ -193,11 +184,10 @@ public void testImport_payloadLength() throws Exception { throw new IllegalStateException("unable to create the on-disk repository"); } - Random random = new Random(System.currentTimeMillis()); File fileF = new File(repositoryDirectory, "testpkg-1.3.3~4-5-x86_64.hpkg"); - byte[] buffer = new byte[1000 + (Math.abs(random.nextInt()) % 10*1000)]; - Files.write(buffer,fileF); - expectedPayloadLength = buffer.length; + byte[] payload = Resources.toByteArray(Resources.getResource(RESOURCE_TEST)); + Files.write(payload, fileF); + expectedPayloadLength = payload.length; } // now load the next package version in @@ -219,7 +209,8 @@ public void testImport_payloadLength() throws Exception { context.commitChanges(); } - // check the length on that package is there and is correct. + // check the length on that package is there and is correct and that the + // package icon is loaded in. { ObjectContext context = serverRuntime.newContext(); @@ -233,10 +224,16 @@ public void testImport_payloadLength() throws Exception { )).get(); Assertions.assertThat(pkgVersion.getPayloadLength()).isEqualTo(expectedPayloadLength); + + List pkgIcons = pkg.getPkgSupplement().getPkgIcons(); + Assertions.assertThat(pkgIcons).hasSize(1); + PkgIcon pkgIcon = Iterables.getOnlyElement(pkgIcons); + byte[] actualIconData = pkgIcon.getPkgIconImage().getData(); + Assertions.assertThat(actualIconData).hasSize(544); } } finally { - if(null!=repositoryDirectory) { + if (null != repositoryDirectory) { FileHelper.delete(repositoryDirectory); } } @@ -344,4 +341,17 @@ public void testImport_versionRegressionDeactivatesNewerVersions() { } + private Pkg createPkg(String minor) { + return new Pkg( + "testpkg", + new PkgVersion("1", minor, "3", "4", 5), + PkgArchitecture.X86_64, + null, + Collections.emptyList(), + Collections.emptyList(), + "test-summary-en", + "test-description-en", + null); + } + } diff --git a/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/support/HpkgHelperTest.java b/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/support/HpkgHelperTest.java new file mode 100644 index 000000000..a47bace80 --- /dev/null +++ b/haikudepotserver-core-test/src/test/java/org/haiku/haikudepotserver/support/HpkgHelperTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2021, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ +package org.haiku.haikudepotserver.support; + +import com.google.common.collect.Iterables; +import com.google.common.io.ByteSource; +import com.google.common.io.Files; +import com.google.common.io.Resources; +import junit.framework.AssertionFailedError; +import org.fest.assertions.Assertions; +import org.haiku.pkg.AttributeContext; +import org.haiku.pkg.HpkgFileExtractor; +import org.haiku.pkg.model.Attribute; +import org.haiku.pkg.model.AttributeId; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class HpkgHelperTest { + + private static final String RESOURCE_TEST = "tipster-1.1.1-1-x86_64.hpkg"; + + private static final int[] HVIF_MAGIC = { + 0x6e, 0x63, 0x69, 0x66 + }; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void testFindIconAttributesFromAppExecutableDirEntries() throws Exception { + // GIVEN + File file = prepareTestFile(RESOURCE_TEST); + HpkgFileExtractor fileExtractor = new HpkgFileExtractor(file); + AttributeContext tocContext = fileExtractor.getTocContext(); + + // WHEN + List attributes = HpkgHelper.findIconAttributesFromExecutableDirEntries( + tocContext, fileExtractor.getToc()); + + // THEN + Assertions.assertThat(attributes).hasSize(1); + Attribute iconA = Iterables.getOnlyElement(attributes); + Attribute iconDataA = iconA.getChildAttribute(AttributeId.DATA); + ByteSource byteSource = (ByteSource) iconDataA.getValue(tocContext); + byte[] data = byteSource.read(); + Assertions.assertThat(data).hasSize(544); + assertIsHvif(data); + } + + File prepareTestFile(String resource) throws IOException { + byte[] payload = Resources.toByteArray(Resources.getResource(resource)); + File temporaryFile = temporaryFolder.newFile(resource); + Files.write(payload, temporaryFile); + return temporaryFile; + } + + private void assertIsHvif(byte[] payload) { + Assertions.assertThat(payload.length).isGreaterThan(HVIF_MAGIC.length); + for (int i = 0; i < HVIF_MAGIC.length; i++) { + if ((0xff & payload[i]) != HVIF_MAGIC[i]) { + throw new AssertionFailedError("mismatch on the magic in the data payload"); + } + } + } + +} diff --git a/haikudepotserver-core-test/src/test/resources/README.TXT b/haikudepotserver-core-test/src/test/resources/README.TXT index 8b3659f73..ec57bc2e7 100644 --- a/haikudepotserver-core-test/src/test/resources/README.TXT +++ b/haikudepotserver-core-test/src/test/resources/README.TXT @@ -2,4 +2,12 @@ The file "sample-repo.hpkr" was obtained from; http://haiku-files.org/files/repo/9818164862edcbf69404a90267090b9d595908a11941951e904dfc6244c3d566/repo -2013-09-30 \ No newline at end of file +2013-09-30 + +--- + +The file "tipster-1.1.1-1-x86_64.hpkg" was obtained from; + +https://eu.hpkg.haiku-os.org/haikuports/master/x86_64/current/packages/tipster-1.1.1-1-x86_64.hpkg + +2021-02-08 diff --git a/haikudepotserver-core-test/src/test/resources/tipster-1.1.1-1-x86_64.hpkg b/haikudepotserver-core-test/src/test/resources/tipster-1.1.1-1-x86_64.hpkg new file mode 100644 index 0000000000000000000000000000000000000000..1cddad7cfa661e1d109450734be3c900e669533a GIT binary patch literal 49334 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 z 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

Y|M*aJU&3$*xw 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 thNV)UUXj2lsl getInUsePkgIconMediaTypes(final ObjectContext context) { return codes .stream() - .map(c -> MediaType.tryGetByCode(context, c).get()) + .map(c -> MediaType.getByCode(context, c)) .collect(Collectors.toList()); } diff --git a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImpl.java b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImpl.java index 48d28f976..e3546744d 100644 --- a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImpl.java +++ b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/PkgImportServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2020, Andrew Lindesay + * Copyright 2018-2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -8,41 +8,55 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.io.ByteSource; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.ObjectId; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.haiku.haikudepotserver.dataobjects.*; -import org.haiku.haikudepotserver.pkg.model.PkgImportService; -import org.haiku.haikudepotserver.pkg.model.PkgLocalizationService; -import org.haiku.haikudepotserver.support.ExposureType; -import org.haiku.haikudepotserver.support.URLHelperService; -import org.haiku.haikudepotserver.support.VersionCoordinates; -import org.haiku.haikudepotserver.support.VersionCoordinatesComparator; +import org.haiku.haikudepotserver.pkg.model.*; +import org.haiku.haikudepotserver.support.*; +import org.haiku.pkg.AttributeContext; +import org.haiku.pkg.HpkgFileExtractor; +import org.haiku.pkg.model.Attribute; +import org.haiku.pkg.model.AttributeId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service public class PkgImportServiceImpl implements PkgImportService { protected static Logger LOGGER = LoggerFactory.getLogger(PkgImportServiceImpl.class); + private final ServerRuntime serverRuntime; private final PkgServiceImpl pkgServiceImpl; + private final PkgIconService pkgIconService; private final PkgLocalizationService pkgLocalizationService; private final URLHelperService urlHelperService; public PkgImportServiceImpl( + ServerRuntime serverRuntime, PkgServiceImpl pkgServiceImpl, + PkgIconService pkgIconService, PkgLocalizationService pkgLocalizationService, URLHelperService urlHelperService) { + this.serverRuntime = Preconditions.checkNotNull(serverRuntime); this.pkgServiceImpl = Preconditions.checkNotNull(pkgServiceImpl); + this.pkgIconService = Preconditions.checkNotNull(pkgIconService); this.pkgLocalizationService = Preconditions.checkNotNull(pkgLocalizationService); this.urlHelperService = Preconditions.checkNotNull(urlHelperService); } @@ -52,7 +66,7 @@ public void importFrom( ObjectContext objectContext, ObjectId repositorySourceObjectId, org.haiku.pkg.model.Pkg pkg, - boolean shouldPopulatePayloadLength) { + boolean populateFromPayload) { Preconditions.checkArgument(null != pkg, "the package must be provided"); Preconditions.checkArgument(null != repositorySourceObjectId, "the repository source is must be provided"); @@ -152,14 +166,14 @@ public void importFrom( // [apl] // If this fails, we will let it go and it can be tried again a bit later on. The system can try to back-fill // those at some later date if any of the latest versions for packages are missing. This is better than - // failing the import at this stage since this is "just" meta data. + // failing the import at this stage since this is "just" meta data. The length of the payload is being used as + // a signal that the payload was downloaded and processed at some point. - if(shouldPopulatePayloadLength && null==persistedPkgVersion.getPayloadLength()) { - populatePayloadLength(persistedPkgVersion); + if(populateFromPayload && shouldPopulateFromPayload(persistedPkgVersion)) { + populateFromPayload(objectContext, persistedPkgVersion); } LOGGER.debug("have processed package {}", pkg.toString()); - } private Pkg createPkg(ObjectContext objectContext, String name) { @@ -254,20 +268,146 @@ private void importCopyrights(ObjectContext objectContext, org.haiku.pkg.model.P } } - private void populatePayloadLength(PkgVersion persistedPkgVersion) { - Optional pkgVersionHpkgURLOptional = persistedPkgVersion.tryGetHpkgURL(ExposureType.INTERNAL_FACING); + private boolean shouldPopulateFromPayload(PkgVersion persistedPkgVersion) { + return null == persistedPkgVersion.getPayloadLength() + && Stream.of( + PkgService.SUFFIX_PKG_DEBUGINFO, + PkgService.SUFFIX_PKG_DEVELOPMENT, + PkgService.SUFFIX_PKG_SOURCE).noneMatch(suffix -> persistedPkgVersion.getPkg().getName().endsWith(suffix)); + } + + /** + *

This will read in the payload into a temporary file. From there it will parse it + * and take up any data from it such as the icon and the length of the download in + * bytes.

+ */ + + private void populateFromPayload(ObjectContext objectContext, PkgVersion persistedPkgVersion) { + persistedPkgVersion.tryGetHpkgURL(ExposureType.INTERNAL_FACING) + .ifPresentOrElse( + u -> populateFromPayload(objectContext, persistedPkgVersion, u), + () -> LOGGER.info( + "no package payload data recorded because there is no " + + "hpkg url for pkg [{}] version [{}]", + persistedPkgVersion.getPkg(), persistedPkgVersion)); + } + + private void populateFromPayload( + ObjectContext objectContext, + PkgVersion persistedPkgVersion, URL url) { + File temporaryFile = null; + + try { + String prefix = persistedPkgVersion.getPkg().getName() + "_" + RandomStringUtils.randomAlphabetic(3) + "_"; + // ^ need to ensure minimum length of the prefix + temporaryFile = File.createTempFile(prefix, ".hpkg"); - if (pkgVersionHpkgURLOptional.isPresent()) { try { - urlHelperService.tryGetPayloadLength(pkgVersionHpkgURLOptional.get()) - .filter(l -> l > 0L) - .ifPresent(persistedPkgVersion::setPayloadLength); - } catch (IOException ioe) { - LOGGER.error("unable to get the payload length for; " + persistedPkgVersion, ioe); + urlHelperService.transferPayloadToFile(url, temporaryFile); } - } else { - LOGGER.info("no package length recorded because there is no " - + "hpkg url for [" + persistedPkgVersion + "]"); + catch (IOException ioe) { + // if we can't download then don't stop the entire import process - just log and carry on. + LOGGER.warn("unable to download from the url [{}] --> [{}]; will ignore", url, temporaryFile); + return; + } + + // the length of the payload is interesting and trivial to capture from + // the data downloaded. + + if (null == persistedPkgVersion.getPayloadLength() + || persistedPkgVersion.getPayloadLength() != temporaryFile.length()) { + persistedPkgVersion.setPayloadLength(temporaryFile.length()); + LOGGER.info("recording new length for [{}] version [{}] of {}bytes", + persistedPkgVersion.getPkg(), persistedPkgVersion, temporaryFile.length()); + } + + // more complex is the capture of the data in the parsed payload data. + + HpkgFileExtractor hpkgFileExtractor; + + try { + hpkgFileExtractor = new HpkgFileExtractor(temporaryFile); + } + catch (Throwable th) { + // if it is not possible to parse the HPKG then log and carry on. + LOGGER.warn("unable to parse the payload from [{}]", url, th); + return; + } + + populateIconFromPayload(objectContext, persistedPkgVersion, hpkgFileExtractor); + } + catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + finally { + if (null != temporaryFile && temporaryFile.exists()) { + if (temporaryFile.delete()) { + LOGGER.debug("did delete the temporary file"); + } + else { + LOGGER.error("unable to delete the temporary file [{}]", temporaryFile); + } + } + } + } + + private void populateIconFromPayload( + ObjectContext objectContext, PkgVersion persistedPkgVersion, + HpkgFileExtractor hpkgFileExtractor) { + AttributeContext context = hpkgFileExtractor.getTocContext(); + List iconAttrs = HpkgHelper.findIconAttributesFromExecutableDirEntries( + context, hpkgFileExtractor.getToc()); + switch (iconAttrs.size()) { + case 0: + LOGGER.info("package [{}] version [{}] has no icons", + persistedPkgVersion.getPkg(), persistedPkgVersion); + break; + case 1: + populateIconFromPayload( + objectContext, persistedPkgVersion, context, + Iterables.getFirst(iconAttrs, null)); + break; + default: + LOGGER.info("package [{}] version [{}] has {} icons --> ambiguous so will not load any", + persistedPkgVersion.getPkg(), persistedPkgVersion, iconAttrs.size()); + break; + } + } + + private void populateIconFromPayload( + ObjectContext objectContext, PkgVersion persistedPkgVersion, + AttributeContext context, Attribute attribute) { + Attribute dataAttr = attribute.tryGetChildAttribute(AttributeId.DATA).orElse(null); + + if (null == dataAttr) { + LOGGER.warn("the icon [{}] found for package [{}] version [{}] does not have a data attribute", + AttributeId.FILE_ATTRIBUTE, persistedPkgVersion.getPkg(), persistedPkgVersion); + } + + ByteSource byteSource = (ByteSource) dataAttr.getValue(context); + + try { + LOGGER.info("did find {} bytes of icon data for package [{}] version [{}]", + byteSource.size(), persistedPkgVersion.getPkg(), persistedPkgVersion); + } + catch (IOException ignore) { + LOGGER.warn("cannot get the size of the icon payload for package [{}] version [{}]", + persistedPkgVersion.getPkg(), persistedPkgVersion); + } + + try (InputStream inputStream = byteSource.openStream()) { + pkgIconService.storePkgIconImage( + inputStream, + MediaType.getByCode(objectContext, MediaType.MEDIATYPE_HAIKUVECTORICONFILE), + null, + objectContext, + persistedPkgVersion.getPkg().getPkgSupplement()); + } + catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + catch (BadPkgIconException e) { + LOGGER.info("a failure has arisen loading a package icon data", e); } } diff --git a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/model/PkgImportService.java b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/model/PkgImportService.java index 5e3032b5e..bf1f8edaf 100644 --- a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/model/PkgImportService.java +++ b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/pkg/model/PkgImportService.java @@ -1,5 +1,5 @@ /* - * Copyright 2016, Andrew Lindesay + * Copyright 2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -16,7 +16,7 @@ public interface PkgImportService { * either creating it or updating it as necessary.

* @param pkg imports into the local database from this package model. * @param repositorySourceObjectId the {@link ObjectId} of the source of the package data. - * @param populatePayloadLength is able to signal to the import process that the length of the package should be + * @param populateFromPayload is able to signal to the import process that the length of the package should be * populated. */ @@ -24,6 +24,6 @@ void importFrom( ObjectContext objectContext, ObjectId repositorySourceObjectId, org.haiku.pkg.model.Pkg pkg, - boolean populatePayloadLength); + boolean populateFromPayload); } diff --git a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/repository/job/RepositoryHpkrIngressJobRunner.java b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/repository/job/RepositoryHpkrIngressJobRunner.java index f7f8db8ac..3fbc884a1 100644 --- a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/repository/job/RepositoryHpkrIngressJobRunner.java +++ b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/repository/job/RepositoryHpkrIngressJobRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019, Andrew Lindesay + * Copyright 2018-2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -63,17 +63,17 @@ public class RepositoryHpkrIngressJobRunner extends AbstractJobRunnerThis will find all of the directory entries. In these it will find all of the + * directory entries that are executable and then it will find those that have an icon. It will return + * a list of those attributes that are the icons.

+ */ + + public static List findIconAttributesFromExecutableDirEntries( + AttributeContext context, + List toc) { + return streamDirEntries(toc) + .flatMap(appA -> streamExecutableDirEntriesRecursively(context, appA)) + .flatMap(execA -> streamIconAttributesFromAppExecutableDirEntry(context, execA)) + .collect(Collectors.toList()); + } + + private static Stream streamDirEntries(List toc) { + return toc.stream().filter(a -> a.getAttributeId() == AttributeId.DIRECTORY_ENTRY); + } + + private static Stream streamExecutableDirEntriesRecursively( + AttributeContext context, + Attribute attribute) { + if (attribute.getAttributeId() != AttributeId.DIRECTORY_ENTRY) { + return Stream.of(); + } + + if (isExecutableDirEntry(context, attribute)) { + return Stream.of(attribute); + } + + return attribute.getChildAttributes() + .stream() + .flatMap(a -> streamExecutableDirEntriesRecursively(context, a)); + } + + private static boolean isExecutableDirEntry( + AttributeContext context, + Attribute attr) { + if (attr.getAttributeId() != AttributeId.DIRECTORY_ENTRY) { + return false; + } + + return attr.tryGetChildAttribute(AttributeId.FILE_PERMISSIONS) + .map(a -> ((Number) a.getValue(context)).intValue()) + .filter(permissions -> 0 != (0100 & permissions)) + // ^^ posix permissions checking to see that the user has the executable flag set + .isPresent(); + } + + private static Stream streamIconAttributesFromAppExecutableDirEntry( + AttributeContext context, + Attribute executableDirEntryAttribute) { + String desiredAttributeValue = FileAttributesValues.BEOS_ICON.getAttributeValue(); + return executableDirEntryAttribute.getChildAttributes(AttributeId.FILE_ATTRIBUTE) + .stream() + .filter(a -> a.getValue(context).toString().equals(desiredAttributeValue)); + } + +} diff --git a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/support/URLHelperService.java b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/support/URLHelperService.java index 381fc1d0b..ee40fef93 100644 --- a/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/support/URLHelperService.java +++ b/haikudepotserver-core/src/main/java/org/haiku/haikudepotserver/support/URLHelperService.java @@ -1,11 +1,12 @@ /* - * Copyright 2018-2019, Andrew Lindesay + * Copyright 2018-2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ package org.haiku.haikudepotserver.support; import com.google.common.base.Preconditions; +import com.google.common.io.*; import com.google.common.net.HttpHeaders; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -53,6 +54,27 @@ public static boolean isValidInfo(String urlString) { return false; } + public void transferPayloadToFile(URL url, File targetFile) throws IOException { + LOGGER.info("will transfer [{}] --> [{}]", url, targetFile); + String protocol = StringUtils.trimToEmpty(url.getProtocol()); + + switch(protocol) { + case "http": + case "https": + FileHelper.streamUrlDataToFile(url, targetFile, PAYLOAD_LENGTH_READ_TIMEOUT); + LOGGER.info("copied [{}] to [{}]", url, targetFile); + break; + case "file": + File sourceFile = new File(url.getPath()); + Files.copy(sourceFile, targetFile); + LOGGER.info("copied [{}] to [{}]", sourceFile, targetFile); + break; + default: + LOGGER.warn("unable to transfer for URL scheme [{}]", protocol); + break; + } + } + public Optional tryGetPayloadLength(URL url) throws IOException { Preconditions.checkArgument(null != url, "the url must be supplied"); diff --git a/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/HpkgFileExtractor.java b/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/HpkgFileExtractor.java index bfb186ce3..5bccb45bb 100644 --- a/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/HpkgFileExtractor.java +++ b/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/HpkgFileExtractor.java @@ -1,14 +1,22 @@ +/* + * Copyright 2021, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ package org.haiku.pkg; import com.google.common.base.Preconditions; import org.haiku.pkg.heap.HeapCompression; import org.haiku.pkg.heap.HpkHeapReader; +import org.haiku.pkg.model.Attribute; import org.haiku.pkg.model.FileType; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** *

This object represents an object that can extract an Hpkg (Haiku Pkg) file. If you are wanting to @@ -104,6 +112,15 @@ public AttributeIterator getTocIterator() { return new AttributeIterator(getTocContext(), tocAttributeOffset); } + public List getToc() { + List assembly = new ArrayList<>(); + AttributeIterator attributeIterator = getTocIterator(); + while(attributeIterator.hasNext()) { + assembly.add(attributeIterator.next()); + } + return Collections.unmodifiableList(assembly); + } + private HpkgHeader readHeader() throws IOException { Preconditions.checkNotNull(file); FileHelper fileHelper = new FileHelper(); diff --git a/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/heap/HpkHeapReader.java b/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/heap/HpkHeapReader.java index 7df7391e1..2f264da72 100644 --- a/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/heap/HpkHeapReader.java +++ b/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/heap/HpkHeapReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2018-2019, Andrew Lindesay + * Copyright 2018-2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ @@ -37,13 +37,13 @@ public class HpkHeapReader implements Closeable, HeapReader { private final long uncompressedSize; // excluding the shorts for the chunks' compressed sizes - private LoadingCache heapChunkUncompressedCache; + private final LoadingCache heapChunkUncompressedCache; - private int[] heapChunkCompressedLengths; + private final int[] heapChunkCompressedLengths; - private RandomAccessFile randomAccessFile; + private final RandomAccessFile randomAccessFile; - private FileHelper fileHelper = new FileHelper(); + private final FileHelper fileHelper = new FileHelper(); public HpkHeapReader( final File file, @@ -57,7 +57,7 @@ public HpkHeapReader( Preconditions.checkNotNull(file); Preconditions.checkNotNull(compression); - Preconditions.checkState(heapOffset > 0 &&heapOffset < Integer.MAX_VALUE); + Preconditions.checkState(heapOffset > 0 && heapOffset < Integer.MAX_VALUE); Preconditions.checkState(chunkSize > 0 && chunkSize < Integer.MAX_VALUE); Preconditions.checkState(compressedSize >= 0 && compressedSize < Integer.MAX_VALUE); Preconditions.checkState(uncompressedSize >= 0 && uncompressedSize < Integer.MAX_VALUE); diff --git a/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/model/FileAttributesValues.java b/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/model/FileAttributesValues.java new file mode 100644 index 000000000..506fdc529 --- /dev/null +++ b/haikudepotserver-packagefile/src/main/java/org/haiku/pkg/model/FileAttributesValues.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ +package org.haiku.pkg.model; + +/** + *

The attribute type {@link AttributeId#FILE_ATTRIBUTE} has a value + * which is a string that defines what sort of attribute it is.

+ */ + +public enum FileAttributesValues { + + BEOS_ICON("BEOS:ICON"); + + private String attributeValue; + + FileAttributesValues(String attributeValue) { + this.attributeValue = attributeValue; + } + + public String getAttributeValue() { + return attributeValue; + } + +} diff --git a/haikudepotserver-packagefile/src/test/java/org/haiku/pkg/HpkgFileExtractorAttributeTest.java b/haikudepotserver-packagefile/src/test/java/org/haiku/pkg/HpkgFileExtractorAttributeTest.java index c5503953a..4409c42a5 100644 --- a/haikudepotserver-packagefile/src/test/java/org/haiku/pkg/HpkgFileExtractorAttributeTest.java +++ b/haikudepotserver-packagefile/src/test/java/org/haiku/pkg/HpkgFileExtractorAttributeTest.java @@ -2,18 +2,15 @@ * Copyright 2021, Andrew Lindesay * Distributed under the terms of the MIT License. */ - package org.haiku.pkg; import com.google.common.base.Preconditions; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import com.google.common.io.ByteSource; +import junit.framework.AssertionFailedError; import org.fest.assertions.Assertions; -import org.haiku.pkg.model.Attribute; -import org.haiku.pkg.model.AttributeId; -import org.haiku.pkg.model.AttributeType; -import org.haiku.pkg.model.IntAttribute; +import org.haiku.pkg.model.*; import org.junit.Test; import java.io.File; @@ -26,6 +23,10 @@ public class HpkgFileExtractorAttributeTest extends AbstractHpkTest { private static final String RESOURCE_TEST = "tipster-1.1.1-1-x86_64.hpkg"; + private static final int[] HVIF_MAGIC = { + 0x6e, 0x63, 0x69, 0x66 + }; + @Test public void testReadFile() throws Exception { @@ -49,12 +50,24 @@ public void testReadFile() throws Exception { // Pull out the actual binary to check. The expected data results were obtained // from a Haiku host with the package installed. - Attribute binaryDirectoryEntry = findByDirectoryEntries(tocAttributes, tocContext, List.of("apps", "Tipster")); - Attribute binaryData = binaryDirectoryEntry.getChildAttribute(AttributeId.DATA); + Attribute tipsterDirectoryEntry = findByDirectoryEntries(tocAttributes, tocContext, List.of("apps", "Tipster")); + + Attribute binaryData = tipsterDirectoryEntry.getChildAttribute(AttributeId.DATA); ByteSource binaryDataByteSource = (ByteSource) binaryData.getValue(tocContext); Assertions.assertThat(binaryDataByteSource.size()).isEqualTo(153840L); HashCode hashCode = binaryDataByteSource.hash(Hashing.md5()); Assertions.assertThat(hashCode.toString().toLowerCase(Locale.ROOT)).isEqualTo("13b16cd7d035ddda09a744c49a8ebdf2"); + + Attribute iconAttribute = tipsterDirectoryEntry.getChildAttributes(AttributeId.FILE_ATTRIBUTE) + .stream() + .map(a -> (StringAttribute) a) + .filter(a -> a.getValue(tocContext).equals("BEOS:ICON")) + .findFirst() + .orElseThrow(() -> new AssertionFailedError("could not find icon attribute")); + Attribute iconBinaryData = iconAttribute.getChildAttribute(AttributeId.DATA); + ByteSource iconDataByteSource = (ByteSource) iconBinaryData.getValue(tocContext); + byte[] iconBytes = iconDataByteSource.read(); + assertIsHvif(iconBytes); } } @@ -89,5 +102,13 @@ private List toList(AttributeIterator attributeIterator) { return assembly; } + private void assertIsHvif(byte[] payload) { + Assertions.assertThat(payload.length).isGreaterThan(HVIF_MAGIC.length); + for (int i = 0; i < HVIF_MAGIC.length; i++) { + if ((0xff & payload[i]) != HVIF_MAGIC[i]) { + throw new AssertionFailedError("mismatch on the magic in the data payload"); + } + } + } } diff --git a/support/deployment/config.properties b/support/deployment/config.properties index 32d3e1f70..747a6f5a6 100644 --- a/support/deployment/config.properties +++ b/support/deployment/config.properties @@ -24,7 +24,7 @@ optipng.path=/usr/bin/optipng # When set (either "true" or "false"), the repository import process will # obtain the data for the package and will thereby figure out the size of # the package. -repository.import.populatepayloadlength=true +repository.import.populatefrompayload=true # Configures a minimum version for the HaikiDepot desktop application. # Versions of HD desktop application less than this minimum are @@ -126,4 +126,4 @@ authentication.jws.issuer=prod.hds #smtp.starttls=false email.from=noreply@haiku-os.org -# ------------------------------------------- \ No newline at end of file +# -------------------------------------------