├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── inventage │ └── nexusaptplugin │ ├── AbstractContentGenerator.java │ ├── ArchetypeCatalog.java │ ├── ArchetypeInstaller.java │ ├── DEBIAN.java │ ├── DebianIndexCreator.java │ ├── FileManagerContentLocator.java │ ├── PackagesContentGenerator.java │ ├── PackagesGzContentGenerator.java │ ├── ReleaseContentGenerator.java │ ├── ReleaseGPGContentGenerator.java │ ├── SignKeyContentGenerator.java │ ├── ar │ ├── ArFile.java │ ├── ArReader.java │ ├── ArUtil.java │ ├── FileNameTooLongException.java │ ├── InvalidArchiveMagicException.java │ ├── InvalidFileMagicException.java │ └── ReadableArFile.java │ ├── cache │ ├── DebianFileManager.java │ ├── FileGenerator.java │ ├── RepositoryData.java │ └── generators │ │ ├── PackagesGenerator.java │ │ ├── PackagesGzGenerator.java │ │ ├── ReleaseGPGGenerator.java │ │ ├── ReleaseGenerator.java │ │ └── SignKeyGenerator.java │ ├── capabilities │ ├── AptCapabilitiesBooter.java │ ├── AptCapability.java │ ├── AptCapabilityConfiguration.java │ ├── AptCapabilityDescriptor.java │ ├── AptCapabilityEvent.java │ ├── AptSigningActivatedEvent.java │ ├── AptSigningDeactivatedEvent.java │ └── AptSigningUpdatedEvent.java │ ├── deb │ ├── DebControlParser.java │ └── GetControl.java │ └── sign │ ├── AptSigningConfiguration.java │ ├── PGPSignatureOutputStream.java │ └── PGPSigner.java └── test ├── java └── com │ └── inventage │ └── nexusaptplugin │ ├── ar │ └── ArReaderTest.java │ └── deb │ ├── DebControlParserTest.java │ └── GetControlTest.java └── resources ├── example-control ├── example-dpkg-scanpackages-output.txt └── php5_5.3.10-1ubuntu3.2_all.deb /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.iml 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nexus APT Plugin 2 | ================ 3 | 4 | this plugin generates a Packages.gz for each nexus repository and allows the repository to be 5 | listed in a debian /etc/apt/sources.list file so that it can be used by aptitude/apt-get/ubuntu 6 | software center. 7 | 8 | Installation 9 | ============ 10 | 11 | The 'Downloads' section of this project contains the latest builds. Please download the latest 12 | nexus-apt-plugin-N.N-bundle.zip and unzip it into the sonatype-work/nexus/plugin-repository/ 13 | and restart nexus. 14 | 15 | > to be sure that the index is regenerated (the plugin adds attributes to index) it could be 16 | neccessary to delete the index files under sonatype-work/nexus/indexer 17 | 18 | All repositories now contain a Packages.gz that lists all debian packages the indexer was able 19 | to find. 20 | 21 | Compatibility 22 | ------------- 23 | 24 | | Nexus Version | Plugin Version | 25 | | ------------------ | -------------- | 26 | | 3.x and greater | Not compatible (see https://github.com/sonatype-nexus-community/nexus-repository-apt) | 27 | | 2.11.x and greater | 1.1.2 | 28 | | 2.8.x and greater | 1.0.2 | 29 | | 2.7.x | 0.6 | 30 | 31 | The plugins might be compatible with earlier Nexus versions, but are not tested. 32 | 33 | 34 | Configuring Release Signing 35 | --------------------------- 36 | 37 | APT will noisely complain when updating from a repository with an unsigned 38 | Packages.gz. 39 | 40 | To configure signing, you need a PGP key. You can generate one by running: 41 | 42 | > gpg --gen-key 43 | 44 | Choose "RSA and RSA (default)", and a key size of 2048. Larger key sizes are 45 | not supported by this plugin. 46 | 47 | Once the key is generated, you can run `gpg --list-keys` to find the 48 | ID of the generated key: 49 | 50 | > gpg --list-keys 51 | /home/nexus/.gnupg/pubring.gpg 52 | ------------------------------ 53 | pub 2048R/4FE5A5F6 2016-10-08 [expires: 2017-10-08] 54 | uid Alexander Bertram 55 | sub 2048R/13E1BD68 2016-10-08 [expires: 2017-10-08] 56 | 57 | The Key ID can be found following the '2048R/'. For example, the ID above 58 | is `4FE5A5F6`. 59 | 60 | You may have to restart Nexus before continuing. 61 | 62 | To configure the plugin to use this key, navigate to "Administration", 63 | and then "Capabilities" in the Nexus web interface. 64 | 65 | Click the "New" button, choose the type "APT: Configuration", and then 66 | reply the requested settings, for example: 67 | 68 | | Setting | Plugin Version | 69 | | ------------------------ | ------------------------------- | 70 | | Secure keyring location | /home/nexus/.gnupg/secring.gpg | 71 | | Key ID | 4FE5A5F6 | 72 | | Passphrase for the key | xxxxxxxxx | 73 | 74 | 75 | In order for APT to verify the signature, you must share the public 76 | key with your users. One way to do this is to publish your key to 77 | a key server. For example: 78 | 79 | gpg --keyserver keyserver.ubuntu.com --send-key 4FE5A5F6 80 | 81 | 82 | Debian Packages from a Maven Build Process 83 | ------------------------------------------ 84 | https://github.com/sannies/blogger-java-deb is a small example on how to create debs in a 85 | Maven process and works together well with this plugin. 86 | 87 | 88 | Pitfall 89 | ------- 90 | 91 | The indexer cannot find packages when there is a main artifact with the same name: 92 | If the artifacts are named like: 93 | 94 | - nexus-apt-plugin-0.5.jar 95 | - nexus-apt-plugin-0.5.deb 96 | 97 | The indexer won't index the debian package. In order to make the indexer index the debian 98 | package it needs a classifier: 99 | 100 | - nexus-apt-plugin-0.5.jar 101 | - nexus-apt-plugin-0.5-all.deb 102 | 103 | This is fine. 104 | 105 | Known Bug 106 | --------- 107 | 108 | When maven uploads an artifact to Nexus, it uploads 2 files: the deb file 109 | and the pom. Nexus then indexes the artifact, but it does this for the 2 files simultaneously. 110 | Depending on which file is uploaded last (seems to be random), the debian package information 111 | may or may not be in the index and therefore may or may not be in the Packages/Packages.gz 112 | files. 113 | 114 | To fix the index contents, start an 'Update Index' action on the repository. 115 | 116 | This is an issue with the indexer in Nexus 2.x.y. Sonatype is planning to use a new indexer 117 | technology in their Nexus 3.0 release. 118 | 119 | Adding a repository to sources.list 120 | =================================== 121 | 122 | First add your PGP key if you have configured signing. If you published your key 123 | to a key server as described above, you can run: 124 | 125 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com --recv-keys 4FE5A5F6 126 | 127 | Then add the line `deb http://repository.yourcompany.com/content/repositories/releases/ ./` 128 | to your `/etc/apt/sources.list`. Type `apt-get update` and all debian packages in the repository 129 | can now be installed via `apt-get install`. 130 | 131 | Author 132 | ====== 133 | 134 | This plugin was created by https://github.com/sannies and is now maintained by https://github.com/inventage. 135 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.sonatype.nexus.plugins 8 | nexus-plugins 9 | 2.8.0-05 10 | 11 | 12 | com.inventage.nexus-apt-plugin 13 | nexus-apt-plugin 14 | 1.1.4-SNAPSHOT 15 | 16 | nexus-plugin 17 | 18 | Nexus APT Repository Plugin 19 | 20 | 21 | scm:git:https://github.com/inventage/nexus-apt-plugin.git 22 | scm:git:https://github.com/inventage/nexus-apt-plugin.git 23 | master 24 | 25 | 26 | 27 | 28 | IDE 29 | 30 | 31 | 32 | org.sonatype.nexus.plugins 33 | nexus-indexer-lucene-plugin 34 | provided 35 | 36 | 37 | 38 | org.sonatype.nexus.plugins 39 | nexus-capabilities-plugin 40 | provided 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.commons 50 | commons-compress 51 | 1.4.1 52 | 53 | 54 | 55 | commons-codec 56 | commons-codec 57 | 1.8 58 | 59 | 60 | 61 | org.bouncycastle 62 | bcpg-jdk15on 63 | 1.48 64 | 65 | 66 | 67 | com.google.guava 68 | guava 69 | 14.0.1 70 | provided 71 | 72 | 73 | 74 | 75 | org.sonatype.nexus 76 | nexus-plugin-api 77 | provided 78 | jar 79 | 80 | 81 | 82 | 83 | 91 | 92 | 93 | org.sonatype.nexus.plugins 94 | nexus-indexer-lucene-plugin 95 | provided 96 | nexus-plugin 97 | 98 | 99 | 100 | org.sonatype.nexus.plugins 101 | nexus-capabilities-plugin 102 | provided 103 | nexus-plugin 104 | 105 | 106 | 107 | 108 | junit 109 | junit 110 | 4.11 111 | test 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.sonatype.nexus 120 | nexus-plugin-bundle-maven-plugin 121 | true 122 | 123 | 124 | create-plugin-bundle 125 | 126 | create-bundle 127 | 128 | package 129 | 130 | 131 | 132 | 134 | 135 | maven-compiler-plugin 136 | 2.0.2 137 | 138 | 1.6 139 | 1.6 140 | 141 | 142 | 143 | maven-deploy-plugin 144 | 2.7 145 | 146 | true 147 | 148 | 149 | 150 | org.sonatype.plugins 151 | nexus-staging-maven-plugin 152 | 153 | true 154 | 155 | 156 | 157 | org.apache.maven.plugins 158 | maven-gpg-plugin 159 | 160 | true 161 | 162 | 163 | 164 | maven-release-plugin 165 | 2.5 166 | 167 | 168 | maven-javadoc-plugin 169 | 2.8.1 170 | 171 | true 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | org.apache.maven.indexer 181 | indexer-artifact 182 | 5.1.1 183 | 184 | 185 | org.apache.maven.indexer 186 | indexer-core 187 | 5.1.1 188 | 189 | 190 | 191 | 192 | 193 | sonatype 194 | http://repository.sonatype.org/content/groups/sonatype-public-grid/ 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/AbstractContentGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | 4 | import javax.inject.Inject; 5 | 6 | import org.apache.maven.index.ArtifactInfo; 7 | import org.apache.maven.index.ArtifactInfoFilter; 8 | import org.apache.maven.index.Indexer; 9 | import org.apache.maven.index.context.IndexingContext; 10 | import org.sonatype.nexus.index.DefaultIndexerManager; 11 | import org.sonatype.nexus.index.IndexArtifactFilter; 12 | import org.sonatype.nexus.index.IndexerManager; 13 | import org.sonatype.nexus.proxy.IllegalOperationException; 14 | import org.sonatype.nexus.proxy.ItemNotFoundException; 15 | import org.sonatype.nexus.proxy.LocalStorageException; 16 | import org.sonatype.nexus.proxy.item.ContentGenerator; 17 | import org.sonatype.nexus.proxy.item.ContentLocator; 18 | import org.sonatype.nexus.proxy.item.StorageFileItem; 19 | import org.sonatype.nexus.proxy.repository.Repository; 20 | 21 | import com.inventage.nexusaptplugin.cache.DebianFileManager; 22 | import com.inventage.nexusaptplugin.cache.RepositoryData; 23 | 24 | /** 25 | * @author Raniz 26 | */ 27 | public abstract class AbstractContentGenerator 28 | implements ContentGenerator { 29 | 30 | @Inject 31 | private IndexerManager indexerManager; 32 | 33 | @Inject 34 | private IndexArtifactFilter indexArtifactFilter; 35 | 36 | @Inject 37 | private Indexer indexer; 38 | 39 | @Inject 40 | private DebianFileManager fileManager; 41 | 42 | private final String mimeType; 43 | 44 | private final String fileName; 45 | 46 | public AbstractContentGenerator(String mimeType, String fileName) { 47 | this.mimeType = mimeType; 48 | this.fileName = fileName; 49 | } 50 | 51 | 52 | @Override 53 | public ContentLocator generateContent(Repository repository, String path, StorageFileItem item) 54 | throws IllegalOperationException, ItemNotFoundException, LocalStorageException { 55 | 56 | RepositoryData data = new RepositoryData(repository.getId(), 57 | ((DefaultIndexerManager) indexerManager).getRepositoryIndexContext(repository), 58 | new ArtifactInfoFilter() { 59 | @Override 60 | public boolean accepts(IndexingContext ctx, ArtifactInfo ai) { 61 | return indexArtifactFilter.filterArtifactInfo(ai); 62 | } 63 | }, 64 | indexer); 65 | 66 | return new FileManagerContentLocator(fileManager, mimeType, data, fileName); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ArchetypeCatalog.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | import java.util.Map; 4 | 5 | import com.google.common.collect.ImmutableMap; 6 | 7 | /** 8 | * @author Dominik Menzi 9 | */ 10 | enum ArchetypeCatalog { 11 | 12 | DEFAULT(new ImmutableMap.Builder() 13 | .put("/Packages", PackagesContentGenerator.ID) 14 | .put("/Packages.gz", PackagesGzContentGenerator.ID) 15 | .put("/Release", ReleaseContentGenerator.ID) 16 | .build()), 17 | 18 | SIGNING(new ImmutableMap.Builder() 19 | .put("/Release.gpg", ReleaseGPGContentGenerator.ID) 20 | .put("/apt-key.gpg.key", SignKeyContentGenerator.ID) 21 | .build()); 22 | 23 | private final Map catalogFiles; 24 | 25 | ArchetypeCatalog(Map catalogFiles) { 26 | this.catalogFiles = catalogFiles; 27 | } 28 | 29 | Map getCatalogFiles() { 30 | return catalogFiles; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ArchetypeInstaller.java: -------------------------------------------------------------------------------- 1 | 2 | package com.inventage.nexusaptplugin; 3 | 4 | import java.io.IOException; 5 | import java.util.EnumSet; 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ConcurrentMap; 9 | 10 | import javax.inject.Inject; 11 | import javax.inject.Named; 12 | import javax.inject.Singleton; 13 | 14 | import org.slf4j.Logger; 15 | import org.sonatype.nexus.events.EventSubscriber; 16 | import org.sonatype.nexus.proxy.AccessDeniedException; 17 | import org.sonatype.nexus.proxy.IllegalOperationException; 18 | import org.sonatype.nexus.proxy.ItemNotFoundException; 19 | import org.sonatype.nexus.proxy.RepositoryNotAvailableException; 20 | import org.sonatype.nexus.proxy.ResourceStoreRequest; 21 | import org.sonatype.nexus.proxy.events.RepositoryConfigurationUpdatedEvent; 22 | import org.sonatype.nexus.proxy.events.RepositoryItemEventDelete; 23 | import org.sonatype.nexus.proxy.events.RepositoryItemEventStore; 24 | import org.sonatype.nexus.proxy.events.RepositoryRegistryEventAdd; 25 | import org.sonatype.nexus.proxy.events.RepositoryRegistryEventRemove; 26 | import org.sonatype.nexus.proxy.item.DefaultStorageFileItem; 27 | import org.sonatype.nexus.proxy.item.StorageItem; 28 | import org.sonatype.nexus.proxy.item.StringContentLocator; 29 | import org.sonatype.nexus.proxy.registry.ContentClass; 30 | import org.sonatype.nexus.proxy.repository.GroupRepository; 31 | import org.sonatype.nexus.proxy.repository.HostedRepository; 32 | import org.sonatype.nexus.proxy.repository.ProxyRepository; 33 | import org.sonatype.nexus.proxy.repository.Repository; 34 | import org.sonatype.nexus.proxy.storage.UnsupportedStorageOperationException; 35 | 36 | import com.google.common.eventbus.Subscribe; 37 | 38 | import com.inventage.nexusaptplugin.capabilities.AptSigningActivatedEvent; 39 | import com.inventage.nexusaptplugin.capabilities.AptSigningDeactivatedEvent; 40 | 41 | /** 42 | * EventInspector that listens to registry events, repo addition and removal, and simply "hooks" in the generated 43 | * Packages.gz file to their root. 44 | * 45 | * @author cstamas 46 | */ 47 | @Singleton 48 | @Named("deb") 49 | public class ArchetypeInstaller implements EventSubscriber { 50 | 51 | private final Logger logger; 52 | 53 | private final ContentClass maven2ContentClass; 54 | 55 | private final EnumSet activeCatalogs = EnumSet.of(ArchetypeCatalog.DEFAULT); 56 | private final ConcurrentMap configuredRepositories = new ConcurrentHashMap(); 57 | 58 | @Inject 59 | public ArchetypeInstaller(Logger logger, @Named("maven2") ContentClass maven2ContentClass) { 60 | this.logger = logger; 61 | this.maven2ContentClass = maven2ContentClass; 62 | } 63 | 64 | @Subscribe 65 | public void onAptSigningActivated(AptSigningActivatedEvent event) { 66 | activeCatalogs.add(ArchetypeCatalog.SIGNING); 67 | for (Repository repository : configuredRepositories.values()) { 68 | installArchetypeCatalog(repository, ArchetypeCatalog.SIGNING); 69 | } 70 | } 71 | 72 | @Subscribe 73 | public void onAptSigningDeactivated(AptSigningDeactivatedEvent event) { 74 | activeCatalogs.remove(ArchetypeCatalog.SIGNING); 75 | for (Repository repository : configuredRepositories.values()) { 76 | removeArcheTypeCatalogIfPresent(repository, ArchetypeCatalog.SIGNING); 77 | } 78 | } 79 | 80 | @Subscribe 81 | public void onRepositoryRegistryEventAdd(RepositoryRegistryEventAdd evt) { 82 | registerNewRepo(evt.getRepository()); 83 | } 84 | 85 | @Subscribe 86 | public void onRepositoryRegistryEventRemove(RepositoryRegistryEventRemove evt) { 87 | deregisterRepo(evt.getRepository()); 88 | } 89 | 90 | @Subscribe 91 | public void onRepositoryConfigurationUpdatedEvent(RepositoryConfigurationUpdatedEvent evt) { 92 | if (isRepositoryInService(evt.getRepository())) { 93 | registerNewRepo(evt.getRepository()); 94 | } else { 95 | deregisterRepo(evt.getRepository()); 96 | } 97 | } 98 | 99 | private boolean isRepositoryInService(Repository repository) { 100 | return repository.getLocalStatus().shouldServiceRequest(); 101 | } 102 | 103 | private void registerNewRepo(Repository repository) { 104 | installArcheTypeCatalogIfCompatible(repository); 105 | configuredRepositories.put(repository.getId(), repository); 106 | } 107 | 108 | private void deregisterRepo(Repository repository) { 109 | final Repository registeredRepo = configuredRepositories.remove(repository.getId()); 110 | if (registeredRepo == null) { 111 | return; 112 | } 113 | removeAllArcheTypeCatalogsIfPresent(registeredRepo); 114 | } 115 | 116 | private void installArcheTypeCatalogIfCompatible(Repository repository) { 117 | if (!isRepositoryCompatible(repository)) { 118 | return; 119 | } 120 | installActiveArchetypeCatalogs(repository); 121 | } 122 | 123 | private boolean isRepositoryCompatible(Repository repository) { 124 | // check is it a maven2 content, and either a "hosted", "proxy" or "group" repository 125 | return maven2ContentClass.isCompatible(repository.getRepositoryContentClass()) 126 | && (repository.getRepositoryKind().isFacetAvailable(HostedRepository.class) 127 | || repository.getRepositoryKind().isFacetAvailable(ProxyRepository.class) || repository.getRepositoryKind().isFacetAvailable( 128 | GroupRepository.class)); 129 | } 130 | 131 | private void installActiveArchetypeCatalogs(Repository repository) { 132 | for (ArchetypeCatalog activeCatalog : activeCatalogs) { 133 | installArchetypeCatalog(repository, activeCatalog); 134 | } 135 | } 136 | 137 | private void installArchetypeCatalog(Repository repository, ArchetypeCatalog archetypeCatalog) { 138 | // new repo added or enabled, "install" the archetype catalogs 139 | try { 140 | for (Map.Entry entry : archetypeCatalog.getCatalogFiles().entrySet()) { 141 | final String filePath = entry.getKey(); 142 | final String generatorId = entry.getValue(); 143 | storeItem(repository, filePath, generatorId); 144 | } 145 | } 146 | catch (RepositoryNotAvailableException e) { 147 | logger.info("Unable to install the generated archetype catalog, repository \"" 148 | + e.getRepository().getId() + "\" is out of service."); 149 | } 150 | catch (Exception e) { 151 | if (logger.isDebugEnabled()) { 152 | logger.info("Unable to install the generated archetype catalog!", e); 153 | } 154 | else { 155 | logger.info("Unable to install the generated archetype catalog:" + e.getMessage()); 156 | } 157 | } 158 | } 159 | 160 | private void removeAllArcheTypeCatalogsIfPresent(Repository repository) { 161 | for (ArchetypeCatalog activeCatalog : ArchetypeCatalog.values()) { 162 | removeArcheTypeCatalogIfPresent(repository, activeCatalog); 163 | } 164 | } 165 | 166 | private void removeArcheTypeCatalogIfPresent(Repository repository, ArchetypeCatalog archetypeCatalog) { 167 | for (Map.Entry entry : archetypeCatalog.getCatalogFiles().entrySet()) { 168 | final String filePath = entry.getKey(); 169 | final String generatorId = entry.getValue(); 170 | final ResourceStoreRequest request = new ResourceStoreRequest(filePath); 171 | try { 172 | repository.deleteItem(request); 173 | } 174 | catch (UnsupportedStorageOperationException e) { 175 | logger.error("e", e); 176 | } 177 | catch (ItemNotFoundException e) { 178 | logger.error("e", e); 179 | } 180 | catch (IllegalOperationException e) { 181 | logger.error("e", e); 182 | } 183 | catch (IOException e) { 184 | logger.error("e", e); 185 | } 186 | catch (AccessDeniedException e) { 187 | logger.error("e", e); 188 | } 189 | } 190 | } 191 | 192 | @Subscribe 193 | public void onRepositoryItemEventStore(RepositoryItemEventStore evt) { 194 | updateDebItemInRepository(evt.getRepository(), evt.getItem()); 195 | } 196 | 197 | @Subscribe 198 | public void onRepositoryItemEventDelete(RepositoryItemEventDelete evt) { 199 | updateDebItemInRepository(evt.getRepository(), evt.getItem()); 200 | } 201 | 202 | private void updateDebItemInRepository(Repository repository, StorageItem item) { 203 | if (item.getName().toLowerCase().endsWith(".deb") && configuredRepositories.containsKey(repository.getId())) { 204 | updateFileModificationDates(repository); 205 | } 206 | } 207 | 208 | /** 209 | * Update the modification time of all items stored by the plugin 210 | * 211 | * @param repository The repository to update files in 212 | */ 213 | private void updateFileModificationDates(Repository repository) { 214 | for (ArchetypeCatalog activeCatalog : activeCatalogs) { 215 | for (Map.Entry entry : activeCatalog.getCatalogFiles().entrySet()) { 216 | try { 217 | updateOrInstallStorageItem(repository, entry.getKey(), entry.getValue()); 218 | } 219 | catch (AccessDeniedException e) { 220 | logger.error("e", e); 221 | } 222 | catch (IllegalOperationException e) { 223 | logger.error("e", e); 224 | } 225 | catch (UnsupportedStorageOperationException e) { 226 | logger.error("e", e); 227 | } 228 | catch (IOException e) { 229 | logger.error("e", e); 230 | } 231 | } 232 | } 233 | } 234 | 235 | private void updateOrInstallStorageItem(Repository repository, String path, String generatorId) throws IllegalOperationException, IOException, AccessDeniedException, UnsupportedStorageOperationException { 236 | try { 237 | // Retrieve the item, update the modification time and save it 238 | final ResourceStoreRequest request = new ResourceStoreRequest(path); 239 | StorageItem item = repository.retrieveItem(request); 240 | item.getRepositoryItemAttributes().setModified(System.currentTimeMillis()); 241 | repository.storeItem(false, item); 242 | } 243 | catch (ItemNotFoundException e) { 244 | logger.info("Storage item not found, creating new item"); 245 | storeItem(repository, path, generatorId); 246 | } 247 | } 248 | 249 | private void storeItem(Repository repository, String filePath, String generatorId) throws UnsupportedStorageOperationException, IllegalOperationException, IOException { 250 | DefaultStorageFileItem file = 251 | new DefaultStorageFileItem(repository, 252 | new ResourceStoreRequest(filePath), true, false, 253 | new StringContentLocator(generatorId)); 254 | file.setContentGeneratorId(generatorId); 255 | repository.storeItem(false, file); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/DEBIAN.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | import org.apache.maven.index.Field; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: sannies 8 | * Date: 6/30/12 9 | * Time: 12:26 PM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public interface DEBIAN { 13 | 14 | String DEBIAN_NAMESPACE = "urn:nexus:debian#"; 15 | 16 | Field PACKAGE = new Field(null, DEBIAN_NAMESPACE, "Package", "Debian Package Name"); 17 | Field VERSION = new Field(null, DEBIAN_NAMESPACE, "Version", "Debian Package Version"); 18 | Field ARCHITECTURE = new Field(null, DEBIAN_NAMESPACE, "Architecture", "Debian Package Architecture"); 19 | Field MAINTAINER = new Field(null, DEBIAN_NAMESPACE, "Maintainer", "Debian Package Maintainer"); 20 | Field INSTALLED_SIZE = new Field(null, DEBIAN_NAMESPACE, "Installed-Size", "Debian Package Installed Size"); 21 | Field DEPENDS = new Field(null, DEBIAN_NAMESPACE, "Depends", "Debian Package Depends"); 22 | Field PRE_DEPENDS = new Field(null, DEBIAN_NAMESPACE, "Pre-Depends", "Debian Package Pre-Depends"); 23 | Field PROVIDES = new Field(null, DEBIAN_NAMESPACE, "Provides", "Debian Package Provides"); 24 | Field RECOMMENDS = new Field(null, DEBIAN_NAMESPACE, "Recommends", "Debian Package Recommends"); 25 | Field SUGGESTS = new Field(null, DEBIAN_NAMESPACE, "Suggests", "Debian Package Suggests"); 26 | Field ENHANCES = new Field(null, DEBIAN_NAMESPACE, "Enhances", "Debian Package Enhances"); 27 | Field BREAKS = new Field(null, DEBIAN_NAMESPACE, "Breaks", "Debian Package Breaks"); 28 | Field CONFLICTS = new Field(null, DEBIAN_NAMESPACE, "Conflicts", "Debian Package Conflicts"); 29 | Field REPLACES = new Field(null, DEBIAN_NAMESPACE, "Replaces", "Debian Package Replaces"); 30 | Field SECTION = new Field(null, DEBIAN_NAMESPACE, "Section", "Debian Package Section"); 31 | Field PRIORITY = new Field(null, DEBIAN_NAMESPACE, "Priority", "Debian Package Priority"); 32 | Field DESCRIPTION = new Field(null, DEBIAN_NAMESPACE, "Description", "Debian Package Description"); 33 | 34 | Field MD5 = new Field( null, DEBIAN_NAMESPACE, "MD5sum", "MD5 checksum" ); 35 | Field FILENAME = new Field( null, DEBIAN_NAMESPACE, "Filename", "Filename" ); 36 | Field SHA256 = new Field( null, DEBIAN_NAMESPACE, "sha256", "SHA256 checksum" ); 37 | Field SHA512 = new Field( null, DEBIAN_NAMESPACE, "sha512", "SHA512 checksum" ); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/DebianIndexCreator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | /* 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | import java.io.FileInputStream; 23 | import java.io.IOException; 24 | import java.security.MessageDigest; 25 | import java.util.Arrays; 26 | import java.util.Collection; 27 | import java.util.List; 28 | 29 | import javax.inject.Named; 30 | 31 | import org.apache.commons.codec.binary.Hex; 32 | import org.apache.commons.codec.digest.DigestUtils; 33 | import org.apache.lucene.document.Document; 34 | import org.apache.lucene.document.Field; 35 | import org.apache.maven.index.ArtifactContext; 36 | import org.apache.maven.index.ArtifactInfo; 37 | import org.apache.maven.index.IndexerField; 38 | import org.apache.maven.index.IndexerFieldVersion; 39 | import org.apache.maven.index.creator.AbstractIndexCreator; 40 | import org.apache.maven.index.creator.MinimalArtifactInfoIndexCreator; 41 | 42 | import com.inventage.nexusaptplugin.deb.DebControlParser; 43 | import com.inventage.nexusaptplugin.deb.GetControl; 44 | 45 | 46 | /** 47 | * A Maven Archetype index creator used to detect and correct the artifact packaging to "maven-archetype" if the 48 | * inspected JAR is an Archetype. Since packaging is already handled by Minimal creator, this Creator only alters the 49 | * supplied ArtifactInfo packaging field during processing, but does not interferes with Lucene document fill-up or the 50 | * ArtifactInfo fill-up (the update* methods are empty). 51 | * 52 | * @author cstamas 53 | */ 54 | @Named(DebianIndexCreator.ID) 55 | @javax.inject.Singleton 56 | public class DebianIndexCreator 57 | extends AbstractIndexCreator { 58 | 59 | public static final String ID = "debian-package"; 60 | 61 | public static final IndexerField PACKAGE = new IndexerField(DEBIAN.PACKAGE, IndexerFieldVersion.V1, "deb_package", 62 | DEBIAN.PACKAGE.getDescription(), Field.Store.YES, Field.Index.NO); 63 | 64 | public static final IndexerField ARCHITECTURE = new IndexerField(DEBIAN.ARCHITECTURE, IndexerFieldVersion.V1, "deb_architecture", 65 | DEBIAN.ARCHITECTURE.getDescription(), Field.Store.YES, Field.Index.NO); 66 | 67 | public static final IndexerField INSTALLED_SIZE = new IndexerField(DEBIAN.INSTALLED_SIZE, IndexerFieldVersion.V1, "deb_installed_size", 68 | DEBIAN.INSTALLED_SIZE.getDescription(), Field.Store.YES, Field.Index.NO); 69 | 70 | public static final IndexerField MAINTAINER = new IndexerField(DEBIAN.MAINTAINER, IndexerFieldVersion.V1, "deb_maintainer", 71 | DEBIAN.MAINTAINER.getDescription(), Field.Store.YES, Field.Index.NO); 72 | 73 | public static final IndexerField VERSION = new IndexerField(DEBIAN.VERSION, IndexerFieldVersion.V1, "deb_version", 74 | DEBIAN.VERSION.getDescription(), Field.Store.YES, Field.Index.NO); 75 | 76 | public static final IndexerField DEPENDS = new IndexerField(DEBIAN.DEPENDS, IndexerFieldVersion.V1, "deb_depends", 77 | DEBIAN.DEPENDS.getDescription(), Field.Store.YES, Field.Index.NO); 78 | 79 | public static final IndexerField PRE_DEPENDS = new IndexerField(DEBIAN.PRE_DEPENDS, IndexerFieldVersion.V1, "deb_pre_depends", 80 | DEBIAN.PRE_DEPENDS.getDescription(), Field.Store.YES, Field.Index.NO); 81 | 82 | public static final IndexerField PROVIDES = new IndexerField(DEBIAN.PROVIDES, IndexerFieldVersion.V1, "deb_provides", 83 | DEBIAN.PROVIDES.getDescription(), Field.Store.YES, Field.Index.NO); 84 | 85 | public static final IndexerField RECOMMENDS = new IndexerField(DEBIAN.RECOMMENDS, IndexerFieldVersion.V1, "deb_recommends", 86 | DEBIAN.RECOMMENDS.getDescription(), Field.Store.YES, Field.Index.NO); 87 | 88 | public static final IndexerField SUGGESTS = new IndexerField(DEBIAN.SUGGESTS, IndexerFieldVersion.V1, "deb_suggests", 89 | DEBIAN.SUGGESTS.getDescription(), Field.Store.YES, Field.Index.NO); 90 | 91 | public static final IndexerField ENHANCES = new IndexerField(DEBIAN.ENHANCES, IndexerFieldVersion.V1, "deb_enhances", 92 | DEBIAN.ENHANCES.getDescription(), Field.Store.YES, Field.Index.NO); 93 | 94 | public static final IndexerField BREAKS = new IndexerField(DEBIAN.BREAKS, IndexerFieldVersion.V1, "deb_breaks", 95 | DEBIAN.BREAKS.getDescription(), Field.Store.YES, Field.Index.NO); 96 | 97 | public static final IndexerField CONFLICTS = new IndexerField(DEBIAN.CONFLICTS, IndexerFieldVersion.V1, "deb_conflicts", 98 | DEBIAN.CONFLICTS.getDescription(), Field.Store.YES, Field.Index.NO); 99 | 100 | public static final IndexerField REPLACES = new IndexerField(DEBIAN.REPLACES, IndexerFieldVersion.V1, "deb_replaces", 101 | DEBIAN.REPLACES.getDescription(), Field.Store.YES, Field.Index.NO); 102 | 103 | public static final IndexerField SECTION = new IndexerField(DEBIAN.SECTION, IndexerFieldVersion.V1, "deb_section", 104 | DEBIAN.SECTION.getDescription(), Field.Store.YES, Field.Index.NO); 105 | 106 | public static final IndexerField PRIORITY = new IndexerField(DEBIAN.PRIORITY, IndexerFieldVersion.V1, "deb_priority", 107 | DEBIAN.PRIORITY.getDescription(), Field.Store.YES, Field.Index.NO); 108 | 109 | public static final IndexerField DESCRIPTION = new IndexerField(DEBIAN.DESCRIPTION, IndexerFieldVersion.V1, "deb_description", 110 | DEBIAN.DESCRIPTION.getDescription(), Field.Store.YES, Field.Index.NO); 111 | 112 | public static final IndexerField MD5 = new IndexerField(DEBIAN.MD5, IndexerFieldVersion.V1, "deb_md5", 113 | DEBIAN.MD5.getDescription(), Field.Store.YES, Field.Index.NO); 114 | 115 | public static final IndexerField FILENAME = new IndexerField(DEBIAN.FILENAME, IndexerFieldVersion.V1, "deb_filename", 116 | DEBIAN.FILENAME.getDescription(), Field.Store.YES, Field.Index.NO); 117 | 118 | public static final IndexerField SHA256 = new IndexerField(DEBIAN.SHA256, IndexerFieldVersion.V1, "deb_sha256", 119 | DEBIAN.SHA256.getDescription(), Field.Store.YES, Field.Index.NO); 120 | 121 | public static final IndexerField SHA512 = new IndexerField(DEBIAN.SHA512, IndexerFieldVersion.V1, "deb_sha512", 122 | DEBIAN.SHA512.getDescription(), Field.Store.YES, Field.Index.NO); 123 | 124 | private final List indexerFields = Arrays.asList(PACKAGE, ARCHITECTURE, INSTALLED_SIZE, MAINTAINER, VERSION, 125 | DEPENDS, PRE_DEPENDS, PROVIDES, RECOMMENDS, SUGGESTS, ENHANCES, BREAKS, CONFLICTS, REPLACES, 126 | SECTION, PRIORITY, DESCRIPTION, FILENAME, SHA256, SHA512); 127 | 128 | public DebianIndexCreator() { 129 | super(ID, Arrays.asList(MinimalArtifactInfoIndexCreator.ID)); 130 | } 131 | 132 | @Override 133 | public void populateArtifactInfo(ArtifactContext ac) throws IOException { 134 | if (ac.getArtifact() != null && "deb".equals(ac.getArtifactInfo().packaging)) { 135 | List control = GetControl.doGet(ac.getArtifact()); 136 | ac.getArtifactInfo().getAttributes().putAll(DebControlParser.parse(control)); 137 | ac.getArtifactInfo().getAttributes().put("Filename", getRelativeFileNameOfArtifact(ac)); 138 | 139 | FileInputStream is = null; 140 | try { 141 | is = new FileInputStream(ac.getArtifact()); 142 | MessageDigest md5d = DigestUtils.getMd5Digest(); 143 | MessageDigest sha256 = DigestUtils.getSha256Digest(); 144 | MessageDigest sha512 = DigestUtils.getSha512Digest(); 145 | 146 | int count; 147 | byte[] b = new byte[512]; 148 | while( (count = is.read(b)) >= 0) { 149 | md5d.update(b, 0, count); 150 | sha256.update(b, 0, count); 151 | sha512.update(b, 0, count); 152 | } 153 | 154 | ac.getArtifactInfo().md5 = Hex.encodeHexString(md5d.digest()); 155 | ac.getArtifactInfo().getAttributes().put(DEBIAN.SHA256.getFieldName(), Hex.encodeHexString(sha256.digest())); 156 | ac.getArtifactInfo().getAttributes().put(DEBIAN.SHA512.getFieldName(), Hex.encodeHexString(sha512.digest())); 157 | } finally { 158 | is.close(); 159 | } 160 | } 161 | } 162 | 163 | private String getRelativeFileNameOfArtifact(ArtifactContext ac) { 164 | return "./" + ac.getArtifactInfo().groupId.replace(".", "/") + "/" + ac.getArtifactInfo().artifactId + "/" + ac.getArtifactInfo().version + "/" + ac.getArtifactInfo().fname; 165 | } 166 | 167 | @Override 168 | public void updateDocument(ArtifactInfo ai, Document doc) { 169 | if ("deb".equals(ai.packaging)) { 170 | for (IndexerField indexerField : indexerFields) { 171 | updateOneDocumentField(ai, doc, indexerField); 172 | } 173 | if (ai.md5 != null) { 174 | doc.add(MD5.toField(ai.md5)); 175 | } 176 | } 177 | } 178 | 179 | private void updateOneDocumentField(ArtifactInfo ai, Document doc, IndexerField indexerField) { 180 | if (ai.getAttributes().get(indexerField.getOntology().getFieldName()) != null) { 181 | doc.add(indexerField.toField(ai.getAttributes().get(indexerField.getOntology().getFieldName()))); 182 | } 183 | } 184 | 185 | @Override 186 | public boolean updateArtifactInfo(Document doc, ArtifactInfo ai) { 187 | String filename = doc.get(FILENAME.getKey()); 188 | if (filename != null && filename.endsWith(".deb")) { 189 | for (IndexerField indexerField : indexerFields) { 190 | updateOneArtifactInfoAttribute(doc, ai, indexerField); 191 | } 192 | ai.md5 = doc.get(MD5.getKey()); 193 | return true; 194 | } 195 | return false; 196 | } 197 | 198 | private void updateOneArtifactInfoAttribute(Document doc, ArtifactInfo ai, IndexerField indexerField) { 199 | ai.getAttributes().put(indexerField.getOntology().getFieldName(), doc.get(indexerField.getKey())); 200 | } 201 | 202 | // == 203 | 204 | @Override 205 | public String toString() { 206 | return ID; 207 | } 208 | 209 | public Collection getIndexerFields() { 210 | return Arrays.asList(SHA256, SHA512); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/FileManagerContentLocator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.concurrent.ExecutionException; 7 | 8 | import org.sonatype.nexus.proxy.item.ContentLocator; 9 | 10 | import com.inventage.nexusaptplugin.cache.DebianFileManager; 11 | import com.inventage.nexusaptplugin.cache.RepositoryData; 12 | 13 | 14 | public class FileManagerContentLocator 15 | implements ContentLocator { 16 | 17 | private final DebianFileManager fileManager; 18 | 19 | private final String mimeType; 20 | 21 | private final RepositoryData data; 22 | 23 | private final String name; 24 | 25 | public FileManagerContentLocator(DebianFileManager fileManager, 26 | String mimeType, RepositoryData data, String name) { 27 | this.fileManager = fileManager; 28 | this.mimeType = mimeType; 29 | this.data = data; 30 | this.name = name; 31 | } 32 | 33 | @Override 34 | public InputStream getContent() 35 | throws IOException { 36 | try { 37 | return new ByteArrayInputStream(fileManager.getFile(name, data)); 38 | } 39 | catch (ExecutionException e) { 40 | throw new IOException("Could not generate " + name, e); 41 | } 42 | } 43 | 44 | @Override 45 | public String getMimeType() { 46 | return mimeType; 47 | } 48 | 49 | @Override 50 | public long getLength() { 51 | return UNKNOWN_LENGTH; 52 | } 53 | 54 | @Override 55 | public boolean isReusable() { 56 | return false; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/PackagesContentGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | 4 | import javax.inject.Named; 5 | 6 | /** 7 | * @author Raniz 8 | */ 9 | @Named(PackagesContentGenerator.ID) 10 | public class PackagesContentGenerator 11 | extends AbstractContentGenerator { 12 | public static final String ID = "PackagesContentGenerator"; 13 | 14 | 15 | public PackagesContentGenerator() { 16 | super("application/text", "Packages"); 17 | } 18 | 19 | @Override 20 | public String getGeneratorId() { 21 | return ID; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/PackagesGzContentGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | 4 | import javax.inject.Named; 5 | 6 | /** 7 | * @author Raniz 8 | */ 9 | @Named(PackagesGzContentGenerator.ID) 10 | public class PackagesGzContentGenerator 11 | extends AbstractContentGenerator { 12 | public static final String ID = "PackagesGzContentGenerator"; 13 | 14 | 15 | public PackagesGzContentGenerator() { 16 | super("application/x-gzip", "Packages.gz"); 17 | } 18 | 19 | @Override 20 | public String getGeneratorId() { 21 | return ID; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ReleaseContentGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | 4 | import javax.inject.Named; 5 | 6 | /** 7 | * @author Raniz 8 | */ 9 | @Named(ReleaseContentGenerator.ID) 10 | public class ReleaseContentGenerator 11 | extends AbstractContentGenerator { 12 | public static final String ID = "ReleaseContentGenerator"; 13 | 14 | 15 | public ReleaseContentGenerator() { 16 | super("application/text", "Release"); 17 | } 18 | 19 | @Override 20 | public String getGeneratorId() { 21 | return ID; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ReleaseGPGContentGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | 4 | import javax.inject.Named; 5 | 6 | /** 7 | * @author Raniz 8 | */ 9 | @Named(ReleaseGPGContentGenerator.ID) 10 | public class ReleaseGPGContentGenerator extends AbstractContentGenerator { 11 | 12 | public static final String ID = "ReleaseGPGContentGenerator"; 13 | 14 | public ReleaseGPGContentGenerator() { 15 | super("application/text", "Release.gpg"); 16 | } 17 | 18 | @Override 19 | public String getGeneratorId() { 20 | return ID; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/SignKeyContentGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin; 2 | 3 | 4 | import javax.inject.Named; 5 | 6 | /** 7 | * @author Raniz 8 | */ 9 | @Named(SignKeyContentGenerator.ID) 10 | public class SignKeyContentGenerator 11 | extends AbstractContentGenerator { 12 | public static final String ID = "SignKeyContentGenerator"; 13 | 14 | 15 | public SignKeyContentGenerator() { 16 | super("application/text", "apt-key.gpg.key"); 17 | } 18 | 19 | @Override 20 | public String getGeneratorId() { 21 | return ID; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/ArFile.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | import java.io.File; 28 | 29 | /** 30 | * @author Trygve Laugstøl 31 | */ 32 | public class ArFile { 33 | protected File file; 34 | protected String name; 35 | protected long lastModified; 36 | protected int ownerId; 37 | protected int groupId; 38 | protected int mode; 39 | protected long size; 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public long getLastModified() { 46 | return lastModified; 47 | } 48 | 49 | public int getOwnerId() { 50 | return ownerId; 51 | } 52 | 53 | public int getGroupId() { 54 | return groupId; 55 | } 56 | 57 | public int getMode() { 58 | return mode; 59 | } 60 | 61 | public long getSize() { 62 | return size; 63 | } 64 | 65 | public static ArFile fromFile(File file) { 66 | if (file == null) { 67 | throw new NullPointerException("file"); 68 | } 69 | ArFile arFile = new ArFile(); 70 | arFile.file = file; 71 | if (arFile.name == null) { 72 | arFile.name = file.getName(); 73 | } 74 | arFile.mode = 420; // 664 75 | arFile.lastModified = file.lastModified() / 1000; 76 | arFile.size = file.length(); 77 | 78 | if (arFile.name.length() > 16) { 79 | throw new FileNameTooLongException(); 80 | } 81 | 82 | return arFile; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/ArReader.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | import java.io.Closeable; 28 | import java.io.File; 29 | import java.io.FileInputStream; 30 | import java.io.IOException; 31 | import java.io.InputStream; 32 | import java.util.Iterator; 33 | import java.util.NoSuchElementException; 34 | 35 | import org.codehaus.plexus.util.IOUtil; 36 | 37 | /** 38 | * @author Trygve Laugstøl 39 | */ 40 | public class ArReader 41 | implements Closeable, Iterable { 42 | private InputStream is; 43 | 44 | public ArReader(File file) 45 | throws IOException { 46 | is = new FileInputStream(file); 47 | 48 | String magic = new String(ArUtil.readBytes(is, 8), ArUtil.US_ASCII); 49 | 50 | if (!magic.equals(ArUtil.AR_ARCHIVE_MAGIC)) { 51 | throw new InvalidArchiveMagicException(); 52 | } 53 | } 54 | 55 | public Iterator iterator() { 56 | return new ArFileIterator(); 57 | } 58 | 59 | public void close() { 60 | IOUtil.close(is); 61 | } 62 | 63 | public ReadableArFile readFile() 64 | throws IOException { 65 | byte[] bytes = ArUtil.readBytes(is, 60); 66 | 67 | if (bytes == null) { 68 | return null; 69 | } 70 | 71 | ReadableArFile arFile = new ReadableArFile(is); 72 | arFile.name = ArUtil.convertString(bytes, 0, 16); 73 | arFile.lastModified = Long.parseLong(ArUtil.convertString(bytes, 16, 12)); 74 | arFile.ownerId = Integer.parseInt(ArUtil.convertString(bytes, 28, 6)); 75 | arFile.groupId = Integer.parseInt(ArUtil.convertString(bytes, 34, 6)); 76 | arFile.mode = Integer.parseInt(ArUtil.convertString(bytes, 40, 8), 8); 77 | arFile.size = Long.parseLong(ArUtil.convertString(bytes, 48, 10)); 78 | 79 | if (arFile.name.endsWith("/")) { 80 | arFile.name = arFile.name.substring(0, arFile.name.length() - 1); 81 | } 82 | 83 | String fileMagic = ArUtil.convertString(bytes, 58, 2); 84 | 85 | if (!fileMagic.equals(ArUtil.AR_FILE_MAGIC)) { 86 | throw new InvalidFileMagicException(); 87 | } 88 | 89 | return arFile; 90 | } 91 | 92 | public class ArFileIterator 93 | implements Iterator { 94 | 95 | private boolean used; 96 | private boolean atEnd; 97 | private ReadableArFile file; 98 | 99 | public boolean hasNext() { 100 | updateNext(); 101 | return file != null; 102 | } 103 | 104 | public ReadableArFile next() { 105 | updateNext(); 106 | 107 | if (file == null) { 108 | throw new NoSuchElementException(); 109 | } 110 | 111 | used = true; 112 | 113 | return file; 114 | } 115 | 116 | private void updateNext() { 117 | try { 118 | if (used) { 119 | file.close(); 120 | file = null; 121 | used = false; 122 | } 123 | 124 | // There already is an element ready 125 | if (file != null) { 126 | return; 127 | } 128 | 129 | // If we're at the end, don't call readFile() anymore as that will throw an IOException 130 | if (atEnd) { 131 | return; 132 | } 133 | 134 | file = readFile(); 135 | atEnd = file == null; 136 | 137 | if (atEnd) { 138 | close(); 139 | } 140 | } 141 | catch (IOException ex) { 142 | // ignore 143 | } 144 | } 145 | 146 | public void remove() { 147 | throw new UnsupportedOperationException("Not implemented."); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/ArUtil.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | import java.io.*; 28 | 29 | /** 30 | * @author Trygve Laugstøl 31 | */ 32 | public class ArUtil 33 | { 34 | public static final String LF = "\n"; 35 | public static final String AR_ARCHIVE_MAGIC = "!" + LF; 36 | public static final String AR_FILE_MAGIC = "`\n"; 37 | public static final String US_ASCII = "US-ASCII"; 38 | 39 | public static String convertString( byte[] bytes, int start, int count ) 40 | throws UnsupportedEncodingException 41 | { 42 | String s = new String( bytes, start, count, ArUtil.US_ASCII ); 43 | 44 | int index = s.indexOf( ' ' ); 45 | 46 | if ( index == -1 ) 47 | { 48 | return s; 49 | } 50 | 51 | s = s.substring( 0, index ); 52 | 53 | return s; 54 | } 55 | 56 | public static byte[] readBytes( InputStream is, long count ) 57 | throws IOException 58 | { 59 | byte[] bytes = new byte[(int) count]; 60 | 61 | int start = 0; 62 | 63 | do 64 | { 65 | int read = is.read( bytes, start, (int) count ); 66 | 67 | // If we're at EOF, but trying to read the first set of bytes, return null 68 | if ( read == -1 ) 69 | { 70 | if ( start > 0 ) 71 | { 72 | throw new EOFException(); 73 | } 74 | 75 | return null; 76 | } 77 | 78 | start += read; 79 | count -= read; 80 | } 81 | while ( count > 0 ); 82 | 83 | return bytes; 84 | } 85 | 86 | public static void skipBytes( InputStream is, long count ) 87 | throws IOException 88 | { 89 | long left = count; 90 | 91 | do 92 | { 93 | long read = is.skip( left ); 94 | 95 | left -= read; 96 | } 97 | while ( left > 0 ); 98 | } 99 | 100 | public static void copy( InputStream input, OutputStream output, int bufferSize ) 101 | throws IOException 102 | { 103 | final byte[] buffer = new byte[bufferSize]; 104 | int n; 105 | while ( -1 != (n = input.read( buffer )) ) 106 | { 107 | output.write( buffer, 0, n ); 108 | } 109 | } 110 | 111 | 112 | 113 | public static void close( Closeable reader ) 114 | { 115 | if ( reader != null ) 116 | { 117 | try 118 | { 119 | reader.close(); 120 | } 121 | catch ( IOException e ) 122 | { 123 | // ignore 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/FileNameTooLongException.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /** 28 | * @author Trygve Laugstøl 29 | */ 30 | public class FileNameTooLongException 31 | extends RuntimeException { 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/InvalidArchiveMagicException.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /** 28 | * @author Trygve Laugstøl 29 | */ 30 | public class InvalidArchiveMagicException 31 | extends RuntimeException { 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/InvalidFileMagicException.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /** 28 | * @author Trygve Laugstøl 29 | */ 30 | public class InvalidFileMagicException 31 | extends RuntimeException { 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/ar/ReadableArFile.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | /* 4 | * The MIT License 5 | * 6 | * Copyright 2009 The Codehaus. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | 30 | import org.codehaus.plexus.util.IOUtil; 31 | 32 | /** 33 | * @author Trygve Laugstøl 34 | */ 35 | public class ReadableArFile 36 | extends ArFile { 37 | private InputStream inputStream; 38 | private ArFileInputStream fileInputStream; 39 | private long left; 40 | 41 | public ReadableArFile(InputStream inputStream) { 42 | this.inputStream = inputStream; 43 | } 44 | 45 | public InputStream open() { 46 | if (inputStream == null) { 47 | throw new RuntimeException("This file has already been read"); 48 | } 49 | 50 | return fileInputStream = new ArFileInputStream(); 51 | } 52 | 53 | void close() { 54 | // If the file havent been opened, skip the bytes 55 | if (fileInputStream == null) { 56 | fileInputStream = new ArFileInputStream(); 57 | } 58 | 59 | IOUtil.close(fileInputStream); 60 | } 61 | 62 | private class ArFileInputStream 63 | extends InputStream { 64 | private InputStream inputStream; 65 | 66 | public ArFileInputStream() { 67 | this.inputStream = ReadableArFile.this.inputStream; 68 | ReadableArFile.this.inputStream = null; 69 | left = size; 70 | } 71 | 72 | public int read() 73 | throws IOException { 74 | if (left <= 0) { 75 | return -1; 76 | } 77 | 78 | int i = inputStream.read(); 79 | 80 | if (i == -1) { 81 | return -1; 82 | } 83 | 84 | left--; 85 | return i; 86 | } 87 | 88 | public int read(byte b[], int off, int len) 89 | throws IOException { 90 | if (left <= 0) { 91 | return -1; 92 | } 93 | 94 | if (len > left) { 95 | len = (int) left; 96 | } 97 | 98 | int i = inputStream.read(b, off, len); 99 | 100 | left -= i; 101 | 102 | return i; 103 | } 104 | 105 | public long skip(long n) 106 | throws IOException { 107 | throw new IOException("Not supported"); 108 | } 109 | 110 | public int available() 111 | throws IOException { 112 | return (int) left; 113 | } 114 | 115 | public void close() 116 | throws IOException { 117 | // TODO: Make sure that we read out all the bytes from the underlying input stream 118 | if (left != 0) { 119 | ArUtil.skipBytes(inputStream, left); 120 | } 121 | 122 | // Read the extra pad byte if size is odd 123 | if (size % 2 == 1) { 124 | ArUtil.skipBytes(inputStream, 1); 125 | } 126 | } 127 | 128 | public synchronized void mark(int readlimit) { 129 | throw new RuntimeException("Not supported"); 130 | } 131 | 132 | public synchronized void reset() 133 | throws IOException { 134 | throw new RuntimeException("Not supported"); 135 | } 136 | 137 | public boolean markSupported() { 138 | return false; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/DebianFileManager.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.ExecutionException; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Singleton; 11 | 12 | import com.google.common.cache.Cache; 13 | import com.google.common.cache.CacheBuilder; 14 | 15 | import com.inventage.nexusaptplugin.cache.generators.PackagesGenerator; 16 | import com.inventage.nexusaptplugin.cache.generators.PackagesGzGenerator; 17 | import com.inventage.nexusaptplugin.cache.generators.ReleaseGPGGenerator; 18 | import com.inventage.nexusaptplugin.cache.generators.ReleaseGenerator; 19 | import com.inventage.nexusaptplugin.cache.generators.SignKeyGenerator; 20 | import com.inventage.nexusaptplugin.sign.AptSigningConfiguration; 21 | 22 | @Singleton 23 | public class DebianFileManager { 24 | 25 | private Cache cache; 26 | 27 | private final Map generators; 28 | 29 | @Inject 30 | public DebianFileManager(AptSigningConfiguration aptSigningConfiguration) { 31 | this.cache = CacheBuilder.newBuilder() 32 | .expireAfterWrite(Integer.parseInt(System.getProperty("DebianFileManager.cacheTimeoutSeconds", "5")), TimeUnit.SECONDS) 33 | .build(); 34 | 35 | this.generators = new HashMap(); 36 | 37 | registerGenerator("Packages", new PackagesGenerator()); 38 | registerGenerator("Packages.gz", new PackagesGzGenerator(this)); 39 | registerGenerator("Release", new ReleaseGenerator(this)); 40 | registerGenerator("Release.gpg", new ReleaseGPGGenerator(this, aptSigningConfiguration)); 41 | registerGenerator("apt-key.gpg.key", new SignKeyGenerator(aptSigningConfiguration)); 42 | } 43 | 44 | public void registerGenerator(String name, FileGenerator generator) { 45 | generators.put(name, generator); 46 | } 47 | 48 | public void setTimeout(int seconds) { 49 | this.cache = CacheBuilder.newBuilder() 50 | .expireAfterWrite(seconds, TimeUnit.SECONDS) 51 | .build(); 52 | } 53 | 54 | public byte[] getFile(final String name, final RepositoryData data) throws ExecutionException { 55 | String key = data.getRepositoryId() + "#" + name; 56 | if (!generators.containsKey(name)) { 57 | throw new IllegalArgumentException("Don't know how to generate " + name); 58 | } 59 | 60 | return cache.get(key, new Callable() { 61 | @Override 62 | public byte[] call() 63 | throws Exception { 64 | return generators.get(name).generateFile(data); 65 | } 66 | }); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/FileGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache; 2 | 3 | 4 | public interface FileGenerator { 5 | 6 | /** 7 | * Generate the file 8 | * 9 | * @param data 10 | * @return 11 | * @throws Exception 12 | */ 13 | byte[] generateFile(RepositoryData data) 14 | throws Exception; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/RepositoryData.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache; 2 | 3 | import org.apache.maven.index.ArtifactInfoFilter; 4 | import org.apache.maven.index.Indexer; 5 | import org.apache.maven.index.context.IndexingContext; 6 | 7 | public class RepositoryData { 8 | private final String repositoryId; 9 | 10 | private final IndexingContext indexingContext; 11 | 12 | private final ArtifactInfoFilter artifactInfoFilter; 13 | 14 | private final Indexer indexer; 15 | 16 | public RepositoryData(String repositoryId, IndexingContext indexingContext, 17 | ArtifactInfoFilter artifactInfoFilter, Indexer indexer) { 18 | this.repositoryId = repositoryId; 19 | this.indexingContext = indexingContext; 20 | this.artifactInfoFilter = artifactInfoFilter; 21 | this.indexer = indexer; 22 | } 23 | 24 | public String getRepositoryId() { 25 | return repositoryId; 26 | } 27 | 28 | public IndexingContext getIndexingContext() { 29 | return indexingContext; 30 | } 31 | 32 | public ArtifactInfoFilter getArtifactInfoFilter() { 33 | return artifactInfoFilter; 34 | } 35 | 36 | public Indexer getIndexer() { 37 | return indexer; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/generators/PackagesGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache.generators; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStreamWriter; 6 | import java.util.Map; 7 | 8 | import org.apache.lucene.search.Query; 9 | import org.apache.maven.index.ArtifactInfo; 10 | import org.apache.maven.index.Field; 11 | import org.apache.maven.index.Indexer; 12 | import org.apache.maven.index.IteratorSearchRequest; 13 | import org.apache.maven.index.IteratorSearchResponse; 14 | import org.apache.maven.index.MAVEN; 15 | import org.apache.maven.index.SearchType; 16 | import org.apache.maven.index.context.IndexingContext; 17 | import org.apache.maven.index.expr.SearchTypedStringSearchExpression; 18 | 19 | import com.inventage.nexusaptplugin.DEBIAN; 20 | import com.inventage.nexusaptplugin.cache.FileGenerator; 21 | import com.inventage.nexusaptplugin.cache.RepositoryData; 22 | 23 | 24 | public class PackagesGenerator 25 | implements FileGenerator { 26 | /* Taken from http://www.debian.org/doc/manuals/debian-reference/ch02.en.html#_package_dependencies */ 27 | private static final Field[] PACKAGE_DEPENDENCIES_FIELD = new Field[]{ 28 | DEBIAN.DEPENDS, DEBIAN.PRE_DEPENDS, DEBIAN.PROVIDES, DEBIAN.RECOMMENDS, DEBIAN.SUGGESTS, DEBIAN.ENHANCES, 29 | DEBIAN.BREAKS, DEBIAN.CONFLICTS, DEBIAN.REPLACES 30 | }; 31 | 32 | public PackagesGenerator() { 33 | } 34 | 35 | @Override 36 | public byte[] generateFile(RepositoryData data) 37 | throws Exception { 38 | final Indexer indexer = data.getIndexer(); 39 | final IndexingContext indexingContext = data.getIndexingContext(); 40 | 41 | final Query pq = indexer.constructQuery(MAVEN.PACKAGING, new SearchTypedStringSearchExpression("deb", SearchType.EXACT)); 42 | 43 | final IteratorSearchRequest sreq = new IteratorSearchRequest(pq, indexingContext); 44 | final IteratorSearchResponse hits = indexer.searchIterator(sreq); 45 | 46 | final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 47 | final OutputStreamWriter w = new OutputStreamWriter(baos); 48 | 49 | for (ArtifactInfo hit : hits) { 50 | Map attrs = hit.getAttributes(); 51 | if (attrs.get("Package") == null || attrs.get("Version") == null 52 | || attrs.get("Filename") == null) { 53 | // This won't produce a real artifact, ignore it 54 | continue; 55 | } 56 | 57 | String sha256 = attrs.get(DEBIAN.SHA256.getFieldName()); 58 | String sha512 = attrs.get(DEBIAN.SHA512.getFieldName()); 59 | 60 | // Verify that this is a valid artifact 61 | w.write("Package: " + attrs.get("Package") + "\n"); 62 | w.write("Version: " + attrs.get("Version") + "\n"); 63 | w.write("Architecture: " + attrs.get("Architecture") + "\n"); 64 | w.write("Maintainer: " + attrs.get("Maintainer") + "\n"); 65 | w.write("Installed-Size: " + attrs.get("Installed-Size") + "\n"); 66 | /* Those are not mandatory */ 67 | for (Field fieldName : PACKAGE_DEPENDENCIES_FIELD) { 68 | writeIfNonEmpty(w, hit, fieldName); 69 | } 70 | w.write("Filename: " + attrs.get("Filename") + "\n"); 71 | w.write("Size: " + hit.size + "\n"); 72 | w.write("MD5sum: " + hit.md5 + "\n"); 73 | w.write("SHA1: " + hit.sha1 + "\n"); 74 | 75 | if(sha256 != null) { 76 | w.write("SHA256: " + sha256 + "\n"); 77 | } 78 | if(sha512 != null) { 79 | w.write("SHA512: " + sha512 + "\n"); 80 | } 81 | 82 | w.write("Section: " + attrs.get("Section") + "\n"); 83 | w.write("Priority: " + attrs.get("Priority") + "\n"); 84 | w.write("Description: " + (attrs.get("Description") != null ? (attrs.get("Description").replace("\n", "\n ")) : "") + "\n"); 85 | w.write("\n"); 86 | } 87 | w.close(); 88 | return baos.toByteArray(); 89 | } 90 | 91 | private void writeIfNonEmpty(OutputStreamWriter w, ArtifactInfo hit, Field field) 92 | throws IOException { 93 | String fieldValue = hit.getAttributes().get(field.getFieldName()); 94 | if (fieldValue != null && !fieldValue.isEmpty()) { 95 | w.write(field.getFieldName() + ": " + fieldValue + "\n"); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/generators/PackagesGzGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache.generators; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.util.zip.GZIPOutputStream; 5 | 6 | import javax.inject.Inject; 7 | 8 | import com.inventage.nexusaptplugin.cache.DebianFileManager; 9 | import com.inventage.nexusaptplugin.cache.FileGenerator; 10 | import com.inventage.nexusaptplugin.cache.RepositoryData; 11 | 12 | 13 | public class PackagesGzGenerator 14 | implements FileGenerator { 15 | private final DebianFileManager fileManager; 16 | 17 | @Inject 18 | public PackagesGzGenerator(DebianFileManager fileManager) { 19 | this.fileManager = fileManager; 20 | } 21 | 22 | @Override 23 | public byte[] generateFile(RepositoryData data) 24 | throws Exception { 25 | byte[] packages = fileManager.getFile("Packages", data); 26 | 27 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 28 | GZIPOutputStream gz = new GZIPOutputStream(baos); 29 | 30 | gz.write(packages); 31 | 32 | gz.close(); 33 | 34 | return baos.toByteArray(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/generators/ReleaseGPGGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache.generators; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.bouncycastle.bcpg.ArmoredOutputStream; 8 | import org.bouncycastle.bcpg.BCPGOutputStream; 9 | import org.bouncycastle.openpgp.PGPSignature; 10 | import org.bouncycastle.openpgp.PGPSignatureGenerator; 11 | import org.bouncycastle.openpgp.PGPUtil; 12 | import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; 13 | 14 | import com.inventage.nexusaptplugin.cache.DebianFileManager; 15 | import com.inventage.nexusaptplugin.cache.FileGenerator; 16 | import com.inventage.nexusaptplugin.cache.RepositoryData; 17 | import com.inventage.nexusaptplugin.sign.AptSigningConfiguration; 18 | import com.inventage.nexusaptplugin.sign.PGPSigner; 19 | 20 | 21 | public class ReleaseGPGGenerator implements FileGenerator { 22 | 23 | private final DebianFileManager fileManager; 24 | private final AptSigningConfiguration aptSigningConfiguration; 25 | 26 | @Inject 27 | public ReleaseGPGGenerator(DebianFileManager fileManager, 28 | AptSigningConfiguration aptSigningConfiguration) { 29 | this.fileManager = fileManager; 30 | this.aptSigningConfiguration = aptSigningConfiguration; 31 | } 32 | 33 | @Override 34 | public byte[] generateFile(RepositoryData data) 35 | throws Exception { 36 | // Read Release 37 | byte[] release = fileManager.getFile("Release", data); 38 | 39 | // Get the key and sign the Release file 40 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 41 | 42 | PGPSigner signer = this.aptSigningConfiguration.getSigner(); 43 | PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(signer.getSecretKey().getPublicKey().getAlgorithm(), PGPUtil.SHA256)); 44 | signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signer.getPrivateKey()); 45 | 46 | BCPGOutputStream out = new BCPGOutputStream(new ArmoredOutputStream(baos)); 47 | signatureGenerator.update(release); 48 | signatureGenerator.generate().encode(out); 49 | 50 | out.close(); 51 | baos.close(); 52 | 53 | return baos.toByteArray(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/generators/ReleaseGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache.generators; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.OutputStreamWriter; 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | import javax.inject.Inject; 11 | 12 | import org.apache.commons.codec.binary.Hex; 13 | 14 | import com.inventage.nexusaptplugin.cache.DebianFileManager; 15 | import com.inventage.nexusaptplugin.cache.FileGenerator; 16 | import com.inventage.nexusaptplugin.cache.RepositoryData; 17 | import java.text.DateFormat; 18 | import java.text.SimpleDateFormat; 19 | import java.util.Date; 20 | import java.util.Locale; 21 | import java.util.TimeZone; 22 | 23 | 24 | public class ReleaseGenerator 25 | implements FileGenerator { 26 | /** 27 | * Enum for the hash algorithms to include included 28 | */ 29 | private static enum Algorithm { 30 | MD5("MD5Sum", "MD5"), 31 | SHA1("SHA1", "SHA1"), 32 | SHA256("SHA256", "SHA256"); 33 | 34 | final String heading; 35 | 36 | final String name; 37 | 38 | private Algorithm(String heading, String name) { 39 | this.heading = heading; 40 | this.name = name; 41 | } 42 | } 43 | 44 | private final String[] FILES = new String[]{"Packages", "Packages.gz"}; 45 | 46 | private final DebianFileManager fileManager; 47 | 48 | @Inject 49 | public ReleaseGenerator(DebianFileManager fileManager) { 50 | this.fileManager = fileManager; 51 | } 52 | 53 | @Override 54 | public byte[] generateFile(RepositoryData data) 55 | throws Exception { 56 | // Gather files 57 | List files = new LinkedList(); 58 | int maxSizeLength = 0; 59 | for (String name : FILES) { 60 | byte[] contents = fileManager.getFile(name, data); 61 | File file = new File(); 62 | file.name = name; 63 | file.contents = contents; 64 | file.size = String.valueOf(contents.length); 65 | files.add(file); 66 | 67 | maxSizeLength = Math.max(maxSizeLength, file.size.length()); 68 | } 69 | 70 | // Create Releases 71 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 72 | OutputStreamWriter w = new OutputStreamWriter(baos); 73 | 74 | // write date to fix apt-get update on version 1.1.10 or newer 75 | w.write("Date: "); 76 | w.write(formatDate(new Date())); 77 | w.write("\n"); 78 | 79 | for (Algorithm algorithm : Algorithm.values()) { 80 | try { 81 | MessageDigest md = MessageDigest.getInstance(algorithm.name); 82 | w.write(algorithm.heading); 83 | w.write(":\n"); 84 | 85 | for (File file : files) { 86 | md.reset(); 87 | md.update(file.contents); 88 | byte[] digest = md.digest(); 89 | w.write(" "); 90 | w.write(Hex.encodeHexString(digest)); 91 | for (int i = 0; i <= maxSizeLength - file.size.length(); i++) { 92 | w.write(" "); 93 | } 94 | w.write(file.size); 95 | w.write(" "); 96 | w.write(file.name); 97 | w.write("\n"); 98 | } 99 | } 100 | catch (NoSuchAlgorithmException e) { 101 | // guess there's not much we can do... 102 | throw new RuntimeException(e); 103 | } 104 | } 105 | w.close(); 106 | 107 | return baos.toByteArray(); 108 | } 109 | 110 | private String formatDate(Date date) { 111 | // RFC 2822 format 112 | final DateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH); 113 | format.setTimeZone(TimeZone.getTimeZone("GMT")); 114 | return format.format(date); 115 | } 116 | 117 | private static final class File { 118 | private String name; 119 | 120 | private byte[] contents; 121 | 122 | private String size; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/cache/generators/SignKeyGenerator.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.cache.generators; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | import javax.inject.Inject; 6 | 7 | import org.bouncycastle.bcpg.ArmoredOutputStream; 8 | import org.bouncycastle.bcpg.BCPGOutputStream; 9 | import org.bouncycastle.openpgp.PGPPublicKey; 10 | 11 | import com.inventage.nexusaptplugin.cache.FileGenerator; 12 | import com.inventage.nexusaptplugin.cache.RepositoryData; 13 | import com.inventage.nexusaptplugin.sign.AptSigningConfiguration; 14 | import com.inventage.nexusaptplugin.sign.PGPSigner; 15 | 16 | 17 | public class SignKeyGenerator implements FileGenerator { 18 | 19 | private final AptSigningConfiguration configuration; 20 | 21 | @Inject 22 | public SignKeyGenerator(AptSigningConfiguration configuration) { 23 | this.configuration = configuration; 24 | } 25 | 26 | @Override 27 | public byte[] generateFile(RepositoryData data) throws Exception { 28 | // Extract the key and return it 29 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 30 | 31 | PGPSigner signer = configuration.getSigner(); 32 | PGPPublicKey publicKey = signer.getSecretKey().getPublicKey(); 33 | 34 | BCPGOutputStream out = new BCPGOutputStream(new ArmoredOutputStream(baos)); 35 | publicKey.encode(out); 36 | 37 | out.close(); 38 | baos.close(); 39 | 40 | return baos.toByteArray(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptCapabilitiesBooter.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.capabilities; 2 | 3 | import javax.inject.Named; 4 | import javax.inject.Singleton; 5 | 6 | import org.sonatype.nexus.plugins.capabilities.CapabilityRegistry; 7 | import org.sonatype.nexus.plugins.capabilities.support.CapabilityBooterSupport; 8 | 9 | @Named 10 | @Singleton 11 | public class AptCapabilitiesBooter 12 | extends CapabilityBooterSupport { 13 | 14 | @Override 15 | protected void boot(final CapabilityRegistry registry) throws Exception { 16 | maybeAddCapability( 17 | registry, 18 | AptCapabilityDescriptor.TYPE, 19 | true, // enabled 20 | null, // no notes 21 | new AptCapabilityConfiguration().asMap() 22 | ); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptCapability.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.capabilities; 2 | 3 | import java.util.Map; 4 | 5 | import javax.inject.Inject; 6 | import javax.inject.Named; 7 | 8 | import org.sonatype.nexus.capability.support.CapabilitySupport; 9 | import org.sonatype.sisu.goodies.eventbus.EventBus; 10 | 11 | import com.inventage.nexusaptplugin.sign.AptSigningConfiguration; 12 | 13 | @Named(AptCapabilityDescriptor.TYPE_ID) 14 | public class AptCapability extends CapabilitySupport { 15 | 16 | private final EventBus eventBus; 17 | 18 | @Inject 19 | public AptCapability(EventBus eventBus, AptSigningConfiguration aptSigningConfiguration) { 20 | this.eventBus = eventBus; 21 | } 22 | 23 | @Override 24 | protected AptCapabilityConfiguration createConfig(Map properties) throws Exception { 25 | return new AptCapabilityConfiguration(properties); 26 | } 27 | 28 | @Override 29 | protected void onUpdate(AptCapabilityConfiguration config) throws Exception { 30 | eventBus.post(new AptSigningUpdatedEvent(this, config)); 31 | } 32 | 33 | @Override 34 | public void onActivate(AptCapabilityConfiguration config) { 35 | eventBus.post(new AptSigningActivatedEvent(this, config)); 36 | } 37 | 38 | @Override 39 | public void onPassivate(AptCapabilityConfiguration config) throws Exception { 40 | eventBus.post(new AptSigningDeactivatedEvent(this)); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return this.getClass().getSimpleName(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptCapabilityConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2007-2013 Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package com.inventage.nexusaptplugin.capabilities; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * Configuration adapter for {@link AptCapability}. 20 | * 21 | * @since 3.0 22 | */ 23 | public class AptCapabilityConfiguration { 24 | 25 | public static final String KEYRING = "keyring"; 26 | 27 | public static final String KEY = "key"; 28 | 29 | public static final String PASSPHRASE = "passphrase"; 30 | 31 | private String keyring; 32 | private String key; 33 | private String passphrase; 34 | 35 | public AptCapabilityConfiguration() { 36 | this(null, null, null); 37 | } 38 | 39 | public AptCapabilityConfiguration(String keyring, String key, String passphrase) { 40 | this.keyring = keyring == null ? "" : keyring; 41 | this.key = key == null ? "" : key; 42 | this.passphrase = passphrase == null ? "" : passphrase; 43 | } 44 | 45 | public AptCapabilityConfiguration(final Map properties) { 46 | this(properties.get(KEYRING), properties.get(KEY), properties.get(PASSPHRASE)); 47 | } 48 | 49 | public String getKeyring() { 50 | return keyring; 51 | } 52 | 53 | public void setKeyring(String keyring) { 54 | this.keyring = keyring; 55 | } 56 | 57 | public String getKey() { 58 | return key; 59 | } 60 | 61 | public void setKey(String key) { 62 | this.key = key; 63 | } 64 | 65 | public String getPassphrase() { 66 | return passphrase; 67 | } 68 | 69 | public void setPassphrase(String passphrase) { 70 | this.passphrase = passphrase; 71 | } 72 | 73 | public Map asMap() { 74 | Map map = new HashMap(); 75 | map.put(KEYRING, keyring); 76 | map.put(KEY, key); 77 | map.put(PASSPHRASE, passphrase); 78 | 79 | return map; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptCapabilityDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonatype Nexus (TM) Open Source Version 3 | * Copyright (c) 2007-2013 Sonatype, Inc. 4 | * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. 5 | * 6 | * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, 7 | * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. 8 | * 9 | * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks 10 | * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the 11 | * Eclipse Foundation. All other trademarks are the property of their respective owners. 12 | */ 13 | package com.inventage.nexusaptplugin.capabilities; 14 | 15 | import static org.sonatype.nexus.plugins.capabilities.CapabilityType.capabilityType; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | import javax.inject.Inject; 21 | import javax.inject.Named; 22 | import javax.inject.Singleton; 23 | 24 | import org.sonatype.nexus.capability.support.CapabilityDescriptorSupport; 25 | import org.sonatype.nexus.formfields.FormField; 26 | import org.sonatype.nexus.formfields.StringTextFormField; 27 | import org.sonatype.nexus.plugins.capabilities.CapabilityDescriptor; 28 | import org.sonatype.nexus.plugins.capabilities.CapabilityIdentity; 29 | import org.sonatype.nexus.plugins.capabilities.CapabilityType; 30 | import org.sonatype.nexus.plugins.capabilities.Validator; 31 | import org.sonatype.nexus.plugins.capabilities.support.validator.Validators; 32 | 33 | /** 34 | * @since 3.0 35 | */ 36 | @Singleton 37 | @Named(AptCapabilityDescriptor.TYPE_ID) 38 | public class AptCapabilityDescriptor 39 | extends CapabilityDescriptorSupport 40 | implements CapabilityDescriptor { 41 | 42 | public static final String TYPE_ID = "apt"; 43 | 44 | public static final CapabilityType TYPE = capabilityType(TYPE_ID); 45 | 46 | private final Validators validators; 47 | 48 | @Inject 49 | public AptCapabilityDescriptor(final Validators validators) { 50 | this.validators = validators; 51 | } 52 | 53 | @Override 54 | public CapabilityType type() { 55 | return TYPE; 56 | } 57 | 58 | @Override 59 | public String name() { 60 | return "APT: Configuration"; 61 | } 62 | 63 | @Override 64 | public String about() { 65 | return "APT plugin configuration."; 66 | } 67 | 68 | @Override 69 | public List formFields() { 70 | return Arrays.asList( 71 | new StringTextFormField( 72 | AptCapabilityConfiguration.KEYRING, 73 | "Secure keyring location", 74 | "The location of the GNU PG secure keyring to be used for signing", 75 | FormField.OPTIONAL 76 | ), 77 | new StringTextFormField( 78 | AptCapabilityConfiguration.KEY, 79 | "Key ID", 80 | "ID of the key in the secure keyring to be used for signing", 81 | FormField.MANDATORY 82 | ), 83 | new StringTextFormField( 84 | AptCapabilityConfiguration.PASSPHRASE, 85 | "Passphrase for the key", 86 | "Passphrase for the key to be used for signing", 87 | FormField.MANDATORY 88 | )); 89 | } 90 | 91 | @Override 92 | public Validator validator() { 93 | return validators.logical().and( 94 | validators.capability().uniquePer(TYPE) 95 | ); 96 | } 97 | 98 | @Override 99 | public Validator validator(final CapabilityIdentity id) { 100 | return validators.logical().and( 101 | validators.capability().uniquePerExcluding(id, TYPE) 102 | ); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptCapabilityEvent.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.capabilities; 2 | 3 | import org.sonatype.nexus.events.AbstractEvent; 4 | 5 | /** 6 | * Superclass for all events originating in the {@link AptCapability}. 7 | * 8 | * @author Dominik Menzi 9 | */ 10 | abstract class AptCapabilityEvent extends AbstractEvent { 11 | public AptCapabilityEvent(AptCapability component) { 12 | super(component); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptSigningActivatedEvent.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.capabilities; 2 | 3 | /** 4 | * Event representing the activation of apt signing. 5 | * 6 | * @author Dominik Menzi 7 | */ 8 | public class AptSigningActivatedEvent extends AptSigningUpdatedEvent { 9 | 10 | public AptSigningActivatedEvent(AptCapability component, AptCapabilityConfiguration aptCapabilityConfiguration) { 11 | super(component, aptCapabilityConfiguration); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptSigningDeactivatedEvent.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.capabilities; 2 | 3 | /** 4 | * Event representing the deactivation of apt signing. 5 | * 6 | * @author Dominik Menzi 7 | */ 8 | public class AptSigningDeactivatedEvent extends AptCapabilityEvent { 9 | public AptSigningDeactivatedEvent(AptCapability component) { 10 | super(component); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/capabilities/AptSigningUpdatedEvent.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.capabilities; 2 | 3 | /** 4 | * Event representing the updating of apt signing, e.g. a configuration change. 5 | * 6 | * @author Dominik Menzi 7 | */ 8 | public class AptSigningUpdatedEvent extends AptCapabilityEvent { 9 | 10 | private final AptCapabilityConfiguration aptCapabilityConfiguration; 11 | 12 | public AptSigningUpdatedEvent(AptCapability component, AptCapabilityConfiguration aptCapabilityConfiguration) { 13 | super(component); 14 | this.aptCapabilityConfiguration = aptCapabilityConfiguration; 15 | } 16 | 17 | /** 18 | * @return the new configuration 19 | */ 20 | public AptCapabilityConfiguration getAptCapabilityConfiguration() { 21 | return aptCapabilityConfiguration; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/deb/DebControlParser.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.deb; 2 | 3 | 4 | import java.io.IOException; 5 | import java.util.HashMap; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | 11 | public class DebControlParser { 12 | 13 | public static Map parse(List allLines) 14 | throws IOException { 15 | LinkedList rest = new LinkedList(allLines); 16 | 17 | Map control = new HashMap(); 18 | String lastKey = null; 19 | 20 | while (!rest.isEmpty()) { 21 | String line = rest.removeFirst(); 22 | 23 | int i = line.indexOf(':'); 24 | 25 | if (i > 0 && !line.startsWith(" ")) { 26 | String parts[] = line.split(":", 2); 27 | lastKey = parts[0]; 28 | control.put(lastKey, parts[1].trim()); 29 | } 30 | else { 31 | control.put(lastKey, control.get(lastKey) + "\n" + line.trim()); 32 | } 33 | 34 | 35 | } 36 | 37 | return control; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/deb/GetControl.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.deb; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | import org.apache.commons.compress.archivers.ArchiveEntry; 9 | import org.apache.commons.compress.archivers.ArchiveException; 10 | import org.apache.commons.compress.archivers.ArchiveInputStream; 11 | import org.apache.commons.compress.archivers.ArchiveStreamFactory; 12 | import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 13 | import org.apache.commons.io.IOUtils; 14 | 15 | import com.inventage.nexusaptplugin.ar.ArReader; 16 | import com.inventage.nexusaptplugin.ar.ReadableArFile; 17 | 18 | public class GetControl { 19 | public static List doGet(File deb) throws IOException { 20 | ArReader arReader = new ArReader(deb); 21 | for (ReadableArFile readableArFile : arReader) { 22 | if ("control.tar.gz".equals(readableArFile.getName())) { 23 | ArchiveInputStream input = null; 24 | try { 25 | input = new ArchiveStreamFactory() 26 | .createArchiveInputStream(new BufferedInputStream(new GzipCompressorInputStream(readableArFile.open()))); 27 | } 28 | catch (ArchiveException e) { 29 | throw new IOException(e); 30 | } 31 | ArchiveEntry ae; 32 | while ((ae = input.getNextEntry()) != null) { 33 | if (ae.getName().endsWith("control")) { 34 | return IOUtils.readLines(input); 35 | } 36 | } 37 | } 38 | } 39 | return null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/sign/AptSigningConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.sign; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.util.Collection; 8 | import java.util.LinkedHashSet; 9 | import java.util.LinkedList; 10 | 11 | import javax.inject.Named; 12 | import javax.inject.Singleton; 13 | 14 | import org.bouncycastle.openpgp.PGPException; 15 | import org.sonatype.nexus.events.EventSubscriber; 16 | 17 | import com.google.common.eventbus.Subscribe; 18 | 19 | import com.inventage.nexusaptplugin.capabilities.AptCapabilityConfiguration; 20 | import com.inventage.nexusaptplugin.capabilities.AptSigningDeactivatedEvent; 21 | import com.inventage.nexusaptplugin.capabilities.AptSigningUpdatedEvent; 22 | 23 | @Named 24 | @Singleton 25 | public class AptSigningConfiguration implements EventSubscriber { 26 | 27 | private String keyring; 28 | private String key; 29 | private String passphrase; 30 | 31 | @Subscribe 32 | public void onSigningUpdated(AptSigningUpdatedEvent event) { 33 | final AptCapabilityConfiguration aptCapabilityConfiguration = event.getAptCapabilityConfiguration(); 34 | keyring = aptCapabilityConfiguration.getKeyring(); 35 | key = aptCapabilityConfiguration.getKey(); 36 | passphrase = aptCapabilityConfiguration.getPassphrase(); 37 | } 38 | 39 | @Subscribe 40 | public void onSigningDeactivated(AptSigningDeactivatedEvent event) { 41 | keyring = null; 42 | key = null; 43 | passphrase = null; 44 | } 45 | 46 | public PGPSigner getSigner() throws IOException, PGPException { 47 | String ring = keyring; 48 | if (ring == null || ring.isEmpty()) { 49 | ring = null; 50 | LinkedList possibleLocations = new LinkedList(getPossiblePGPSecureRingLocations()); 51 | for (String location : possibleLocations) { 52 | if (new File(location).exists()) { 53 | ring = location; 54 | break; 55 | } 56 | } 57 | // If it's still null, throw a FileNotFoundException 58 | if (ring == null) { 59 | throw new FileNotFoundException("Keyring location is not set and it could not be found automatically"); 60 | } 61 | } 62 | FileInputStream stream = new FileInputStream(ring); 63 | return new PGPSigner(stream, key, passphrase); 64 | } 65 | 66 | /** 67 | * Get the possible locations where the secure keyring can be located. 68 | * Looks through known locations of the GNU PG secure keyring. 69 | * return null; 70 | * 71 | * @return The location of the PGP secure keyring if it was found, 72 | * null otherwise 73 | */ 74 | private static Collection getPossiblePGPSecureRingLocations() { 75 | LinkedHashSet locations = new LinkedHashSet(); 76 | 77 | // The user's roaming profile on Windows, via environment 78 | String windowsRoaming = System.getenv("APPDATA"); 79 | if (windowsRoaming != null) { 80 | locations.add(joinPaths(windowsRoaming, "gnupg", "secring.gpg")); 81 | } 82 | 83 | // The user's local profile on Windows, via environment 84 | String windowsLocal = System.getenv("LOCALAPPDATA"); 85 | if (windowsLocal != null) { 86 | locations.add(joinPaths(windowsLocal, "gnupg", "secring.gpg")); 87 | } 88 | 89 | // The user's home directory 90 | String home = System.getProperty("user.home"); 91 | if (home != null) { 92 | // *nix, including OS X 93 | locations.add(joinPaths(home, ".gnupg", "secring.gpg")); 94 | 95 | // These are for various flavours of Windows if the environment variables above should fail 96 | locations.add(joinPaths(home, "AppData", "Roaming", "gnupg", "secring.gpg")); // Roaming profile on Vista and later 97 | locations.add(joinPaths(home, "AppData", "Local", "gnupg", "secring.gpg")); // Local profile on Vista and later 98 | locations.add(joinPaths(home, "Application Data", "gnupg", "secring.gpg")); // Roaming profile on 2000 and XP 99 | locations.add(joinPaths(home, "Local Settings", "Application Data", "gnupg", "secring.gpg")); // Local profile on 2000 and XP 100 | } 101 | 102 | // The Windows installation directory 103 | String windir = System.getProperty("WINDIR"); 104 | if (windir != null) { 105 | // Local Profile on Windows 98 and ME 106 | locations.add(joinPaths(windir, "Application Data", "gnupg", "secring.gpg")); 107 | } 108 | 109 | return locations; 110 | } 111 | 112 | /** 113 | * Join together path elements with File.separator. Filters out null 114 | * elements. 115 | * 116 | * @param elements The path elements to join 117 | * @return elements concatenated together with File.separator 118 | */ 119 | private static String joinPaths(String... elements) { 120 | StringBuilder builder = new StringBuilder(); 121 | boolean first = true; 122 | for (int i = 0; i < elements.length; i++) { 123 | // Skip null elements 124 | if (elements[i] == null) { 125 | // This won't change the value of first if we skip elements 126 | // in the beginning of the array 127 | continue; 128 | } 129 | if (!first) { 130 | builder.append(File.separatorChar); 131 | } 132 | builder.append(elements[i]); 133 | first = false; 134 | } 135 | return builder.toString(); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/sign/PGPSignatureOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.sign; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.security.SignatureException; 7 | 8 | import org.bouncycastle.bcpg.ArmoredOutputStream; 9 | import org.bouncycastle.openpgp.PGPException; 10 | import org.bouncycastle.openpgp.PGPSignature; 11 | import org.bouncycastle.openpgp.PGPSignatureGenerator; 12 | 13 | /** 14 | * An output stream that calculates the signature of the input data as it 15 | * is written 16 | * 17 | * @author mpoindexter 18 | */ 19 | public class PGPSignatureOutputStream extends OutputStream { 20 | private final PGPSignatureGenerator signatureGenerator; 21 | 22 | public PGPSignatureOutputStream(PGPSignatureGenerator signatureGenerator) { 23 | super(); 24 | this.signatureGenerator = signatureGenerator; 25 | } 26 | 27 | public void write(int b) throws IOException { 28 | try { 29 | signatureGenerator.update(new byte[]{(byte) b}); 30 | } 31 | catch (SignatureException e) { 32 | throw new IOException(e); 33 | } 34 | } 35 | 36 | public void write(byte[] b) throws IOException { 37 | try { 38 | signatureGenerator.update(b); 39 | } 40 | catch (SignatureException e) { 41 | throw new IOException(e); 42 | } 43 | } 44 | 45 | public void write(byte[] b, int off, int len) throws IOException { 46 | try { 47 | signatureGenerator.update(b, off, len); 48 | } 49 | catch (SignatureException e) { 50 | throw new IOException(e); 51 | } 52 | } 53 | 54 | public PGPSignature generateSignature() throws SignatureException, PGPException { 55 | return signatureGenerator.generate(); 56 | } 57 | 58 | public String generateASCIISignature() throws SignatureException, PGPException { 59 | try { 60 | PGPSignature signature = generateSignature(); 61 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 62 | ArmoredOutputStream armorStream = new ArmoredOutputStream(buffer); 63 | signature.encode(armorStream); 64 | armorStream.close(); 65 | return new String(buffer.toByteArray()); 66 | } 67 | catch (IOException e) { 68 | //Should never happen since we are just using a memory buffer 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/inventage/nexusaptplugin/sign/PGPSigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 The jdeb developers. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.inventage.nexusaptplugin.sign; 18 | 19 | import java.io.BufferedReader; 20 | import java.io.ByteArrayInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.InputStreamReader; 24 | import java.io.OutputStream; 25 | import java.nio.charset.Charset; 26 | import java.security.GeneralSecurityException; 27 | import java.util.Iterator; 28 | 29 | import org.bouncycastle.bcpg.ArmoredOutputStream; 30 | import org.bouncycastle.bcpg.BCPGOutputStream; 31 | import org.bouncycastle.openpgp.PGPException; 32 | import org.bouncycastle.openpgp.PGPPrivateKey; 33 | import org.bouncycastle.openpgp.PGPSecretKey; 34 | import org.bouncycastle.openpgp.PGPSecretKeyRing; 35 | import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 36 | import org.bouncycastle.openpgp.PGPSignature; 37 | import org.bouncycastle.openpgp.PGPSignatureGenerator; 38 | import org.bouncycastle.openpgp.PGPUtil; 39 | import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; 40 | import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; 41 | import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; 42 | 43 | /** 44 | * Signing with OpenPGP. 45 | * 46 | * @author Torsten Curdt 47 | * @author Emmanuel Bourg 48 | */ 49 | public class PGPSigner { 50 | 51 | private static final byte[] EOL = "\r\n".getBytes(Charset.forName("UTF-8")); 52 | 53 | private PGPSecretKey secretKey; 54 | private PGPPrivateKey privateKey; 55 | 56 | public PGPSigner(InputStream keyring, String keyId, String passphrase) throws IOException, PGPException { 57 | secretKey = getSecretKey(keyring, keyId); 58 | if (secretKey == null) { 59 | throw new PGPException(String.format("Specified key %s does not exist in key ring %s", keyId, keyring)); 60 | } 61 | privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passphrase.toCharArray())); 62 | } 63 | 64 | /** 65 | * Creates a clear sign signature over the input data. (Not detached) 66 | * 67 | * @param input the content to be signed 68 | * @param output the output destination of the signature 69 | */ 70 | public void clearSign(String input, OutputStream output) throws IOException, PGPException, GeneralSecurityException { 71 | clearSign(new ByteArrayInputStream(input.getBytes("UTF-8")), output); 72 | } 73 | 74 | /** 75 | * Creates a clear sign signature over the input data. (Not detached) 76 | * 77 | * @param input the content to be signed 78 | * @param output the output destination of the signature 79 | */ 80 | public void clearSign(InputStream input, OutputStream output) throws IOException, PGPException, GeneralSecurityException { 81 | int digest = PGPUtil.SHA256; 82 | 83 | PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(privateKey.getPublicKeyPacket().getAlgorithm(), digest)); 84 | signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privateKey); 85 | 86 | ArmoredOutputStream armoredOutput = new ArmoredOutputStream(output); 87 | armoredOutput.beginClearText(digest); 88 | 89 | BufferedReader reader = new BufferedReader(new InputStreamReader(input)); 90 | 91 | String line; 92 | while ((line = reader.readLine()) != null) { 93 | // trailing spaces must be removed for signature calculation (see http://tools.ietf.org/html/rfc4880#section-7.1) 94 | byte[] data = trim(line).getBytes("UTF-8"); 95 | 96 | armoredOutput.write(data); 97 | armoredOutput.write(EOL); 98 | 99 | signatureGenerator.update(data); 100 | signatureGenerator.update(EOL); 101 | } 102 | 103 | armoredOutput.endClearText(); 104 | 105 | PGPSignature signature = signatureGenerator.generate(); 106 | signature.encode(new BCPGOutputStream(armoredOutput)); 107 | 108 | armoredOutput.close(); 109 | } 110 | 111 | /** 112 | * Returns the secret key. 113 | */ 114 | public PGPSecretKey getSecretKey() { 115 | return secretKey; 116 | } 117 | 118 | /** 119 | * Returns the private key. 120 | */ 121 | public PGPPrivateKey getPrivateKey() { 122 | return privateKey; 123 | } 124 | 125 | /** 126 | * Returns the secret key matching the specified identifier. 127 | * 128 | * @param input the input stream containing the keyring collection 129 | * @param keyId the 4 bytes identifier of the key 130 | */ 131 | private PGPSecretKey getSecretKey(InputStream input, String keyId) throws IOException, PGPException { 132 | PGPSecretKeyRingCollection keyrings = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input)); 133 | 134 | Iterator rIt = keyrings.getKeyRings(); 135 | 136 | while (rIt.hasNext()) { 137 | PGPSecretKeyRing kRing = (PGPSecretKeyRing) rIt.next(); 138 | Iterator kIt = kRing.getSecretKeys(); 139 | 140 | while (kIt.hasNext()) { 141 | PGPSecretKey key = (PGPSecretKey) kIt.next(); 142 | 143 | if (key.isSigningKey() && Long.toHexString(key.getKeyID() & 0xFFFFFFFFL).equals(keyId.toLowerCase())) { 144 | return key; 145 | } 146 | } 147 | } 148 | 149 | return null; 150 | } 151 | 152 | /** 153 | * Trim the trailing spaces. 154 | * 155 | * @param line 156 | */ 157 | private String trim(String line) { 158 | char[] chars = line.toCharArray(); 159 | int len = chars.length; 160 | 161 | while (len > 0) { 162 | if (!Character.isWhitespace(chars[len - 1])) { 163 | break; 164 | } 165 | len--; 166 | } 167 | 168 | return line.substring(0, len); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/test/java/com/inventage/nexusaptplugin/ar/ArReaderTest.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.ar; 2 | 3 | 4 | import org.junit.Test; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | public class ArReaderTest { 10 | 11 | @Test 12 | public void test() throws IOException { 13 | File deb = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile(), "php5_5.3.10-1ubuntu3.2_all.deb"); 14 | ArReader arReader = new ArReader(deb); 15 | for (ReadableArFile readableArFile : arReader) { 16 | System.err.println(readableArFile.getName()); 17 | readableArFile.open(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/inventage/nexusaptplugin/deb/DebControlParserTest.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.deb; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.core.Is.is; 5 | 6 | import java.io.IOException; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import org.apache.commons.io.IOUtils; 11 | import org.junit.Test; 12 | 13 | 14 | public class DebControlParserTest { 15 | 16 | @Test 17 | public void test() throws IOException { 18 | List control = IOUtils.readLines(getClass().getResourceAsStream("/example-control")); 19 | Map values = DebControlParser.parse(control); 20 | assertThat("php5", is(values.get("Package"))); 21 | assertThat("5.3.10-1ubuntu3.2", is(values.get("Version"))); 22 | assertThat("all", is(values.get("Architecture"))); 23 | assertThat("Ubuntu Developers ", is(values.get("Maintainer"))); 24 | assertThat("server-side, HTML-embedded scripting language (metapackage)\n" + 25 | "This package is a metapackage that, when installed, guarantees that you\n" + 26 | "have at least one of the four server-side versions of the PHP5 interpreter\n" + 27 | "installed. Removing this package won't remove PHP5 from your system, however\n" + 28 | "it may remove other packages that depend on this one.\n" + 29 | ".\n" + 30 | "PHP5 is a widely-used general-purpose scripting language that is\n" + 31 | "especially suited for Web development and can be embedded into HTML.\n" + 32 | "The goal of the language is to allow web developers to write\n" + 33 | "dynamically generated pages quickly.", is(values.get("Description"))); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/inventage/nexusaptplugin/deb/GetControlTest.java: -------------------------------------------------------------------------------- 1 | package com.inventage.nexusaptplugin.deb; 2 | 3 | import org.apache.commons.compress.archivers.ArchiveException; 4 | import org.junit.Test; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: sannies 12 | * Date: 6/30/12 13 | * Time: 11:24 AM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | public class GetControlTest { 17 | 18 | @Test 19 | public void test() throws IOException, ArchiveException { 20 | File deb = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile(), "php5_5.3.10-1ubuntu3.2_all.deb"); 21 | GetControl getControl = new GetControl(); 22 | System.err.println( getControl.doGet(deb)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/example-control: -------------------------------------------------------------------------------- 1 | Package: php5 2 | Version: 5.3.10-1ubuntu3.2 3 | Architecture: all 4 | Maintainer: Ubuntu Developers 5 | Installed-Size: 21 6 | Depends: libapache2-mod-php5 (>= 5.3.10-1ubuntu3.2) | libapache2-mod-php5filter (>= 5.3.10-1ubuntu3.2) | php5-cgi (>= 5.3.10-1ubuntu3.2) | php5-fpm (>= 5.3.10-1ubuntu3.2), php5-common (>= 5.3.10-1ubuntu3.2) 7 | Section: php 8 | Priority: optional 9 | Homepage: http://www.php.net/ 10 | Description: server-side, HTML-embedded scripting language (metapackage) 11 | This package is a metapackage that, when installed, guarantees that you 12 | have at least one of the four server-side versions of the PHP5 interpreter 13 | installed. Removing this package won't remove PHP5 from your system, however 14 | it may remove other packages that depend on this one. 15 | . 16 | PHP5 is a widely-used general-purpose scripting language that is 17 | especially suited for Web development and can be embedded into HTML. 18 | The goal of the language is to allow web developers to write 19 | dynamically generated pages quickly. 20 | Original-Maintainer: Debian PHP Maintainers -------------------------------------------------------------------------------- /src/test/resources/example-dpkg-scanpackages-output.txt: -------------------------------------------------------------------------------- 1 | Package: php5 2 | Version: 5.3.10-1ubuntu3.2 3 | Architecture: all 4 | Maintainer: Ubuntu Developers 5 | Installed-Size: 21 6 | Depends: libapache2-mod-php5 (>= 5.3.10-1ubuntu3.2) | libapache2-mod-php5filter (>= 5.3.10-1ubuntu3.2) | php5-cgi (>= 5.3.10-1ubuntu3.2) | php5-fpm (>= 5.3.10-1ubuntu3.2), php5-common (>= 5.3.10-1ubuntu3.2) 7 | Filename: ./php5_5.3.10-1ubuntu3.2_all.deb 8 | Size: 1078 9 | MD5sum: 4cf8418628a5f7c70904aa7c6ecb6a36 10 | SHA1: cb4a12b4f56038fb08f72b174cd3e46bd72ee726 11 | SHA256: 7c599d3594843c5d3427c347fa73a5447c8391c54bd97829df463af08e160d20 12 | Section: php 13 | Priority: optional 14 | Homepage: http://www.php.net/ 15 | Description: server-side, HTML-embedded scripting language (metapackage) 16 | This package is a metapackage that, when installed, guarantees that you 17 | have at least one of the four server-side versions of the PHP5 interpreter 18 | installed. Removing this package won't remove PHP5 from your system, however 19 | it may remove other packages that depend on this one. 20 | . 21 | PHP5 is a widely-used general-purpose scripting language that is 22 | especially suited for Web development and can be embedded into HTML. 23 | The goal of the language is to allow web developers to write 24 | dynamically generated pages quickly. 25 | Original-Maintainer: Debian PHP Maintainers 26 | 27 | -------------------------------------------------------------------------------- /src/test/resources/php5_5.3.10-1ubuntu3.2_all.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inventage/nexus-apt-plugin/43a77e4b74265d6ab889d1fa31b419e24009699b/src/test/resources/php5_5.3.10-1ubuntu3.2_all.deb --------------------------------------------------------------------------------