diff --git a/apidocs/index.html b/apidocs/index.html index 58fa3c30a..6c9702c73 100644 --- a/apidocs/index.html +++ b/apidocs/index.html @@ -1 +1 @@ - + diff --git a/release/1.11/apidocs/allclasses-index.html b/release/1.11/apidocs/allclasses-index.html new file mode 100644 index 000000000..53509db07 --- /dev/null +++ b/release/1.11/apidocs/allclasses-index.html @@ -0,0 +1,251 @@ + + + +
+ +The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.
+Each package has a page that contains a list of its classes and interfaces, with a summary for each. These pages may contain six categories:
+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+Each annotation type has its own separate page with the following sections:
+Each enum has its own separate page with the following sections:
+Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its "Use" page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. Classes are organized by inheritance structure starting with java.lang.Object
. Interfaces do not inherit from java.lang.Object
.
The Index contains an alphabetic index of all classes, interfaces, constructors, methods, and fields, as well as lists of all packages and all classes.
+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to those who implement rather than use the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See Also" section of the class description.
+You can search for definitions of modules, packages, types, fields, methods, system properties and other terms defined in the API, using some or all of the name, optionally using "camel-case" abbreviations. For example:
+j.l.obj
will match "java.lang.Object"InpStr
will match "java.io.InputStream"HM.cK
will match "java.util.HashMap.containsKey(Object)"Refer to the Javadoc Search Specification for a full description of search features.
+Serializable
, Comparable<Compression>
static Compression
static Compression
static Compression[]
values()
name
- the name of the enum constant to be returned.IllegalArgumentException
- if this enum type has no constant with the specified nameNullPointerException
- if the argument is nullorg.apache.commons.compress.compressors.CompressorException
name
- the name of the compression methodvoid
onEachDir(org.apache.commons.compress.archivers.tar.TarArchiveEntry dirEntry)
void
onEachFile(InputStream input,
+ org.apache.commons.compress.archivers.tar.TarArchiveEntry fileEntry)
void
onEachLink(org.apache.commons.compress.archivers.tar.TarArchiveEntry linkEntry)
IOException
IOException
IOException
AbstractDataProducer
, Data
, Data
, DataProducerArchive
, DataProducerDirectory
, DataProducerFile
, DataProducerFiles
, DataProducerFileSet
, DataProducerLink
, DataProducerPathTemplate
IOException
DebMaker(Console console,
+ Collection<DataProducer> dataProducers,
+ Collection<DataProducer> conffileProducers)
createDeb(Compression compression)
createSignedDeb(Compression compression,
+ org.bouncycastle.openpgp.PGPSignatureGenerator signatureGenerator,
+ PGPSigner signer)
void
makeDeb()
void
setChangesIn(File changes)
void
setChangesOut(File changes)
void
setChangesSave(File changes)
void
setCloseReplaceToken(String closeReplaceToken)
void
setCompression(String compression)
void
setControl(File control)
void
void
setDepends(String depends)
void
setDescription(String description)
void
void
setHomepage(String homepage)
void
void
setKeyring(File keyring)
void
setOpenReplaceToken(String openReplaceToken)
void
setOutputTimestampMs(Long outputTimestampMs)
void
setPackage(String packageName)
void
setPassphrase(String passphrase)
void
setResolver(VariableResolver variableResolver)
void
setSection(String section)
void
setSignChanges(boolean signChanges)
void
setSignDigest(String digest)
void
setSignMethod(String signMethod)
void
setSignPackage(boolean signPackage)
void
setSignRole(String signRole)
void
setTarBigNumberMode(String tarBigNumberMode)
void
setTarLongFileMode(String tarLongFileMode)
void
validate()
PackagingException
PackagingException
compression
- the compression method used for the data filePackagingException
compression
- the compression method used for the data file (gzip, bzip2 or anything else for no compression)signatureGenerator
- the signature generatorPackagingException
Serializable
PackagingException(String message)
PackagingException(String message,
+ Throwable cause)
PackagingException(Throwable cause)
addSuppressed, fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, getSuppressed, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
int
bigNumberMode(String input)
compression(Compression compression)
int
longFileMode(String input)
Cloneable
, DataProducer
org.apache.tools.ant.types.PatternSet.NameEntry, org.apache.tools.ant.types.PatternSet.PatternFileNameEntry
checked, ref
description, location, project
void
getType()
void
produce(DataConsumer pReceiver)
void
setConffile(Boolean conffile)
void
void
setMissingSrc(String missingSrc)
void
void
addConfiguredInvert, addConfiguredPatternset, append, clone, createExclude, createExcludesFile, createInclude, createIncludesFile, getExcludePatterns, getIncludePatterns, hasPatterns, setExcludes, setExcludesfile, setIncludes, setIncludesfile, setRefid, toString
checkAttributesAllowed, checkChildrenAllowed, circularReference, dieOnCircularReference, dieOnCircularReference, dieOnCircularReference, getCheckedRef, getCheckedRef, getCheckedRef, getCheckedRef, getCheckedRef, getDataTypeName, getRefid, invokeCircularReferenceCheck, isChecked, isReference, noChildrenAllowed, pushAndInvokeCircularReferenceCheck, setChecked, tooManyAttributes
getDescription, getLocation, getProject, log, log, setDescription, setLocation, setProject
produce
in interface DataProducer
IOException
Cloneable
, org.apache.tools.ant.types.selectors.SelectorContainer
fileset
target, taskName, taskType, wrapper
description, location, project
void
void
addFileSet(org.apache.tools.ant.types.FileSet fileset)
void
void
addTarFileSet(org.apache.tools.ant.taskdefs.Tar.TarFileSet fileset)
void
execute()
void
setChangesIn(File changes)
void
setChangesOut(File changes)
void
setChangesSave(File changes)
void
setCompression(String compression)
void
setControl(File control)
void
setDestfile(File deb)
void
void
void
setKeyring(File keyring)
void
setPassphrase(String passphrase)
void
setVerbose(boolean verbose)
add, addAnd, addContains, addContainsRegexp, addCustom, addDate, addDepend, addDepth, addDifferent, addFilename, addMajority, addModified, addNone, addNot, addOr, addPresent, addSelector, addSize, addType, appendSelector, createExclude, createExcludesFile, createInclude, createIncludesFile, createPatternSet, getDirectoryScanner, getImplicitFileSet, getSelectors, hasSelectors, selectorCount, selectorElements, setCaseSensitive, setDefaultexcludes, setExcludes, setExcludesfile, setFollowSymlinks, setIncludes, setIncludesfile, setProject, XsetIgnore, XsetItems
bindToOwner, getOwningTarget, getRuntimeConfigurableWrapper, getTaskName, getTaskType, getWrapper, handleErrorFlush, handleErrorOutput, handleFlush, handleInput, handleOutput, init, isInvalid, log, log, log, log, maybeConfigure, perform, reconfigure, setOwningTarget, setRuntimeConfigurableWrapper, setTaskName, setTaskType
clone, getDescription, getLocation, getProject, setDescription, setLocation
execute
in class org.apache.tools.ant.Task
int
getGid()
getGroup()
int
getMode()
getName()
int
getUid()
boolean
void
setGid(int gid)
void
void
void
void
setSymbolic(boolean symbolic)
void
void
setUid(int uid)
void
setUsername(String username)
void
setDirMode(String pDirMode)
void
setFileMode(String pFileMode)
void
setGid(int pGid)
void
void
void
void
setStrip(int pStrip)
void
void
setUid(int pUid)
void
IOException
Serializable
, Comparable<MissingSourceBehavior>
static MissingSourceBehavior
static MissingSourceBehavior[]
values()
name
- the name of the enum constant to be returned.IllegalArgumentException
- if this enum type has no constant with the specified nameNullPointerException
- if the argument is nullstatic MissingSourceBehavior
static MissingSourceBehavior[]
values()
+ package (version) distribution(s); urgency=urgency + [optional blank line(s), stripped] + * change details + more change details + [blank line(s), included in output of dpkg-parsechangelog] + * even more change details + [optional blank line(s), stripped] + -- maintainer name <email address>[two spaces] date +
TextfileChangesProvider
ChangesProvider
TextfileChangesProvider(InputStream pInput,
+ BinaryPackageControlFile packageControlFile)
IOException
ParseException
IOException
getChangesSets
in interface ChangesProvider
getChangesSets()
getChangesSets()
class
compression()
static Compression
static Compression
static Compression[]
values()
compression(Compression compression)
createDeb(Compression compression)
createSignedDeb(Compression compression,
+ org.bouncycastle.openpgp.PGPSignatureGenerator signatureGenerator,
+ PGPSigner signer)
DebMaker(Console console,
+ Collection<DataProducer> dataProducers,
+ Collection<DataProducer> conffileProducers)
void
produce(DataConsumer pReceiver)
void
produce(DataConsumer pReceiver)
void
produce(DataConsumer pReceiver)
void
produce(DataConsumer receiver)
void
produce(DataConsumer pReceiver)
void
produce(DataConsumer pReceiver)
void
produce(DataConsumer pReceiver)
void
produceDir(DataConsumer consumer,
+ String dirName)
void
produceFile(DataConsumer consumer,
+ File file,
+ String fileName)
DebMaker(Console console,
+ Collection<DataProducer> dataProducers,
+ Collection<DataProducer> conffileProducers)
class
class
class
class
class
class
class
class
class
class
createDeb(Compression compression)
createSignedDeb(Compression compression,
+ org.bouncycastle.openpgp.PGPSignatureGenerator signatureGenerator,
+ PGPSigner signer)
void
makeDeb()
void
validate()
static int
getDigestCode(String digestName)
bigNumberMode(String input)
compression(Compression compression)
longFileMode(String input)
userDefinedFieldNames, userDefinedFields, values
BinaryPackageControlFile(InputStream input)
BinaryPackageControlFile(String input)
protected ControlField[]
protected char
void
get, getMandatoryFields, getUserDefinedFieldName, getUserDefinedFieldNames, getUserDefinedFields, invalidFields, isUserDefinedField, isValid, parse, parse, toString, toString
IOException
ParseException
IOException
ParseException
set
in class ControlFile
getFields
in class ControlFile
ControlFile
getUserDefinedFieldLetter
in class ControlFile
userDefinedFieldNames, userDefinedFields, values
static String
formatDate(Date date)
protected ControlField[]
protected char
void
initialize(BinaryPackageControlFile packageControlFile)
void
setChanges(ChangeSet[] changeSets)
get, getMandatoryFields, getUserDefinedFieldName, getUserDefinedFieldNames, getUserDefinedFields, invalidFields, isUserDefinedField, isValid, parse, parse, set, toString, toString
packageControlFile
- getFields
in class ControlFile
ControlFile
getUserDefinedFieldLetter
in class ControlFile
Serializable
, Comparable<ControlField.Type>
static ControlField.Type
static ControlField.Type[]
values()
name
- the name of the enum constant to be returned.IllegalArgumentException
- if this enum type has no constant with the specified nameNullPointerException
- if the argument is nullstatic class
ControlField(String name)
ControlField(String name,
+ boolean mandatory)
ControlField(String name,
+ boolean mandatory,
+ ControlField.Type type)
ControlField(String name,
+ boolean mandatory,
+ ControlField.Type type,
+ boolean firstLineEmpty)
+ Field-Name: value +
BinaryPackageControlFile
, ChangesFile
protected abstract ControlField[]
protected abstract char
protected String
getUserDefinedFieldName(String field)
protected Set<ControlField>
protected boolean
isUserDefinedField(String field)
boolean
isValid()
void
parse(InputStream input)
void
void
toString()
toString(ControlField... fields)
IOException
ParseException
IOException
ParseException
field
- the name of the fieldfield
- the name of the user defined fieldcreateDeb(Compression compression)
createSignedDeb(Compression compression,
+ org.bouncycastle.openpgp.PGPSignatureGenerator signatureGenerator,
+ PGPSigner signer)
TextfileChangesProvider(InputStream pInput,
+ BinaryPackageControlFile packageControlFile)
void
initialize(BinaryPackageControlFile packageControlFile)
getType()
static ControlField.Type
static ControlField.Type[]
values()
ControlField(String name,
+ boolean mandatory,
+ ControlField.Type type)
ControlField(String name,
+ boolean mandatory,
+ ControlField.Type type,
+ boolean firstLineEmpty)
protected Set<ControlField>
userDefinedFieldNames
protected ControlField[]
getFields()
protected ControlField[]
getFields()
protected abstract ControlField[]
getFields()
protected Set<ControlField>
getUserDefinedFieldNames()
class
class
Serializable
addSuppressed, fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, getSuppressed, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toString
Mapper
org.apache.commons.compress.archivers.tar.TarArchiveEntry
map(org.apache.commons.compress.archivers.tar.TarArchiveEntry pEntry)
IOException
LsMapper.ParseError
LsMapper
, NullMapper
, PermMapper
org.apache.commons.compress.archivers.tar.TarArchiveEntry
map(org.apache.commons.compress.archivers.tar.TarArchiveEntry entry)
Mapper
org.apache.commons.compress.archivers.tar.TarArchiveEntry
map(org.apache.commons.compress.archivers.tar.TarArchiveEntry pEntry)
Mapper
PermMapper(int uid,
+ int gid,
+ String user,
+ String group,
+ int fileMode,
+ int dirMode,
+ int strip,
+ String prefix)
PermMapper(int uid,
+ int gid,
+ String user,
+ String group,
+ String fileMode,
+ String dirMode,
+ int strip,
+ String prefix)
class
class
class
AbstractDataProducer(String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMapper)
DataProducerArchive(File pArchive,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMappers)
DataProducerDirectory(File pDir,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMappers)
DataProducerFile(File pFile,
+ String pDestinationName,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMapper)
DataProducerFiles(String[] files,
+ String destDir,
+ Mapper[] mappers)
DataProducerLink(String path,
+ String linkName,
+ boolean symlink,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMapper)
DataProducerPathTemplate(String[] pLiteralPaths,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMapper)
DataProducer
boolean
void
produce(DataConsumer pReceiver)
void
setConffile(boolean conffile)
void
void
setExcludes(String excludes)
void
setIncludes(String includes)
void
setLinkName(String linkName)
void
setLinkTarget(String linkTarget)
void
setMissingSrc(String missingSrc)
void
void
setSymlink(boolean symlink)
void
String[]
splitPatterns(String patterns)
produce
in interface DataProducer
IOException
org.apache.maven.plugin.ContextEnabled
, org.apache.maven.plugin.Mojo
ROLE
void
execute()
protected VariableResolver
initializeVariableResolver(Map<String,String> variables)
readPropertiesFromActiveProfiles(String prefix,
+ String... properties)
void
setCloseReplaceToken(String closeReplaceToken)
protected void
void
setOpenReplaceToken(String openReplaceToken)
getLog, getPluginContext, setLog, setPluginContext
org.apache.maven.plugin.MojoExecutionException
- on errorprefix
- The prefix to use or null if no prefix should be usedproperties
- The properties to readIOException
Serializable
, Comparable<MissingSourceBehavior>
static MissingSourceBehavior
static MissingSourceBehavior[]
values()
name
- the name of the enum constant to be returned.IllegalArgumentException
- if this enum type has no constant with the specified nameNullPointerException
- if the argument is nullstatic MissingSourceBehavior
static MissingSourceBehavior[]
values()
DataProducer
DataProducerArchive
, DataProducerDirectory
, DataProducerFile
, DataProducerFiles
, DataProducerLink
, DataProducerPathTemplate
AbstractDataProducer(String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMapper)
boolean
isIncluded(String pName)
org.apache.commons.compress.archivers.tar.TarArchiveEntry
map(org.apache.commons.compress.archivers.tar.TarArchiveEntry pEntry)
void
produceDir(DataConsumer consumer,
+ String dirName)
void
produceFile(DataConsumer consumer,
+ File file,
+ String fileName)
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
produce
IOException
IOException
DataProducer
DataProducerArchive(File pArchive,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMappers)
isIncluded, map, produceDir, produceFile
produce
in interface DataProducer
IOException
DataProducer
DataProducerDirectory(File pDir,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMappers)
isIncluded, map, produceDir, produceFile
produce
in interface DataProducer
IOException
DataProducer
isIncluded, map, produceDir, produceFile
produce
in interface DataProducer
IOException
DataProducer
produce
in interface DataProducer
IOException
DataProducer
DataProducerFiles(String[] files,
+ String destDir,
+ Mapper[] mappers)
isIncluded, map, produceDir, produceFile
IOException
DataProducer
isIncluded, map, produceDir, produceFile
produce
in interface DataProducer
IOException
DataProducer
DataProducerPathTemplate(String[] pLiteralPaths,
+ String[] pIncludes,
+ String[] pExcludes,
+ Mapper[] pMapper)
isIncluded, map, produceDir, produceFile
produce
in interface DataProducer
IOException
class
class
class
class
class
class
PGPSigner(InputStream keyring,
+ String keyId,
+ String passphrase,
+ int digest)
void
clearSign(InputStream input,
+ OutputStream output)
void
clearSign(String input,
+ OutputStream output)
static int
getDigestCode(String digestName)
org.bouncycastle.openpgp.PGPPrivateKey
org.bouncycastle.openpgp.PGPSecretKey
IOException
org.bouncycastle.openpgp.PGPException
PackagingException
input
- the content to be signedoutput
- the output destination of the signatureIOException
org.bouncycastle.openpgp.PGPException
input
- the content to be signedoutput
- the output destination of the signatureIOException
org.bouncycastle.openpgp.PGPException
createSignedDeb(Compression compression,
+ org.bouncycastle.openpgp.PGPSignatureGenerator signatureGenerator,
+ PGPSigner signer)
void
setCloseToken(String token)
void
setOpenToken(String token)
toString()
IOException
Closeable
, AutoCloseable
in
available, close, mark, markSupported, read, reset, skip
nullInputStream, readAllBytes, readNBytes, readNBytes, transferTo
read
in class FilterInputStream
IOException
read
in class FilterInputStream
IOException
Closeable
, Flushable
, AutoCloseable
out
InformationOutputStream(OutputStream pStream,
+ MessageDigest pDigest)
getMessageDigest, on, setMessageDigest, toString
close, flush, write
nullOutputStream
write
in class DigestOutputStream
IOException
write
in class DigestOutputStream
IOException
VariableResolver
get
in interface VariableResolver
Closeable
, Flushable
, AutoCloseable
PGPSignatureOutputStream(org.bouncycastle.openpgp.PGPSignatureGenerator signatureGenerator)
close, flush, nullOutputStream
write
in class OutputStream
IOException
write
in class OutputStream
IOException
write
in class OutputStream
IOException
org.bouncycastle.openpgp.PGPException
org.bouncycastle.openpgp.PGPException
static boolean
isSymbolicLink(File file)
static String
readSymbolicLink(File file)
IOException
IOException
static String
convertToDebianVersion(String version,
+ boolean apply,
+ String envName,
+ String template,
+ Date timestamp)
static int
copy(InputStream pInput,
+ OutputStream pOutput)
static String
defaultString(String str,
+ String fallback)
static Collection<String>
static File
static boolean
isBlank(CharSequence cs)
static boolean
isNullOrEmpty(String str)
static String
joinLocalPath(String... paths)
static String
joinUnixPath(String... paths)
static String
static String
static String
replaceVariables(VariableResolver pResolver,
+ String pExpression,
+ String pOpen,
+ String pClose)
static String
static String
static String
toHex(byte[] bytes)
static byte[]
toUnixLineEndings(InputStream input)
IOException
pResolver
- pExpression
- input
- IOException
version
- the project version to convert to a Debian package versiontemplate
- the template used to replace -SNAPSHOT, the timestamp format is in brackets,
+ the rest of the string is preserved (prefix[yyMMdd]suffix -> prefix151230suffix)timestamp
- the UTC date used as the timestamp to replace the SNAPSHOT suffix.file
- path to movetarget
- new path directoryvalue
- current valueprops
- properties to extract value fromkey
- property name to extractFileNotFoundException
- if no keyring file found+ StringUtils.isBlank(null) = true + StringUtils.isBlank("") = true + StringUtils.isBlank(" ") = true + StringUtils.isBlank("bob") = false + StringUtils.isBlank(" bob ") = false +
cs
- the CharSequence to check, may be nulltrue
if the CharSequence is null, empty or whitespaceMapVariableResolver
void
setResolver(VariableResolver variableResolver)
protected VariableResolver
initializeVariableResolver(Map<String,String> variables)
class
static String
replaceVariables(VariableResolver pResolver,
+ String pExpression,
+ String pOpen,
+ String pClose)
FilteredFile(InputStream in,
+ VariableResolver resolver)
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb; +018 +019import java.io.OutputStream; +020 +021import org.apache.commons.compress.compressors.CompressorException; +022import org.apache.commons.compress.compressors.CompressorStreamFactory; +023 +024/** +025 * Compression method used for the data file. +026 */ +027public enum Compression { +028 +029 NONE(""), +030 GZIP(".gz"), +031 BZIP2(".bz2"), +032 XZ(".xz"); +033 +034 private String extension; +035 +036 Compression(String extension) { +037 this.extension = extension; +038 } +039 +040 /** +041 * Returns the extension of the compression method +042 */ +043 public String getExtension() { +044 return extension; +045 } +046 +047 public OutputStream toCompressedOutputStream(OutputStream out) throws CompressorException { +048 switch (this) { +049 case GZIP: +050 return new CompressorStreamFactory().createCompressorOutputStream("gz", out); +051 case BZIP2: +052 return new CompressorStreamFactory().createCompressorOutputStream("bzip2", out); +053 case XZ: +054 return new CompressorStreamFactory().createCompressorOutputStream("xz", out); +055 default: +056 return out; +057 } +058 } +059 +060 /** +061 * Returns the compression method corresponding to the specified name. +062 * The matching is case insensitive. +063 * +064 * @param name the name of the compression method +065 * @return the compression method, or null if not recognized +066 */ +067 public static Compression toEnum(String name) { +068 if ("gzip".equalsIgnoreCase(name) || "gz".equalsIgnoreCase(name)) { +069 return GZIP; +070 } else if ("bzip2".equalsIgnoreCase(name) || "bz2".equalsIgnoreCase(name)) { +071 return BZIP2; +072 } else if ("xz".equalsIgnoreCase(name)) { +073 return XZ; +074 } else if ("none".equalsIgnoreCase(name)) { +075 return NONE; +076 } else { +077 return null; +078 } +079 } +080} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb; +018 +019/** +020 * Plug in your favorite log implementation. +021 */ +022public interface Console { +023 +024 void debug( String message ); +025 +026 void info( String message ); +027 +028 void warn( String message ); +029 +030} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb; +017 +018import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +019 +020import java.io.IOException; +021import java.io.InputStream; +022 +023/** +024 * A DataConsumer consumes Data produced from a producer. +025 */ +026public interface DataConsumer { +027 +028 void onEachDir( TarArchiveEntry dirEntry ) throws IOException; +029 +030 void onEachFile( InputStream input, TarArchiveEntry fileEntry ) throws IOException; +031 +032 void onEachLink( TarArchiveEntry linkEntry ) throws IOException; +033 +034} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb; +017 +018import java.io.IOException; +019 +020/** +021 * Provides Data to a DataConsumer. +022 */ +023public interface DataProducer { +024 +025 void produce( DataConsumer receiver ) throws IOException; +026 +027} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb; +018 +019import org.apache.commons.compress.archivers.ar.ArArchiveEntry; +020import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream; +021import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +022import org.apache.commons.io.FileUtils; +023import org.apache.commons.io.FilenameUtils; +024import org.apache.commons.io.IOUtils; +025import org.bouncycastle.crypto.digests.MD5Digest; +026import org.bouncycastle.jce.provider.BouncyCastleProvider; +027import org.bouncycastle.openpgp.PGPSignature; +028import org.bouncycastle.openpgp.PGPSignatureGenerator; +029import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +030import org.bouncycastle.util.encoders.Hex; +031import org.vafer.jdeb.changes.ChangeSet; +032import org.vafer.jdeb.changes.ChangesProvider; +033import org.vafer.jdeb.changes.TextfileChangesProvider; +034import org.vafer.jdeb.debian.BinaryPackageControlFile; +035import org.vafer.jdeb.debian.ChangesFile; +036import org.vafer.jdeb.signing.PGPSigner; +037import org.vafer.jdeb.utils.PGPSignatureOutputStream; +038import org.vafer.jdeb.utils.Utils; +039import org.vafer.jdeb.utils.VariableResolver; +040 +041import java.io.ByteArrayOutputStream; +042import java.io.File; +043import java.io.FileInputStream; +044import java.io.FileOutputStream; +045import java.io.IOException; +046import java.io.InputStream; +047import java.math.BigInteger; +048import java.nio.charset.StandardCharsets; +049import java.security.MessageDigest; +050import java.security.NoSuchAlgorithmException; +051import java.security.Security; +052import java.text.SimpleDateFormat; +053import java.util.ArrayList; +054import java.util.Collection; +055import java.util.Date; +056import java.util.List; +057import java.util.Locale; +058import java.util.concurrent.TimeUnit; +059 +060/** +061 * A generic class for creating Debian archives. Even supports signed changes +062 * files. +063 */ +064public class DebMaker { +065 +066 private static final int DEFAULT_MODE = 33188; +067 +068 /** A console to output log message with */ +069 private Console console; +070 +071 /** The Debian package produced */ +072 private File deb; +073 +074 /** The directory containing the control files to build the package */ +075 private File control; +076 +077 /** The name of the package. Default value if not specified in the control file */ +078 private String packageName; +079 +080 /** The section of the package. Default value if not specified in the control file */ +081 private String section = "java"; +082 +083 /** The dependencies of the package. */ +084 private String depends; +085 +086 /** The description of the package. Default value if not specified in the control file */ +087 private String description; +088 +089 /** The homepage of the application. Default value if not specified in the control file */ +090 private String homepage; +091 +092 /** The file containing the PGP keys */ +093 private File keyring; +094 +095 /** The key to use in the keyring */ +096 private String key; +097 +098 /** The passphrase for the key to sign the changes file */ +099 private String passphrase; +100 +101 /** The file to read the changes from */ +102 private File changesIn; +103 +104 /** The file where to write the changes to */ +105 private File changesOut; +106 +107 /** The file where to write the changes of the changes input to */ +108 private File changesSave; +109 +110 /** The compression method used for the data file (none, gzip, bzip2 or xz) */ +111 private String compression = "gzip"; +112 +113 /** Whether to sign the package that is created */ +114 private boolean signPackage; +115 +116 /** Whether to sign the changes file that is created */ +117 private boolean signChanges; +118 +119 /** Defines which utility is used to verify the signed package */ +120 private String signMethod; +121 +122 /** Defines the role to sign with */ +123 private String signRole; +124 +125 /** Defines the digest for the signing */ +126 private String signDigest = "SHA256"; +127 +128 /** Defines the longFileMode of the tar file that is built */ +129 private String tarLongFileMode; +130 +131 /** Defines the bigNumberMode of the tar file that is built */ +132 private String tarBigNumberMode; +133 +134 private Long outputTimestampMs; +135 +136 private VariableResolver variableResolver; +137 private String openReplaceToken; +138 private String closeReplaceToken; +139 +140 private final Collection<DataProducer> dataProducers = new ArrayList<>(); +141 +142 private final Collection<DataProducer> conffilesProducers = new ArrayList<>(); +143 private String digest = "SHA256"; +144 +145 public DebMaker(Console console, Collection<DataProducer> dataProducers, Collection<DataProducer> conffileProducers) { +146 this.console = console; +147 if (dataProducers != null) { +148 this.dataProducers.addAll(dataProducers); +149 } +150 if (conffileProducers != null) { +151 this.conffilesProducers.addAll(conffileProducers); +152 } +153 +154 Security.addProvider(new BouncyCastleProvider()); +155 } +156 +157 public void setDeb(File deb) { +158 this.deb = deb; +159 } +160 +161 public void setControl(File control) { +162 this.control = control; +163 } +164 +165 public void setPackage(String packageName) { +166 this.packageName = packageName; +167 } +168 +169 public void setSection(String section) { +170 this.section = section; +171 } +172 +173 public void setDepends(String depends) { +174 this.depends = depends; +175 } +176 +177 public void setDescription(String description) { +178 this.description = description; +179 } +180 +181 public void setHomepage(String homepage) { +182 this.homepage = homepage; +183 } +184 +185 public void setChangesIn(File changes) { +186 this.changesIn = changes; +187 } +188 +189 public void setChangesOut(File changes) { +190 this.changesOut = changes; +191 } +192 +193 public void setChangesSave(File changes) { +194 this.changesSave = changes; +195 } +196 +197 public void setSignPackage(boolean signPackage) { +198 this.signPackage = signPackage; +199 } +200 +201 public void setSignChanges(boolean signChanges) { +202 this.signChanges = signChanges; +203 } +204 +205 public void setSignMethod(String signMethod) { +206 this.signMethod = signMethod; +207 } +208 +209 public void setSignRole(String signRole) { +210 this.signRole = signRole; +211 } +212 +213 public String getSignDigest() { +214 return signDigest; +215 } +216 +217 public void setSignDigest(String digest) { +218 this.signDigest = digest; +219 } +220 +221 +222 public void setKeyring(File keyring) { +223 this.keyring = keyring; +224 } +225 +226 public void setKey(String key) { +227 this.key = key; +228 } +229 +230 public void setPassphrase(String passphrase) { +231 this.passphrase = passphrase; +232 } +233 +234 public void setCompression(String compression) { +235 this.compression = compression; +236 } +237 +238 public void setResolver(VariableResolver variableResolver) { +239 this.variableResolver = variableResolver; +240 } +241 +242 private boolean isWritableFile(File file) { +243 return !file.exists() || file.isFile() && file.canWrite(); +244 } +245 +246 public String getDigest() { +247 return digest; +248 } +249 +250 public void setDigest(String digest) { +251 this.digest = digest; +252 } +253 +254 public void setTarLongFileMode(String tarLongFileMode) { +255 this.tarLongFileMode = tarLongFileMode; +256 } +257 +258 public void setTarBigNumberMode(String tarBigNumberMode) { +259 this.tarBigNumberMode = tarBigNumberMode; +260 } +261 +262 public void setOutputTimestampMs(Long outputTimestampMs) { +263 this.outputTimestampMs = outputTimestampMs; +264 } +265 +266 /** +267 * Validates the input parameters. +268 */ +269 public void validate() throws PackagingException { +270 if (control == null || !control.isDirectory()) { +271 throw new PackagingException("The 'control' attribute doesn't point to a directory. " + control); +272 } +273 +274 if (changesIn != null) { +275 +276 if (changesIn.exists() && (!changesIn.isFile() || !changesIn.canRead())) { +277 throw new PackagingException("The 'changesIn' setting needs to point to a readable file. " + changesIn + " was not found/readable."); +278 } +279 +280 if (changesOut != null && !isWritableFile(changesOut)) { +281 throw new PackagingException("Cannot write the output for 'changesOut' to " + changesOut); +282 } +283 +284 if (changesSave != null && !isWritableFile(changesSave)) { +285 throw new PackagingException("Cannot write the output for 'changesSave' to " + changesSave); +286 } +287 +288 } else { +289 if (changesOut != null || changesSave != null) { +290 throw new PackagingException("The 'changesOut' or 'changesSave' settings may only be used when there is a 'changesIn' specified."); +291 } +292 } +293 +294 if (Compression.toEnum(compression) == null) { +295 throw new PackagingException("The compression method '" + compression + "' is not supported (expected 'none', 'gzip', 'bzip2' or 'xz')"); +296 } +297 +298 if (deb == null) { +299 throw new PackagingException("You need to specify where the deb file is supposed to be created."); +300 } +301 +302 PGPSigner.getDigestCode(digest); +303 } +304 +305 public void makeDeb() throws PackagingException { +306 BinaryPackageControlFile packageControlFile; +307 try { +308 console.info("Creating debian package: " + deb); +309 +310 // If we should sign the package +311 if (signPackage) { +312 +313 if (keyring == null || !keyring.exists()) { +314 console.warn("Signing requested, but no keyring supplied"); +315 } +316 +317 if (key == null) { +318 console.warn("Signing requested, but no key supplied"); +319 } +320 +321 if (passphrase == null) { +322 console.warn("Signing requested, but no passphrase supplied"); +323 } +324 +325 final int digestCode = PGPSigner.getDigestCode(signDigest); +326 +327 PGPSigner signer; +328 try (FileInputStream keyRingInput = new FileInputStream(keyring)) { +329 signer = new PGPSigner(keyRingInput, key, passphrase, digestCode); +330 } +331 +332 PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(signer.getSecretKey().getPublicKey().getAlgorithm(), digestCode)); +333 signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signer.getPrivateKey()); +334 +335 packageControlFile = createSignedDeb(Compression.toEnum(compression), signatureGenerator, signer); +336 } else { +337 packageControlFile = createDeb(Compression.toEnum(compression)); +338 } +339 +340 } catch (Exception e) { +341 throw new PackagingException("Failed to create debian package " + deb, e); +342 } +343 +344 makeChangesFiles(packageControlFile); +345 } +346 +347 private void makeChangesFiles(final BinaryPackageControlFile packageControlFile) throws PackagingException { +348 if (changesOut == null) { +349 changesOut = new File(deb.getParentFile(), FilenameUtils.getBaseName(deb.getName()) + ".changes"); +350 } +351 +352 ChangesProvider changesProvider; +353 FileOutputStream out = null; +354 +355 try { +356 console.info("Creating changes file: " + changesOut); +357 +358 out = new FileOutputStream(changesOut); +359 +360 if (changesIn != null && changesIn.exists()) { +361 // read the changes form a textfile provider +362 changesProvider = new TextfileChangesProvider(new FileInputStream(changesIn), packageControlFile); +363 } else { +364 // create an empty changelog +365 changesProvider = new ChangesProvider() { +366 public ChangeSet[] getChangesSets() { +367 return new ChangeSet[] { +368 new ChangeSet(packageControlFile.get("Package"), +369 packageControlFile.get("Version"), +370 new Date(), +371 packageControlFile.get("Distribution"), +372 packageControlFile.get("Urgency"), +373 packageControlFile.get("Maintainer"), +374 new String[0]) +375 }; +376 } +377 }; +378 } +379 +380 ChangesFileBuilder builder = new ChangesFileBuilder(); +381 ChangesFile changesFile = builder.createChanges(packageControlFile, deb, changesProvider); +382 +383 final int digestCode = PGPSigner.getDigestCode(signDigest); +384 +385 // (signChanges || signPackage) - for backward compatibility. signPackage is signing both changes and deb. +386 if ((signChanges || signPackage) && keyring != null && key != null && passphrase != null) { +387 console.info("Signing the changes file with the key " + key); +388 PGPSigner signer = new PGPSigner(new FileInputStream(keyring), key, passphrase, digestCode); +389 signer.clearSign(changesFile.toString(), out); +390 } else { +391 out.write(changesFile.toString().getBytes(StandardCharsets.UTF_8)); +392 } +393 out.flush(); +394 +395 } catch (Exception e) { +396 throw new PackagingException("Failed to create the Debian changes file " + changesOut, e); +397 } finally { +398 IOUtils.closeQuietly(out); +399 } +400 +401 if (changesSave == null || !(changesProvider instanceof TextfileChangesProvider)) { +402 return; +403 } +404 +405 try { +406 console.info("Saving changes to file: " + changesSave); +407 +408 ((TextfileChangesProvider) changesProvider).save(new FileOutputStream(changesSave)); +409 +410 } catch (Exception e) { +411 throw new PackagingException("Failed to save debian changes file " + changesSave, e); +412 } +413 } +414 +415 private List<String> populateConffiles(Collection<DataProducer> producers) { +416 final List<String> result = new ArrayList<>(); +417 +418 if (producers == null || producers.isEmpty()) { +419 return result; +420 } +421 +422 final DataConsumer receiver = new DataConsumer() { +423 public void onEachFile(InputStream input, TarArchiveEntry entry) { +424 String tempConffileItem = entry.getName(); +425 +426 // Make sure the conffile path is absolute +427 if (tempConffileItem.startsWith(".")) { +428 tempConffileItem = tempConffileItem.substring(1); +429 } +430 if (!tempConffileItem.startsWith("/")) { +431 tempConffileItem = "/" + tempConffileItem; +432 } +433 +434 console.info("Adding conffile: " + tempConffileItem); +435 result.add(tempConffileItem); +436 } +437 +438 public void onEachLink(TarArchiveEntry entry) { +439 } +440 +441 public void onEachDir(TarArchiveEntry tarArchiveEntry) { +442 } +443 }; +444 +445 try { +446 for (DataProducer data : producers) { +447 data.produce(receiver); +448 } +449 } catch(Exception e) { +450 // +451 } +452 +453 return result; +454 } +455 +456 /** +457 * Create the debian archive with from the provided control files and data producers. +458 * +459 * @param compression the compression method used for the data file +460 * @return BinaryPackageControlFile +461 * @throws PackagingException +462 */ +463 public BinaryPackageControlFile createDeb(Compression compression) throws PackagingException { +464 return createSignedDeb(compression, null, null); +465 } +466 /** +467 * Create the debian archive with from the provided control files and data producers. +468 * +469 * @param compression the compression method used for the data file (gzip, bzip2 or anything else for no compression) +470 * @param signatureGenerator the signature generator +471 * +472 * @return PackageDescriptor +473 * @throws PackagingException +474 */ +475 public BinaryPackageControlFile createSignedDeb(Compression compression, final PGPSignatureGenerator signatureGenerator, PGPSigner signer ) throws PackagingException { +476 File tempData = null; +477 File tempControl = null; +478 +479 try { +480 tempData = File.createTempFile("deb", "data"); +481 tempControl = File.createTempFile("deb", "control"); +482 +483 console.debug("Building data"); +484 DataBuilder dataBuilder = new DataBuilder(console, outputTimestampMs); +485 StringBuilder md5s = new StringBuilder(); +486 TarOptions options = new TarOptions() +487 .compression(compression) +488 .longFileMode(tarLongFileMode) +489 .bigNumberMode(tarBigNumberMode); +490 BigInteger size = dataBuilder.buildData(dataProducers, tempData, md5s, options); +491 +492 console.info("Building conffiles"); +493 List<String> tempConffiles = populateConffiles(conffilesProducers); +494 +495 console.debug("Building control"); +496 ControlBuilder controlBuilder = new ControlBuilder(console, variableResolver, openReplaceToken, closeReplaceToken, outputTimestampMs); +497 BinaryPackageControlFile packageControlFile = controlBuilder.createPackageControlFile(new File(control, "control"), size); +498 if (packageControlFile.get("Package") == null) { +499 packageControlFile.set("Package", packageName); +500 } +501 if (packageControlFile.get("Section") == null) { +502 packageControlFile.set("Section", section); +503 } +504 if (packageControlFile.get("Description") == null) { +505 packageControlFile.set("Description", description); +506 } +507 if (packageControlFile.get("Depends") == null) { +508 // Only add a depends entry to the control file if the field in this object has actually been set +509 if (depends != null && depends.length() > 0) { +510 packageControlFile.set("Depends", depends); +511 } +512 } +513 if (packageControlFile.get("Homepage") == null) { +514 packageControlFile.set("Homepage", homepage); +515 } +516 +517 controlBuilder.buildControl(packageControlFile, control.listFiles(), tempConffiles , md5s, tempControl); +518 +519 if (!packageControlFile.isValid()) { +520 throw new PackagingException("Control file fields are invalid " + packageControlFile.invalidFields() + +521 ". The following fields are mandatory: " + packageControlFile.getMandatoryFields() + +522 ". Please check your pom.xml/build.xml and your control file."); +523 } +524 +525 deb.getParentFile().mkdirs(); +526 +527 ArArchiveOutputStream ar = new ArArchiveOutputStream(new FileOutputStream(deb)); +528 +529 String binaryName = "debian-binary"; +530 String binaryContent = "2.0\n"; +531 String controlName = "control.tar.gz"; +532 String dataName = "data.tar" + compression.getExtension(); +533 +534 addTo(ar, binaryName, binaryContent); +535 addTo(ar, controlName, tempControl); +536 addTo(ar, dataName, tempData); +537 +538 if (signatureGenerator != null) { +539 console.info("Signing package with key " + key); +540 +541 if(signRole == null) { +542 signRole = "origin"; +543 } +544 +545 // Use debsig-verify as default +546 if (!"dpkg-sig".equals(signMethod)) { +547 // Sign file to verify with debsig-verify +548 PGPSignatureOutputStream sigStream = new PGPSignatureOutputStream(signatureGenerator); +549 +550 addTo(sigStream, binaryContent); +551 addTo(sigStream, tempControl); +552 addTo(sigStream, tempData); +553 addTo(ar, "_gpg" + signRole, sigStream.generateASCIISignature()); +554 +555 } else { +556 +557 // Sign file to verify with dpkg-sig --verify +558 final String outputStr = +559 "Version: 4\n" + +560 "Signer: \n" + +561 "Date: " + new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.ENGLISH).format(new Date()) + "\n" + +562 "Role: " + signRole +"\n" + +563 "Files: \n" + +564 addFile(binaryName, binaryContent) + +565 addFile(controlName, tempControl) + +566 addFile(dataName, tempData); +567 +568 ByteArrayOutputStream message = new ByteArrayOutputStream(); +569 signer.clearSign(outputStr, message); +570 +571 addTo(ar, "_gpg" + signRole, message.toString()); +572 } +573 } +574 +575 ar.close(); +576 +577 return packageControlFile; +578 +579 } catch (Exception e) { +580 throw new PackagingException("Could not create deb package", e); +581 } finally { +582 if (tempData != null) { +583 if (!tempData.delete()) { +584 console.warn("Could not delete the temporary file " + tempData); +585 } +586 } +587 if (tempControl != null) { +588 if (!tempControl.delete()) { +589 console.warn("Could not delete the temporary file " + tempControl); +590 } +591 } +592 } +593 } +594 +595 private String addFile(String name, String input){ +596 return addLine(md5Hash(input), sha1Hash(input), input.length(), name); +597 } +598 +599 private String addFile(String name, File input){ +600 return addLine(md5Hash(input), sha1Hash(input), input.length(), name); +601 } +602 +603 private String addLine(String md5, String sha1, long size, String name){ +604 return "\t" + md5 + " " + sha1 + " " + size + " " + name + "\n"; +605 } +606 +607 private String md5Hash(String input){ +608 return md5Hash(input.getBytes()); +609 } +610 +611 private String md5Hash(File input){ +612 try { +613 return md5Hash(FileUtils.readFileToByteArray(input)); +614 } catch (IOException e) { +615 // TODO Auto-generated catch block +616 e.printStackTrace(); +617 } +618 +619 return null; +620 } +621 +622 private String md5Hash(byte[] input){ +623 //update the input of MD5 +624 MD5Digest md5 = new MD5Digest(); +625 md5.update(input, 0, input.length); +626 +627 //get the output/ digest size and hash it +628 byte[] digest = new byte[md5.getDigestSize()]; +629 md5.doFinal(digest, 0); +630 +631 return new String(Hex.encode(digest)); +632 } +633 +634 private String sha1Hash(String input){ +635 return sha1Hash(input.getBytes()); +636 } +637 +638 private String sha1Hash(File input){ +639 try { +640 return sha1Hash(FileUtils.readFileToByteArray(input)); +641 } catch (IOException e) { +642 // TODO Auto-generated catch block +643 e.printStackTrace(); +644 } +645 +646 return null; +647 } +648 +649 private String sha1Hash(byte[] input){ +650 try +651 { +652 //prepare the input +653 MessageDigest hash = MessageDigest.getInstance(digest); +654 hash.update(input); +655 +656 //proceed .... +657 byte[] digest = hash.digest(); +658 +659 return new String(Hex.encode(digest)); +660 } +661 catch (NoSuchAlgorithmException e) +662 { +663 System.err.println("No such algorithm"); +664 e.printStackTrace(); +665 } +666 +667 return null; +668 } +669 +670 private void addTo(ArArchiveOutputStream pOutput, String pName, String pContent) throws IOException { +671 final byte[] content = pContent.getBytes(); +672 ArArchiveEntry archiveEntry = createArArchiveEntry(pName, content.length); +673 +674 pOutput.putArchiveEntry(archiveEntry); +675 pOutput.write(content); +676 pOutput.closeArchiveEntry(); +677 } +678 +679 private void addTo(ArArchiveOutputStream pOutput, String pName, File pContent) throws IOException { +680 ArArchiveEntry archiveEntry = createArArchiveEntry(pName, pContent.length()); +681 +682 pOutput.putArchiveEntry(archiveEntry); +683 try (InputStream input = new FileInputStream(pContent)) { +684 Utils.copy(input, pOutput); +685 } +686 +687 pOutput.closeArchiveEntry(); +688 } +689 +690 private void addTo(final PGPSignatureOutputStream pOutput, final String pContent) throws IOException { +691 final byte[] content = pContent.getBytes(); +692 pOutput.write(content); +693 } +694 +695 private void addTo(final PGPSignatureOutputStream pOutput, final File pContent) throws IOException { +696 try (InputStream input = new FileInputStream(pContent)) { +697 Utils.copy(input, pOutput); +698 } +699 } +700 +701 public void setOpenReplaceToken(String openReplaceToken) { +702 this.openReplaceToken = openReplaceToken; +703 } +704 +705 public void setCloseReplaceToken(String closeReplaceToken) { +706 this.closeReplaceToken = closeReplaceToken; +707 } +708 +709 private ArArchiveEntry createArArchiveEntry(String pName, long contentLength) { +710 if (outputTimestampMs != null) { +711 return new ArArchiveEntry(pName, contentLength, 0, 0, DEFAULT_MODE, outputTimestampMs / TimeUnit.SECONDS.toMillis(1)); +712 } +713 +714 return new ArArchiveEntry(pName, contentLength); +715 } +716} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb; +017 +018/** +019 * Something went wrong while building the package +020 */ +021public final class PackagingException extends Exception { +022 +023 private static final long serialVersionUID = 1L; +024 +025 public PackagingException() { +026 super(); +027 } +028 +029 public PackagingException( String message, Throwable cause ) { +030 super(message, cause); +031 } +032 +033 public PackagingException( String message ) { +034 super(message); +035 } +036 +037 public PackagingException( Throwable cause ) { +038 super(cause); +039 } +040 +041} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001package org.vafer.jdeb; +002 +003import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +004 +005public class TarOptions { +006 +007 private Compression compression = Compression.GZIP; +008 private int longFileMode = TarArchiveOutputStream.LONGFILE_GNU; +009 private int bigNumberMode = TarArchiveOutputStream.BIGNUMBER_STAR; +010 +011 public TarOptions compression(Compression compression) { +012 this.compression = compression; +013 +014 return this; +015 } +016 +017 public TarOptions longFileMode(String input) { +018 if ("posix".equals(input)) { +019 longFileMode = TarArchiveOutputStream.LONGFILE_POSIX; +020 } else if ("error".equals(input)) { +021 longFileMode = TarArchiveOutputStream.LONGFILE_ERROR; +022 } else if ("truncate".equals(input)) { +023 longFileMode = TarArchiveOutputStream.LONGFILE_TRUNCATE; +024 } else { +025 longFileMode = TarArchiveOutputStream.LONGFILE_GNU; +026 } +027 +028 return this; +029 } +030 +031 public TarOptions bigNumberMode(String input) { +032 if ("error".equals(input)) { +033 bigNumberMode = TarArchiveOutputStream.BIGNUMBER_ERROR; +034 } else if ("posix".equals(input)) { +035 bigNumberMode = TarArchiveOutputStream.BIGNUMBER_POSIX; +036 } else { +037 bigNumberMode = TarArchiveOutputStream.BIGNUMBER_STAR; +038 } +039 +040 return this; +041 } +042 +043 public int longFileMode() { +044 return longFileMode; +045 } +046 +047 public int bigNumberMode() { +048 return bigNumberMode; +049 } +050 +051 public Compression compression() { +052 return compression; +053 } +054} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.ant; +017 +018import java.io.File; +019import java.io.FileNotFoundException; +020import java.io.IOException; +021import java.util.ArrayList; +022import java.util.Collection; +023import java.util.Iterator; +024 +025import org.apache.tools.ant.types.PatternSet; +026import org.vafer.jdeb.DataConsumer; +027import org.vafer.jdeb.DataProducer; +028import org.vafer.jdeb.producers.DataProducerArchive; +029import org.vafer.jdeb.producers.DataProducerDirectory; +030import org.vafer.jdeb.producers.DataProducerFile; +031 +032import static org.vafer.jdeb.ant.MissingSourceBehavior.*; +033 +034/** +035 * Ant "data" element acting as a factory for DataProducers. +036 * So far Archive and Directory producers are supported. +037 * Both support the usual ant pattern set matching. +038 */ +039public final class Data extends PatternSet implements DataProducer { +040 +041 private final Collection<Mapper> mapperWrapper = new ArrayList<>(); +042 +043 private File src; +044 +045 private String type; +046 +047 private Boolean conffile; +048 +049 private String destinationName; +050 +051 private MissingSourceBehavior missingSrc = FAIL; +052 +053 public void setSrc(File src) { +054 this.src = src; +055 } +056 +057 public String getType() { +058 return type; +059 } +060 +061 public void setType(String type) { +062 this.type = type; +063 } +064 +065 public void setConffile(Boolean conffile) { +066 this.conffile = conffile; +067 } +068 +069 public Boolean getConffile() { +070 return this.conffile; +071 } +072 +073 public void setDst(String destinationName) { +074 this.destinationName = destinationName; +075 } +076 +077 public void addMapper(Mapper mapper) { +078 mapperWrapper.add(mapper); +079 } +080 +081 +082 public void setMissingSrc( String missingSrc ) { +083 this.missingSrc = MissingSourceBehavior.valueOf(missingSrc.trim().toUpperCase()); +084 } +085 +086 public void produce( final DataConsumer pReceiver ) throws IOException { +087 +088 if (src == null || !src.exists()) { +089 if (missingSrc == IGNORE) { +090 return; +091 } else { +092 throw new FileNotFoundException("Data source not found : " + src); +093 } +094 } +095 +096 org.vafer.jdeb.mapping.Mapper[] mappers = new org.vafer.jdeb.mapping.Mapper[mapperWrapper.size()]; +097 final Iterator<Mapper> it = mapperWrapper.iterator(); +098 for (int i = 0; i < mappers.length; i++) { +099 mappers[i] = it.next().createMapper(); +100 } +101 +102 if ("file".equalsIgnoreCase(type)) { +103 new DataProducerFile( +104 src, +105 destinationName, +106 getIncludePatterns(getProject()), +107 getExcludePatterns(getProject()), +108 mappers +109 ).produce(pReceiver); +110 +111 } else if ("archive".equalsIgnoreCase(type)) { +112 new DataProducerArchive( +113 src, +114 getIncludePatterns(getProject()), +115 getExcludePatterns(getProject()), +116 mappers +117 ).produce(pReceiver); +118 +119 } else if ("directory".equalsIgnoreCase(type)) { +120 new DataProducerDirectory( +121 src, +122 getIncludePatterns(getProject()), +123 getExcludePatterns(getProject()), +124 mappers +125 ).produce(pReceiver); +126 } +127 } +128} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.ant; +018 +019import java.io.File; +020import java.util.ArrayList; +021import java.util.Arrays; +022import java.util.Collection; +023 +024import org.apache.tools.ant.BuildException; +025import org.apache.tools.ant.Project; +026import org.apache.tools.ant.taskdefs.MatchingTask; +027import org.apache.tools.ant.taskdefs.Tar; +028import org.apache.tools.ant.types.FileSet; +029import org.vafer.jdeb.Console; +030import org.vafer.jdeb.DataProducer; +031import org.vafer.jdeb.DebMaker; +032import org.vafer.jdeb.PackagingException; +033import org.vafer.jdeb.producers.DataProducerFileSet; +034import org.vafer.jdeb.utils.OutputTimestampResolver; +035 +036/** +037 * AntTask for creating debian archives. +038 */ +039public class DebAntTask extends MatchingTask { +040 +041 /** The Debian package produced */ +042 private File deb; +043 +044 /** The directory containing the control files to build the package */ +045 private File control; +046 +047 /** The file containing the PGP keys */ +048 private File keyring; +049 +050 /** The key to use in the keyring */ +051 private String key; +052 +053 /** The passphrase for the key to sign the changes file */ +054 private String passphrase; +055 +056 /** The file to read the changes from */ +057 private File changesIn; +058 +059 /** The file where to write the changes to */ +060 private File changesOut; +061 +062 /** The file where to write the changes of the changes input to */ +063 private File changesSave; +064 +065 /** The compression method used for the data file (none, gzip, bzip2 or xz) */ +066 private String compression = "gzip"; +067 +068 /** +069 * The digest algorithm to use. +070 * +071 * @see org.bouncycastle.bcpg.HashAlgorithmTags +072 */ +073 private String digest = "SHA256"; +074 +075 /** Trigger the verbose mode detailing all operations */ +076 private boolean verbose; +077 +078 private Collection<Link> links = new ArrayList<>(); +079 +080 private Collection<DataProducer> dataProducers = new ArrayList<>(); +081 private Collection<DataProducer> conffilesProducers = new ArrayList<>(); +082 +083 +084 public void setDestfile( File deb ) { +085 this.deb = deb; +086 } +087 +088 public void setControl( File control ) { +089 this.control = control; +090 } +091 +092 public void setChangesIn( File changes ) { +093 this.changesIn = changes; +094 } +095 +096 public void setChangesOut( File changes ) { +097 this.changesOut = changes; +098 } +099 +100 public void setChangesSave( File changes ) { +101 this.changesSave = changes; +102 } +103 +104 public void setKeyring( File keyring ) { +105 this.keyring = keyring; +106 } +107 +108 public void setKey( String key ) { +109 this.key = key; +110 } +111 +112 public void setPassphrase( String passphrase ) { +113 this.passphrase = passphrase; +114 } +115 +116 public void setCompression( String compression ) { +117 this.compression = compression; +118 } +119 +120 public void setVerbose( boolean verbose ) { +121 this.verbose = verbose; +122 } +123 +124 public void addFileSet( FileSet fileset ) { +125 dataProducers.add(new DataProducerFileSet(fileset)); +126 } +127 +128 public void addTarFileSet( Tar.TarFileSet fileset ) { +129 dataProducers.add(new DataProducerFileSet(fileset)); +130 } +131 +132 public void addData( Data data ) { +133 dataProducers.add(data); +134 } +135 +136 public void addLink( Link link ) { +137 links.add(link); +138 } +139 +140 public void setDigest(String digest) { +141 this.digest = digest; +142 } +143 +144 public void execute() { +145 // add the data producers for the links +146 for (Link link : links) { +147 dataProducers.add(link.toDataProducer()); +148 } +149 +150 // validate the type of the <data> elements +151 for (DataProducer dataProducer : dataProducers) { +152 if (dataProducer instanceof Data) { +153 Data data = (Data) dataProducer; +154 if (data.getType() == null) { +155 throw new BuildException("The type of the data element wasn't specified (expected 'file', 'directory' or 'archive')"); +156 } else if (!Arrays.asList("file", "directory", "archive").contains(data.getType().toLowerCase())) { +157 throw new BuildException("The type '" + data.getType() + "' of the data element is unknown (expected 'file', 'directory' or 'archive')"); +158 } +159 if (data.getConffile() != null && data.getConffile()) { +160 conffilesProducers.add(dataProducer); +161 } +162 } +163 } +164 +165 Console console = new TaskConsole(this, verbose); +166 +167 DebMaker debMaker = new DebMaker(console, dataProducers, conffilesProducers); +168 debMaker.setDeb(deb); +169 debMaker.setControl(control); +170 debMaker.setChangesIn(changesIn); +171 debMaker.setChangesOut(changesOut); +172 debMaker.setChangesSave(changesSave); +173 debMaker.setKeyring(keyring); +174 debMaker.setKey(key); +175 debMaker.setPassphrase(passphrase); +176 debMaker.setCompression(compression); +177 debMaker.setDigest(digest); +178 Long outputTimestampMs = new OutputTimestampResolver(console).resolveOutputTimestamp(null); +179 debMaker.setOutputTimestampMs(outputTimestampMs); +180 +181 try { +182 debMaker.validate(); +183 debMaker.makeDeb(); +184 +185 } catch (PackagingException e) { +186 log("Failed to create the Debian package " + deb, e, Project.MSG_ERR); +187 throw new BuildException("Failed to create the Debian package " + deb, e); +188 } +189 } +190} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.ant; +018 +019import org.apache.commons.compress.archivers.zip.UnixStat; +020import org.vafer.jdeb.DataProducer; +021import org.vafer.jdeb.mapping.PermMapper; +022import org.vafer.jdeb.producers.DataProducerLink; +023 +024/** +025 * Defines a symbolic or hard link. +026 */ +027public final class Link { +028 +029 private String name; +030 private String target; +031 private boolean symbolic = true; +032 private String username = "root"; +033 private String group = "root"; +034 private int uid = 0; +035 private int gid = 0; +036 private int mode = UnixStat.LINK_FLAG | UnixStat.DEFAULT_LINK_PERM; +037 +038 DataProducer toDataProducer() { +039 org.vafer.jdeb.mapping.Mapper mapper = new PermMapper(uid, gid, username, group, mode, mode, 0, null); +040 return new DataProducerLink(name, target, symbolic, null, null, new org.vafer.jdeb.mapping.Mapper[]{mapper}); +041 } +042 +043 public String getName() { +044 return name; +045 } +046 +047 public void setName(String name) { +048 this.name = name; +049 } +050 +051 public String getTarget() { +052 return target; +053 } +054 +055 public void setTarget(String target) { +056 this.target = target; +057 } +058 +059 public boolean isSymbolic() { +060 return symbolic; +061 } +062 +063 public void setSymbolic(boolean symbolic) { +064 this.symbolic = symbolic; +065 } +066 +067 public String getUsername() { +068 return username; +069 } +070 +071 public void setUsername(String username) { +072 this.username = username; +073 } +074 +075 public String getGroup() { +076 return group; +077 } +078 +079 public void setGroup(String group) { +080 this.group = group; +081 } +082 +083 public int getUid() { +084 return uid; +085 } +086 +087 public void setUid(int uid) { +088 this.uid = uid; +089 } +090 +091 public int getGid() { +092 return gid; +093 } +094 +095 public void setGid(int gid) { +096 this.gid = gid; +097 } +098 +099 public int getMode() { +100 return mode; +101 } +102 +103 public void setMode(String mode) { +104 this.mode = UnixStat.LINK_FLAG | Integer.parseInt(mode, 8); +105 } +106} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.ant; +017 +018import java.io.File; +019import java.io.FileInputStream; +020import java.io.IOException; +021 +022import org.vafer.jdeb.mapping.LsMapper; +023import org.vafer.jdeb.mapping.PermMapper; +024 +025/** +026 * Ant "mapper" element acting as factory for the entry mapper. +027 * Supported types: ls, perm +028 */ +029public final class Mapper { +030 +031 private String mapperType = "perm"; +032 private File src; +033 +034 private String prefix; +035 private int strip; +036 private int uid = -1; +037 private int gid = -1; +038 private String user; +039 private String group; +040 private String fileMode; +041 private String dirMode; +042 +043 public void setType( final String pType ) { +044 mapperType = pType; +045 } +046 +047 public void setSrc( final File pSrc ) { +048 src = pSrc; +049 } +050 +051 +052 public void setPrefix( final String pPrefix ) { +053 prefix = pPrefix; +054 } +055 +056 public void setStrip( final int pStrip ) { +057 strip = pStrip; +058 } +059 +060 +061 public void setUid( final int pUid ) { +062 uid = pUid; +063 } +064 +065 public void setGid( final int pGid ) { +066 gid = pGid; +067 } +068 +069 public void setUser( final String pUser ) { +070 user = pUser; +071 } +072 +073 public void setGroup( final String pGroup ) { +074 group = pGroup; +075 } +076 +077 public void setFileMode( final String pFileMode ) { +078 fileMode = pFileMode; +079 } +080 +081 public void setDirMode( final String pDirMode ) { +082 dirMode = pDirMode; +083 } +084 +085 public org.vafer.jdeb.mapping.Mapper createMapper() throws IOException { +086 +087 if ("perm".equalsIgnoreCase(mapperType)) { +088 return new PermMapper(uid, gid, user, group, fileMode, dirMode, strip, prefix); +089 } +090 +091 if ("ls".equalsIgnoreCase(mapperType)) { +092 try { +093 return new LsMapper(new FileInputStream(src)); +094 } catch (Exception e) { +095 e.printStackTrace(); +096 } +097 } +098 +099 throw new IOException("Unknown mapper type '" + mapperType + "'"); +100 } +101 +102} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.ant; +018 +019public enum MissingSourceBehavior { +020 IGNORE, FAIL +021} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.changes; +018 +019import java.util.Date; +020 +021/** +022 * A ChangeSet basically reflect a release as defined in the changes file. +023 * +024 * <pre> +025 * package (version) distribution(s); urgency=urgency +026 * [optional blank line(s), stripped] +027 * * change details +028 * more change details +029 * [blank line(s), included in output of dpkg-parsechangelog] +030 * * even more change details +031 * [optional blank line(s), stripped] +032 * -- maintainer name <email address>[two spaces] date +033 * </pre> +034 * +035 * @see <a href="http://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog">Debian Policy Manual - Debian changelog</a> +036 */ +037public final class ChangeSet { +038 +039 private final String packageName; +040 private final String version; +041 private final Date date; +042 private final String distribution; +043 private final String urgency; +044 private final String changedBy; +045 private final String[] changes; +046 +047 public ChangeSet(String packageName, String version, Date date, String distribution, String urgency, String changedBy, String[] changes) { +048 this.packageName = packageName; +049 this.version = version; +050 this.date = date; +051 this.distribution = distribution; +052 this.urgency = urgency; +053 this.changedBy = changedBy; +054 this.changes = changes; +055 } +056 +057 public String getPackage() { +058 return packageName; +059 } +060 +061 public String getVersion() { +062 return version; +063 } +064 +065 public Date getDate() { +066 return date; +067 } +068 +069 public String getDistribution() { +070 return distribution; +071 } +072 +073 public String getUrgency() { +074 return urgency; +075 } +076 +077 public String getChangedBy() { +078 return changedBy; +079 } +080 +081 public String[] getChanges() { +082 return changes; +083 } +084 +085 public String toString() { +086 StringBuilder sb = new StringBuilder(); +087 +088 sb.append(getTitle()).append('\n'); +089 +090 if (changes.length > 0) { +091 sb.append("\n"); +092 } +093 +094 for (String change : changes) { +095 sb.append(" * ").append(change).append('\n'); +096 } +097 +098 return sb.toString(); +099 } +100 +101 private String getTitle() { +102 return getPackage() + " (" + getVersion() + ") " + getDistribution() + "; urgency=" + getUrgency(); +103 } +104} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.changes; +017 +018public interface ChangesProvider { +019 +020 ChangeSet[] getChangesSets(); +021 +022} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.changes; +018 +019import java.io.BufferedReader; +020import java.io.BufferedWriter; +021import java.io.IOException; +022import java.io.InputStream; +023import java.io.InputStreamReader; +024import java.io.OutputStream; +025import java.io.OutputStreamWriter; +026import java.text.DateFormat; +027import java.text.ParseException; +028import java.text.SimpleDateFormat; +029import java.util.ArrayList; +030import java.util.Collection; +031import java.util.Date; +032 +033import org.vafer.jdeb.debian.BinaryPackageControlFile; +034 +035/** +036 * Gets the changes from a changes file. The first entry are the current changes. +037 * The release line will be added. Example: +038 * +039 * release date=22:13 19.08.2007,version=1.5+r90114,urgency=low,by=Torsten Curdt <torsten@vafer.org> +040 * * debian changes support +041 * release date=20:13 17.08.2007,version=1.4+r89114,urgency=low,by=Torsten Curdt <torsten@vafer.org> +042 * * debian changes support +043 * +044 */ +045public final class TextfileChangesProvider implements ChangesProvider { +046 +047 private final ChangeSet[] changeSets; +048 +049 private DateFormat fmt = new SimpleDateFormat("HH:mm dd.MM.yyyy"); +050 +051 public TextfileChangesProvider( final InputStream pInput, final BinaryPackageControlFile packageControlFile ) throws IOException, ParseException { +052 +053 final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput)); +054 +055 String packageName = packageControlFile.get("Package"); +056 String version = packageControlFile.get("Version"); +057 Date date = new Date(); +058 String distribution = packageControlFile.get("Distribution"); +059 String urgency = packageControlFile.get("Urgency"); +060 String changedBy = packageControlFile.get("Maintainer"); +061 Collection<String> changesColl = new ArrayList<>(); +062 Collection<ChangeSet> changeSetColl = new ArrayList<>(); +063 +064 +065 while (true) { +066 final String line = reader.readLine(); +067 if (line == null) { +068 final String[] changes = changesColl.toArray(new String[changesColl.size()]); +069 final ChangeSet changeSet = new ChangeSet(packageName, version, date, distribution, urgency, changedBy, changes); +070 changeSetColl.add(changeSet); +071 break; +072 } +073 +074 if (line.startsWith("release ")) { +075 +076 if (changesColl.size() > 0) { +077 final String[] changes = changesColl.toArray(new String[changesColl.size()]); +078 final ChangeSet changeSet = new ChangeSet(packageName, version, date, distribution, urgency, changedBy, changes); +079 changeSetColl.add(changeSet); +080 changesColl.clear(); +081 } +082 +083 final String[] tokens = line.substring("release ".length()).split(","); +084 for (String token : tokens) { +085 final String[] lr = token.trim().split("="); +086 final String key = lr[0]; +087 final String value = lr[1]; +088 +089 if ("urgency".equals(key)) { +090 urgency = value; +091 } else if ("by".equals(key)) { +092 changedBy = value; +093 } else if ("date".equals(key)) { +094 date = fmt.parse(value); +095 } else if ("version".equals(key)) { +096 version = value; +097 } else if ("distribution".equals(key)) { +098 distribution = value; +099 } +100 } +101 continue; +102 } +103 +104 if (line.startsWith(" * ")) { +105 changesColl.add(line.substring(" * ".length())); +106 continue; +107 } +108 +109 throw new ParseException("Unknown line syntax [" + line + "]", 0); +110 } +111 +112 reader.close(); +113 +114 changeSets = changeSetColl.toArray(new ChangeSet[changeSetColl.size()]); +115 } +116 +117 public void save(OutputStream pOutput) throws IOException { +118 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(pOutput)); +119 +120 for (ChangeSet changeSet : changeSets) { +121 writer.write("release "); +122 writer.write("date=" + fmt.format(changeSet.getDate()) + ","); +123 writer.write("version=" + changeSet.getVersion() + ","); +124 writer.write("urgency=" + changeSet.getUrgency() + ","); +125 writer.write("by=" + changeSet.getChangedBy() + ","); +126 writer.write("distribution=" + changeSet.getDistribution()); +127 writer.write("\n"); +128 +129 for (String change : changeSet.getChanges()) { +130 writer.write(" * "); +131 writer.write(change); +132 writer.write("\n"); +133 } +134 } +135 +136 writer.close(); +137 } +138 +139 public ChangeSet[] getChangesSets() { +140 return changeSets; +141 } +142} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.debian; +018 +019import java.io.IOException; +020import java.io.InputStream; +021import java.text.ParseException; +022 +023/** +024 * Binary package control file. +025 * +026 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-binarycontrolfiles">Debian Policy Manual - Binary package control files</a> +027 */ +028public final class BinaryPackageControlFile extends ControlFile { +029 +030 private static final ControlField[] FIELDS = { +031 new ControlField("Package", true), +032 new ControlField("Source"), +033 new ControlField("Version", true), +034 new ControlField("Section", true), +035 new ControlField("Priority", true), +036 new ControlField("Architecture", true), +037 new ControlField("Essential"), +038 new ControlField("Depends"), +039 new ControlField("Pre-Depends"), +040 new ControlField("Recommends"), +041 new ControlField("Suggests"), +042 new ControlField("Breaks"), +043 new ControlField("Enhances"), +044 new ControlField("Conflicts"), +045 new ControlField("Provides"), +046 new ControlField("Replaces"), +047 new ControlField("Installed-Size"), +048 new ControlField("Maintainer", true), +049 new ControlField("Description", true, ControlField.Type.MULTILINE), +050 new ControlField("Homepage"), +051 new ControlField("Multi-Arch") +052 }; +053 +054 public BinaryPackageControlFile() { +055 set("Architecture", "all"); +056 set("Priority", "optional"); +057 } +058 +059 public BinaryPackageControlFile(String input) throws IOException, ParseException { +060 parse(input); +061 } +062 +063 public BinaryPackageControlFile(InputStream input) throws IOException, ParseException { +064 parse(input); +065 } +066 +067 public void set(final String field, final String value) { +068 super.set(field, value); +069 } +070 +071 protected ControlField[] getFields() { +072 return FIELDS; +073 } +074 +075 /** +076 * Returns the short description of the package. The short description +077 * consists in the first line of the Description field. +078 * +079 * @return +080 */ +081 public String getShortDescription() { +082 if (get("Description") == null) { +083 return null; +084 } +085 +086 return get("Description").split("\n")[0]; +087 } +088 +089 protected char getUserDefinedFieldLetter() { +090 return 'B'; +091 } +092} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.debian; +018 +019import org.vafer.jdeb.utils.Utils; +020import org.vafer.jdeb.changes.ChangeSet; +021 +022import java.text.DateFormat; +023import java.text.SimpleDateFormat; +024import java.util.Date; +025import java.util.Locale; +026import java.util.Map.Entry; +027 +028/** +029 * Reflecting a changes file +030 * +031 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-debianchangesfiles">Debian Policy Manual - Debian changes files</a> +032 */ +033public final class ChangesFile extends ControlFile { +034 +035 private static final ControlField[] FIELDS = { +036 new ControlField("Format", true), +037 new ControlField("Date", true), +038 new ControlField("Source", true), +039 new ControlField("Binary", true), +040 new ControlField("Architecture", true), +041 new ControlField("Version", true), +042 new ControlField("Distribution", true), +043 new ControlField("Urgency", true), +044 new ControlField("Maintainer", true), +045 new ControlField("Changed-By"), +046 new ControlField("Description", true, ControlField.Type.MULTILINE, true), +047 new ControlField("Changes", true, ControlField.Type.MULTILINE, true), +048 new ControlField("Closes"), +049 new ControlField("Checksums-Sha1", true, ControlField.Type.MULTILINE, true), +050 new ControlField("Checksums-Sha256", true, ControlField.Type.MULTILINE, true), +051 new ControlField("Files", true, ControlField.Type.MULTILINE, true) +052 }; +053 +054 public ChangesFile() { +055 set("Format", "1.8"); +056 set("Urgency", "low"); +057 set("Distribution", "stable"); +058 } +059 +060 /** +061 * Initializes the fields on the changes file with the values of the specified +062 * binary package control file. +063 * +064 * @param packageControlFile +065 */ +066 public void initialize(BinaryPackageControlFile packageControlFile) { +067 set("Binary", packageControlFile.get("Package")); +068 set("Source", Utils.defaultString(packageControlFile.get("Source"), packageControlFile.get("Package"))); +069 set("Architecture", packageControlFile.get("Architecture")); +070 set("Version", packageControlFile.get("Version")); +071 set("Maintainer", packageControlFile.get("Maintainer")); +072 set("Distribution", packageControlFile.get("Distribution")); +073 +074 for (Entry<String, String> entry : packageControlFile.getUserDefinedFields().entrySet()) { +075 set(entry.getKey(), entry.getValue()); +076 } +077 +078 StringBuilder description = new StringBuilder(); +079 description.append(packageControlFile.get("Package")); +080 if (packageControlFile.get("Description") != null) { +081 description.append(" - "); +082 description.append(packageControlFile.getShortDescription()); +083 } +084 set("Description", description.toString()); +085 } +086 +087 public void setChanges(ChangeSet[] changeSets) { +088 StringBuilder sb = new StringBuilder(); +089 +090 if (changeSets.length > 0) { +091 final ChangeSet mostRecentChangeSet = changeSets[0]; +092 set("Urgency", mostRecentChangeSet.getUrgency()); +093 set("Changed-By", mostRecentChangeSet.getChangedBy()); +094 +095 for (ChangeSet changeSet : changeSets) { +096 sb.append(changeSet.toString()); +097 } +098 } +099 +100 set("Changes", sb.toString()); +101 } +102 +103 protected ControlField[] getFields() { +104 return FIELDS; +105 } +106 +107 protected char getUserDefinedFieldLetter() { +108 return 'C'; +109 } +110 +111 public static String formatDate(Date date) { +112 final DateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH); // RFC 2822 format +113 return format.format(date); +114 } +115} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.debian; +018 +019import java.io.BufferedReader; +020import java.io.IOException; +021import java.io.StringReader; +022 +023/** +024 * A field of a control file. This class is immutable. +025 */ +026public class ControlField { +027 +028 /** +029 * The format of a field. +030 */ +031 public enum Type { +032 /** Value on a single line */ +033 SIMPLE, +034 /** Value on multiple lines, space characters are ignored */ +035 FOLDED, +036 /** Value on multiple lines, space characters are preserved */ +037 MULTILINE +038 } +039 +040 /** The name of the field */ +041 private final String name; +042 +043 /** Tells if the field is mandatory */ +044 private final boolean mandatory; +045 +046 /** The type of the field */ +047 private final Type type; +048 +049 /** Tells is the first line of the field must be empty (for MULTILINE values only) */ +050 private final boolean firstLineEmpty; +051 +052 +053 public ControlField(String name) { +054 this(name, false); +055 } +056 +057 public ControlField(String name, boolean mandatory) { +058 this(name, mandatory, Type.SIMPLE); +059 } +060 +061 public ControlField(String name, boolean mandatory, Type type) { +062 this(name, mandatory, type, false); +063 } +064 +065 public ControlField(String name, boolean mandatory, Type type, boolean firstLineEmpty) { +066 this.name = name; +067 this.mandatory = mandatory; +068 this.type = type; +069 this.firstLineEmpty = firstLineEmpty; +070 } +071 +072 public String getName() { +073 return name; +074 } +075 +076 public boolean isMandatory() { +077 return mandatory; +078 } +079 +080 public Type getType() { +081 return type; +082 } +083 +084 public boolean isFirstLineEmpty() { +085 return firstLineEmpty; +086 } +087 +088 /** +089 * Returns the field with the specified value properly formatted. Multiline +090 * values are automatically indented, and dots are added on the empty lines. +091 * +092 * <pre> +093 * Field-Name: value +094 * </pre> +095 */ +096 public String format(String value) { +097 StringBuilder s = new StringBuilder(); +098 +099 if (value != null && value.trim().length() > 0) { +100 boolean continuationLine = false; +101 +102 s.append(getName()).append(":"); +103 if (isFirstLineEmpty()) { +104 s.append("\n"); +105 continuationLine = true; +106 } +107 +108 try { +109 BufferedReader reader = new BufferedReader(new StringReader(value)); +110 String line; +111 while ((line = reader.readLine()) != null) { +112 if (continuationLine && line.trim().length() == 0) { +113 // put a dot on the empty continuation lines +114 s.append(" .\n"); +115 } else { +116 s.append(" ").append(line).append("\n"); +117 } +118 +119 continuationLine = true; +120 } +121 } catch (IOException e) { +122 e.printStackTrace(); +123 } +124 } +125 +126 return s.toString(); +127 } +128} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.debian; +018 +019import java.io.BufferedReader; +020import java.io.IOException; +021import java.io.StringReader; +022 +023/** +024 * A field of a control file. This class is immutable. +025 */ +026public class ControlField { +027 +028 /** +029 * The format of a field. +030 */ +031 public enum Type { +032 /** Value on a single line */ +033 SIMPLE, +034 /** Value on multiple lines, space characters are ignored */ +035 FOLDED, +036 /** Value on multiple lines, space characters are preserved */ +037 MULTILINE +038 } +039 +040 /** The name of the field */ +041 private final String name; +042 +043 /** Tells if the field is mandatory */ +044 private final boolean mandatory; +045 +046 /** The type of the field */ +047 private final Type type; +048 +049 /** Tells is the first line of the field must be empty (for MULTILINE values only) */ +050 private final boolean firstLineEmpty; +051 +052 +053 public ControlField(String name) { +054 this(name, false); +055 } +056 +057 public ControlField(String name, boolean mandatory) { +058 this(name, mandatory, Type.SIMPLE); +059 } +060 +061 public ControlField(String name, boolean mandatory, Type type) { +062 this(name, mandatory, type, false); +063 } +064 +065 public ControlField(String name, boolean mandatory, Type type, boolean firstLineEmpty) { +066 this.name = name; +067 this.mandatory = mandatory; +068 this.type = type; +069 this.firstLineEmpty = firstLineEmpty; +070 } +071 +072 public String getName() { +073 return name; +074 } +075 +076 public boolean isMandatory() { +077 return mandatory; +078 } +079 +080 public Type getType() { +081 return type; +082 } +083 +084 public boolean isFirstLineEmpty() { +085 return firstLineEmpty; +086 } +087 +088 /** +089 * Returns the field with the specified value properly formatted. Multiline +090 * values are automatically indented, and dots are added on the empty lines. +091 * +092 * <pre> +093 * Field-Name: value +094 * </pre> +095 */ +096 public String format(String value) { +097 StringBuilder s = new StringBuilder(); +098 +099 if (value != null && value.trim().length() > 0) { +100 boolean continuationLine = false; +101 +102 s.append(getName()).append(":"); +103 if (isFirstLineEmpty()) { +104 s.append("\n"); +105 continuationLine = true; +106 } +107 +108 try { +109 BufferedReader reader = new BufferedReader(new StringReader(value)); +110 String line; +111 while ((line = reader.readLine()) != null) { +112 if (continuationLine && line.trim().length() == 0) { +113 // put a dot on the empty continuation lines +114 s.append(" .\n"); +115 } else { +116 s.append(" ").append(line).append("\n"); +117 } +118 +119 continuationLine = true; +120 } +121 } catch (IOException e) { +122 e.printStackTrace(); +123 } +124 } +125 +126 return s.toString(); +127 } +128} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.debian; +018 +019import java.io.BufferedReader; +020import java.io.ByteArrayInputStream; +021import java.io.IOException; +022import java.io.InputStream; +023import java.io.InputStreamReader; +024import java.text.ParseException; +025import java.util.ArrayList; +026import java.util.Arrays; +027import java.util.HashSet; +028import java.util.LinkedHashMap; +029import java.util.List; +030import java.util.Map; +031import java.util.Set; +032 +033import static java.nio.charset.StandardCharsets.*; +034 +035/** +036 * A control file as specified by the <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html">Debian policy</a>. +037 */ +038public abstract class ControlFile { +039 +040 protected final Map<String, String> values = new LinkedHashMap<>(); +041 protected final Map<String, String> userDefinedFields = new LinkedHashMap<>(); +042 protected final Set<ControlField> userDefinedFieldNames = new HashSet<>(); +043 +044 public void parse(String input) throws IOException, ParseException { +045 parse(new ByteArrayInputStream(input.getBytes(UTF_8))); +046 } +047 +048 public void parse(InputStream input) throws IOException, ParseException { +049 BufferedReader reader = new BufferedReader(new InputStreamReader(input, UTF_8)); +050 StringBuilder buffer = new StringBuilder(); +051 String field = null; +052 int linenr = 0; +053 while (true) { +054 final String line = reader.readLine(); +055 +056 if (line == null) { +057 // flush value of the previous field +058 set(field, buffer.toString()); +059 break; +060 } +061 +062 linenr++; +063 +064 if (line.length() == 0) { +065 throw new ParseException("Empty line", linenr); +066 } +067 +068 final char first = line.charAt(0); +069 if (first == '#') { +070 // ignore commented out lines +071 continue; +072 } +073 +074 if (Character.isLetter(first)) { +075 +076 // new field +077 +078 // flush value of the previous field +079 set(field, buffer.toString()); +080 buffer = new StringBuilder(); +081 +082 +083 final int i = line.indexOf(':'); +084 +085 if (i < 0) { +086 throw new ParseException("Line misses ':' delimiter", linenr); +087 } +088 +089 field = line.substring(0, i); +090 buffer.append(line.substring(i + 1).trim()); +091 +092 continue; +093 } +094 +095 // continuing old value, lines with only a dot are ignored +096 buffer.append('\n'); +097 if (!".".equals(line.substring(1).trim())) { +098 buffer.append(line.substring(1)); +099 } +100 } +101 reader.close(); +102 +103 } +104 +105 public void set(String field, final String value) { +106 if (field != null && isUserDefinedField(field)) { +107 userDefinedFields.put(field, value); +108 String fieldName = getUserDefinedFieldName(field); +109 +110 if (fieldName != null) { +111 userDefinedFieldNames.add(new ControlField(fieldName)); +112 } +113 +114 field = fieldName; +115 } +116 +117 if (field != null && !"".equals(field)) { +118 values.put(field, value); +119 } +120 } +121 +122 public String get(String field) { +123 return values.get(field); +124 } +125 +126 protected abstract ControlField[] getFields(); +127 +128 protected Map<String, String> getUserDefinedFields() { +129 return userDefinedFields; +130 } +131 +132 protected Set<ControlField> getUserDefinedFieldNames() { +133 return userDefinedFieldNames; +134 } +135 +136 public List<String> getMandatoryFields() { +137 List<String> fields = new ArrayList<>(); +138 +139 for (ControlField field : getFields()) { +140 if (field.isMandatory()) { +141 fields.add(field.getName()); +142 } +143 } +144 +145 return fields; +146 } +147 +148 public boolean isValid() { +149 return invalidFields().size() == 0; +150 } +151 +152 public Set<String> invalidFields() { +153 Set<String> invalid = new HashSet<>(); +154 +155 for (ControlField field : getFields()) { +156 if (field.isMandatory() && get(field.getName()) == null) { +157 invalid.add(field.getName()); +158 } +159 } +160 +161 return invalid; +162 } +163 +164 public String toString(ControlField... fields) { +165 StringBuilder s = new StringBuilder(); +166 for (ControlField field : fields) { +167 String value = values.get(field.getName()); +168 s.append(field.format(value)); +169 } +170 return s.toString(); +171 } +172 +173 public String toString() { +174 List<ControlField> fields = new ArrayList<>(); +175 fields.addAll(Arrays.asList(getFields())); +176 fields.addAll(getUserDefinedFieldNames()); +177 return toString(fields.toArray(new ControlField[fields.size()])); +178 } +179 +180 /** +181 * Returns the letter expected in the prefix of a user defined field +182 * in order to include the field in this control file. +183 * +184 * @return The letter returned is: +185 * <ul> +186 * <li>B: for a binary package</li> +187 * <li>S: for a source package</li> +188 * <li>C: for a changes file</li> +189 * </ul> +190 * +191 * @since 1.1 +192 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.7">Debian Policy - User-defined fields</a> +193 */ +194 protected abstract char getUserDefinedFieldLetter(); +195 +196 /** +197 * Tells if the specified field name is a user defined field. +198 * User-defined fields must begin with an 'X', followed by one or more +199 * letters that specify the output file and a hyphen. +200 * +201 * @param field the name of the field +202 * +203 * @since 1.1 +204 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.7">Debian Policy - User-defined fields</a> +205 */ +206 protected boolean isUserDefinedField(String field) { +207 return field.startsWith("X") && field.indexOf("-") > 0; +208 } +209 +210 /** +211 * Returns the user defined field without its prefix. +212 * +213 * @param field the name of the user defined field +214 * @return the user defined field without the prefix, or null if the fields +215 * doesn't apply to this control file. +216 * @since 1.1 +217 */ +218 protected String getUserDefinedFieldName(String field) { +219 int index = field.indexOf('-'); +220 char letter = getUserDefinedFieldLetter(); +221 +222 for (int i = 0; i < index; ++i) { +223 if (field.charAt(i) == letter) { +224 return field.substring(index + 1); +225 } +226 } +227 +228 return null; +229 } +230} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.mapping; +017 +018import java.io.BufferedReader; +019import java.io.IOException; +020import java.io.InputStream; +021import java.io.InputStreamReader; +022import java.util.HashMap; +023import java.util.Map; +024import java.util.regex.Matcher; +025import java.util.regex.Pattern; +026 +027import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +028 +029/** +030 * Reads permissions and ownerships from a "ls -laR > mapping.txt" dump and +031 * maps entries accordingly. +032 */ +033public final class LsMapper implements Mapper { +034 +035 private final Map<String, TarArchiveEntry> mapping; +036 +037 +038 public final static class ParseError extends Exception { +039 +040 private static final long serialVersionUID = 1L; +041 +042 public ParseError( String message ) { +043 super(message); +044 } +045 } +046 +047 +048 public LsMapper( final InputStream pInput ) throws IOException, ParseError { +049 mapping = parse(pInput); +050 } +051 +052 /* +053./trunk/target/test-classes/org/vafer/dependency: +054total 176 +055drwxr-xr-x 23 tcurdt tcurdt 782 Jun 25 03:48 . +056drwxr-xr-x 3 tcurdt tcurdt 102 Jun 25 03:48 .. +057-rw-r--r-- 1 tcurdt tcurdt 2934 Jun 25 03:48 DependenciesTestCase.class +058-rw-r--r-- 1 tcurdt tcurdt 786 Jun 25 03:48 JarCombiningTestCase$1.class +059-rw-r--r-- 1 tcurdt tcurdt 2176 Jun 25 03:48 WarTestCase.class +060drwxr-xr-x 4 tcurdt tcurdt 136 Jun 25 03:48 classes +061 +062./trunk/target/test-classes/org/vafer/dependency/classes: +063 */ +064 +065 final private Pattern basePattern = Pattern.compile("^\\./(.*):$"); +066 final private Pattern totalPattern = Pattern.compile("^total ([0-9]+)$"); +067 final private Pattern dirPattern = Pattern.compile("^d([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+[.]{1,2}$"); +068 final private Pattern filePattern = Pattern.compile("^([d-])([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+(.*)$"); +069 final private Pattern newlinePattern = Pattern.compile("$"); +070 +071 private String readBase( final BufferedReader reader ) throws IOException, ParseError { +072 final String line = reader.readLine(); +073 if (line == null) { +074 return null; +075 } +076 final Matcher matcher = basePattern.matcher(line); +077 if (!matcher.matches()) { +078 throw new ParseError("expected base line but got \"" + line + "\""); +079 } +080 return matcher.group(1); +081 } +082 +083 private String readTotal( final BufferedReader reader ) throws IOException, ParseError { +084 final String line = reader.readLine(); +085 final Matcher matcher = totalPattern.matcher(line); +086 if (!matcher.matches()) { +087 throw new ParseError("expected total line but got \"" + line + "\""); +088 } +089 return matcher.group(1); +090 } +091 +092 private TarArchiveEntry readDir( final BufferedReader reader, final String base ) throws IOException, ParseError { +093 final String current = reader.readLine(); +094 final Matcher currentMatcher = dirPattern.matcher(current); +095 if (!currentMatcher.matches()) { +096 throw new ParseError("expected dirline but got \"" + current + "\""); +097 } +098 +099 final String parent = reader.readLine(); +100 final Matcher parentMatcher = dirPattern.matcher(parent); +101 if (!parentMatcher.matches()) { +102 throw new ParseError("expected dirline but got \"" + parent + "\""); +103 } +104 +105 final TarArchiveEntry entry = new TarArchiveEntry(base, true); +106 +107 entry.setMode(convertModeFromString(currentMatcher.group(1))); +108 entry.setUserName(currentMatcher.group(3)); +109 entry.setGroupName(currentMatcher.group(4)); +110 +111 return entry; +112 } +113 +114 +115 private int convertModeFromString( final String mode ) { +116 +117 final char[] m = mode.toCharArray(); +118 /* +119 -rwxrwxrwx +120 +121 4000 set-user-ID-on-execution bit +122 2000 set-user-ID-on-execution bit +123 1000 sticky bit +124 0400 allow read by owner. +125 0200 allow write by owner. +126 0100 execute / search +127 0040 allow read by group members. +128 0020 allow write by group members. +129 0010 execute / search +130 0004 allow read by others. +131 0002 allow write by others. +132 0001 execute / search +133 */ +134 // TODO: simplified - needs fixing +135 int sum = 0; +136 int bit = 1; +137 for (int i = m.length - 1; i >= 0; i--) { +138 if (m[i] != '-') { +139 sum += bit; +140 } +141 bit += bit; +142 } +143 return sum; +144 } +145 +146 private TarArchiveEntry readFile( final BufferedReader reader, final String base ) throws IOException, ParseError { +147 +148 while (true) { +149 final String line = reader.readLine(); +150 +151 if (line == null) { +152 return null; +153 } +154 +155 final Matcher currentMatcher = filePattern.matcher(line); +156 if (!currentMatcher.matches()) { +157 final Matcher newlineMatcher = newlinePattern.matcher(line); +158 if (newlineMatcher.matches()) { +159 return null; +160 } +161 throw new ParseError("expected file line but got \"" + line + "\""); +162 } +163 +164 final String type = currentMatcher.group(1); +165 if (type.startsWith("-")) { +166 final TarArchiveEntry entry = new TarArchiveEntry(base + "/" + currentMatcher.group(8), true); +167 +168 entry.setMode(convertModeFromString(currentMatcher.group(2))); +169 entry.setUserName(currentMatcher.group(4)); +170 entry.setGroupName(currentMatcher.group(5)); +171 +172 return entry; +173 } +174 } +175 +176 } +177 +178 private Map<String, TarArchiveEntry> parse( final InputStream pInput ) throws IOException, ParseError { +179 final Map<String, TarArchiveEntry> mapping = new HashMap<>(); +180 +181 final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput)); +182 +183 boolean first = true; +184 while (true) { +185 +186 final String base; +187 if (first) { +188 base = ""; +189 first = false; +190 } else { +191 base = readBase(reader); +192 if (base == null) { +193 break; +194 } +195 } +196 +197 readTotal(reader); +198 final TarArchiveEntry dir = readDir(reader, base); +199 mapping.put(dir.getName(), dir); +200 +201 while (true) { +202 final TarArchiveEntry file = readFile(reader, base); +203 +204 if (file == null) { +205 break; +206 } +207 +208 mapping.put(file.getName(), file); +209 } +210 } +211 +212 return mapping; +213 } +214 +215 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +216 final TarArchiveEntry entry = mapping.get(pEntry.getName()); +217 if (entry != null) { +218 return entry; +219 } +220 return pEntry; +221 } +222 +223} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.mapping; +017 +018import java.io.BufferedReader; +019import java.io.IOException; +020import java.io.InputStream; +021import java.io.InputStreamReader; +022import java.util.HashMap; +023import java.util.Map; +024import java.util.regex.Matcher; +025import java.util.regex.Pattern; +026 +027import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +028 +029/** +030 * Reads permissions and ownerships from a "ls -laR > mapping.txt" dump and +031 * maps entries accordingly. +032 */ +033public final class LsMapper implements Mapper { +034 +035 private final Map<String, TarArchiveEntry> mapping; +036 +037 +038 public final static class ParseError extends Exception { +039 +040 private static final long serialVersionUID = 1L; +041 +042 public ParseError( String message ) { +043 super(message); +044 } +045 } +046 +047 +048 public LsMapper( final InputStream pInput ) throws IOException, ParseError { +049 mapping = parse(pInput); +050 } +051 +052 /* +053./trunk/target/test-classes/org/vafer/dependency: +054total 176 +055drwxr-xr-x 23 tcurdt tcurdt 782 Jun 25 03:48 . +056drwxr-xr-x 3 tcurdt tcurdt 102 Jun 25 03:48 .. +057-rw-r--r-- 1 tcurdt tcurdt 2934 Jun 25 03:48 DependenciesTestCase.class +058-rw-r--r-- 1 tcurdt tcurdt 786 Jun 25 03:48 JarCombiningTestCase$1.class +059-rw-r--r-- 1 tcurdt tcurdt 2176 Jun 25 03:48 WarTestCase.class +060drwxr-xr-x 4 tcurdt tcurdt 136 Jun 25 03:48 classes +061 +062./trunk/target/test-classes/org/vafer/dependency/classes: +063 */ +064 +065 final private Pattern basePattern = Pattern.compile("^\\./(.*):$"); +066 final private Pattern totalPattern = Pattern.compile("^total ([0-9]+)$"); +067 final private Pattern dirPattern = Pattern.compile("^d([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+[.]{1,2}$"); +068 final private Pattern filePattern = Pattern.compile("^([d-])([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+(.*)$"); +069 final private Pattern newlinePattern = Pattern.compile("$"); +070 +071 private String readBase( final BufferedReader reader ) throws IOException, ParseError { +072 final String line = reader.readLine(); +073 if (line == null) { +074 return null; +075 } +076 final Matcher matcher = basePattern.matcher(line); +077 if (!matcher.matches()) { +078 throw new ParseError("expected base line but got \"" + line + "\""); +079 } +080 return matcher.group(1); +081 } +082 +083 private String readTotal( final BufferedReader reader ) throws IOException, ParseError { +084 final String line = reader.readLine(); +085 final Matcher matcher = totalPattern.matcher(line); +086 if (!matcher.matches()) { +087 throw new ParseError("expected total line but got \"" + line + "\""); +088 } +089 return matcher.group(1); +090 } +091 +092 private TarArchiveEntry readDir( final BufferedReader reader, final String base ) throws IOException, ParseError { +093 final String current = reader.readLine(); +094 final Matcher currentMatcher = dirPattern.matcher(current); +095 if (!currentMatcher.matches()) { +096 throw new ParseError("expected dirline but got \"" + current + "\""); +097 } +098 +099 final String parent = reader.readLine(); +100 final Matcher parentMatcher = dirPattern.matcher(parent); +101 if (!parentMatcher.matches()) { +102 throw new ParseError("expected dirline but got \"" + parent + "\""); +103 } +104 +105 final TarArchiveEntry entry = new TarArchiveEntry(base, true); +106 +107 entry.setMode(convertModeFromString(currentMatcher.group(1))); +108 entry.setUserName(currentMatcher.group(3)); +109 entry.setGroupName(currentMatcher.group(4)); +110 +111 return entry; +112 } +113 +114 +115 private int convertModeFromString( final String mode ) { +116 +117 final char[] m = mode.toCharArray(); +118 /* +119 -rwxrwxrwx +120 +121 4000 set-user-ID-on-execution bit +122 2000 set-user-ID-on-execution bit +123 1000 sticky bit +124 0400 allow read by owner. +125 0200 allow write by owner. +126 0100 execute / search +127 0040 allow read by group members. +128 0020 allow write by group members. +129 0010 execute / search +130 0004 allow read by others. +131 0002 allow write by others. +132 0001 execute / search +133 */ +134 // TODO: simplified - needs fixing +135 int sum = 0; +136 int bit = 1; +137 for (int i = m.length - 1; i >= 0; i--) { +138 if (m[i] != '-') { +139 sum += bit; +140 } +141 bit += bit; +142 } +143 return sum; +144 } +145 +146 private TarArchiveEntry readFile( final BufferedReader reader, final String base ) throws IOException, ParseError { +147 +148 while (true) { +149 final String line = reader.readLine(); +150 +151 if (line == null) { +152 return null; +153 } +154 +155 final Matcher currentMatcher = filePattern.matcher(line); +156 if (!currentMatcher.matches()) { +157 final Matcher newlineMatcher = newlinePattern.matcher(line); +158 if (newlineMatcher.matches()) { +159 return null; +160 } +161 throw new ParseError("expected file line but got \"" + line + "\""); +162 } +163 +164 final String type = currentMatcher.group(1); +165 if (type.startsWith("-")) { +166 final TarArchiveEntry entry = new TarArchiveEntry(base + "/" + currentMatcher.group(8), true); +167 +168 entry.setMode(convertModeFromString(currentMatcher.group(2))); +169 entry.setUserName(currentMatcher.group(4)); +170 entry.setGroupName(currentMatcher.group(5)); +171 +172 return entry; +173 } +174 } +175 +176 } +177 +178 private Map<String, TarArchiveEntry> parse( final InputStream pInput ) throws IOException, ParseError { +179 final Map<String, TarArchiveEntry> mapping = new HashMap<>(); +180 +181 final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput)); +182 +183 boolean first = true; +184 while (true) { +185 +186 final String base; +187 if (first) { +188 base = ""; +189 first = false; +190 } else { +191 base = readBase(reader); +192 if (base == null) { +193 break; +194 } +195 } +196 +197 readTotal(reader); +198 final TarArchiveEntry dir = readDir(reader, base); +199 mapping.put(dir.getName(), dir); +200 +201 while (true) { +202 final TarArchiveEntry file = readFile(reader, base); +203 +204 if (file == null) { +205 break; +206 } +207 +208 mapping.put(file.getName(), file); +209 } +210 } +211 +212 return mapping; +213 } +214 +215 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +216 final TarArchiveEntry entry = mapping.get(pEntry.getName()); +217 if (entry != null) { +218 return entry; +219 } +220 return pEntry; +221 } +222 +223} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.mapping; +017 +018import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +019 +020 +021/** +022 * Maps one entry to another. So you modify ownerships permissions etc in a Mapper. +023 */ +024public interface Mapper { +025 +026 TarArchiveEntry map( final TarArchiveEntry entry ); +027 +028} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.mapping; +017 +018import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +019 +020public final class NullMapper implements Mapper { +021 +022 public static final Mapper INSTANCE = new NullMapper(); +023 +024 private NullMapper() { +025 } +026 +027 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +028 return pEntry; +029 } +030 +031} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.mapping; +017 +018import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +019import org.vafer.jdeb.utils.Utils; +020 +021/** +022 * Applies a uniform set of permissions and ownership to all entries. +023 */ +024public final class PermMapper implements Mapper { +025 +026 private final int strip; +027 private final String prefix; +028 private int uid = -1; +029 private int gid = -1; +030 private String user; +031 private String group; +032 private int fileMode = -1; +033 private int dirMode = -1; +034 +035 public static int toMode( String modeString ) { +036 int mode = -1; +037 if (modeString != null && modeString.length() > 0) { +038 mode = Integer.parseInt(modeString, 8); +039 } +040 return mode; +041 } +042 +043 public PermMapper( int uid, int gid, String user, String group, int fileMode, int dirMode, int strip, String prefix ) { +044 this.strip = strip; +045 this.prefix = (prefix == null) ? "" : prefix; +046 this.uid = uid; +047 this.gid = gid; +048 this.user = user; +049 this.group = group; +050 this.fileMode = fileMode; +051 this.dirMode = dirMode; +052 } +053 +054 public PermMapper( int uid, int gid, String user, String group, String fileMode, String dirMode, int strip, String prefix ) { +055 this(uid, gid, user, group, toMode(fileMode), toMode(dirMode), strip, prefix); +056 } +057 +058 public TarArchiveEntry map( final TarArchiveEntry entry ) { +059 entry.setName(Utils.stripLeadingSlash(Utils.joinUnixPath( +060 prefix, +061 Utils.stripPath(strip, entry.getName()) +062 ))); +063 +064 // Set ownership +065 if (uid > -1) { +066 entry.setUserId(uid); +067 } +068 if (gid > -1) { +069 entry.setGroupId(gid); +070 } +071 if (user != null) { +072 entry.setUserName(user); +073 } +074 if (group != null) { +075 entry.setGroupName(group); +076 } +077 +078 // Set permissions +079 if (entry.isDirectory()) { +080 if (dirMode > -1) { +081 entry.setMode(dirMode); +082 } +083 } else { +084 if (fileMode > -1) { +085 entry.setMode(fileMode); +086 } +087 } +088 +089 return entry; +090 } +091} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.maven; +018 +019import java.io.File; +020import java.io.FileNotFoundException; +021import java.io.IOException; +022import java.util.ArrayList; +023import java.util.List; +024import java.util.StringTokenizer; +025 +026import org.apache.maven.plugins.annotations.Parameter; +027import org.vafer.jdeb.DataConsumer; +028import org.vafer.jdeb.DataProducer; +029import org.vafer.jdeb.producers.DataProducerArchive; +030import org.vafer.jdeb.producers.DataProducerDirectory; +031import org.vafer.jdeb.producers.DataProducerFile; +032import org.vafer.jdeb.producers.DataProducerFiles; +033import org.vafer.jdeb.producers.DataProducerLink; +034import org.vafer.jdeb.producers.DataProducerPathTemplate; +035 +036import static org.vafer.jdeb.maven.MissingSourceBehavior.*; +037 +038/** +039 * Maven "data" element acting as a factory for DataProducers. So far Archive and +040 * Directory producers are supported. Both support the usual ant pattern set +041 * matching. +042 */ +043public final class Data implements DataProducer { +044 +045 @Parameter +046 private File src; +047 +048 public void setSrc( File src ) { +049 this.src = src; +050 } +051 +052 @Parameter +053 private String dst; +054 +055 public void setDst( String dst ) { +056 this.dst = dst; +057 } +058 +059 @Parameter +060 private String type; +061 +062 public void setType( String type ) { +063 this.type = type; +064 } +065 +066 @Parameter +067 private MissingSourceBehavior missingSrc = FAIL; +068 +069 public void setMissingSrc( String missingSrc ) { +070 this.missingSrc = MissingSourceBehavior.valueOf(missingSrc.trim().toUpperCase()); +071 } +072 +073 @Parameter +074 private String linkName; +075 +076 public void setLinkName(String linkName) { +077 this.linkName = linkName; +078 } +079 +080 @Parameter +081 private String linkTarget; +082 +083 public void setLinkTarget(String linkTarget) { +084 this.linkTarget = linkTarget; +085 } +086 +087 @Parameter +088 private boolean symlink = true; +089 +090 public void setSymlink(boolean symlink) { +091 this.symlink = symlink; +092 } +093 +094 private boolean conffile = false; +095 +096 /** +097 * @parameter expression="${conffile}" +098 */ +099 public void setConffile(boolean conffile) { +100 this.conffile = conffile; +101 } +102 +103 public boolean getConffile() { +104 return this.conffile; +105 } +106 +107 @Parameter(alias = "includes") +108 private String[] includePatterns; +109 +110 public void setIncludes( String includes ) { +111 includePatterns = splitPatterns(includes); +112 } +113 +114 @Parameter(alias = "excludes") +115 private String[] excludePatterns; +116 +117 public void setExcludes( String excludes ) { +118 excludePatterns = splitPatterns(excludes); +119 } +120 +121 @Parameter +122 private Mapper mapper; +123 +124 @Parameter +125 private String[] paths; +126 +127 /* For testing only */ +128 void setPaths( String[] paths ) { +129 this.paths = paths; +130 } +131 +132 public String[] splitPatterns( String patterns ) { +133 String[] result = null; +134 if (patterns != null && patterns.length() > 0) { +135 List<String> tokens = new ArrayList<>(); +136 StringTokenizer tok = new StringTokenizer(patterns, ", ", false); +137 while (tok.hasMoreTokens()) { +138 tokens.add(tok.nextToken()); +139 } +140 result = tokens.toArray(new String[tokens.size()]); +141 } +142 return result; +143 } +144 +145 public void produce( final DataConsumer pReceiver ) throws IOException { +146 org.vafer.jdeb.mapping.Mapper[] mappers = null; +147 if (mapper != null) { +148 mappers = new org.vafer.jdeb.mapping.Mapper[] { mapper.createMapper() }; +149 } +150 +151 // link type +152 +153 if (typeIs("link")) { +154 if (linkName == null) { +155 throw new RuntimeException("linkName is not set"); +156 } +157 if (linkTarget == null) { +158 throw new RuntimeException("linkTarget is not set"); +159 } +160 +161 new DataProducerLink(linkName, linkTarget, symlink, includePatterns, excludePatterns, mappers).produce(pReceiver); +162 return; +163 } +164 +165 // template type +166 +167 if (typeIs("template")) { +168 checkPaths(); +169 new DataProducerPathTemplate(paths, includePatterns, excludePatterns, mappers).produce(pReceiver); +170 return; +171 } +172 +173 if (typeIs("files")) { +174 checkPaths(); +175 new DataProducerFiles(paths, dst, mappers).produce(pReceiver); +176 return; +177 } +178 +179 // Types that require src to exist +180 +181 if (src == null || !src.exists()) { +182 if (missingSrc == IGNORE) { +183 return; +184 } else { +185 throw new FileNotFoundException("Data source not found : " + src); +186 } +187 } +188 +189 if (typeIs("file")) { +190 new DataProducerFile(src, dst, includePatterns, excludePatterns, mappers).produce(pReceiver); +191 return; +192 } +193 +194 if (typeIs("archive")) { +195 new DataProducerArchive(src, includePatterns, excludePatterns, mappers).produce(pReceiver); +196 return; +197 } +198 +199 if (typeIs("directory")) { +200 new DataProducerDirectory(src, includePatterns, excludePatterns, mappers).produce(pReceiver); +201 return; +202 } +203 +204 throw new IOException("Unknown type '" + type + "' (file|directory|archive|template|link) for " + src); +205 } +206 +207 private boolean typeIs( final String type ) { +208 return type.equalsIgnoreCase(this.type); +209 } +210 +211 private void checkPaths() { +212 if (paths == null || paths.length == 0) { +213 throw new RuntimeException("paths parameter is not set"); +214 } +215 } +216} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.maven; +018 +019import java.io.File; +020import java.io.FileInputStream; +021import java.io.FileNotFoundException; +022import java.util.ArrayList; +023import java.util.Collection; +024import java.util.Collections; +025import java.util.HashMap; +026import java.util.HashSet; +027import java.util.List; +028import java.util.Map; +029import java.util.Set; +030 +031import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +032import org.apache.commons.compress.archivers.tar.TarConstants; +033import org.apache.maven.artifact.Artifact; +034import org.apache.maven.execution.MavenSession; +035import org.apache.maven.plugin.AbstractMojo; +036import org.apache.maven.plugin.MojoExecutionException; +037import org.apache.maven.plugins.annotations.Component; +038import org.apache.maven.plugins.annotations.LifecyclePhase; +039import org.apache.maven.plugins.annotations.Mojo; +040import org.apache.maven.plugins.annotations.Parameter; +041import org.apache.maven.project.MavenProject; +042import org.apache.maven.project.MavenProjectHelper; +043import org.apache.maven.settings.Profile; +044import org.apache.maven.settings.Settings; +045import org.apache.tools.tar.TarEntry; +046import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; +047import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException; +048import org.vafer.jdeb.Console; +049import org.vafer.jdeb.DataConsumer; +050import org.vafer.jdeb.DataProducer; +051import org.vafer.jdeb.DebMaker; +052import org.vafer.jdeb.PackagingException; +053import org.vafer.jdeb.utils.MapVariableResolver; +054import org.vafer.jdeb.utils.OutputTimestampResolver; +055import org.vafer.jdeb.utils.SymlinkUtils; +056import org.vafer.jdeb.utils.Utils; +057import org.vafer.jdeb.utils.VariableResolver; +058 +059import static org.vafer.jdeb.utils.Utils.isBlank; +060import static org.vafer.jdeb.utils.Utils.lookupIfEmpty; +061 +062/** +063 * Creates Debian package +064 */ +065@Mojo(name = "jdeb", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true) +066public class DebMojo extends AbstractMojo { +067 +068 @Component +069 private MavenProjectHelper projectHelper; +070 +071 @Component(hint = "jdeb-sec") +072 private SecDispatcher secDispatcher; +073 +074 /** +075 * Defines the name of deb package. +076 */ +077 @Parameter +078 private String name; +079 +080 /** +081 * Defines the pattern of the name of final artifacts. Possible +082 * substitutions are [[baseDir]] [[buildDir]] [[artifactId]] [[version]] +083 * [[extension]] and [[groupId]]. +084 */ +085 @Parameter(defaultValue = "[[buildDir]]/[[artifactId]]_[[version]]_all.[[extension]]") +086 private String deb; +087 +088 /** +089 * Explicitly defines the path to the control directory. At least the +090 * control file is mandatory. +091 */ +092 @Parameter(defaultValue = "[[baseDir]]/src/deb/control") +093 private String controlDir; +094 +095 /** +096 * Explicitly define the file to read the changes from. +097 */ +098 @Parameter(defaultValue = "[[baseDir]]/CHANGES.txt") +099 private String changesIn; +100 +101 /** +102 * Explicitly define the file where to write the changes to. +103 */ +104 @Parameter(defaultValue = "[[buildDir]]/[[artifactId]]_[[version]]_all.changes") +105 private String changesOut; +106 +107 /** +108 * Explicitly define the file where to write the changes of the changes input to. +109 */ +110 @Parameter(defaultValue = "[[baseDir]]/CHANGES.txt") +111 private String changesSave; +112 +113 /** +114 * The compression method used for the data file (none, gzip, bzip2 or xz) +115 */ +116 @Parameter(defaultValue = "gzip") +117 private String compression; +118 +119 /** +120 * Boolean option whether to attach the artifact to the project +121 */ +122 @Parameter(defaultValue = "true") +123 private String attach; +124 +125 /** +126 * The location where all package files will be installed. By default, all +127 * packages are installed in /opt (see the FHS here: +128 * http://www.pathname.com/ +129 * fhs/pub/fhs-2.3.html#OPTADDONAPPLICATIONSOFTWAREPACKAGES) +130 */ +131 @Parameter(defaultValue = "/opt/[[artifactId]]") +132 private String installDir; +133 +134 /** +135 * The type of attached artifact +136 */ +137 @Parameter(defaultValue = "deb") +138 private String type; +139 +140 /** +141 * The project base directory +142 */ +143 @Parameter(defaultValue = "${basedir}", required = true, readonly = true) +144 private File baseDir; +145 +146 /** +147 * The Maven Session Object +148 */ +149 @Parameter( defaultValue = "${session}", readonly = true ) +150 private MavenSession session; +151 +152 /** +153 * The Maven Project Object +154 */ +155 @Parameter( defaultValue = "${project}", readonly = true ) +156 private MavenProject project; +157 +158 /** +159 * The build directory +160 */ +161 @Parameter(property = "project.build.directory", required = true, readonly = true) +162 private File buildDirectory; +163 +164 /** +165 * The classifier of attached artifact +166 */ +167 @Parameter +168 private String classifier; +169 +170 /** +171 * The digest algorithm to use. +172 * +173 * @see org.bouncycastle.bcpg.HashAlgorithmTags +174 */ +175 @Parameter(defaultValue = "SHA256") +176 private String digest; +177 +178 /** +179 * "data" entries used to determine which files should be added to this deb. +180 * The "data" entries may specify a tarball (tar.gz, tar.bz2, tgz), a +181 * directory, or a normal file. An entry would look something like this in +182 * your pom.xml: +183 * +184 * +185 * <pre> +186 * <build> +187 * <plugins> +188 * <plugin> +189 * <artifactId>jdeb</artifactId> +190 * <groupId>org.vafer</groupId> +191 * ... +192 * <configuration> +193 * ... +194 * <dataSet> +195 * <data> +196 * <src>${project.basedir}/target/my_archive.tar.gz</src> +197 * <include>...</include> +198 * <exclude>...</exclude> +199 * <mapper> +200 * <type>perm</type> +201 * <strip>1</strip> +202 * <prefix>/somewhere/else</prefix> +203 * <user>santbj</user> +204 * <group>santbj</group> +205 * <mode>600</mode> +206 * </mapper> +207 * </data> +208 * <data> +209 * <src>${project.build.directory}/data</src> +210 * <include></include> +211 * <exclude>**/.svn</exclude> +212 * <mapper> +213 * <type>ls</type> +214 * <src>mapping.txt</src> +215 * </mapper> +216 * </data> +217 * <data> +218 * <type>link</type> +219 * <linkName>/a/path/on/the/target/fs</linkName> +220 * <linkTarget>/a/sym/link/to/the/scr/file</linkTarget> +221 * <symlink>true</symlink> +222 * </data> +223 * <data> +224 * <src>${project.basedir}/README.txt</src> +225 * </data> +226 * </dataSet> +227 * </configuration> +228 * </plugins> +229 * </build> +230 * </pre> +231 * +232 */ +233 @Parameter +234 private Data[] dataSet; +235 +236 /** +237 * When enabled SNAPSHOT inside the version gets replaced with current timestamp or +238 * if set a value of a environment variable. +239 */ +240 @Parameter(defaultValue = "false") +241 private boolean snapshotExpand; +242 +243 /** +244 * Which environment variable to check for the SNAPSHOT value. +245 * If the variable is not set/empty it will default to use the timestamp. +246 */ +247 @Parameter(defaultValue = "SNAPSHOT") +248 private String snapshotEnv; +249 +250 /** +251 * Template for replacing the SNAPSHOT value. A timestamp format can be provided in brackets. +252 * prefix[yyMMdd]suffix -> prefix151230suffix +253 */ +254 @Parameter +255 private String snapshotTemplate; +256 +257 /** +258 * If verbose is true more build messages are logged. +259 */ +260 @Parameter(defaultValue = "false") +261 private boolean verbose; +262 +263 /** +264 * Indicates if the execution should be disabled. If <code>true</code>, nothing will occur during execution. +265 * +266 * @since 1.1 +267 */ +268 @Parameter(property = "jdeb.skip", defaultValue = "false") +269 private boolean skip; +270 +271 @Parameter(property = "jdeb.skipPOMs", defaultValue = "true") +272 private boolean skipPOMs; +273 +274 @Parameter(property = "jdeb.skipSubmodules", defaultValue = "false") +275 private boolean skipSubmodules; +276 +277 @Deprecated +278 @Parameter(defaultValue = "true") +279 private boolean submodules; +280 +281 +282 /** +283 * If signPackage is true then a origin signature will be placed +284 * in the generated package. +285 */ +286 @Parameter(defaultValue = "false") +287 private boolean signPackage; +288 +289 /** +290 * If signChanges is true then changes file will be signed. +291 */ +292 @Parameter(defaultValue = "false") +293 private boolean signChanges; +294 +295 /** +296 * Defines which utility is used to verify the signed package +297 */ +298 @Parameter(defaultValue = "debsig-verify") +299 private String signMethod; +300 +301 /** +302 * Defines the role to sign with +303 */ +304 @Parameter(defaultValue = "origin") +305 private String signRole; +306 +307 /** +308 * The keyring to use for signing operations. +309 */ +310 @Parameter +311 private String keyring; +312 +313 /** +314 * The key to use for signing operations. +315 */ +316 @Parameter +317 private String key; +318 +319 /** +320 * The passphrase to use for signing operations. +321 */ +322 @Parameter +323 private String passphrase; +324 +325 /** +326 * The prefix to use when reading signing variables +327 * from settings. +328 */ +329 @Parameter(defaultValue = "jdeb.") +330 private String signCfgPrefix; +331 +332 /** +333 * The settings. +334 */ +335 @Parameter(defaultValue = "${settings}") +336 private Settings settings; +337 +338 @Parameter(defaultValue = "") +339 private String propertyPrefix; +340 +341 /** +342 * Sets the long file mode for the resulting tar file. Valid values are "gnu", "posix", "error" or "truncate" +343 * @see org.apache.commons.compress.archivers.tar.TarArchiveOutputStream#setLongFileMode(int) +344 */ +345 @Parameter(defaultValue = "gnu") +346 private String tarLongFileMode; +347 +348 /** +349 * Sets the big number mode for the resulting tar file. Valid values are "gnu", "posix" or "error" +350 * @see org.apache.commons.compress.archivers.tar.TarArchiveOutputStream#setBigNumberMode(int) +351 */ +352 @Parameter(defaultValue = "gnu") +353 private String tarBigNumberMode; +354 +355 /** +356 * Timestamp for reproducible output archive entries, either formatted as ISO 8601 +357 * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like +358 * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>). +359 * +360 * @since 1.9 +361 */ +362 @Parameter(defaultValue = "${project.build.outputTimestamp}") +363 private String outputTimestamp; +364 +365 /* end of parameters */ +366 +367 private static final String KEY = "key"; +368 private static final String KEYRING = "keyring"; +369 private static final String PASSPHRASE = "passphrase"; +370 +371 private String openReplaceToken = "[["; +372 private String closeReplaceToken = "]]"; +373 private Console console; +374 private Collection<DataProducer> dataProducers = new ArrayList<>(); +375 private Collection<DataProducer> conffileProducers = new ArrayList<>(); +376 +377 public void setOpenReplaceToken( String openReplaceToken ) { +378 this.openReplaceToken = openReplaceToken; +379 } +380 +381 public void setCloseReplaceToken( String closeReplaceToken ) { +382 this.closeReplaceToken = closeReplaceToken; +383 } +384 +385 protected void setData( Data[] dataSet ) { +386 this.dataSet = dataSet; +387 dataProducers.clear(); +388 conffileProducers.clear(); +389 if (dataSet != null) { +390 Collections.addAll(dataProducers, dataSet); +391 +392 for (Data item : dataSet) { +393 if (item.getConffile()) { +394 conffileProducers.add(item); +395 } +396 } +397 } +398 } +399 +400 @SuppressWarnings("unchecked,rawtypes") +401 protected VariableResolver initializeVariableResolver( Map<String, String> variables ) { +402 variables.putAll((Map) getProject().getProperties()); +403 variables.putAll((Map) System.getProperties()); +404 variables.put("name", name != null ? name : getProject().getName()); +405 variables.put("artifactId", getProject().getArtifactId()); +406 variables.put("groupId", getProject().getGroupId()); +407 variables.put("version", getProjectVersion()); +408 variables.put("description", getProject().getDescription()); +409 variables.put("extension", "deb"); +410 variables.put("baseDir", getProject().getBasedir().getAbsolutePath()); +411 variables.put("buildDir", buildDirectory.getAbsolutePath()); +412 variables.put("project.version", getProject().getVersion()); +413 +414 if (getProject().getInceptionYear() != null) { +415 variables.put("project.inceptionYear", getProject().getInceptionYear()); +416 } +417 if (getProject().getOrganization() != null) { +418 if (getProject().getOrganization().getName() != null) { +419 variables.put("project.organization.name", getProject().getOrganization().getName()); +420 } +421 if (getProject().getOrganization().getUrl() != null) { +422 variables.put("project.organization.url", getProject().getOrganization().getUrl()); +423 } +424 } +425 +426 variables.put("url", getProject().getUrl()); +427 +428 return new MapVariableResolver(variables); +429 } +430 +431 /** +432 * Doc some cleanup and conversion on the Maven project version. +433 * <ul> +434 * <li>any "-" is replaced by "+"</li> +435 * <li>"SNAPSHOT" is replaced with the current time and date, prepended by "~"</li> +436 * </ul> +437 * +438 * @return the Maven project version +439 */ +440 private String getProjectVersion() { +441 return Utils.convertToDebianVersion(getProject().getVersion(), this.snapshotExpand, this.snapshotEnv, this.snapshotTemplate, session.getStartTime()); +442 } +443 +444 /** +445 * @return whether the artifact is a POM or not +446 */ +447 private boolean isPOM() { +448 String type = getProject().getArtifact().getType(); +449 return "pom".equalsIgnoreCase(type); +450 } +451 +452 /** +453 * @return whether the artifact is of configured type (i.e. the package to generate is the main artifact) +454 */ +455 private boolean isType() { +456 return type.equals(getProject().getArtifact().getType()); +457 } +458 +459 /** +460 * @return whether or not Maven is currently operating in the execution root +461 */ +462 private boolean isSubmodule() { +463 // FIXME there must be a better way +464 return !session.getExecutionRootDirectory().equalsIgnoreCase(baseDir.toString()); +465 } +466 +467 /** +468 * @return whether or not the main artifact was created +469 */ +470 private boolean hasMainArtifact() { +471 final MavenProject project = getProject(); +472 final Artifact artifact = project.getArtifact(); +473 return artifact.getFile() != null && artifact.getFile().isFile(); +474 } +475 +476 /** +477 * Main entry point +478 * +479 * @throws MojoExecutionException on error +480 */ +481 public void execute() throws MojoExecutionException { +482 +483 final MavenProject project = getProject(); +484 +485 if (skip) { +486 getLog().info("skipping as configured (skip)"); +487 return; +488 } +489 +490 if (skipPOMs && isPOM()) { +491 getLog().info("skipping because artifact is a pom (skipPOMs)"); +492 return; +493 } +494 +495 if (skipSubmodules && isSubmodule()) { +496 getLog().info("skipping submodule (skipSubmodules)"); +497 return; +498 } +499 +500 +501 setData(dataSet); +502 +503 console = new MojoConsole(getLog(), verbose); +504 +505 initializeSignProperties(); +506 +507 final VariableResolver resolver = initializeVariableResolver(new HashMap<String, String>()); +508 +509 final File debFile = new File(Utils.replaceVariables(resolver, deb, openReplaceToken, closeReplaceToken)); +510 final File controlDirFile = new File(Utils.replaceVariables(resolver, controlDir, openReplaceToken, closeReplaceToken)); +511 final File installDirFile = new File(Utils.replaceVariables(resolver, installDir, openReplaceToken, closeReplaceToken)); +512 final File changesInFile = new File(Utils.replaceVariables(resolver, changesIn, openReplaceToken, closeReplaceToken)); +513 final File changesOutFile = new File(Utils.replaceVariables(resolver, changesOut, openReplaceToken, closeReplaceToken)); +514 final File changesSaveFile = new File(Utils.replaceVariables(resolver, changesSave, openReplaceToken, closeReplaceToken)); +515 final File keyringFile = keyring == null ? null : new File(Utils.replaceVariables(resolver, keyring, openReplaceToken, closeReplaceToken)); +516 +517 // if there are no producers defined we try to use the artifacts +518 if (dataProducers.isEmpty()) { +519 +520 if (hasMainArtifact()) { +521 Set<Artifact> artifacts = new HashSet<>(); +522 +523 artifacts.add(project.getArtifact()); +524 +525 @SuppressWarnings("unchecked") +526 final Set<Artifact> projectArtifacts = project.getArtifacts(); +527 +528 artifacts.addAll(projectArtifacts); +529 +530 @SuppressWarnings("unchecked") +531 final List<Artifact> attachedArtifacts = project.getAttachedArtifacts(); +532 +533 artifacts.addAll(attachedArtifacts); +534 +535 for (Artifact artifact : artifacts) { +536 final File file = artifact.getFile(); +537 if (file != null) { +538 dataProducers.add(new DataProducer() { +539 public void produce( final DataConsumer receiver ) { +540 try { +541 final File path = new File(installDirFile.getPath(), file.getName()); +542 final String entryName = path.getPath(); +543 +544 final boolean symbolicLink = SymlinkUtils.isSymbolicLink(path); +545 final TarArchiveEntry e; +546 if (symbolicLink) { +547 e = new TarArchiveEntry(entryName, TarConstants.LF_SYMLINK); +548 e.setLinkName(SymlinkUtils.readSymbolicLink(path)); +549 } else { +550 e = new TarArchiveEntry(entryName, true); +551 } +552 +553 e.setUserId(0); +554 e.setGroupId(0); +555 e.setUserName("root"); +556 e.setGroupName("root"); +557 e.setMode(TarEntry.DEFAULT_FILE_MODE); +558 e.setSize(file.length()); +559 +560 receiver.onEachFile(new FileInputStream(file), e); +561 } catch (Exception e) { +562 getLog().error(e); +563 } +564 } +565 }); +566 } else { +567 getLog().error("No file for artifact " + artifact); +568 } +569 } +570 } +571 } +572 +573 try { +574 DebMaker debMaker = new DebMaker(console, dataProducers, conffileProducers); +575 debMaker.setDeb(debFile); +576 debMaker.setControl(controlDirFile); +577 debMaker.setPackage(getProject().getArtifactId()); +578 debMaker.setDescription(getProject().getDescription()); +579 debMaker.setHomepage(getProject().getUrl()); +580 debMaker.setChangesIn(changesInFile); +581 debMaker.setChangesOut(changesOutFile); +582 debMaker.setChangesSave(changesSaveFile); +583 debMaker.setCompression(compression); +584 debMaker.setKeyring(keyringFile); +585 debMaker.setKey(key); +586 debMaker.setPassphrase(passphrase); +587 debMaker.setSignPackage(signPackage); +588 debMaker.setSignChanges(signChanges); +589 debMaker.setSignMethod(signMethod); +590 debMaker.setSignRole(signRole); +591 debMaker.setResolver(resolver); +592 debMaker.setOpenReplaceToken(openReplaceToken); +593 debMaker.setCloseReplaceToken(closeReplaceToken); +594 debMaker.setDigest(digest); +595 debMaker.setTarBigNumberMode(tarBigNumberMode); +596 debMaker.setTarLongFileMode(tarLongFileMode); +597 Long outputTimestampMs = new OutputTimestampResolver(console).resolveOutputTimestamp(outputTimestamp); +598 debMaker.setOutputTimestampMs(outputTimestampMs); +599 debMaker.validate(); +600 debMaker.makeDeb(); +601 +602 // Always attach unless explicitly set to false +603 if ("true".equalsIgnoreCase(attach)) { +604 console.info("Attaching created debian package " + debFile); +605 if (!isType()) { +606 projectHelper.attachArtifact(project, type, classifier, debFile); +607 } else { +608 project.getArtifact().setFile(debFile); +609 } +610 } +611 +612 } catch (PackagingException e) { +613 getLog().error("Failed to create debian package " + debFile, e); +614 throw new MojoExecutionException("Failed to create debian package " + debFile, e); +615 } +616 +617 if (!isBlank(propertyPrefix)) { +618 project.getProperties().put(propertyPrefix+"version", getProjectVersion() ); +619 project.getProperties().put(propertyPrefix+"deb", debFile.getAbsolutePath()); +620 project.getProperties().put(propertyPrefix+"deb.name", debFile.getName()); +621 project.getProperties().put(propertyPrefix+"changes", changesOutFile.getAbsolutePath()); +622 project.getProperties().put(propertyPrefix+"changes.name", changesOutFile.getName()); +623 project.getProperties().put(propertyPrefix+"changes.txt", changesSaveFile.getAbsolutePath()); +624 project.getProperties().put(propertyPrefix+"changes.txt.name", changesSaveFile.getName()); +625 } +626 +627 } +628 +629 /** +630 * Initializes unspecified sign properties using available defaults +631 * and global settings. +632 */ +633 private void initializeSignProperties() { +634 if (!signPackage && !signChanges) { +635 return; +636 } +637 +638 if (key != null && keyring != null && passphrase != null) { +639 return; +640 } +641 +642 Map<String, String> properties = +643 readPropertiesFromActiveProfiles(signCfgPrefix, KEY, KEYRING, PASSPHRASE); +644 +645 key = lookupIfEmpty(key, properties, KEY); +646 keyring = lookupIfEmpty(keyring, properties, KEYRING); +647 passphrase = decrypt(lookupIfEmpty(passphrase, properties, PASSPHRASE)); +648 +649 if (keyring == null) { +650 try { +651 keyring = Utils.guessKeyRingFile().getAbsolutePath(); +652 console.info("Located keyring at " + keyring); +653 } catch (FileNotFoundException e) { +654 console.warn(e.getMessage()); +655 } +656 } +657 } +658 +659 /** +660 * Decrypts given passphrase if needed using maven security dispatcher. +661 * See http://maven.apache.org/guides/mini/guide-encryption.html for details. +662 * +663 * @param maybeEncryptedPassphrase possibly encrypted passphrase +664 * @return decrypted passphrase +665 */ +666 private String decrypt( final String maybeEncryptedPassphrase ) { +667 if (maybeEncryptedPassphrase == null) { +668 return null; +669 } +670 +671 try { +672 final String decrypted = secDispatcher.decrypt(maybeEncryptedPassphrase); +673 if (maybeEncryptedPassphrase.equals(decrypted)) { +674 console.info("Passphrase was not encrypted"); +675 } else { +676 console.info("Passphrase was successfully decrypted"); +677 } +678 return decrypted; +679 } catch (SecDispatcherException e) { +680 console.warn("Unable to decrypt passphrase: " + e.getMessage()); +681 } +682 +683 return maybeEncryptedPassphrase; +684 } +685 +686 /** +687 * +688 * @return the maven project used by this mojo +689 */ +690 private MavenProject getProject() { +691 if (project.getExecutionProject() != null) { +692 return project.getExecutionProject(); +693 } +694 +695 return project; +696 } +697 +698 +699 +700 /** +701 * Read properties from the active profiles. +702 * +703 * Goes through all active profiles (in the order the +704 * profiles are defined in settings.xml) and extracts +705 * the desired properties (if present). The prefix is +706 * used when looking up properties in the profile but +707 * not in the returned map. +708 * +709 * @param prefix The prefix to use or null if no prefix should be used +710 * @param properties The properties to read +711 * +712 * @return A map containing the values for the properties that were found +713 */ +714 public Map<String, String> readPropertiesFromActiveProfiles( final String prefix, +715 final String... properties ) { +716 if (settings == null) { +717 console.debug("No maven setting injected"); +718 return Collections.emptyMap(); +719 } +720 +721 final List<String> activeProfilesList = settings.getActiveProfiles(); +722 if (activeProfilesList.isEmpty()) { +723 console.debug("No active profiles found"); +724 return Collections.emptyMap(); +725 } +726 +727 final Map<String, String> map = new HashMap<>(); +728 final Set<String> activeProfiles = new HashSet<>(activeProfilesList); +729 +730 // Iterate over all active profiles in order +731 for (final Profile profile : settings.getProfiles()) { +732 // Check if the profile is active +733 final String profileId = profile.getId(); +734 if (activeProfiles.contains(profileId)) { +735 console.debug("Trying active profile " + profileId); +736 for (final String property : properties) { +737 final String propKey = prefix != null ? prefix + property : property; +738 final String value = profile.getProperties().getProperty(propKey); +739 if (value != null) { +740 console.debug("Found property " + property + " in profile " + profileId); +741 map.put(property, value); +742 } +743 } +744 } +745 } +746 +747 return map; +748 } +749 +750} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.maven; +018 +019import java.io.File; +020import java.io.FileInputStream; +021import java.io.IOException; +022 +023import org.apache.maven.plugins.annotations.Parameter; +024import org.vafer.jdeb.mapping.LsMapper; +025import org.vafer.jdeb.mapping.NullMapper; +026import org.vafer.jdeb.mapping.PermMapper; +027 +028/** +029 * Maven "mapper" element acting as factory for the entry mapper. +030 * Supported types: ls, perm +031 */ +032public final class Mapper { +033 +034 @Parameter(required = true) +035 private String type; +036 +037 @Parameter +038 private int uid = -1; +039 +040 @Parameter +041 private int gid = -1; +042 +043 @Parameter +044 private String user; +045 +046 @Parameter +047 private String group; +048 +049 @Parameter +050 private String filemode; +051 +052 @Parameter +053 private String dirmode; +054 +055 @Parameter +056 private String prefix; +057 +058 @Parameter +059 private int strip; +060 +061 @Parameter +062 private File src; +063 +064 +065 public org.vafer.jdeb.mapping.Mapper createMapper() throws IOException { +066 +067 if ("ls".equalsIgnoreCase(type)) { +068 try { +069 return new LsMapper(new FileInputStream(src)); +070 } catch (Exception e) { +071 e.printStackTrace(); +072 } +073 } +074 +075 if ("perm".equalsIgnoreCase(type)) { +076 return new PermMapper(uid, gid, user, group, filemode, dirmode, strip, prefix); +077 } +078 +079 /* NullMapper required for DataProducerPathTemplate */ +080 return NullMapper.INSTANCE; +081 } +082 +083} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.maven; +018 +019public enum MissingSourceBehavior { +020 IGNORE, FAIL +021} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +019import org.apache.tools.ant.types.selectors.SelectorUtils; +020import org.vafer.jdeb.DataConsumer; +021import org.vafer.jdeb.DataProducer; +022import org.vafer.jdeb.mapping.Mapper; +023 +024import java.io.File; +025import java.io.FileInputStream; +026import java.io.IOException; +027 +028/** +029 * Base Producer class providing including/excluding. +030 */ +031public abstract class AbstractDataProducer implements DataProducer { +032 +033 private final String[] includes; +034 private final String[] excludes; +035 private final Mapper[] mappers; +036 +037 +038 public AbstractDataProducer( final String[] pIncludes, final String[] pExcludes, final Mapper[] pMapper ) { +039 excludes = (pExcludes != null) ? pExcludes : new String[0]; +040 includes = (pIncludes != null) ? pIncludes : new String[] { "**" }; +041 mappers = (pMapper != null) ? pMapper : new Mapper[0]; +042 } +043 +044 public boolean isIncluded( final String pName ) { +045 if (!isIncluded(pName, includes)) { +046 return false; +047 } +048 if (isExcluded(pName, excludes)) { +049 return false; +050 } +051 return true; +052 } +053 +054 private boolean isIncluded( String name, String[] includes ) { +055 for (String include : includes) { +056 if (SelectorUtils.matchPath(include, name)) { +057 return true; +058 } +059 } +060 return false; +061 } +062 +063 +064 private boolean isExcluded( String name, String[] excludes ) { +065 for (String exclude : excludes) { +066 if (SelectorUtils.matchPath(exclude, name)) { +067 return true; +068 } +069 } +070 return false; +071 } +072 +073 public void produceDir( final DataConsumer consumer, +074 final String dirName ) throws IOException { +075 final String name = dirName.endsWith("/") ? dirName : dirName + "/"; +076 TarArchiveEntry entry = Producers.defaultDirEntryWithName(name); +077 entry = map(entry); +078 entry.setSize(0); +079 Producers.produceDirEntry(consumer, entry); +080 } +081 +082 public void produceFile( final DataConsumer consumer, +083 final File file, +084 final String fileName ) throws IOException { +085 TarArchiveEntry fileEntry = Producers.defaultFileEntryWithName(fileName); +086 fileEntry.setSize(file.length()); +087 fileEntry = map(fileEntry); +088 Producers.produceInputStreamWithEntry(consumer, new FileInputStream(file), fileEntry); +089 } +090 +091 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +092 +093 TarArchiveEntry entry = pEntry; +094 +095 for (Mapper mapper : mappers) { +096 entry = mapper.map(entry); +097 } +098 +099 return entry; +100 } +101} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import java.io.BufferedInputStream; +019import java.io.File; +020import java.io.FileInputStream; +021import java.io.IOException; +022import java.io.InputStream; +023 +024import org.apache.commons.compress.archivers.ArchiveEntry; +025import org.apache.commons.compress.archivers.ArchiveException; +026import org.apache.commons.compress.archivers.ArchiveInputStream; +027import org.apache.commons.compress.archivers.ArchiveStreamFactory; +028import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +029import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +030import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +031import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +032import org.apache.commons.compress.compressors.CompressorException; +033import org.apache.commons.compress.compressors.CompressorInputStream; +034import org.apache.commons.compress.compressors.CompressorStreamFactory; +035import org.vafer.jdeb.DataConsumer; +036import org.vafer.jdeb.DataProducer; +037import org.vafer.jdeb.mapping.Mapper; +038 +039/** +040 * Providing data from an archive keeping permissions and ownerships. +041 */ +042public final class DataProducerArchive extends AbstractDataProducer implements DataProducer { +043 +044 private final File archive; +045 +046 public DataProducerArchive( final File pArchive, final String[] pIncludes, final String[] pExcludes, final Mapper[] pMappers ) { +047 super(pIncludes, pExcludes, pMappers); +048 archive = pArchive; +049 } +050 +051 public void produce( final DataConsumer pReceiver ) throws IOException { +052 +053 InputStream is = new BufferedInputStream(new FileInputStream(archive)); +054 +055 CompressorInputStream compressorInputStream = null; +056 +057 try { +058 compressorInputStream = new CompressorStreamFactory().createCompressorInputStream(is); +059 } catch (CompressorException e) { +060 // expected if the input file is a zip archive +061 } +062 +063 if (compressorInputStream != null) { +064 is = new BufferedInputStream(compressorInputStream); +065 } +066 +067 ArchiveInputStream archiveInputStream; +068 +069 try { +070 archiveInputStream = new ArchiveStreamFactory().createArchiveInputStream(is); +071 } catch (ArchiveException e) { +072 throw new IOException("Unsupported archive format: " + archive, e); +073 } +074 +075 EntryConverter converter; +076 +077 if (archiveInputStream instanceof TarArchiveInputStream) { +078 +079 converter = new EntryConverter() { +080 public TarArchiveEntry convert( ArchiveEntry entry ) { +081 return (TarArchiveEntry) entry; +082 } +083 }; +084 +085 } else if (archiveInputStream instanceof ZipArchiveInputStream) { +086 +087 converter = new EntryConverter() { +088 public TarArchiveEntry convert( ArchiveEntry entry ) { +089 ZipArchiveEntry src = (ZipArchiveEntry) entry; +090 final TarArchiveEntry dst = new TarArchiveEntry(src.getName(), true); +091 //TODO: if (src.isUnixSymlink()) { +092 //} +093 +094 dst.setSize(src.getSize()); +095 dst.setMode(src.getUnixMode()); +096 dst.setModTime(src.getTime()); +097 +098 return dst; +099 } +100 }; +101 +102 } else { +103 throw new IOException("Unsupported archive format: " + archive); +104 } +105 +106 +107 try { +108 while (true) { +109 +110 ArchiveEntry archiveEntry = archiveInputStream.getNextEntry(); +111 +112 if (archiveEntry == null) { +113 break; +114 } +115 +116 if (!isIncluded(archiveEntry.getName())) { +117 continue; +118 } +119 +120 TarArchiveEntry entry = converter.convert(archiveEntry); +121 +122 entry = map(entry); +123 +124 if (entry.isSymbolicLink()) { +125 pReceiver.onEachLink(entry); +126 continue; +127 } +128 +129 if (entry.isDirectory()) { +130 pReceiver.onEachDir(entry); +131 continue; +132 } +133 +134 pReceiver.onEachFile(archiveInputStream, entry); +135 } +136 +137 } finally { +138 if (archiveInputStream != null) { +139 archiveInputStream.close(); +140 } +141 } +142 } +143 +144 private interface EntryConverter { +145 TarArchiveEntry convert( ArchiveEntry entry ); +146 } +147} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import java.io.File; +019import java.io.IOException; +020 +021import org.apache.tools.ant.DirectoryScanner; +022import org.vafer.jdeb.DataConsumer; +023import org.vafer.jdeb.DataProducer; +024import org.vafer.jdeb.mapping.Mapper; +025import org.vafer.jdeb.utils.Utils; +026 +027/** +028 * DataProducer iterating over a directory. +029 * For cross-platform permissions and ownerships you probably want to use a Mapper, too. +030 */ +031public final class DataProducerDirectory extends AbstractDataProducer implements DataProducer { +032 +033 private final DirectoryScanner scanner = new DirectoryScanner(); +034 +035 public DataProducerDirectory( final File pDir, final String[] pIncludes, final String[] pExcludes, final Mapper[] pMappers ) { +036 super(pIncludes, pExcludes, pMappers); +037 scanner.setBasedir(pDir); +038 scanner.setIncludes(pIncludes); +039 scanner.setExcludes(pExcludes); +040 scanner.setCaseSensitive(true); +041 scanner.setFollowSymlinks(true); +042 } +043 +044 public void produce( final DataConsumer pReceiver ) throws IOException { +045 +046 scanner.scan(); +047 +048 final File baseDir = scanner.getBasedir(); +049 +050 for (String dir : scanner.getIncludedDirectories()) { +051 final File file = new File(baseDir, dir); +052 String dirname = getFilename(baseDir, file); +053 +054 if ("".equals(dirname)) { +055 continue; +056 } +057 +058 if ('/' != File.separatorChar) { +059 dirname = dirname.replace(File.separatorChar, '/'); +060 } +061 +062 if (!isIncluded(dirname)) { +063 continue; +064 } +065 +066 if (!dirname.endsWith("/")) { +067 dirname += "/"; +068 } +069 +070 produceDir(pReceiver, dirname); +071 } +072 +073 +074 for (String f : scanner.getIncludedFiles()) { +075 final File file = new File(baseDir, f); +076 String filename = getFilename(baseDir, file); +077 +078 if ('/' != File.separatorChar) { +079 filename = filename.replace(File.separatorChar, '/'); +080 } +081 +082 if (!isIncluded(filename)) { +083 continue; +084 } +085 +086 produceFile(pReceiver, file, filename); +087 } +088 } +089 +090 private String getFilename( File root, File file ) { +091 +092 final String relativeFilename = file.getAbsolutePath().substring(root.getAbsolutePath().length()); +093 +094 return Utils.stripLeadingSlash(relativeFilename); +095 } +096 +097} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import java.io.File; +019import java.io.FileInputStream; +020import java.io.IOException; +021 +022import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +023import org.vafer.jdeb.DataConsumer; +024import org.vafer.jdeb.DataProducer; +025import org.vafer.jdeb.mapping.Mapper; +026 +027/** +028 * DataProducer representing a single file +029 * For cross-platform permissions and ownerships you probably want to use a Mapper, too. +030 */ +031public final class DataProducerFile extends AbstractDataProducer implements DataProducer { +032 +033 private final File file; +034 +035 private final String destinationName; +036 +037 public DataProducerFile( final File pFile, String pDestinationName, String[] pIncludes, String[] pExcludes, Mapper[] pMapper ) { +038 super(pIncludes, pExcludes, pMapper); +039 file = pFile; +040 destinationName = pDestinationName; +041 } +042 +043 public void produce( final DataConsumer pReceiver ) throws IOException { +044 String fileName; +045 if (destinationName != null && destinationName.trim().length() > 0) { +046 fileName = destinationName.trim(); +047 } else { +048 fileName = file.getName(); +049 } +050 +051 TarArchiveEntry entry = Producers.defaultFileEntryWithName(fileName); +052 +053 entry = map(entry); +054 +055 entry.setSize(file.length()); +056 +057 Producers.produceInputStreamWithEntry(pReceiver, new FileInputStream(file), entry); +058 } +059 +060} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import java.io.File; +019import java.io.FileInputStream; +020import java.io.IOException; +021import java.io.InputStream; +022 +023import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +024import org.apache.commons.compress.archivers.tar.TarConstants; +025import org.apache.tools.ant.DirectoryScanner; +026import org.apache.tools.ant.taskdefs.Tar; +027import org.apache.tools.ant.types.FileSet; +028import org.apache.tools.tar.TarEntry; +029import org.vafer.jdeb.DataConsumer; +030import org.vafer.jdeb.DataProducer; +031import org.vafer.jdeb.utils.SymlinkUtils; +032 +033/** +034 * DataProducer providing data from an Ant fileset. TarFileSets are also +035 * supported with their permissions. +036 */ +037public final class DataProducerFileSet implements DataProducer { +038 +039 private final FileSet fileset; +040 +041 public DataProducerFileSet( final FileSet fileset ) { +042 this.fileset = fileset; +043 } +044 +045 public void produce( final DataConsumer pReceiver ) throws IOException { +046 String user = Producers.ROOT_NAME; +047 int uid = Producers.ROOT_UID; +048 String group = Producers.ROOT_NAME; +049 int gid = Producers.ROOT_UID; +050 int filemode = TarEntry.DEFAULT_FILE_MODE; +051 int dirmode = TarEntry.DEFAULT_DIR_MODE; +052 String prefix = ""; +053 String fullpath = ""; +054 +055 if (fileset instanceof Tar.TarFileSet) { +056 Tar.TarFileSet tarfileset = (Tar.TarFileSet) fileset; +057 user = tarfileset.getUserName(); +058 uid = tarfileset.getUid(); +059 group = tarfileset.getGroup(); +060 gid = tarfileset.getGid(); +061 filemode = tarfileset.getMode(); +062 dirmode = tarfileset.getDirMode(tarfileset.getProject()); +063 prefix = tarfileset.getPrefix(tarfileset.getProject()); +064 fullpath = tarfileset.getFullpath(tarfileset.getProject()); +065 } +066 +067 final DirectoryScanner scanner = fileset.getDirectoryScanner(fileset.getProject()); +068 scanner.scan(); +069 +070 final File basedir = scanner.getBasedir(); +071 +072 if (scanner.getIncludedFilesCount() != 1 || scanner.getIncludedDirsCount() != 0) { +073 // the full path attribute only have sense in this context +074 // if it's a single-file fileset, we ignore it otherwise +075 fullpath = ""; +076 } +077 +078 for (String directory : scanner.getIncludedDirectories()) { +079 String name = directory.replace('\\', '/'); +080 +081 final TarArchiveEntry entry = new TarArchiveEntry(prefix + "/" + name); +082 entry.setUserName(user); +083 entry.setUserId(uid); +084 entry.setGroupName(group); +085 entry.setGroupId(gid); +086 entry.setMode(dirmode); +087 +088 pReceiver.onEachDir(entry); +089 } +090 +091 for (String filename : scanner.getIncludedFiles()) { +092 final String name = filename.replace('\\', '/'); +093 final File file = new File(basedir, name); +094 +095 try (InputStream inputStream = new FileInputStream(file)) { +096 final String entryName = "".equals(fullpath) ? prefix + "/" + name : fullpath; +097 +098 final File entryPath = new File(entryName); +099 +100 final boolean symbolicLink = SymlinkUtils.isSymbolicLink(entryPath); +101 final TarArchiveEntry e; +102 if (symbolicLink) { +103 e = new TarArchiveEntry(entryName, TarConstants.LF_SYMLINK); +104 e.setLinkName(SymlinkUtils.readSymbolicLink(entryPath)); +105 } else { +106 e = new TarArchiveEntry(entryName, true); +107 } +108 +109 e.setUserId(uid); +110 e.setGroupId(gid); +111 e.setUserName(user); +112 e.setGroupName(group); +113 e.setMode(filemode); +114 e.setSize(file.length()); +115 +116 pReceiver.onEachFile(inputStream, e); +117 } +118 } +119 } +120} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import org.vafer.jdeb.DataConsumer; +019import org.vafer.jdeb.mapping.Mapper; +020import org.vafer.jdeb.utils.Utils; +021 +022import java.io.File; +023import java.io.IOException; +024 +025/** +026 * Data producer that places multiple files into a single +027 * destination directory. +028 */ +029public class DataProducerFiles extends AbstractDataProducer { +030 +031 private final String[] files; +032 private final String destDir; +033 +034 public DataProducerFiles( final String[] files, +035 final String destDir, +036 final Mapper[] mappers ) { +037 super(null, null, mappers); +038 this.files = files; +039 this.destDir = destDir; +040 } +041 +042 public void produce( DataConsumer receiver ) throws IOException { +043 boolean hasDestDir = !Utils.isNullOrEmpty(destDir); +044 +045 for (String fileName : files) { +046 File f = new File(fileName); +047 +048 if (hasDestDir) { +049 fileName = Utils.movePath(fileName, destDir); +050 } +051 +052 produceFile(receiver, f, fileName); +053 } +054 } +055} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import java.io.IOException; +019 +020import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +021import org.vafer.jdeb.DataConsumer; +022import org.vafer.jdeb.DataProducer; +023import org.vafer.jdeb.mapping.Mapper; +024 +025/** +026 * DataProducer representing a single file +027 * For cross-platform permissions and ownerships you probably want to use a Mapper, too. +028 */ +029public final class DataProducerLink extends AbstractDataProducer implements DataProducer { +030 +031 private final String path; +032 private final String linkName; +033 private final boolean symlink; +034 +035 public DataProducerLink(final String path, final String linkName, final boolean symlink, String[] pIncludes, String[] pExcludes, Mapper[] pMapper) { +036 super(pIncludes, pExcludes, pMapper); +037 this.path = path; +038 this.symlink = symlink; +039 this.linkName = linkName; +040 } +041 +042 public void produce( final DataConsumer pReceiver ) throws IOException { +043 TarArchiveEntry entry = new TarArchiveEntry(path, symlink ? TarArchiveEntry.LF_SYMLINK : TarArchiveEntry.LF_LINK); +044 entry.setLinkName(linkName); +045 +046 entry.setUserId(Producers.ROOT_UID); +047 entry.setUserName(Producers.ROOT_NAME); +048 entry.setGroupId(Producers.ROOT_UID); +049 entry.setGroupName(Producers.ROOT_NAME); +050 entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE); +051 +052 entry = map(entry); +053 +054 entry.setName(path); +055 entry.setLinkName(linkName); +056 +057 pReceiver.onEachLink(entry); +058 } +059 +060} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.producers; +017 +018import java.io.IOException; +019 +020import org.vafer.jdeb.DataConsumer; +021import org.vafer.jdeb.DataProducer; +022import org.vafer.jdeb.mapping.Mapper; +023 +024public class DataProducerPathTemplate extends AbstractDataProducer implements DataProducer { +025 +026 private final String[] literalPaths; +027 +028 public DataProducerPathTemplate( String[] pLiteralPaths, String[] pIncludes, String[] pExcludes, Mapper[] pMapper ) { +029 super(pIncludes, pExcludes, pMapper); +030 literalPaths = pLiteralPaths; +031 } +032 +033 public void produce( DataConsumer pReceiver ) throws IOException { +034 for (String literalPath : literalPaths) { +035 produceDir(pReceiver, literalPath); +036 } +037 } +038 +039} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.signing; +018 +019import java.io.ByteArrayInputStream; +020import java.io.IOException; +021import java.io.InputStream; +022import java.io.InputStreamReader; +023import java.io.OutputStream; +024import java.util.Iterator; +025 +026import org.apache.commons.io.LineIterator; +027import org.bouncycastle.bcpg.ArmoredOutputStream; +028import org.bouncycastle.bcpg.BCPGOutputStream; +029import org.bouncycastle.bcpg.HashAlgorithmTags; +030import org.bouncycastle.openpgp.PGPException; +031import org.bouncycastle.openpgp.PGPPrivateKey; +032import org.bouncycastle.openpgp.PGPSecretKey; +033import org.bouncycastle.openpgp.PGPSecretKeyRing; +034import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +035import org.bouncycastle.openpgp.PGPSignature; +036import org.bouncycastle.openpgp.PGPSignatureGenerator; +037import org.bouncycastle.openpgp.PGPUtil; +038import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +039import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +040import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +041import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +042 +043import static java.nio.charset.StandardCharsets.*; +044 +045import org.vafer.jdeb.PackagingException; +046 +047/** +048 * Signing with OpenPGP. +049 */ +050public class PGPSigner { +051 +052 private static final byte[] EOL = "\n".getBytes(UTF_8); +053 +054 private PGPSecretKey secretKey; +055 private PGPPrivateKey privateKey; +056 private int digest; +057 +058 private org.bouncycastle.crypto.digests.SHA1Digest keepSHA1; +059 private org.bouncycastle.crypto.digests.MD2Digest keepMD2; +060 private org.bouncycastle.crypto.digests.MD5Digest keepMD5; +061 private org.bouncycastle.crypto.digests.RIPEMD160Digest keepRIPEMD160; +062 private org.bouncycastle.crypto.digests.SHA256Digest keepSHA256; +063 private org.bouncycastle.crypto.digests.SHA384Digest keepSHA384; +064 private org.bouncycastle.crypto.digests.SHA512Digest keepSHA512; +065 private org.bouncycastle.crypto.digests.SHA224Digest keepSHA224; +066 +067 public static int getDigestCode(String digestName) throws PackagingException { +068 if ("SHA1".equals(digestName)) { +069 return HashAlgorithmTags.SHA1; +070 } else if ("MD2".equals(digestName)) { +071 return HashAlgorithmTags.MD2; +072 } else if ("MD5".equals(digestName)) { +073 return HashAlgorithmTags.MD5; +074 } else if ("RIPEMD160".equals(digestName)) { +075 return HashAlgorithmTags.RIPEMD160; +076 } else if ("SHA256".equals(digestName)) { +077 return HashAlgorithmTags.SHA256; +078 } else if ("SHA384".equals(digestName)) { +079 return HashAlgorithmTags.SHA384; +080 } else if ("SHA512".equals(digestName)) { +081 return HashAlgorithmTags.SHA512; +082 } else if ("SHA224".equals(digestName)) { +083 return HashAlgorithmTags.SHA224; +084 } else { +085 throw new PackagingException("unknown hash algorithm tag in digestName: " + digestName); +086 } +087 } +088 +089 public PGPSigner(InputStream keyring, String keyId, String passphrase, int digest) throws IOException, PGPException { +090 secretKey = getSecretKey(keyring, keyId); +091 if(secretKey == null) +092 { +093 throw new PGPException(String.format("Specified key %s does not exist in key ring %s", keyId, keyring)); +094 } +095 privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passphrase.toCharArray())); +096 this.digest = digest; +097 } +098 +099 /** +100 * Creates a clear sign signature over the input data. (Not detached) +101 * +102 * @param input the content to be signed +103 * @param output the output destination of the signature +104 */ +105 public void clearSign(String input, OutputStream output) throws IOException, PGPException { +106 clearSign(new ByteArrayInputStream(input.getBytes(UTF_8)), output); +107 } +108 +109 /** +110 * Creates a clear sign signature over the input data. (Not detached) +111 * +112 * @param input the content to be signed +113 * @param output the output destination of the signature +114 */ +115 public void clearSign(InputStream input, OutputStream output) throws IOException, PGPException { +116 +117 PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(privateKey.getPublicKeyPacket().getAlgorithm(), digest)); +118 signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privateKey); +119 +120 ArmoredOutputStream armoredOutput = new ArmoredOutputStream(output); +121 armoredOutput.beginClearText(digest); +122 +123 LineIterator iterator = new LineIterator(new InputStreamReader(input)); +124 +125 while (iterator.hasNext()) { +126 String line = iterator.nextLine(); +127 +128 // trailing spaces must be removed for signature calculation (see http://tools.ietf.org/html/rfc4880#section-7.1) +129 byte[] data = trim(line).getBytes(UTF_8); +130 +131 armoredOutput.write(data); +132 armoredOutput.write(EOL); +133 +134 signatureGenerator.update(data); +135 if (iterator.hasNext()) { +136 signatureGenerator.update(EOL); +137 } +138 } +139 +140 armoredOutput.endClearText(); +141 +142 PGPSignature signature = signatureGenerator.generate(); +143 signature.encode(new BCPGOutputStream(armoredOutput)); +144 +145 armoredOutput.close(); +146 } +147 +148 /** +149 * Returns the secret key. +150 */ +151 public PGPSecretKey getSecretKey() +152 { +153 return secretKey; +154 } +155 +156 /** +157 * Returns the private key. +158 */ +159 public PGPPrivateKey getPrivateKey() +160 { +161 return privateKey; +162 } +163 +164 /** +165 * Returns the secret key matching the specified identifier. +166 * +167 * @param input the input stream containing the keyring collection +168 * @param keyId the 4 bytes identifier of the key +169 */ +170 private PGPSecretKey getSecretKey(InputStream input, String keyId) throws IOException, PGPException { +171 PGPSecretKeyRingCollection keyrings = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); +172 +173 Iterator<PGPSecretKeyRing> rIt = keyrings.getKeyRings(); +174 +175 while (rIt.hasNext()) { +176 PGPSecretKeyRing kRing = rIt.next(); +177 Iterator<PGPSecretKey> kIt = kRing.getSecretKeys(); +178 +179 while (kIt.hasNext()) { +180 PGPSecretKey key = kIt.next(); +181 +182 if (key.isSigningKey() && String.format("%08x", key.getKeyID() & 0xFFFFFFFFL).equals(keyId.toLowerCase())) { +183 return key; +184 } +185 } +186 } +187 +188 return null; +189 } +190 +191 /** +192 * Trim the trailing spaces. +193 * +194 * @param line +195 */ +196 private String trim(String line) { +197 char[] chars = line.toCharArray(); +198 int len = chars.length; +199 +200 while (len > 0) { +201 if (!Character.isWhitespace(chars[len - 1])) { +202 break; +203 } +204 len--; +205 } +206 +207 return line.substring(0, len); +208 } +209} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.utils; +018 +019import java.io.BufferedReader; +020import java.io.IOException; +021import java.io.InputStream; +022import java.io.InputStreamReader; +023import java.util.ArrayList; +024import java.util.List; +025 +026public class FilteredFile { +027 +028 private String openToken = "[["; +029 private String closeToken = "]]"; +030 private List<String> lines = new ArrayList<>(); +031 +032 public FilteredFile(InputStream in, VariableResolver resolver) throws IOException { +033 parse(in, resolver); +034 } +035 +036 public void setOpenToken(String token) { +037 openToken = token; +038 } +039 +040 public void setCloseToken(String token) { +041 closeToken = token; +042 } +043 +044 private void parse(InputStream in, VariableResolver resolver) throws IOException { +045 try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { +046 String line; +047 while ((line = reader.readLine()) != null) { +048 if (resolver != null) { +049 lines.add(Utils.replaceVariables(resolver, line, openToken, closeToken)); +050 } else { +051 lines.add(line); +052 } +053 } +054 } +055 } +056 +057 public String toString() { +058 StringBuilder builder = new StringBuilder(); +059 for (String line : lines) { +060 builder.append(line).append('\n'); +061 } +062 return builder.toString(); +063 } +064} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016 +017package org.vafer.jdeb.utils; +018 +019import java.io.FilterInputStream; +020import java.io.IOException; +021import java.io.InputStream; +022import java.util.HashMap; +023import java.util.Map; +024 +025public final class InformationInputStream extends FilterInputStream { +026 +027 private long i; +028 private long ascii; +029 private long nonascii; +030 private long cr; +031 private long lf; +032 private long zero; +033 +034 private final Map<BOM, Integer> bomPositions = new HashMap<>(); +035 private final Map<Shell, Integer> shellPositions = new HashMap<>(); +036 +037 /** +038 * Byte Order Marks +039 */ +040 private enum BOM { +041 NONE(null), +042 UTF8("UTF-8", 0xEF, 0xBB, 0xBF), +043 UTF16LE("UTF-16LE", 0xFF, 0xFE), +044 UTF16BE("UTF-16BE", 0xFE, 0xFF); +045 +046 int[] sequence; +047 String encoding; +048 +049 BOM( String encoding, int... sequence ) { +050 this.encoding = encoding; +051 this.sequence = sequence; +052 } +053 } +054 +055 /** +056 * Shebang for shell scripts in various encodings. +057 */ +058 private enum Shell { +059 NONE, +060 ASCII(0x23, 0x21), +061 UTF16BE(0x00, 0x23, 0x00, 0x21), +062 UTF16LE(0x23, 0x00, 0x21, 0x00); +063 +064 int[] header; +065 +066 Shell( int... header ) { +067 this.header = header; +068 } +069 } +070 +071 private BOM bom = BOM.NONE; +072 private Shell shell = Shell.NONE; +073 +074 public InformationInputStream( InputStream in ) { +075 super(in); +076 } +077 +078 public boolean hasBom() { +079 return bom != BOM.NONE; +080 } +081 +082 public boolean isShell() { +083 return shell != Shell.NONE; +084 } +085 +086 public boolean hasUnixLineEndings() { +087 return cr == 0; +088 } +089 +090 public String getEncoding() { +091 String encoding = bom.encoding; +092 +093 if (encoding == null) { +094 // guess the encoding from the shebang +095 if (shell == Shell.UTF16BE) { +096 encoding = BOM.UTF16BE.encoding; +097 } else if (shell == Shell.UTF16LE) { +098 encoding = BOM.UTF16LE.encoding; +099 } +100 } +101 +102 return encoding; +103 } +104 +105 private void add( int c ) { +106 if (i < 10) { +107 if (shell == Shell.NONE) { +108 for (Shell shell : Shell.values()) { +109 int position = shellPositions.containsKey(shell) ? shellPositions.get(shell) : 0; +110 if (position < shell.header.length) { +111 if (c == shell.header[position]) { +112 shellPositions.put(shell, position + 1); +113 } else { +114 shellPositions.put(shell, 0); +115 } +116 } else { +117 this.shell = shell; +118 } +119 } +120 } +121 +122 if (bom == BOM.NONE) { +123 for (BOM bom : BOM.values()) { +124 int position = bomPositions.containsKey(bom) ? bomPositions.get(bom) : 0; +125 if (position < bom.sequence.length) { +126 if (c == bom.sequence[position] && position == i) { +127 bomPositions.put(bom, position + 1); +128 } else { +129 bomPositions.put(bom, 0); +130 } +131 } else { +132 this.bom = bom; +133 } +134 } +135 } +136 } +137 +138 i++; +139 +140 if (c == '\n') { +141 lf++; +142 return; +143 } +144 if (c == '\r') { +145 cr++; +146 return; +147 } +148 if (c >= ' ' && c <= '~') { +149 ascii++; +150 return; +151 } +152 if (c == 0) { +153 zero++; +154 return; +155 } +156 nonascii++; +157 } +158 +159 public int read() throws IOException { +160 int b = super.read(); +161 if (b != -1) { +162 add(b & 0xFF); +163 } +164 return b; +165 } +166 +167 public int read( byte[] b, int off, int len ) throws IOException { +168 int length = super.read(b, off, len); +169 for (int i = 0; i < length; i++) { +170 add(b[off + i] & 0xFF); +171 } +172 return length; +173 } +174 +175 public String toString() { +176 StringBuilder sb = new StringBuilder(); +177 sb.append("{"); +178 sb.append("total=").append(i); +179 sb.append(",noascii=").append(nonascii); +180 sb.append(",ascii=").append(ascii); +181 sb.append(",cr=").append(cr); +182 sb.append(",lf=").append(lf); +183 sb.append(",zero=").append(zero); +184 sb.append("}"); +185 return sb.toString(); +186 } +187} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.utils; +017 +018import java.io.IOException; +019import java.io.OutputStream; +020import java.security.DigestOutputStream; +021import java.security.MessageDigest; +022 +023/** +024 * Convenience class to provide digest info and length of a stream. +025 * +026 * ATTENTION: don't use outside of jdeb +027 */ +028public class InformationOutputStream extends DigestOutputStream { +029 +030 private final MessageDigest digest; +031 private long size; +032 +033 public InformationOutputStream( OutputStream pStream, MessageDigest pDigest ) { +034 super(pStream, pDigest); +035 digest = pDigest; +036 size = 0; +037 } +038 +039 public String getHexDigest() { +040 return Utils.toHex(digest.digest()); +041 } +042 +043 public void write( byte[] b, int off, int len ) throws IOException { +044 super.write(b, off, len); +045 size += len; +046 } +047 +048 public void write( int b ) throws IOException { +049 super.write(b); +050 size++; +051 } +052 +053 public long getSize() { +054 return size; +055 } +056} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.utils; +017 +018import java.util.Map; +019 +020/** +021 * Resolve variables based on a Map. +022 * +023 * ATTENTION: don't use outside of jdeb +024 */ +025public final class MapVariableResolver implements VariableResolver { +026 +027 private final Map<String, String> map; +028 +029 public MapVariableResolver( Map<String, String> map ) { +030 this.map = map; +031 } +032 +033 public String get( String key ) { +034 return map.get(key); +035 } +036 +037} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001package org.vafer.jdeb.utils; +002 +003import java.util.Date; +004import java.util.concurrent.TimeUnit; +005 +006import org.apache.maven.archiver.MavenArchiver; +007import org.vafer.jdeb.Console; +008 +009public class OutputTimestampResolver { +010 private final Console console; +011 private final EnvironmentVariablesReader envReader; +012 +013 public OutputTimestampResolver(Console console) { +014 this(console, new EnvironmentVariablesReader()); +015 } +016 +017 OutputTimestampResolver(Console console, EnvironmentVariablesReader envReader) { +018 this.console = console; +019 this.envReader = envReader; +020 } +021 +022 public Long resolveOutputTimestamp(String paramValue) { +023 if (paramValue != null) { +024 Date outputDate = new MavenArchiver().parseOutputTimestamp(paramValue); +025 if (outputDate != null) { +026 console.info("Accepted outputTimestamp parameter: " + paramValue); +027 return outputDate.getTime(); +028 } +029 } +030 +031 String sourceDate = envReader.getSourceDateEpoch(); +032 if (sourceDate != null && !sourceDate.isEmpty()) { +033 try { +034 long sourceDateVal = Long.parseLong(sourceDate); +035 console.info("Accepted SOURCE_DATE_EPOCH environment variable: " + sourceDate); +036 return sourceDateVal * TimeUnit.SECONDS.toMillis(1); +037 } catch (NumberFormatException e) { +038 throw new IllegalArgumentException("Invalid SOURCE_DATE_EPOCH environment variable value: " + sourceDate, e); +039 } +040 } +041 +042 return null; +043 } +044 +045 static class EnvironmentVariablesReader { +046 String getSourceDateEpoch() { +047 return System.getenv("SOURCE_DATE_EPOCH"); +048 } +049 } +050} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001package org.vafer.jdeb.utils; +002 +003import java.io.ByteArrayOutputStream; +004import java.io.IOException; +005import java.io.OutputStream; +006 +007import org.bouncycastle.bcpg.ArmoredOutputStream; +008import org.bouncycastle.openpgp.PGPException; +009import org.bouncycastle.openpgp.PGPSignature; +010import org.bouncycastle.openpgp.PGPSignatureGenerator; +011 +012/** +013 * An output stream that calculates the signature of the input data as it +014 * is written +015 */ +016public class PGPSignatureOutputStream extends OutputStream { +017 private final PGPSignatureGenerator signatureGenerator; +018 +019 public PGPSignatureOutputStream( PGPSignatureGenerator signatureGenerator ) { +020 super(); +021 this.signatureGenerator = signatureGenerator; +022 } +023 +024 public void write( int b ) throws IOException { +025 signatureGenerator.update(new byte[] { (byte)b }); +026 } +027 +028 public void write( byte[] b ) throws IOException { +029 signatureGenerator.update(b); +030 } +031 +032 public void write( byte[] b, int off, int len ) throws IOException { +033 signatureGenerator.update(b, off, len); +034 } +035 +036 public PGPSignature generateSignature() throws PGPException { +037 return signatureGenerator.generate(); +038 } +039 +040 public String generateASCIISignature() throws PGPException { +041 try { +042 PGPSignature signature = generateSignature(); +043 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); +044 ArmoredOutputStream armorStream = new ArmoredOutputStream(buffer); +045 signature.encode(armorStream); +046 armorStream.close(); +047 return buffer.toString(); +048 } catch(IOException e) { +049 //Should never happen since we are just using a memory buffer +050 throw new RuntimeException(e); +051 } +052 } +053 +054} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.utils; +017 +018import java.io.File; +019import java.io.IOException; +020 +021/** +022 * Simple utilities to deal with symbolic links +023 */ +024public final class SymlinkUtils { +025 private SymlinkUtils() { +026 } +027 +028 public static boolean isSymbolicLink(final File file) throws IOException { +029 final File canon; +030 if (file.getParent() == null) { +031 canon = file; +032 } else { +033 final File canonDir = file.getParentFile().getCanonicalFile(); +034 canon = new File(canonDir, file.getName()); +035 } +036 return !canon.getCanonicalFile().equals(canon.getAbsoluteFile()); +037 } +038 +039 public static String readSymbolicLink(final File file) throws IOException { +040 return file.getCanonicalFile().getPath(); +041 } +042 +043} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.utils; +017 +018import java.io.ByteArrayOutputStream; +019import java.io.File; +020import java.io.FileNotFoundException; +021import java.io.IOException; +022import java.io.InputStream; +023import java.io.InputStreamReader; +024import java.io.OutputStream; +025import java.text.SimpleDateFormat; +026import java.util.Collection; +027import java.util.Date; +028import java.util.Iterator; +029import java.util.LinkedHashSet; +030import java.util.Map; +031import java.util.TimeZone; +032import java.util.regex.Matcher; +033import java.util.regex.Pattern; +034 +035import org.apache.tools.ant.filters.FixCrLfFilter; +036import org.apache.tools.ant.util.ReaderInputStream; +037 +038/** +039 * Simple utils functions. +040 * +041 * ATTENTION: don't use outside of jdeb +042 */ +043public final class Utils { +044 private static final Pattern BETA_PATTERN = Pattern.compile("^(?:(?:(.*?)([.\\-_]))|(.*[^a-z]))(alpha|a|beta|b|milestone|m|cr|rc)([^a-z].*)?$", Pattern.CASE_INSENSITIVE); +045 +046 private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(.*)[\\-+]SNAPSHOT"); +047 +048 public static int copy( final InputStream pInput, final OutputStream pOutput ) throws IOException { +049 final byte[] buffer = new byte[2048]; +050 int count = 0; +051 int n; +052 while (-1 != (n = pInput.read(buffer))) { +053 pOutput.write(buffer, 0, n); +054 count += n; +055 } +056 return count; +057 } +058 +059 public static String toHex( final byte[] bytes ) { +060 final StringBuilder sb = new StringBuilder(); +061 +062 for (byte b : bytes) { +063 sb.append(Integer.toHexString((b >> 4) & 0x0f)); +064 sb.append(Integer.toHexString(b & 0x0f)); +065 } +066 +067 return sb.toString(); +068 } +069 +070 public static String stripPath( final int p, final String s ) { +071 +072 if (p <= 0) { +073 return s; +074 } +075 +076 int x = 0; +077 for (int i = 0; i < p; i++) { +078 x = s.indexOf('/', x + 1); +079 if (x < 0) { +080 return s; +081 } +082 } +083 +084 return s.substring(x + 1); +085 } +086 +087 private static String joinPath(char sep, String ...paths) { +088 final StringBuilder sb = new StringBuilder(); +089 for (String p : paths) { +090 if (p == null) continue; +091 if (!p.startsWith("/") && sb.length() > 0) { +092 sb.append(sep); +093 } +094 sb.append(p); +095 } +096 return sb.toString(); +097 } +098 +099 public static String joinUnixPath(String ...paths) { +100 return joinPath('/', paths); +101 } +102 +103 public static String joinLocalPath(String ...paths) { +104 return joinPath(File.separatorChar, paths); +105 } +106 +107 public static String stripLeadingSlash( final String s ) { +108 if (s == null) { +109 return s; +110 } +111 if (s.length() == 0) { +112 return s; +113 } +114 if (s.charAt(0) == '/' || s.charAt(0) == '\\') { +115 return s.substring(1); +116 } +117 return s; +118 } +119 +120 /** +121 * Substitute the variables in the given expression with the +122 * values from the resolver +123 * +124 * @param pResolver +125 * @param pExpression +126 */ +127 public static String replaceVariables( final VariableResolver pResolver, final String pExpression, final String pOpen, final String pClose ) { +128 final char[] open = pOpen.toCharArray(); +129 final char[] close = pClose.toCharArray(); +130 +131 final StringBuilder out = new StringBuilder(); +132 StringBuilder sb = new StringBuilder(); +133 char[] last = null; +134 int wo = 0; +135 int wc = 0; +136 int level = 0; +137 for (char c : pExpression.toCharArray()) { +138 if (c == open[wo]) { +139 if (wc > 0) { +140 sb.append(close, 0, wc); +141 } +142 wc = 0; +143 wo++; +144 if (open.length == wo) { +145 // found open +146 if (last == open) { +147 out.append(open); +148 } +149 level++; +150 out.append(sb); +151 sb = new StringBuilder(); +152 wo = 0; +153 last = open; +154 } +155 } else if (c == close[wc]) { +156 if (wo > 0) { +157 sb.append(open, 0, wo); +158 } +159 wo = 0; +160 wc++; +161 if (close.length == wc) { +162 // found close +163 if (last == open) { +164 final String variable = pResolver.get(sb.toString()); +165 if (variable != null) { +166 out.append(variable); +167 } else { +168 out.append(open); +169 out.append(sb); +170 out.append(close); +171 } +172 } else { +173 out.append(sb); +174 out.append(close); +175 } +176 sb = new StringBuilder(); +177 level--; +178 wc = 0; +179 last = close; +180 } +181 } else { +182 +183 if (wo > 0) { +184 sb.append(open, 0, wo); +185 } +186 +187 if (wc > 0) { +188 sb.append(close, 0, wc); +189 } +190 +191 sb.append(c); +192 +193 wo = wc = 0; +194 } +195 } +196 +197 if (wo > 0) { +198 sb.append(open, 0, wo); +199 } +200 +201 if (wc > 0) { +202 sb.append(close, 0, wc); +203 } +204 +205 if (level > 0) { +206 out.append(open); +207 } +208 out.append(sb); +209 +210 return out.toString(); +211 } +212 +213 /** +214 * Replaces new line delimiters in the input stream with the Unix line feed. +215 * +216 * @param input +217 */ +218 public static byte[] toUnixLineEndings( InputStream input ) throws IOException { +219 String encoding = "ISO-8859-1"; +220 FixCrLfFilter filter = new FixCrLfFilter(new InputStreamReader(input, encoding)); +221 filter.setEol(FixCrLfFilter.CrLf.newInstance("unix")); +222 +223 ByteArrayOutputStream filteredFile = new ByteArrayOutputStream(); +224 Utils.copy(new ReaderInputStream(filter, encoding), filteredFile); +225 +226 return filteredFile.toByteArray(); +227 } +228 +229 private static String formatSnapshotTemplate( String template, Date timestamp ) { +230 int startBracket = template.indexOf('['); +231 int endBracket = template.indexOf(']'); +232 if(startBracket == -1 || endBracket == -1) { +233 return template; +234 } else { +235 // prefix[yyMMdd]suffix +236 final String date = new SimpleDateFormat(template.substring(startBracket + 1, endBracket)).format(timestamp); +237 String datePrefix = startBracket == 0 ? "" : template.substring(0, startBracket); +238 String dateSuffix = endBracket == template.length() ? "" : template.substring(endBracket + 1); +239 return datePrefix + date + dateSuffix; +240 } +241 } +242 +243 /** +244 * Convert the project version to a version suitable for a Debian package. +245 * -SNAPSHOT suffixes are replaced with a timestamp (~yyyyMMddHHmmss). +246 * The separator before a rc, alpha or beta version is replaced with '~' +247 * such that the version is always ordered before the final or GA release. +248 * +249 * @param version the project version to convert to a Debian package version +250 * @param template the template used to replace -SNAPSHOT, the timestamp format is in brackets, +251 * the rest of the string is preserved (prefix[yyMMdd]suffix -> prefix151230suffix) +252 * @param timestamp the UTC date used as the timestamp to replace the SNAPSHOT suffix. +253 */ +254 public static String convertToDebianVersion( String version, boolean apply, String envName, String template, Date timestamp ) { +255 Matcher matcher = SNAPSHOT_PATTERN.matcher(version); +256 if (matcher.matches()) { +257 version = matcher.group(1) + "~"; +258 +259 if (apply) { +260 final String envValue = System.getenv(envName); +261 if(template != null && template.length() > 0) { +262 version += formatSnapshotTemplate(template, timestamp); +263 } else if (envValue != null && envValue.length() > 0) { +264 version += envValue; +265 } else { +266 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); +267 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); +268 version += dateFormat.format(timestamp); +269 } +270 } else { +271 version += "SNAPSHOT"; +272 } +273 } else { +274 matcher = BETA_PATTERN.matcher(version); +275 if (matcher.matches()) { +276 if (matcher.group(1) != null) { +277 version = matcher.group(1) + "~" + matcher.group(4) + matcher.group(5); +278 } else { +279 version = matcher.group(3) + "~" + matcher.group(4) + matcher.group(5); +280 } +281 } +282 } +283 +284 // safest upstream_version should only contain full stop, plus, tilde, and alphanumerics +285 // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version +286 version = version.replaceAll("[^.+~A-Za-z0-9]", "+").replaceAll("\\++", "+"); +287 +288 return version; +289 } +290 +291 /** +292 * Construct new path by replacing file directory part. No +293 * files are actually modified. +294 * @param file path to move +295 * @param target new path directory +296 */ +297 public static String movePath( final String file, +298 final String target ) { +299 final String name = new File(file).getName(); +300 return target.endsWith("/") ? target + name : target + '/' + name; +301 } +302 +303 /** +304 * Extracts value from map if given value is null. +305 * @param value current value +306 * @param props properties to extract value from +307 * @param key property name to extract +308 * @return initial value or value extracted from map +309 */ +310 public static String lookupIfEmpty( final String value, +311 final Map<String, String> props, +312 final String key ) { +313 return value != null ? value : props.get(key); +314 } +315 +316 /** +317 * Get the known locations where the secure keyring can be located. +318 * Looks through known locations of the GNU PG secure keyring. +319 * +320 * @return The location of the PGP secure keyring if it was found, +321 * null otherwise +322 */ +323 public static Collection<String> getKnownPGPSecureRingLocations() { +324 final LinkedHashSet<String> locations = new LinkedHashSet<>(); +325 +326 final String os = System.getProperty("os.name"); +327 final boolean runOnWindows = os == null || os.toLowerCase().contains("win"); +328 +329 if (runOnWindows) { +330 // The user's roaming profile on Windows, via environment +331 final String windowsRoaming = System.getenv("APPDATA"); +332 if (windowsRoaming != null) { +333 locations.add(joinLocalPath(windowsRoaming, "gnupg", "secring.gpg")); +334 } +335 +336 // The user's local profile on Windows, via environment +337 final String windowsLocal = System.getenv("LOCALAPPDATA"); +338 if (windowsLocal != null) { +339 locations.add(joinLocalPath(windowsLocal, "gnupg", "secring.gpg")); +340 } +341 +342 // The Windows installation directory +343 final String windir = System.getProperty("WINDIR"); +344 if (windir != null) { +345 // Local Profile on Windows 98 and ME +346 locations.add(joinLocalPath(windir, "Application Data", "gnupg", "secring.gpg")); +347 } +348 } +349 +350 final String home = System.getProperty("user.home"); +351 +352 if (home != null && runOnWindows) { +353 // These are for various flavours of Windows +354 // if the environment variables above have failed +355 +356 // Roaming profile on Vista and later +357 locations.add(joinLocalPath(home, "AppData", "Roaming", "gnupg", "secring.gpg")); +358 // Local profile on Vista and later +359 locations.add(joinLocalPath(home, "AppData", "Local", "gnupg", "secring.gpg")); +360 // Roaming profile on 2000 and XP +361 locations.add(joinLocalPath(home, "Application Data", "gnupg", "secring.gpg")); +362 // Local profile on 2000 and XP +363 locations.add(joinLocalPath(home, "Local Settings", "Application Data", "gnupg", "secring.gpg")); +364 } +365 +366 // *nix, including OS X +367 if (home != null) { +368 locations.add(joinLocalPath(home, ".gnupg", "secring.gpg")); +369 } +370 +371 return locations; +372 } +373 +374 /** +375 * Tries to guess location of the user secure keyring using various +376 * heuristics. +377 * +378 * @return path to the keyring file +379 * @throws FileNotFoundException if no keyring file found +380 */ +381 public static File guessKeyRingFile() throws FileNotFoundException { +382 final Collection<String> possibleLocations = getKnownPGPSecureRingLocations(); +383 for (final String location : possibleLocations) { +384 final File candidate = new File(location); +385 if (candidate.exists()) { +386 return candidate; +387 } +388 } +389 final StringBuilder message = new StringBuilder("Could not locate secure keyring, locations tried: "); +390 final Iterator<String> it = possibleLocations.iterator(); +391 while (it.hasNext()) { +392 message.append(it.next()); +393 if (it.hasNext()) { +394 message.append(", "); +395 } +396 } +397 throw new FileNotFoundException(message.toString()); +398 } +399 +400 /** +401 * Returns true if string is null or empty. +402 */ +403 public static boolean isNullOrEmpty(final String str) { +404 return str == null || str.length() == 0; +405 } +406 +407 /** +408 * Return fallback if first string is null or empty +409 */ +410 public static String defaultString(final String str, final String fallback) { +411 return isNullOrEmpty(str) ? fallback : str; +412 } +413 +414 +415 /** +416 * Check if a CharSequence is whitespace, empty ("") or null. +417 * +418 * <pre> +419 * StringUtils.isBlank(null) = true +420 * StringUtils.isBlank("") = true +421 * StringUtils.isBlank(" ") = true +422 * StringUtils.isBlank("bob") = false +423 * StringUtils.isBlank(" bob ") = false +424 * </pre> +425 * +426 * @param cs +427 * the CharSequence to check, may be null +428 * @return {@code true} if the CharSequence is null, empty or whitespace +429 */ +430 public static boolean isBlank(final CharSequence cs) { +431 int strLen; +432 if (cs == null || (strLen = cs.length()) == 0) { +433 return true; +434 } +435 for (int i = 0; i < strLen; i++) { +436 if (!Character.isWhitespace(cs.charAt(i))) { +437 return false; +438 } +439 } +440 return true; +441 } +442} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Copyright 2007-2024 The jdeb developers. +003 * +004 * Licensed under the Apache License, Version 2.0 (the "License"); +005 * you may not use this file except in compliance with the License. +006 * You may obtain a copy of the License at +007 * +008 * http://www.apache.org/licenses/LICENSE-2.0 +009 * +010 * Unless required by applicable law or agreed to in writing, software +011 * distributed under the License is distributed on an "AS IS" BASIS, +012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +013 * See the License for the specific language governing permissions and +014 * limitations under the License. +015 */ +016package org.vafer.jdeb.utils; +017 +018/** +019 * Interface for resolving variables. +020 * See Utils.replaceVariables() +021 * +022 * ATTENTION: don't use outside of jdeb +023 */ +024 +025public interface VariableResolver { +026 String get( final String pKey ); +027} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb; +18 +19 import java.io.File; +20 import java.io.IOException; +21 import java.security.MessageDigest; +22 import java.security.NoSuchAlgorithmException; +23 import java.util.Date; +24 +25 import org.apache.commons.io.FileUtils; +26 import org.apache.commons.io.output.NullOutputStream; +27 import org.vafer.jdeb.changes.ChangesProvider; +28 import org.vafer.jdeb.debian.BinaryPackageControlFile; +29 import org.vafer.jdeb.debian.ChangesFile; +30 import org.vafer.jdeb.utils.InformationOutputStream; +31 +32 /** +33 * Builds the Debian changes file. +34 */ +35 class ChangesFileBuilder { +36 +37 public ChangesFile createChanges(BinaryPackageControlFile packageControlFile, File binaryPackage, ChangesProvider changesProvider) throws IOException, PackagingException { +38 +39 ChangesFile changesFile = new ChangesFile(); +40 changesFile.setChanges(changesProvider.getChangesSets()); +41 changesFile.initialize(packageControlFile); +42 +43 changesFile.set("Date", ChangesFile.formatDate(new Date())); +44 +45 try { +46 // compute the checksums of the binary package +47 InformationOutputStream md5output = new InformationOutputStream(NullOutputStream.INSTANCE, MessageDigest.getInstance("MD5")); +48 InformationOutputStream sha1output = new InformationOutputStream(md5output, MessageDigest.getInstance("SHA1")); +49 InformationOutputStream sha256output = new InformationOutputStream(sha1output, MessageDigest.getInstance("SHA-256")); +50 +51 FileUtils.copyFile(binaryPackage, sha256output); +52 +53 // Checksums-Sha1: +54 // 56ef4c6249dc3567fd2967f809c42d1f9b61adf7 45964 jdeb.deb +55 changesFile.set("Checksums-Sha1", sha1output.getHexDigest() + " " + binaryPackage.length() + " " + binaryPackage.getName()); +56 +57 // Checksums-Sha256: +58 // 38c6fa274eb9299a69b739bcbdbd05c7ffd1d8d6472f4245ed732a25c0e5d616 45964 jdeb.deb +59 changesFile.set("Checksums-Sha256", sha256output.getHexDigest() + " " + binaryPackage.length() + " " + binaryPackage.getName()); +60 +61 StringBuilder files = new StringBuilder(md5output.getHexDigest()); +62 files.append(' ').append(binaryPackage.length()); +63 files.append(' ').append(packageControlFile.get("Section")); +64 files.append(' ').append(packageControlFile.get("Priority")); +65 files.append(' ').append(binaryPackage.getName()); +66 changesFile.set("Files", files.toString()); +67 +68 } catch (NoSuchAlgorithmException e) { +69 throw new PackagingException("Unable to compute the checksums for " + binaryPackage, e); +70 } +71 +72 if (!changesFile.isValid()) { +73 throw new PackagingException("Changes file fields are invalid " + changesFile.invalidFields() + +74 ". The following fields are mandatory: " + changesFile.getMandatoryFields() + +75 ". Please check your pom.xml/build.xml and your control file."); +76 } +77 +78 return changesFile; +79 } +80 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb; +18 +19 import java.io.OutputStream; +20 +21 import org.apache.commons.compress.compressors.CompressorException; +22 import org.apache.commons.compress.compressors.CompressorStreamFactory; +23 +24 /** +25 * Compression method used for the data file. +26 */ +27 public enum Compression { +28 +29 NONE(""), +30 GZIP(".gz"), +31 BZIP2(".bz2"), +32 XZ(".xz"); +33 +34 private String extension; +35 +36 Compression(String extension) { +37 this.extension = extension; +38 } +39 +40 /** +41 * Returns the extension of the compression method +42 */ +43 public String getExtension() { +44 return extension; +45 } +46 +47 public OutputStream toCompressedOutputStream(OutputStream out) throws CompressorException { +48 switch (this) { +49 case GZIP: +50 return new CompressorStreamFactory().createCompressorOutputStream("gz", out); +51 case BZIP2: +52 return new CompressorStreamFactory().createCompressorOutputStream("bzip2", out); +53 case XZ: +54 return new CompressorStreamFactory().createCompressorOutputStream("xz", out); +55 default: +56 return out; +57 } +58 } +59 +60 /** +61 * Returns the compression method corresponding to the specified name. +62 * The matching is case insensitive. +63 * +64 * @param name the name of the compression method +65 * @return the compression method, or null if not recognized +66 */ +67 public static Compression toEnum(String name) { +68 if ("gzip".equalsIgnoreCase(name) || "gz".equalsIgnoreCase(name)) { +69 return GZIP; +70 } else if ("bzip2".equalsIgnoreCase(name) || "bz2".equalsIgnoreCase(name)) { +71 return BZIP2; +72 } else if ("xz".equalsIgnoreCase(name)) { +73 return XZ; +74 } else if ("none".equalsIgnoreCase(name)) { +75 return NONE; +76 } else { +77 return null; +78 } +79 } +80 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb; +18 +19 /** +20 * Plug in your favorite log implementation. +21 */ +22 public interface Console { +23 +24 void debug( String message ); +25 +26 void info( String message ); +27 +28 void warn( String message ); +29 +30 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb; +18 +19 import java.io.ByteArrayInputStream; +20 import java.io.File; +21 import java.io.FileInputStream; +22 import java.io.FileNotFoundException; +23 import java.io.FileOutputStream; +24 import java.io.IOException; +25 import java.io.InputStream; +26 import java.math.BigInteger; +27 import java.text.ParseException; +28 import java.util.Arrays; +29 import java.util.Comparator; +30 import java.util.HashSet; +31 import java.util.List; +32 import java.util.Set; +33 import java.util.zip.GZIPOutputStream; +34 +35 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +36 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +37 import org.apache.commons.io.IOUtils; +38 import org.apache.commons.io.output.NullOutputStream; +39 import org.apache.tools.ant.DirectoryScanner; +40 import org.vafer.jdeb.debian.BinaryPackageControlFile; +41 import org.vafer.jdeb.mapping.PermMapper; +42 import org.vafer.jdeb.utils.FilteredFile; +43 import org.vafer.jdeb.utils.InformationInputStream; +44 import org.vafer.jdeb.utils.Utils; +45 import org.vafer.jdeb.utils.VariableResolver; +46 +47 import static java.nio.charset.StandardCharsets.*; +48 +49 /** +50 * Builds the control archive of the Debian package. +51 */ +52 class ControlBuilder { +53 +54 /** The name of the package maintainer scripts */ +55 private static final Set<String> MAINTAINER_SCRIPTS = new HashSet<>(Arrays.asList("preinst", "postinst", "prerm", "postrm", "config")); +56 +57 /** The name of the other control files subject to token substitution */ +58 private static final Set<String> CONFIGURATION_FILENAMES = new HashSet<>(Arrays.asList("conffiles", "templates", "triggers", "copyright")); +59 +60 private Console console; +61 private VariableResolver resolver; +62 private final String openReplaceToken; +63 private final String closeReplaceToken; +64 private final Long outputTimestampMs; +65 +66 ControlBuilder(Console console, VariableResolver resolver, String openReplaceToken, String closeReplaceToken, Long outputTimestampMs) { +67 this.console = console; +68 this.resolver = resolver; +69 this.openReplaceToken = openReplaceToken; +70 this.closeReplaceToken = closeReplaceToken; +71 this.outputTimestampMs = outputTimestampMs; +72 } +73 +74 /** +75 * Build control archive of the deb +76 * +77 * @param packageControlFile the package control file +78 * @param controlFiles the other control information files (maintainer scripts, etc) +79 * @param conffiles the configuration files +80 * @param checksums the md5 checksums of the files in the data archive +81 * @param output +82 * @return +83 * @throws java.io.FileNotFoundException +84 * @throws java.io.IOException +85 * @throws java.text.ParseException +86 */ +87 void buildControl(BinaryPackageControlFile packageControlFile, File[] controlFiles, List<String> conffiles, StringBuilder checksums, File output) throws IOException, ParseException { +88 +89 if (packageControlFile == null) { +90 throw new FileNotFoundException("No 'control' file found in " + controlFiles.toString()); +91 } +92 +93 final File dir = output.getParentFile(); +94 if (dir != null && (!dir.exists() || !dir.isDirectory())) { +95 throw new IOException("Cannot write control file at '" + output.getAbsolutePath() + "'"); +96 } +97 +98 final TarArchiveOutputStream outputStream = new TarArchiveOutputStream(new GZIPOutputStream(new FileOutputStream(output))); +99 outputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); +100 +101 boolean foundConffiles = false; +102 +103 // sort files to have consistent/reproducible builds +104 Arrays.sort(controlFiles, Comparator.comparing(File::toString)); +105 +106 // create the final package control file out of the "control" file, copy all other files, ignore the directories +107 for (File file : controlFiles) { +108 if (file.isDirectory()) { +109 // warn about the misplaced directory, except for directories ignored by default (.svn, cvs, etc) +110 if (!isDefaultExcludes(file)) { +111 console.warn("Found directory '" + file + "' in the control directory. Maybe you are pointing to wrong dir?"); +112 } +113 continue; +114 } +115 +116 if ("conffiles".equals(file.getName())) { +117 foundConffiles = true; +118 } +119 +120 if ("control".equals(file.getName())) { +121 continue; +122 } +123 +124 if (CONFIGURATION_FILENAMES.contains(file.getName()) || MAINTAINER_SCRIPTS.contains(file.getName())) { +125 +126 FilteredFile configurationFile = new FilteredFile(new FileInputStream(file), resolver); +127 configurationFile.setOpenToken(openReplaceToken); +128 configurationFile.setCloseToken(closeReplaceToken); +129 addControlEntry(file.getName(), configurationFile.toString(), outputStream); +130 +131 } else { +132 +133 // initialize the information stream to guess the type of the file +134 InformationInputStream infoStream = new InformationInputStream(new FileInputStream(file)); +135 Utils.copy(infoStream, NullOutputStream.INSTANCE); +136 infoStream.close(); +137 +138 // fix line endings for shell scripts +139 InputStream in = new FileInputStream(file); +140 if (infoStream.isShell() && !infoStream.hasUnixLineEndings()) { +141 byte[] buf = Utils.toUnixLineEndings(in); +142 in = new ByteArrayInputStream(buf); +143 } +144 +145 addControlEntry(file.getName(), IOUtils.toString(in, java.nio.charset.StandardCharsets.UTF_8), outputStream); +146 +147 in.close(); +148 } +149 } +150 +151 if (foundConffiles) { +152 console.info("Found file 'conffiles' in the control directory. Skipping conffiles generation."); +153 } else if ((conffiles != null) && (conffiles.size() > 0)) { +154 addControlEntry("conffiles", createPackageConffilesFile(conffiles), outputStream); +155 } else { +156 console.info("Skipping 'conffiles' generation. No entries defined in maven/pom or ant/build.xml."); +157 } +158 +159 addControlEntry("control", packageControlFile.toString(), outputStream); +160 addControlEntry("md5sums", checksums.toString(), outputStream); +161 +162 outputStream.close(); +163 } +164 +165 private String createPackageConffilesFile(final List<String> conffiles) { +166 StringBuilder content = new StringBuilder(); +167 +168 if (conffiles != null && !conffiles.isEmpty()) { +169 for (String nextFileName : conffiles) { +170 content.append(nextFileName).append("\n"); +171 } +172 } +173 +174 return content.toString(); +175 } +176 +177 +178 /** +179 * Creates a package control file from the specified file and adds the +180 * <tt>Date</tt>, <tt>Distribution</tt> and <tt>Urgency</tt> fields if missing. +181 * The <tt>Installed-Size</tt> field is also initialized to the actual size of +182 * the package. The <tt>Maintainer</tt> field is overridden by the <tt>DEBEMAIL</tt> +183 * and <tt>DEBFULLNAME</tt> environment variables if defined. +184 * +185 * @param file the control file +186 * @param pDataSize the size of the installed package +187 */ +188 public BinaryPackageControlFile createPackageControlFile(File file, BigInteger pDataSize) throws IOException, ParseException { +189 FilteredFile controlFile = new FilteredFile(new FileInputStream(file), resolver); +190 BinaryPackageControlFile packageControlFile = new BinaryPackageControlFile(controlFile.toString()); +191 +192 if (packageControlFile.get("Distribution") == null) { +193 packageControlFile.set("Distribution", "unknown"); +194 } +195 +196 if (packageControlFile.get("Urgency") == null) { +197 packageControlFile.set("Urgency", "low"); +198 } +199 +200 packageControlFile.set("Installed-Size", pDataSize.divide(BigInteger.valueOf(1024)).toString()); +201 +202 // override the Version if the DEBVERSION environment variable is defined +203 final String debVersion = System.getenv("DEBVERSION"); +204 if (debVersion != null) { +205 packageControlFile.set("Version", debVersion); +206 console.debug("Using version'" + debVersion + "' from the environment variables."); +207 } +208 +209 +210 // override the Maintainer field if the DEBFULLNAME and DEBEMAIL environment variables are defined +211 final String debFullName = System.getenv("DEBFULLNAME"); +212 final String debEmail = System.getenv("DEBEMAIL"); +213 +214 if (debFullName != null && debEmail != null) { +215 final String maintainer = debFullName + " <" + debEmail + ">"; +216 packageControlFile.set("Maintainer", maintainer); +217 console.debug("Using maintainer '" + maintainer + "' from the environment variables."); +218 } +219 +220 return packageControlFile; +221 } +222 +223 +224 private void addControlEntry(final String pName, final String pContent, final TarArchiveOutputStream pOutput) throws IOException { +225 +226 console.info("Adding control: " + pName); +227 +228 final byte[] data = pContent.getBytes(UTF_8); +229 +230 final TarArchiveEntry entry = new TarArchiveEntry("./" + pName, true); +231 entry.setSize(data.length); +232 entry.setNames("root", "root"); +233 if (outputTimestampMs != null) { +234 entry.setModTime(outputTimestampMs); +235 } +236 +237 if (MAINTAINER_SCRIPTS.contains(pName)) { +238 entry.setMode(PermMapper.toMode("755")); +239 } else { +240 entry.setMode(PermMapper.toMode("644")); +241 } +242 +243 pOutput.putArchiveEntry(entry); +244 pOutput.write(data); +245 pOutput.closeArchiveEntry(); +246 } +247 +248 /** +249 * Tells if the specified directory is ignored by default (.svn, cvs, etc) +250 * +251 * @param directory +252 */ +253 private boolean isDefaultExcludes(File directory) { +254 for (String pattern : DirectoryScanner.getDefaultExcludes()) { +255 if (DirectoryScanner.match(pattern, directory.getAbsolutePath().replace("\\", "/"))) { +256 return true; +257 } +258 } +259 +260 return false; +261 } +262 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb; +18 +19 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +20 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +21 import org.apache.commons.compress.archivers.tar.TarConstants; +22 import org.apache.commons.compress.archivers.zip.ZipEncoding; +23 import org.apache.commons.compress.archivers.zip.ZipEncodingHelper; +24 import org.apache.commons.compress.compressors.CompressorException; +25 import org.apache.commons.io.IOUtils; +26 import org.vafer.jdeb.utils.Utils; +27 +28 import java.io.File; +29 import java.io.FileOutputStream; +30 import java.io.IOException; +31 import java.io.InputStream; +32 import java.math.BigInteger; +33 import java.nio.ByteBuffer; +34 import java.security.DigestOutputStream; +35 import java.security.MessageDigest; +36 import java.security.NoSuchAlgorithmException; +37 import java.util.ArrayList; +38 import java.util.Collection; +39 import java.util.List; +40 +41 /** +42 * Builds the data archive of the Debian package. +43 */ +44 class DataBuilder { +45 +46 private Console console; +47 +48 private ZipEncoding encoding; +49 +50 private final Long outputTimestampMs; +51 +52 private static final class Total { +53 private BigInteger count = BigInteger.valueOf(0); +54 +55 public void add( long size ) { +56 count = count.add(BigInteger.valueOf(size)); +57 } +58 +59 public String toString() { +60 return "" + count; +61 } +62 } +63 +64 DataBuilder(Console console, Long outputTimestampMs) { +65 this.console = console; +66 String empty = null; +67 this.encoding = ZipEncodingHelper.getZipEncoding(empty); +68 this.outputTimestampMs = outputTimestampMs; +69 } +70 +71 private void checkField(String name, int length) throws IOException { +72 if (name != null) { +73 ByteBuffer b = encoding.encode(name); +74 if (b.limit() > length) { +75 throw new IllegalArgumentException("Field '" + name + "' too long, maximum is " + length); +76 } +77 } +78 } +79 +80 /** +81 * Build the data archive of the deb from the provided DataProducers +82 * +83 * @param producers +84 * @param output +85 * @param checksums +86 * @param options Options used to build the data file +87 * @return +88 * @throws java.security.NoSuchAlgorithmException +89 * @throws java.io.IOException +90 * @throws org.apache.commons.compress.compressors.CompressorException +91 */ +92 BigInteger buildData(Collection<DataProducer> producers, File output, final StringBuilder checksums, TarOptions options) throws NoSuchAlgorithmException, IOException, CompressorException { +93 +94 final File dir = output.getParentFile(); +95 if (dir != null && (!dir.exists() || !dir.isDirectory())) { +96 throw new IOException("Cannot write data file at '" + output.getAbsolutePath() + "'"); +97 } +98 +99 final TarArchiveOutputStream tarOutputStream = new TarArchiveOutputStream( +100 options.compression().toCompressedOutputStream(new FileOutputStream(output)) +101 ); +102 tarOutputStream.setLongFileMode(options.longFileMode()); +103 tarOutputStream.setBigNumberMode(options.bigNumberMode()); +104 +105 final MessageDigest digest = MessageDigest.getInstance("MD5"); +106 +107 final Total dataSize = new Total(); +108 +109 final List<String> addedDirectories = new ArrayList<>(); +110 final DataConsumer receiver = new DataConsumer() { +111 +112 public void onEachDir(TarArchiveEntry dirEntry) throws IOException { +113 // Check link name +114 checkField(dirEntry.getLinkName(), TarConstants.NAMELEN); +115 // Check user name +116 checkField(dirEntry.getUserName(), TarConstants.UNAMELEN); +117 // Check group name +118 checkField(dirEntry.getUserName(), TarConstants.GNAMELEN); +119 +120 dirEntry.setName(fixPathTar(dirEntry.getName())); +121 +122 createParentDirectories(dirEntry.getName(), dirEntry.getUserName(), dirEntry.getLongUserId(), dirEntry.getGroupName(), dirEntry.getLongGroupId()); +123 +124 // The directory passed in explicitly by the caller also gets the passed-in mode. (Unlike +125 // the parent directories for now. See related comments at "int mode =" in +126 // createParentDirectories, including about a possible bug.) +127 createDirectory(dirEntry.getName(), dirEntry.getUserName(), dirEntry.getLongUserId(), dirEntry.getGroupName(), dirEntry.getLongGroupId(), dirEntry.getMode(), 0); +128 +129 console.debug("dir: " + dirEntry.getName()); +130 } +131 +132 public void onEachFile(InputStream input, TarArchiveEntry fileEntry) throws IOException { +133 // Check link name +134 checkField(fileEntry.getLinkName(), TarConstants.NAMELEN); +135 // Check user name +136 checkField(fileEntry.getUserName(), TarConstants.UNAMELEN); +137 // Check group name +138 checkField(fileEntry.getGroupName(), TarConstants.GNAMELEN); +139 +140 // For md5sum +141 String rawFileEntryName = fileEntry.getName(); +142 +143 fileEntry.setName(fixPathTar(fileEntry.getName())); +144 if (outputTimestampMs != null) { +145 fileEntry.setModTime(outputTimestampMs); +146 } +147 +148 createParentDirectories(fileEntry.getName(), fileEntry.getUserName(), fileEntry.getLongUserId(), fileEntry.getGroupName(), fileEntry.getLongGroupId()); +149 +150 tarOutputStream.putArchiveEntry(fileEntry); +151 +152 dataSize.add(fileEntry.getSize()); +153 digest.reset(); +154 +155 Utils.copy(input, new DigestOutputStream(tarOutputStream, digest)); +156 +157 final String md5 = Utils.toHex(digest.digest()); +158 +159 tarOutputStream.closeArchiveEntry(); +160 +161 console.debug( +162 "file:" + fileEntry.getName() + +163 " size:" + fileEntry.getSize() + +164 " mode:" + fileEntry.getMode() + +165 " linkname:" + fileEntry.getLinkName() + +166 " username:" + fileEntry.getUserName() + +167 " userid:" + fileEntry.getLongUserId() + +168 " groupname:" + fileEntry.getGroupName() + +169 " groupid:" + fileEntry.getLongGroupId() + +170 " modtime:" + fileEntry.getModTime() + +171 " md5: " + md5 +172 ); +173 +174 // append to file md5 list, two spaces to be compatible with GNU coreutils md5sum +175 checksums.append(md5).append(" ").append(fixPathMd5(rawFileEntryName)).append('\n'); +176 } +177 +178 public void onEachLink(TarArchiveEntry entry) throws IOException { +179 // Check user name +180 checkField(entry.getUserName(), TarConstants.UNAMELEN); +181 // Check group name +182 checkField(entry.getGroupName(), TarConstants.GNAMELEN); +183 +184 entry.setName(fixPathTar(entry.getName())); +185 if (outputTimestampMs != null) { +186 entry.setModTime(outputTimestampMs); +187 } +188 +189 createParentDirectories(entry.getName(), entry.getUserName(), entry.getLongUserId(), entry.getGroupName(), entry.getLongGroupId()); +190 +191 tarOutputStream.putArchiveEntry(entry); +192 tarOutputStream.closeArchiveEntry(); +193 +194 console.debug( +195 "link:" + entry.getName() + +196 " mode:" + entry.getMode() + +197 " linkname:" + entry.getLinkName() + +198 " username:" + entry.getUserName() + +199 " userid:" + entry.getLongUserId() + +200 " groupname:" + entry.getGroupName() + +201 " groupid:" + entry.getLongGroupId() +202 ); +203 } +204 +205 +206 private void createDirectory( String directory, String user, long uid, String group, long gid, int mode, long size ) throws IOException { +207 // All dirs should end with "/" when created, or the test DebAndTaskTestCase.testTarFileSet() thinks its a file +208 // and so thinks it has the wrong permission. +209 // This consistency also helps when checking if a directory already exists in addedDirectories. +210 +211 if (!directory.endsWith("/")) { +212 directory += "/"; +213 } +214 +215 if (!addedDirectories.contains(directory)) { +216 TarArchiveEntry entry = new TarArchiveEntry(directory, true); +217 entry.setUserName(user); +218 entry.setUserId(uid); +219 entry.setGroupName(group); +220 entry.setGroupId(gid); +221 entry.setMode(mode); +222 entry.setSize(size); +223 +224 if (outputTimestampMs != null) { +225 entry.setModTime(outputTimestampMs); +226 } +227 +228 tarOutputStream.putArchiveEntry(entry); +229 tarOutputStream.closeArchiveEntry(); +230 addedDirectories.add(directory); // so addedDirectories consistently have "/" for finding duplicates. +231 } +232 } +233 +234 private void createParentDirectories( String filename, String user, long uid, String group, long gid ) throws IOException { +235 String dirname = fixPathTar(new File(filename).getParent()); +236 +237 // Debian packages must have parent directories created +238 // before sub-directories or files can be installed. +239 // For example, if an entry of ./usr/lib/foo/bar existed +240 // in a .deb package, but the ./usr/lib/foo directory didn't +241 // exist, the package installation would fail. The .deb must +242 // then have an entry for ./usr/lib/foo and then ./usr/lib/foo/bar +243 +244 if (dirname == null) { +245 return; +246 } +247 +248 // The loop below will create entries for all parent directories +249 // to ensure that .deb packages will install correctly. +250 String[] pathParts = dirname.split("/"); +251 String parentDir = "./"; +252 for (int i = 1; i < pathParts.length; i++) { +253 parentDir += pathParts[i] + "/"; +254 // Make it so the dirs can be traversed by users. +255 // We could instead try something more granular, like setting the directory +256 // permission to 'rx' for each of the 3 user/group/other read permissions +257 // found on the file being added (ie, only if "other" has read +258 // permission on the main node, then add o+rx permission on all the containing +259 // directories, same w/ user & group), and then also we'd have to +260 // check the parentDirs collection of those already added to +261 // see if those permissions need to be similarly updated. (Note, it hasn't +262 // been demonstrated, but there might be a bug if a user specifically +263 // requests a directory with certain permissions, +264 // that has already been auto-created because it was a parent, and if so, go set +265 // the user-requested mode on that directory instead of this automatic one.) +266 // But for now, keeping it simple by making every dir a+rx. Examples are: +267 // drw-r----- fs/fs # what you get with setMode(mode) +268 // drwxr-xr-x fs/fs # Usable. Too loose? +269 int mode = TarArchiveEntry.DEFAULT_DIR_MODE; +270 +271 createDirectory(parentDir, user, uid, group, gid, mode, 0); +272 } +273 } +274 }; +275 +276 boolean finishedWithoutErrors = true; +277 try { +278 for (DataProducer data : producers) { +279 data.produce(receiver); +280 } +281 } catch (Exception e) { +282 finishedWithoutErrors = false; +283 throw e; +284 } finally { +285 if (finishedWithoutErrors) { +286 tarOutputStream.close(); +287 } else { +288 IOUtils.closeQuietly(tarOutputStream); +289 } +290 } +291 +292 console.debug("Total size: " + dataSize); +293 +294 return dataSize.count; +295 } +296 +297 private String fixPathBase( String path ) { +298 if (path == null || path.equals(".")) { +299 return path; +300 } +301 +302 // If we're receiving directory names from Windows, then we'll convert to use slash +303 // This does eliminate the ability to use of a backslash in a directory name on *NIX, +304 // but in practice, this is a non-issue +305 if (path.contains("\\")) { +306 path = path.replace('\\', '/'); +307 } +308 return path; +309 } +310 +311 private String fixPathTar( String path ) { +312 if (path == null || path.equals(".")) { +313 return path; +314 } +315 +316 path = fixPathBase(path); +317 +318 // ensure the path is like : ./foo/bar +319 if (path.startsWith("/")) { +320 path = "." + path; +321 } else if (!path.startsWith("./")) { +322 path = "./" + path; +323 } +324 return path; +325 } +326 +327 private String fixPathMd5( String path ) { +328 if (path == null || path.equals(".")) { +329 return path; +330 } +331 +332 path = fixPathBase(path); +333 +334 // ensure the path is like : foo/bar +335 if (path.startsWith("/")) { +336 path = path.substring(1); +337 } else if (path.startsWith("./")) { +338 path = path.substring(2); +339 } +340 return path; +341 } +342 +343 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb; +17 +18 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +19 +20 import java.io.IOException; +21 import java.io.InputStream; +22 +23 /** +24 * A DataConsumer consumes Data produced from a producer. +25 */ +26 public interface DataConsumer { +27 +28 void onEachDir( TarArchiveEntry dirEntry ) throws IOException; +29 +30 void onEachFile( InputStream input, TarArchiveEntry fileEntry ) throws IOException; +31 +32 void onEachLink( TarArchiveEntry linkEntry ) throws IOException; +33 +34 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb; +17 +18 import java.io.IOException; +19 +20 /** +21 * Provides Data to a DataConsumer. +22 */ +23 public interface DataProducer { +24 +25 void produce( DataConsumer receiver ) throws IOException; +26 +27 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb; +18 +19 import org.apache.commons.compress.archivers.ar.ArArchiveEntry; +20 import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream; +21 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +22 import org.apache.commons.io.FileUtils; +23 import org.apache.commons.io.FilenameUtils; +24 import org.apache.commons.io.IOUtils; +25 import org.bouncycastle.crypto.digests.MD5Digest; +26 import org.bouncycastle.jce.provider.BouncyCastleProvider; +27 import org.bouncycastle.openpgp.PGPSignature; +28 import org.bouncycastle.openpgp.PGPSignatureGenerator; +29 import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +30 import org.bouncycastle.util.encoders.Hex; +31 import org.vafer.jdeb.changes.ChangeSet; +32 import org.vafer.jdeb.changes.ChangesProvider; +33 import org.vafer.jdeb.changes.TextfileChangesProvider; +34 import org.vafer.jdeb.debian.BinaryPackageControlFile; +35 import org.vafer.jdeb.debian.ChangesFile; +36 import org.vafer.jdeb.signing.PGPSigner; +37 import org.vafer.jdeb.utils.PGPSignatureOutputStream; +38 import org.vafer.jdeb.utils.Utils; +39 import org.vafer.jdeb.utils.VariableResolver; +40 +41 import java.io.ByteArrayOutputStream; +42 import java.io.File; +43 import java.io.FileInputStream; +44 import java.io.FileOutputStream; +45 import java.io.IOException; +46 import java.io.InputStream; +47 import java.math.BigInteger; +48 import java.nio.charset.StandardCharsets; +49 import java.security.MessageDigest; +50 import java.security.NoSuchAlgorithmException; +51 import java.security.Security; +52 import java.text.SimpleDateFormat; +53 import java.util.ArrayList; +54 import java.util.Collection; +55 import java.util.Date; +56 import java.util.List; +57 import java.util.Locale; +58 import java.util.concurrent.TimeUnit; +59 +60 /** +61 * A generic class for creating Debian archives. Even supports signed changes +62 * files. +63 */ +64 public class DebMaker { +65 +66 private static final int DEFAULT_MODE = 33188; +67 +68 /** A console to output log message with */ +69 private Console console; +70 +71 /** The Debian package produced */ +72 private File deb; +73 +74 /** The directory containing the control files to build the package */ +75 private File control; +76 +77 /** The name of the package. Default value if not specified in the control file */ +78 private String packageName; +79 +80 /** The section of the package. Default value if not specified in the control file */ +81 private String section = "java"; +82 +83 /** The dependencies of the package. */ +84 private String depends; +85 +86 /** The description of the package. Default value if not specified in the control file */ +87 private String description; +88 +89 /** The homepage of the application. Default value if not specified in the control file */ +90 private String homepage; +91 +92 /** The file containing the PGP keys */ +93 private File keyring; +94 +95 /** The key to use in the keyring */ +96 private String key; +97 +98 /** The passphrase for the key to sign the changes file */ +99 private String passphrase; +100 +101 /** The file to read the changes from */ +102 private File changesIn; +103 +104 /** The file where to write the changes to */ +105 private File changesOut; +106 +107 /** The file where to write the changes of the changes input to */ +108 private File changesSave; +109 +110 /** The compression method used for the data file (none, gzip, bzip2 or xz) */ +111 private String compression = "gzip"; +112 +113 /** Whether to sign the package that is created */ +114 private boolean signPackage; +115 +116 /** Whether to sign the changes file that is created */ +117 private boolean signChanges; +118 +119 /** Defines which utility is used to verify the signed package */ +120 private String signMethod; +121 +122 /** Defines the role to sign with */ +123 private String signRole; +124 +125 /** Defines the digest for the signing */ +126 private String signDigest = "SHA256"; +127 +128 /** Defines the longFileMode of the tar file that is built */ +129 private String tarLongFileMode; +130 +131 /** Defines the bigNumberMode of the tar file that is built */ +132 private String tarBigNumberMode; +133 +134 private Long outputTimestampMs; +135 +136 private VariableResolver variableResolver; +137 private String openReplaceToken; +138 private String closeReplaceToken; +139 +140 private final Collection<DataProducer> dataProducers = new ArrayList<>(); +141 +142 private final Collection<DataProducer> conffilesProducers = new ArrayList<>(); +143 private String digest = "SHA256"; +144 +145 public DebMaker(Console console, Collection<DataProducer> dataProducers, Collection<DataProducer> conffileProducers) { +146 this.console = console; +147 if (dataProducers != null) { +148 this.dataProducers.addAll(dataProducers); +149 } +150 if (conffileProducers != null) { +151 this.conffilesProducers.addAll(conffileProducers); +152 } +153 +154 Security.addProvider(new BouncyCastleProvider()); +155 } +156 +157 public void setDeb(File deb) { +158 this.deb = deb; +159 } +160 +161 public void setControl(File control) { +162 this.control = control; +163 } +164 +165 public void setPackage(String packageName) { +166 this.packageName = packageName; +167 } +168 +169 public void setSection(String section) { +170 this.section = section; +171 } +172 +173 public void setDepends(String depends) { +174 this.depends = depends; +175 } +176 +177 public void setDescription(String description) { +178 this.description = description; +179 } +180 +181 public void setHomepage(String homepage) { +182 this.homepage = homepage; +183 } +184 +185 public void setChangesIn(File changes) { +186 this.changesIn = changes; +187 } +188 +189 public void setChangesOut(File changes) { +190 this.changesOut = changes; +191 } +192 +193 public void setChangesSave(File changes) { +194 this.changesSave = changes; +195 } +196 +197 public void setSignPackage(boolean signPackage) { +198 this.signPackage = signPackage; +199 } +200 +201 public void setSignChanges(boolean signChanges) { +202 this.signChanges = signChanges; +203 } +204 +205 public void setSignMethod(String signMethod) { +206 this.signMethod = signMethod; +207 } +208 +209 public void setSignRole(String signRole) { +210 this.signRole = signRole; +211 } +212 +213 public String getSignDigest() { +214 return signDigest; +215 } +216 +217 public void setSignDigest(String digest) { +218 this.signDigest = digest; +219 } +220 +221 +222 public void setKeyring(File keyring) { +223 this.keyring = keyring; +224 } +225 +226 public void setKey(String key) { +227 this.key = key; +228 } +229 +230 public void setPassphrase(String passphrase) { +231 this.passphrase = passphrase; +232 } +233 +234 public void setCompression(String compression) { +235 this.compression = compression; +236 } +237 +238 public void setResolver(VariableResolver variableResolver) { +239 this.variableResolver = variableResolver; +240 } +241 +242 private boolean isWritableFile(File file) { +243 return !file.exists() || file.isFile() && file.canWrite(); +244 } +245 +246 public String getDigest() { +247 return digest; +248 } +249 +250 public void setDigest(String digest) { +251 this.digest = digest; +252 } +253 +254 public void setTarLongFileMode(String tarLongFileMode) { +255 this.tarLongFileMode = tarLongFileMode; +256 } +257 +258 public void setTarBigNumberMode(String tarBigNumberMode) { +259 this.tarBigNumberMode = tarBigNumberMode; +260 } +261 +262 public void setOutputTimestampMs(Long outputTimestampMs) { +263 this.outputTimestampMs = outputTimestampMs; +264 } +265 +266 /** +267 * Validates the input parameters. +268 */ +269 public void validate() throws PackagingException { +270 if (control == null || !control.isDirectory()) { +271 throw new PackagingException("The 'control' attribute doesn't point to a directory. " + control); +272 } +273 +274 if (changesIn != null) { +275 +276 if (changesIn.exists() && (!changesIn.isFile() || !changesIn.canRead())) { +277 throw new PackagingException("The 'changesIn' setting needs to point to a readable file. " + changesIn + " was not found/readable."); +278 } +279 +280 if (changesOut != null && !isWritableFile(changesOut)) { +281 throw new PackagingException("Cannot write the output for 'changesOut' to " + changesOut); +282 } +283 +284 if (changesSave != null && !isWritableFile(changesSave)) { +285 throw new PackagingException("Cannot write the output for 'changesSave' to " + changesSave); +286 } +287 +288 } else { +289 if (changesOut != null || changesSave != null) { +290 throw new PackagingException("The 'changesOut' or 'changesSave' settings may only be used when there is a 'changesIn' specified."); +291 } +292 } +293 +294 if (Compression.toEnum(compression) == null) { +295 throw new PackagingException("The compression method '" + compression + "' is not supported (expected 'none', 'gzip', 'bzip2' or 'xz')"); +296 } +297 +298 if (deb == null) { +299 throw new PackagingException("You need to specify where the deb file is supposed to be created."); +300 } +301 +302 PGPSigner.getDigestCode(digest); +303 } +304 +305 public void makeDeb() throws PackagingException { +306 BinaryPackageControlFile packageControlFile; +307 try { +308 console.info("Creating debian package: " + deb); +309 +310 // If we should sign the package +311 if (signPackage) { +312 +313 if (keyring == null || !keyring.exists()) { +314 console.warn("Signing requested, but no keyring supplied"); +315 } +316 +317 if (key == null) { +318 console.warn("Signing requested, but no key supplied"); +319 } +320 +321 if (passphrase == null) { +322 console.warn("Signing requested, but no passphrase supplied"); +323 } +324 +325 final int digestCode = PGPSigner.getDigestCode(signDigest); +326 +327 PGPSigner signer; +328 try (FileInputStream keyRingInput = new FileInputStream(keyring)) { +329 signer = new PGPSigner(keyRingInput, key, passphrase, digestCode); +330 } +331 +332 PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(signer.getSecretKey().getPublicKey().getAlgorithm(), digestCode)); +333 signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signer.getPrivateKey()); +334 +335 packageControlFile = createSignedDeb(Compression.toEnum(compression), signatureGenerator, signer); +336 } else { +337 packageControlFile = createDeb(Compression.toEnum(compression)); +338 } +339 +340 } catch (Exception e) { +341 throw new PackagingException("Failed to create debian package " + deb, e); +342 } +343 +344 makeChangesFiles(packageControlFile); +345 } +346 +347 private void makeChangesFiles(final BinaryPackageControlFile packageControlFile) throws PackagingException { +348 if (changesOut == null) { +349 changesOut = new File(deb.getParentFile(), FilenameUtils.getBaseName(deb.getName()) + ".changes"); +350 } +351 +352 ChangesProvider changesProvider; +353 FileOutputStream out = null; +354 +355 try { +356 console.info("Creating changes file: " + changesOut); +357 +358 out = new FileOutputStream(changesOut); +359 +360 if (changesIn != null && changesIn.exists()) { +361 // read the changes form a textfile provider +362 changesProvider = new TextfileChangesProvider(new FileInputStream(changesIn), packageControlFile); +363 } else { +364 // create an empty changelog +365 changesProvider = new ChangesProvider() { +366 public ChangeSet[] getChangesSets() { +367 return new ChangeSet[] { +368 new ChangeSet(packageControlFile.get("Package"), +369 packageControlFile.get("Version"), +370 new Date(), +371 packageControlFile.get("Distribution"), +372 packageControlFile.get("Urgency"), +373 packageControlFile.get("Maintainer"), +374 new String[0]) +375 }; +376 } +377 }; +378 } +379 +380 ChangesFileBuilder builder = new ChangesFileBuilder(); +381 ChangesFile changesFile = builder.createChanges(packageControlFile, deb, changesProvider); +382 +383 final int digestCode = PGPSigner.getDigestCode(signDigest); +384 +385 // (signChanges || signPackage) - for backward compatibility. signPackage is signing both changes and deb. +386 if ((signChanges || signPackage) && keyring != null && key != null && passphrase != null) { +387 console.info("Signing the changes file with the key " + key); +388 PGPSigner signer = new PGPSigner(new FileInputStream(keyring), key, passphrase, digestCode); +389 signer.clearSign(changesFile.toString(), out); +390 } else { +391 out.write(changesFile.toString().getBytes(StandardCharsets.UTF_8)); +392 } +393 out.flush(); +394 +395 } catch (Exception e) { +396 throw new PackagingException("Failed to create the Debian changes file " + changesOut, e); +397 } finally { +398 IOUtils.closeQuietly(out); +399 } +400 +401 if (changesSave == null || !(changesProvider instanceof TextfileChangesProvider)) { +402 return; +403 } +404 +405 try { +406 console.info("Saving changes to file: " + changesSave); +407 +408 ((TextfileChangesProvider) changesProvider).save(new FileOutputStream(changesSave)); +409 +410 } catch (Exception e) { +411 throw new PackagingException("Failed to save debian changes file " + changesSave, e); +412 } +413 } +414 +415 private List<String> populateConffiles(Collection<DataProducer> producers) { +416 final List<String> result = new ArrayList<>(); +417 +418 if (producers == null || producers.isEmpty()) { +419 return result; +420 } +421 +422 final DataConsumer receiver = new DataConsumer() { +423 public void onEachFile(InputStream input, TarArchiveEntry entry) { +424 String tempConffileItem = entry.getName(); +425 +426 // Make sure the conffile path is absolute +427 if (tempConffileItem.startsWith(".")) { +428 tempConffileItem = tempConffileItem.substring(1); +429 } +430 if (!tempConffileItem.startsWith("/")) { +431 tempConffileItem = "/" + tempConffileItem; +432 } +433 +434 console.info("Adding conffile: " + tempConffileItem); +435 result.add(tempConffileItem); +436 } +437 +438 public void onEachLink(TarArchiveEntry entry) { +439 } +440 +441 public void onEachDir(TarArchiveEntry tarArchiveEntry) { +442 } +443 }; +444 +445 try { +446 for (DataProducer data : producers) { +447 data.produce(receiver); +448 } +449 } catch(Exception e) { +450 // +451 } +452 +453 return result; +454 } +455 +456 /** +457 * Create the debian archive with from the provided control files and data producers. +458 * +459 * @param compression the compression method used for the data file +460 * @return BinaryPackageControlFile +461 * @throws PackagingException +462 */ +463 public BinaryPackageControlFile createDeb(Compression compression) throws PackagingException { +464 return createSignedDeb(compression, null, null); +465 } +466 /** +467 * Create the debian archive with from the provided control files and data producers. +468 * +469 * @param compression the compression method used for the data file (gzip, bzip2 or anything else for no compression) +470 * @param signatureGenerator the signature generator +471 * +472 * @return PackageDescriptor +473 * @throws PackagingException +474 */ +475 public BinaryPackageControlFile createSignedDeb(Compression compression, final PGPSignatureGenerator signatureGenerator, PGPSigner signer ) throws PackagingException { +476 File tempData = null; +477 File tempControl = null; +478 +479 try { +480 tempData = File.createTempFile("deb", "data"); +481 tempControl = File.createTempFile("deb", "control"); +482 +483 console.debug("Building data"); +484 DataBuilder dataBuilder = new DataBuilder(console, outputTimestampMs); +485 StringBuilder md5s = new StringBuilder(); +486 TarOptions options = new TarOptions() +487 .compression(compression) +488 .longFileMode(tarLongFileMode) +489 .bigNumberMode(tarBigNumberMode); +490 BigInteger size = dataBuilder.buildData(dataProducers, tempData, md5s, options); +491 +492 console.info("Building conffiles"); +493 List<String> tempConffiles = populateConffiles(conffilesProducers); +494 +495 console.debug("Building control"); +496 ControlBuilder controlBuilder = new ControlBuilder(console, variableResolver, openReplaceToken, closeReplaceToken, outputTimestampMs); +497 BinaryPackageControlFile packageControlFile = controlBuilder.createPackageControlFile(new File(control, "control"), size); +498 if (packageControlFile.get("Package") == null) { +499 packageControlFile.set("Package", packageName); +500 } +501 if (packageControlFile.get("Section") == null) { +502 packageControlFile.set("Section", section); +503 } +504 if (packageControlFile.get("Description") == null) { +505 packageControlFile.set("Description", description); +506 } +507 if (packageControlFile.get("Depends") == null) { +508 // Only add a depends entry to the control file if the field in this object has actually been set +509 if (depends != null && depends.length() > 0) { +510 packageControlFile.set("Depends", depends); +511 } +512 } +513 if (packageControlFile.get("Homepage") == null) { +514 packageControlFile.set("Homepage", homepage); +515 } +516 +517 controlBuilder.buildControl(packageControlFile, control.listFiles(), tempConffiles , md5s, tempControl); +518 +519 if (!packageControlFile.isValid()) { +520 throw new PackagingException("Control file fields are invalid " + packageControlFile.invalidFields() + +521 ". The following fields are mandatory: " + packageControlFile.getMandatoryFields() + +522 ". Please check your pom.xml/build.xml and your control file."); +523 } +524 +525 deb.getParentFile().mkdirs(); +526 +527 ArArchiveOutputStream ar = new ArArchiveOutputStream(new FileOutputStream(deb)); +528 +529 String binaryName = "debian-binary"; +530 String binaryContent = "2.0\n"; +531 String controlName = "control.tar.gz"; +532 String dataName = "data.tar" + compression.getExtension(); +533 +534 addTo(ar, binaryName, binaryContent); +535 addTo(ar, controlName, tempControl); +536 addTo(ar, dataName, tempData); +537 +538 if (signatureGenerator != null) { +539 console.info("Signing package with key " + key); +540 +541 if(signRole == null) { +542 signRole = "origin"; +543 } +544 +545 // Use debsig-verify as default +546 if (!"dpkg-sig".equals(signMethod)) { +547 // Sign file to verify with debsig-verify +548 PGPSignatureOutputStream sigStream = new PGPSignatureOutputStream(signatureGenerator); +549 +550 addTo(sigStream, binaryContent); +551 addTo(sigStream, tempControl); +552 addTo(sigStream, tempData); +553 addTo(ar, "_gpg" + signRole, sigStream.generateASCIISignature()); +554 +555 } else { +556 +557 // Sign file to verify with dpkg-sig --verify +558 final String outputStr = +559 "Version: 4\n" + +560 "Signer: \n" + +561 "Date: " + new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.ENGLISH).format(new Date()) + "\n" + +562 "Role: " + signRole +"\n" + +563 "Files: \n" + +564 addFile(binaryName, binaryContent) + +565 addFile(controlName, tempControl) + +566 addFile(dataName, tempData); +567 +568 ByteArrayOutputStream message = new ByteArrayOutputStream(); +569 signer.clearSign(outputStr, message); +570 +571 addTo(ar, "_gpg" + signRole, message.toString()); +572 } +573 } +574 +575 ar.close(); +576 +577 return packageControlFile; +578 +579 } catch (Exception e) { +580 throw new PackagingException("Could not create deb package", e); +581 } finally { +582 if (tempData != null) { +583 if (!tempData.delete()) { +584 console.warn("Could not delete the temporary file " + tempData); +585 } +586 } +587 if (tempControl != null) { +588 if (!tempControl.delete()) { +589 console.warn("Could not delete the temporary file " + tempControl); +590 } +591 } +592 } +593 } +594 +595 private String addFile(String name, String input){ +596 return addLine(md5Hash(input), sha1Hash(input), input.length(), name); +597 } +598 +599 private String addFile(String name, File input){ +600 return addLine(md5Hash(input), sha1Hash(input), input.length(), name); +601 } +602 +603 private String addLine(String md5, String sha1, long size, String name){ +604 return "\t" + md5 + " " + sha1 + " " + size + " " + name + "\n"; +605 } +606 +607 private String md5Hash(String input){ +608 return md5Hash(input.getBytes()); +609 } +610 +611 private String md5Hash(File input){ +612 try { +613 return md5Hash(FileUtils.readFileToByteArray(input)); +614 } catch (IOException e) { +615 // TODO Auto-generated catch block +616 e.printStackTrace(); +617 } +618 +619 return null; +620 } +621 +622 private String md5Hash(byte[] input){ +623 //update the input of MD5 +624 MD5Digest md5 = new MD5Digest(); +625 md5.update(input, 0, input.length); +626 +627 //get the output/ digest size and hash it +628 byte[] digest = new byte[md5.getDigestSize()]; +629 md5.doFinal(digest, 0); +630 +631 return new String(Hex.encode(digest)); +632 } +633 +634 private String sha1Hash(String input){ +635 return sha1Hash(input.getBytes()); +636 } +637 +638 private String sha1Hash(File input){ +639 try { +640 return sha1Hash(FileUtils.readFileToByteArray(input)); +641 } catch (IOException e) { +642 // TODO Auto-generated catch block +643 e.printStackTrace(); +644 } +645 +646 return null; +647 } +648 +649 private String sha1Hash(byte[] input){ +650 try +651 { +652 //prepare the input +653 MessageDigest hash = MessageDigest.getInstance(digest); +654 hash.update(input); +655 +656 //proceed .... +657 byte[] digest = hash.digest(); +658 +659 return new String(Hex.encode(digest)); +660 } +661 catch (NoSuchAlgorithmException e) +662 { +663 System.err.println("No such algorithm"); +664 e.printStackTrace(); +665 } +666 +667 return null; +668 } +669 +670 private void addTo(ArArchiveOutputStream pOutput, String pName, String pContent) throws IOException { +671 final byte[] content = pContent.getBytes(); +672 ArArchiveEntry archiveEntry = createArArchiveEntry(pName, content.length); +673 +674 pOutput.putArchiveEntry(archiveEntry); +675 pOutput.write(content); +676 pOutput.closeArchiveEntry(); +677 } +678 +679 private void addTo(ArArchiveOutputStream pOutput, String pName, File pContent) throws IOException { +680 ArArchiveEntry archiveEntry = createArArchiveEntry(pName, pContent.length()); +681 +682 pOutput.putArchiveEntry(archiveEntry); +683 try (InputStream input = new FileInputStream(pContent)) { +684 Utils.copy(input, pOutput); +685 } +686 +687 pOutput.closeArchiveEntry(); +688 } +689 +690 private void addTo(final PGPSignatureOutputStream pOutput, final String pContent) throws IOException { +691 final byte[] content = pContent.getBytes(); +692 pOutput.write(content); +693 } +694 +695 private void addTo(final PGPSignatureOutputStream pOutput, final File pContent) throws IOException { +696 try (InputStream input = new FileInputStream(pContent)) { +697 Utils.copy(input, pOutput); +698 } +699 } +700 +701 public void setOpenReplaceToken(String openReplaceToken) { +702 this.openReplaceToken = openReplaceToken; +703 } +704 +705 public void setCloseReplaceToken(String closeReplaceToken) { +706 this.closeReplaceToken = closeReplaceToken; +707 } +708 +709 private ArArchiveEntry createArArchiveEntry(String pName, long contentLength) { +710 if (outputTimestampMs != null) { +711 return new ArArchiveEntry(pName, contentLength, 0, 0, DEFAULT_MODE, outputTimestampMs / TimeUnit.SECONDS.toMillis(1)); +712 } +713 +714 return new ArArchiveEntry(pName, contentLength); +715 } +716 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb; +17 +18 /** +19 * Something went wrong while building the package +20 */ +21 public final class PackagingException extends Exception { +22 +23 private static final long serialVersionUID = 1L; +24 +25 public PackagingException() { +26 super(); +27 } +28 +29 public PackagingException( String message, Throwable cause ) { +30 super(message, cause); +31 } +32 +33 public PackagingException( String message ) { +34 super(message); +35 } +36 +37 public PackagingException( Throwable cause ) { +38 super(cause); +39 } +40 +41 } ++
+1 package org.vafer.jdeb; +2 +3 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +4 +5 public class TarOptions { +6 +7 private Compression compression = Compression.GZIP; +8 private int longFileMode = TarArchiveOutputStream.LONGFILE_GNU; +9 private int bigNumberMode = TarArchiveOutputStream.BIGNUMBER_STAR; +10 +11 public TarOptions compression(Compression compression) { +12 this.compression = compression; +13 +14 return this; +15 } +16 +17 public TarOptions longFileMode(String input) { +18 if ("posix".equals(input)) { +19 longFileMode = TarArchiveOutputStream.LONGFILE_POSIX; +20 } else if ("error".equals(input)) { +21 longFileMode = TarArchiveOutputStream.LONGFILE_ERROR; +22 } else if ("truncate".equals(input)) { +23 longFileMode = TarArchiveOutputStream.LONGFILE_TRUNCATE; +24 } else { +25 longFileMode = TarArchiveOutputStream.LONGFILE_GNU; +26 } +27 +28 return this; +29 } +30 +31 public TarOptions bigNumberMode(String input) { +32 if ("error".equals(input)) { +33 bigNumberMode = TarArchiveOutputStream.BIGNUMBER_ERROR; +34 } else if ("posix".equals(input)) { +35 bigNumberMode = TarArchiveOutputStream.BIGNUMBER_POSIX; +36 } else { +37 bigNumberMode = TarArchiveOutputStream.BIGNUMBER_STAR; +38 } +39 +40 return this; +41 } +42 +43 public int longFileMode() { +44 return longFileMode; +45 } +46 +47 public int bigNumberMode() { +48 return bigNumberMode; +49 } +50 +51 public Compression compression() { +52 return compression; +53 } +54 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.ant; +17 +18 import java.io.File; +19 import java.io.FileNotFoundException; +20 import java.io.IOException; +21 import java.util.ArrayList; +22 import java.util.Collection; +23 import java.util.Iterator; +24 +25 import org.apache.tools.ant.types.PatternSet; +26 import org.vafer.jdeb.DataConsumer; +27 import org.vafer.jdeb.DataProducer; +28 import org.vafer.jdeb.producers.DataProducerArchive; +29 import org.vafer.jdeb.producers.DataProducerDirectory; +30 import org.vafer.jdeb.producers.DataProducerFile; +31 +32 import static org.vafer.jdeb.ant.MissingSourceBehavior.*; +33 +34 /** +35 * Ant "data" element acting as a factory for DataProducers. +36 * So far Archive and Directory producers are supported. +37 * Both support the usual ant pattern set matching. +38 */ +39 public final class Data extends PatternSet implements DataProducer { +40 +41 private final Collection<Mapper> mapperWrapper = new ArrayList<>(); +42 +43 private File src; +44 +45 private String type; +46 +47 private Boolean conffile; +48 +49 private String destinationName; +50 +51 private MissingSourceBehavior missingSrc = FAIL; +52 +53 public void setSrc(File src) { +54 this.src = src; +55 } +56 +57 public String getType() { +58 return type; +59 } +60 +61 public void setType(String type) { +62 this.type = type; +63 } +64 +65 public void setConffile(Boolean conffile) { +66 this.conffile = conffile; +67 } +68 +69 public Boolean getConffile() { +70 return this.conffile; +71 } +72 +73 public void setDst(String destinationName) { +74 this.destinationName = destinationName; +75 } +76 +77 public void addMapper(Mapper mapper) { +78 mapperWrapper.add(mapper); +79 } +80 +81 +82 public void setMissingSrc( String missingSrc ) { +83 this.missingSrc = MissingSourceBehavior.valueOf(missingSrc.trim().toUpperCase()); +84 } +85 +86 public void produce( final DataConsumer pReceiver ) throws IOException { +87 +88 if (src == null || !src.exists()) { +89 if (missingSrc == IGNORE) { +90 return; +91 } else { +92 throw new FileNotFoundException("Data source not found : " + src); +93 } +94 } +95 +96 org.vafer.jdeb.mapping.Mapper[] mappers = new org.vafer.jdeb.mapping.Mapper[mapperWrapper.size()]; +97 final Iterator<Mapper> it = mapperWrapper.iterator(); +98 for (int i = 0; i < mappers.length; i++) { +99 mappers[i] = it.next().createMapper(); +100 } +101 +102 if ("file".equalsIgnoreCase(type)) { +103 new DataProducerFile( +104 src, +105 destinationName, +106 getIncludePatterns(getProject()), +107 getExcludePatterns(getProject()), +108 mappers +109 ).produce(pReceiver); +110 +111 } else if ("archive".equalsIgnoreCase(type)) { +112 new DataProducerArchive( +113 src, +114 getIncludePatterns(getProject()), +115 getExcludePatterns(getProject()), +116 mappers +117 ).produce(pReceiver); +118 +119 } else if ("directory".equalsIgnoreCase(type)) { +120 new DataProducerDirectory( +121 src, +122 getIncludePatterns(getProject()), +123 getExcludePatterns(getProject()), +124 mappers +125 ).produce(pReceiver); +126 } +127 } +128 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.ant; +18 +19 import java.io.File; +20 import java.util.ArrayList; +21 import java.util.Arrays; +22 import java.util.Collection; +23 +24 import org.apache.tools.ant.BuildException; +25 import org.apache.tools.ant.Project; +26 import org.apache.tools.ant.taskdefs.MatchingTask; +27 import org.apache.tools.ant.taskdefs.Tar; +28 import org.apache.tools.ant.types.FileSet; +29 import org.vafer.jdeb.Console; +30 import org.vafer.jdeb.DataProducer; +31 import org.vafer.jdeb.DebMaker; +32 import org.vafer.jdeb.PackagingException; +33 import org.vafer.jdeb.producers.DataProducerFileSet; +34 import org.vafer.jdeb.utils.OutputTimestampResolver; +35 +36 /** +37 * AntTask for creating debian archives. +38 */ +39 public class DebAntTask extends MatchingTask { +40 +41 /** The Debian package produced */ +42 private File deb; +43 +44 /** The directory containing the control files to build the package */ +45 private File control; +46 +47 /** The file containing the PGP keys */ +48 private File keyring; +49 +50 /** The key to use in the keyring */ +51 private String key; +52 +53 /** The passphrase for the key to sign the changes file */ +54 private String passphrase; +55 +56 /** The file to read the changes from */ +57 private File changesIn; +58 +59 /** The file where to write the changes to */ +60 private File changesOut; +61 +62 /** The file where to write the changes of the changes input to */ +63 private File changesSave; +64 +65 /** The compression method used for the data file (none, gzip, bzip2 or xz) */ +66 private String compression = "gzip"; +67 +68 /** +69 * The digest algorithm to use. +70 * +71 * @see org.bouncycastle.bcpg.HashAlgorithmTags +72 */ +73 private String digest = "SHA256"; +74 +75 /** Trigger the verbose mode detailing all operations */ +76 private boolean verbose; +77 +78 private Collection<Link> links = new ArrayList<>(); +79 +80 private Collection<DataProducer> dataProducers = new ArrayList<>(); +81 private Collection<DataProducer> conffilesProducers = new ArrayList<>(); +82 +83 +84 public void setDestfile( File deb ) { +85 this.deb = deb; +86 } +87 +88 public void setControl( File control ) { +89 this.control = control; +90 } +91 +92 public void setChangesIn( File changes ) { +93 this.changesIn = changes; +94 } +95 +96 public void setChangesOut( File changes ) { +97 this.changesOut = changes; +98 } +99 +100 public void setChangesSave( File changes ) { +101 this.changesSave = changes; +102 } +103 +104 public void setKeyring( File keyring ) { +105 this.keyring = keyring; +106 } +107 +108 public void setKey( String key ) { +109 this.key = key; +110 } +111 +112 public void setPassphrase( String passphrase ) { +113 this.passphrase = passphrase; +114 } +115 +116 public void setCompression( String compression ) { +117 this.compression = compression; +118 } +119 +120 public void setVerbose( boolean verbose ) { +121 this.verbose = verbose; +122 } +123 +124 public void addFileSet( FileSet fileset ) { +125 dataProducers.add(new DataProducerFileSet(fileset)); +126 } +127 +128 public void addTarFileSet( Tar.TarFileSet fileset ) { +129 dataProducers.add(new DataProducerFileSet(fileset)); +130 } +131 +132 public void addData( Data data ) { +133 dataProducers.add(data); +134 } +135 +136 public void addLink( Link link ) { +137 links.add(link); +138 } +139 +140 public void setDigest(String digest) { +141 this.digest = digest; +142 } +143 +144 public void execute() { +145 // add the data producers for the links +146 for (Link link : links) { +147 dataProducers.add(link.toDataProducer()); +148 } +149 +150 // validate the type of the <data> elements +151 for (DataProducer dataProducer : dataProducers) { +152 if (dataProducer instanceof Data) { +153 Data data = (Data) dataProducer; +154 if (data.getType() == null) { +155 throw new BuildException("The type of the data element wasn't specified (expected 'file', 'directory' or 'archive')"); +156 } else if (!Arrays.asList("file", "directory", "archive").contains(data.getType().toLowerCase())) { +157 throw new BuildException("The type '" + data.getType() + "' of the data element is unknown (expected 'file', 'directory' or 'archive')"); +158 } +159 if (data.getConffile() != null && data.getConffile()) { +160 conffilesProducers.add(dataProducer); +161 } +162 } +163 } +164 +165 Console console = new TaskConsole(this, verbose); +166 +167 DebMaker debMaker = new DebMaker(console, dataProducers, conffilesProducers); +168 debMaker.setDeb(deb); +169 debMaker.setControl(control); +170 debMaker.setChangesIn(changesIn); +171 debMaker.setChangesOut(changesOut); +172 debMaker.setChangesSave(changesSave); +173 debMaker.setKeyring(keyring); +174 debMaker.setKey(key); +175 debMaker.setPassphrase(passphrase); +176 debMaker.setCompression(compression); +177 debMaker.setDigest(digest); +178 Long outputTimestampMs = new OutputTimestampResolver(console).resolveOutputTimestamp(null); +179 debMaker.setOutputTimestampMs(outputTimestampMs); +180 +181 try { +182 debMaker.validate(); +183 debMaker.makeDeb(); +184 +185 } catch (PackagingException e) { +186 log("Failed to create the Debian package " + deb, e, Project.MSG_ERR); +187 throw new BuildException("Failed to create the Debian package " + deb, e); +188 } +189 } +190 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.ant; +18 +19 import org.apache.commons.compress.archivers.zip.UnixStat; +20 import org.vafer.jdeb.DataProducer; +21 import org.vafer.jdeb.mapping.PermMapper; +22 import org.vafer.jdeb.producers.DataProducerLink; +23 +24 /** +25 * Defines a symbolic or hard link. +26 */ +27 public final class Link { +28 +29 private String name; +30 private String target; +31 private boolean symbolic = true; +32 private String username = "root"; +33 private String group = "root"; +34 private int uid = 0; +35 private int gid = 0; +36 private int mode = UnixStat.LINK_FLAG | UnixStat.DEFAULT_LINK_PERM; +37 +38 DataProducer toDataProducer() { +39 org.vafer.jdeb.mapping.Mapper mapper = new PermMapper(uid, gid, username, group, mode, mode, 0, null); +40 return new DataProducerLink(name, target, symbolic, null, null, new org.vafer.jdeb.mapping.Mapper[]{mapper}); +41 } +42 +43 public String getName() { +44 return name; +45 } +46 +47 public void setName(String name) { +48 this.name = name; +49 } +50 +51 public String getTarget() { +52 return target; +53 } +54 +55 public void setTarget(String target) { +56 this.target = target; +57 } +58 +59 public boolean isSymbolic() { +60 return symbolic; +61 } +62 +63 public void setSymbolic(boolean symbolic) { +64 this.symbolic = symbolic; +65 } +66 +67 public String getUsername() { +68 return username; +69 } +70 +71 public void setUsername(String username) { +72 this.username = username; +73 } +74 +75 public String getGroup() { +76 return group; +77 } +78 +79 public void setGroup(String group) { +80 this.group = group; +81 } +82 +83 public int getUid() { +84 return uid; +85 } +86 +87 public void setUid(int uid) { +88 this.uid = uid; +89 } +90 +91 public int getGid() { +92 return gid; +93 } +94 +95 public void setGid(int gid) { +96 this.gid = gid; +97 } +98 +99 public int getMode() { +100 return mode; +101 } +102 +103 public void setMode(String mode) { +104 this.mode = UnixStat.LINK_FLAG | Integer.parseInt(mode, 8); +105 } +106 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.ant; +17 +18 import java.io.File; +19 import java.io.FileInputStream; +20 import java.io.IOException; +21 +22 import org.vafer.jdeb.mapping.LsMapper; +23 import org.vafer.jdeb.mapping.PermMapper; +24 +25 /** +26 * Ant "mapper" element acting as factory for the entry mapper. +27 * Supported types: ls, perm +28 */ +29 public final class Mapper { +30 +31 private String mapperType = "perm"; +32 private File src; +33 +34 private String prefix; +35 private int strip; +36 private int uid = -1; +37 private int gid = -1; +38 private String user; +39 private String group; +40 private String fileMode; +41 private String dirMode; +42 +43 public void setType( final String pType ) { +44 mapperType = pType; +45 } +46 +47 public void setSrc( final File pSrc ) { +48 src = pSrc; +49 } +50 +51 +52 public void setPrefix( final String pPrefix ) { +53 prefix = pPrefix; +54 } +55 +56 public void setStrip( final int pStrip ) { +57 strip = pStrip; +58 } +59 +60 +61 public void setUid( final int pUid ) { +62 uid = pUid; +63 } +64 +65 public void setGid( final int pGid ) { +66 gid = pGid; +67 } +68 +69 public void setUser( final String pUser ) { +70 user = pUser; +71 } +72 +73 public void setGroup( final String pGroup ) { +74 group = pGroup; +75 } +76 +77 public void setFileMode( final String pFileMode ) { +78 fileMode = pFileMode; +79 } +80 +81 public void setDirMode( final String pDirMode ) { +82 dirMode = pDirMode; +83 } +84 +85 public org.vafer.jdeb.mapping.Mapper createMapper() throws IOException { +86 +87 if ("perm".equalsIgnoreCase(mapperType)) { +88 return new PermMapper(uid, gid, user, group, fileMode, dirMode, strip, prefix); +89 } +90 +91 if ("ls".equalsIgnoreCase(mapperType)) { +92 try { +93 return new LsMapper(new FileInputStream(src)); +94 } catch (Exception e) { +95 e.printStackTrace(); +96 } +97 } +98 +99 throw new IOException("Unknown mapper type '" + mapperType + "'"); +100 } +101 +102 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.ant; +18 +19 public enum MissingSourceBehavior { +20 IGNORE, FAIL +21 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.ant; +18 +19 import org.apache.tools.ant.Project; +20 import org.apache.tools.ant.Task; +21 import org.vafer.jdeb.Console; +22 +23 /** +24 * Console implementation for Ant tasks. debug messages are only displayed +25 * when the <tt>verbose</tt> parameter is true. +26 */ +27 class TaskConsole implements Console { +28 +29 private final Task task; +30 private final boolean verbose; +31 +32 public TaskConsole(Task task, boolean verbose) { +33 this.task = task; +34 this.verbose = verbose; +35 } +36 +37 public void debug(String message) { +38 if (verbose) { +39 task.log(message); +40 } +41 } +42 +43 public void info(String message) { +44 task.log(message); +45 } +46 +47 public void warn(String message) { +48 task.log(message, Project.MSG_WARN); +49 } +50 +51 } ++
Class | +
---|
+ Data + | +
+ DebAntTask + | +
+ Link + | +
+ Mapper + | +
+ MissingSourceBehavior + | +
+ TaskConsole + | +
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.changes; +18 +19 import java.util.Date; +20 +21 /** +22 * A ChangeSet basically reflect a release as defined in the changes file. +23 * +24 * <pre> +25 * package (version) distribution(s); urgency=urgency +26 * [optional blank line(s), stripped] +27 * * change details +28 * more change details +29 * [blank line(s), included in output of dpkg-parsechangelog] +30 * * even more change details +31 * [optional blank line(s), stripped] +32 * -- maintainer name <email address>[two spaces] date +33 * </pre> +34 * +35 * @see <a href="http://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog">Debian Policy Manual - Debian changelog</a> +36 */ +37 public final class ChangeSet { +38 +39 private final String packageName; +40 private final String version; +41 private final Date date; +42 private final String distribution; +43 private final String urgency; +44 private final String changedBy; +45 private final String[] changes; +46 +47 public ChangeSet(String packageName, String version, Date date, String distribution, String urgency, String changedBy, String[] changes) { +48 this.packageName = packageName; +49 this.version = version; +50 this.date = date; +51 this.distribution = distribution; +52 this.urgency = urgency; +53 this.changedBy = changedBy; +54 this.changes = changes; +55 } +56 +57 public String getPackage() { +58 return packageName; +59 } +60 +61 public String getVersion() { +62 return version; +63 } +64 +65 public Date getDate() { +66 return date; +67 } +68 +69 public String getDistribution() { +70 return distribution; +71 } +72 +73 public String getUrgency() { +74 return urgency; +75 } +76 +77 public String getChangedBy() { +78 return changedBy; +79 } +80 +81 public String[] getChanges() { +82 return changes; +83 } +84 +85 public String toString() { +86 StringBuilder sb = new StringBuilder(); +87 +88 sb.append(getTitle()).append('\n'); +89 +90 if (changes.length > 0) { +91 sb.append("\n"); +92 } +93 +94 for (String change : changes) { +95 sb.append(" * ").append(change).append('\n'); +96 } +97 +98 return sb.toString(); +99 } +100 +101 private String getTitle() { +102 return getPackage() + " (" + getVersion() + ") " + getDistribution() + "; urgency=" + getUrgency(); +103 } +104 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.changes; +17 +18 public interface ChangesProvider { +19 +20 ChangeSet[] getChangesSets(); +21 +22 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.changes; +18 +19 import java.io.BufferedReader; +20 import java.io.BufferedWriter; +21 import java.io.IOException; +22 import java.io.InputStream; +23 import java.io.InputStreamReader; +24 import java.io.OutputStream; +25 import java.io.OutputStreamWriter; +26 import java.text.DateFormat; +27 import java.text.ParseException; +28 import java.text.SimpleDateFormat; +29 import java.util.ArrayList; +30 import java.util.Collection; +31 import java.util.Date; +32 +33 import org.vafer.jdeb.debian.BinaryPackageControlFile; +34 +35 /** +36 * Gets the changes from a changes file. The first entry are the current changes. +37 * The release line will be added. Example: +38 * +39 * release date=22:13 19.08.2007,version=1.5+r90114,urgency=low,by=Torsten Curdt <torsten@vafer.org> +40 * * debian changes support +41 * release date=20:13 17.08.2007,version=1.4+r89114,urgency=low,by=Torsten Curdt <torsten@vafer.org> +42 * * debian changes support +43 * +44 */ +45 public final class TextfileChangesProvider implements ChangesProvider { +46 +47 private final ChangeSet[] changeSets; +48 +49 private DateFormat fmt = new SimpleDateFormat("HH:mm dd.MM.yyyy"); +50 +51 public TextfileChangesProvider( final InputStream pInput, final BinaryPackageControlFile packageControlFile ) throws IOException, ParseException { +52 +53 final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput)); +54 +55 String packageName = packageControlFile.get("Package"); +56 String version = packageControlFile.get("Version"); +57 Date date = new Date(); +58 String distribution = packageControlFile.get("Distribution"); +59 String urgency = packageControlFile.get("Urgency"); +60 String changedBy = packageControlFile.get("Maintainer"); +61 Collection<String> changesColl = new ArrayList<>(); +62 Collection<ChangeSet> changeSetColl = new ArrayList<>(); +63 +64 +65 while (true) { +66 final String line = reader.readLine(); +67 if (line == null) { +68 final String[] changes = changesColl.toArray(new String[changesColl.size()]); +69 final ChangeSet changeSet = new ChangeSet(packageName, version, date, distribution, urgency, changedBy, changes); +70 changeSetColl.add(changeSet); +71 break; +72 } +73 +74 if (line.startsWith("release ")) { +75 +76 if (changesColl.size() > 0) { +77 final String[] changes = changesColl.toArray(new String[changesColl.size()]); +78 final ChangeSet changeSet = new ChangeSet(packageName, version, date, distribution, urgency, changedBy, changes); +79 changeSetColl.add(changeSet); +80 changesColl.clear(); +81 } +82 +83 final String[] tokens = line.substring("release ".length()).split(","); +84 for (String token : tokens) { +85 final String[] lr = token.trim().split("="); +86 final String key = lr[0]; +87 final String value = lr[1]; +88 +89 if ("urgency".equals(key)) { +90 urgency = value; +91 } else if ("by".equals(key)) { +92 changedBy = value; +93 } else if ("date".equals(key)) { +94 date = fmt.parse(value); +95 } else if ("version".equals(key)) { +96 version = value; +97 } else if ("distribution".equals(key)) { +98 distribution = value; +99 } +100 } +101 continue; +102 } +103 +104 if (line.startsWith(" * ")) { +105 changesColl.add(line.substring(" * ".length())); +106 continue; +107 } +108 +109 throw new ParseException("Unknown line syntax [" + line + "]", 0); +110 } +111 +112 reader.close(); +113 +114 changeSets = changeSetColl.toArray(new ChangeSet[changeSetColl.size()]); +115 } +116 +117 public void save(OutputStream pOutput) throws IOException { +118 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(pOutput)); +119 +120 for (ChangeSet changeSet : changeSets) { +121 writer.write("release "); +122 writer.write("date=" + fmt.format(changeSet.getDate()) + ","); +123 writer.write("version=" + changeSet.getVersion() + ","); +124 writer.write("urgency=" + changeSet.getUrgency() + ","); +125 writer.write("by=" + changeSet.getChangedBy() + ","); +126 writer.write("distribution=" + changeSet.getDistribution()); +127 writer.write("\n"); +128 +129 for (String change : changeSet.getChanges()) { +130 writer.write(" * "); +131 writer.write(change); +132 writer.write("\n"); +133 } +134 } +135 +136 writer.close(); +137 } +138 +139 public ChangeSet[] getChangesSets() { +140 return changeSets; +141 } +142 } ++
Class | +
---|
+ ChangeSet + | +
+ ChangesProvider + | +
+ TextfileChangesProvider + | +
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.debian; +18 +19 import java.io.IOException; +20 import java.io.InputStream; +21 import java.text.ParseException; +22 +23 /** +24 * Binary package control file. +25 * +26 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-binarycontrolfiles">Debian Policy Manual - Binary package control files</a> +27 */ +28 public final class BinaryPackageControlFile extends ControlFile { +29 +30 private static final ControlField[] FIELDS = { +31 new ControlField("Package", true), +32 new ControlField("Source"), +33 new ControlField("Version", true), +34 new ControlField("Section", true), +35 new ControlField("Priority", true), +36 new ControlField("Architecture", true), +37 new ControlField("Essential"), +38 new ControlField("Depends"), +39 new ControlField("Pre-Depends"), +40 new ControlField("Recommends"), +41 new ControlField("Suggests"), +42 new ControlField("Breaks"), +43 new ControlField("Enhances"), +44 new ControlField("Conflicts"), +45 new ControlField("Provides"), +46 new ControlField("Replaces"), +47 new ControlField("Installed-Size"), +48 new ControlField("Maintainer", true), +49 new ControlField("Description", true, ControlField.Type.MULTILINE), +50 new ControlField("Homepage"), +51 new ControlField("Multi-Arch") +52 }; +53 +54 public BinaryPackageControlFile() { +55 set("Architecture", "all"); +56 set("Priority", "optional"); +57 } +58 +59 public BinaryPackageControlFile(String input) throws IOException, ParseException { +60 parse(input); +61 } +62 +63 public BinaryPackageControlFile(InputStream input) throws IOException, ParseException { +64 parse(input); +65 } +66 +67 public void set(final String field, final String value) { +68 super.set(field, value); +69 } +70 +71 protected ControlField[] getFields() { +72 return FIELDS; +73 } +74 +75 /** +76 * Returns the short description of the package. The short description +77 * consists in the first line of the Description field. +78 * +79 * @return +80 */ +81 public String getShortDescription() { +82 if (get("Description") == null) { +83 return null; +84 } +85 +86 return get("Description").split("\n")[0]; +87 } +88 +89 protected char getUserDefinedFieldLetter() { +90 return 'B'; +91 } +92 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.debian; +18 +19 import org.vafer.jdeb.utils.Utils; +20 import org.vafer.jdeb.changes.ChangeSet; +21 +22 import java.text.DateFormat; +23 import java.text.SimpleDateFormat; +24 import java.util.Date; +25 import java.util.Locale; +26 import java.util.Map.Entry; +27 +28 /** +29 * Reflecting a changes file +30 * +31 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-debianchangesfiles">Debian Policy Manual - Debian changes files</a> +32 */ +33 public final class ChangesFile extends ControlFile { +34 +35 private static final ControlField[] FIELDS = { +36 new ControlField("Format", true), +37 new ControlField("Date", true), +38 new ControlField("Source", true), +39 new ControlField("Binary", true), +40 new ControlField("Architecture", true), +41 new ControlField("Version", true), +42 new ControlField("Distribution", true), +43 new ControlField("Urgency", true), +44 new ControlField("Maintainer", true), +45 new ControlField("Changed-By"), +46 new ControlField("Description", true, ControlField.Type.MULTILINE, true), +47 new ControlField("Changes", true, ControlField.Type.MULTILINE, true), +48 new ControlField("Closes"), +49 new ControlField("Checksums-Sha1", true, ControlField.Type.MULTILINE, true), +50 new ControlField("Checksums-Sha256", true, ControlField.Type.MULTILINE, true), +51 new ControlField("Files", true, ControlField.Type.MULTILINE, true) +52 }; +53 +54 public ChangesFile() { +55 set("Format", "1.8"); +56 set("Urgency", "low"); +57 set("Distribution", "stable"); +58 } +59 +60 /** +61 * Initializes the fields on the changes file with the values of the specified +62 * binary package control file. +63 * +64 * @param packageControlFile +65 */ +66 public void initialize(BinaryPackageControlFile packageControlFile) { +67 set("Binary", packageControlFile.get("Package")); +68 set("Source", Utils.defaultString(packageControlFile.get("Source"), packageControlFile.get("Package"))); +69 set("Architecture", packageControlFile.get("Architecture")); +70 set("Version", packageControlFile.get("Version")); +71 set("Maintainer", packageControlFile.get("Maintainer")); +72 set("Distribution", packageControlFile.get("Distribution")); +73 +74 for (Entry<String, String> entry : packageControlFile.getUserDefinedFields().entrySet()) { +75 set(entry.getKey(), entry.getValue()); +76 } +77 +78 StringBuilder description = new StringBuilder(); +79 description.append(packageControlFile.get("Package")); +80 if (packageControlFile.get("Description") != null) { +81 description.append(" - "); +82 description.append(packageControlFile.getShortDescription()); +83 } +84 set("Description", description.toString()); +85 } +86 +87 public void setChanges(ChangeSet[] changeSets) { +88 StringBuilder sb = new StringBuilder(); +89 +90 if (changeSets.length > 0) { +91 final ChangeSet mostRecentChangeSet = changeSets[0]; +92 set("Urgency", mostRecentChangeSet.getUrgency()); +93 set("Changed-By", mostRecentChangeSet.getChangedBy()); +94 +95 for (ChangeSet changeSet : changeSets) { +96 sb.append(changeSet.toString()); +97 } +98 } +99 +100 set("Changes", sb.toString()); +101 } +102 +103 protected ControlField[] getFields() { +104 return FIELDS; +105 } +106 +107 protected char getUserDefinedFieldLetter() { +108 return 'C'; +109 } +110 +111 public static String formatDate(Date date) { +112 final DateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH); // RFC 2822 format +113 return format.format(date); +114 } +115 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.debian; +18 +19 import java.io.BufferedReader; +20 import java.io.IOException; +21 import java.io.StringReader; +22 +23 /** +24 * A field of a control file. This class is immutable. +25 */ +26 public class ControlField { +27 +28 /** +29 * The format of a field. +30 */ +31 public enum Type { +32 /** Value on a single line */ +33 SIMPLE, +34 /** Value on multiple lines, space characters are ignored */ +35 FOLDED, +36 /** Value on multiple lines, space characters are preserved */ +37 MULTILINE +38 } +39 +40 /** The name of the field */ +41 private final String name; +42 +43 /** Tells if the field is mandatory */ +44 private final boolean mandatory; +45 +46 /** The type of the field */ +47 private final Type type; +48 +49 /** Tells is the first line of the field must be empty (for MULTILINE values only) */ +50 private final boolean firstLineEmpty; +51 +52 +53 public ControlField(String name) { +54 this(name, false); +55 } +56 +57 public ControlField(String name, boolean mandatory) { +58 this(name, mandatory, Type.SIMPLE); +59 } +60 +61 public ControlField(String name, boolean mandatory, Type type) { +62 this(name, mandatory, type, false); +63 } +64 +65 public ControlField(String name, boolean mandatory, Type type, boolean firstLineEmpty) { +66 this.name = name; +67 this.mandatory = mandatory; +68 this.type = type; +69 this.firstLineEmpty = firstLineEmpty; +70 } +71 +72 public String getName() { +73 return name; +74 } +75 +76 public boolean isMandatory() { +77 return mandatory; +78 } +79 +80 public Type getType() { +81 return type; +82 } +83 +84 public boolean isFirstLineEmpty() { +85 return firstLineEmpty; +86 } +87 +88 /** +89 * Returns the field with the specified value properly formatted. Multiline +90 * values are automatically indented, and dots are added on the empty lines. +91 * +92 * <pre> +93 * Field-Name: value +94 * </pre> +95 */ +96 public String format(String value) { +97 StringBuilder s = new StringBuilder(); +98 +99 if (value != null && value.trim().length() > 0) { +100 boolean continuationLine = false; +101 +102 s.append(getName()).append(":"); +103 if (isFirstLineEmpty()) { +104 s.append("\n"); +105 continuationLine = true; +106 } +107 +108 try { +109 BufferedReader reader = new BufferedReader(new StringReader(value)); +110 String line; +111 while ((line = reader.readLine()) != null) { +112 if (continuationLine && line.trim().length() == 0) { +113 // put a dot on the empty continuation lines +114 s.append(" .\n"); +115 } else { +116 s.append(" ").append(line).append("\n"); +117 } +118 +119 continuationLine = true; +120 } +121 } catch (IOException e) { +122 e.printStackTrace(); +123 } +124 } +125 +126 return s.toString(); +127 } +128 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.debian; +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.text.ParseException; +25 import java.util.ArrayList; +26 import java.util.Arrays; +27 import java.util.HashSet; +28 import java.util.LinkedHashMap; +29 import java.util.List; +30 import java.util.Map; +31 import java.util.Set; +32 +33 import static java.nio.charset.StandardCharsets.*; +34 +35 /** +36 * A control file as specified by the <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html">Debian policy</a>. +37 */ +38 public abstract class ControlFile { +39 +40 protected final Map<String, String> values = new LinkedHashMap<>(); +41 protected final Map<String, String> userDefinedFields = new LinkedHashMap<>(); +42 protected final Set<ControlField> userDefinedFieldNames = new HashSet<>(); +43 +44 public void parse(String input) throws IOException, ParseException { +45 parse(new ByteArrayInputStream(input.getBytes(UTF_8))); +46 } +47 +48 public void parse(InputStream input) throws IOException, ParseException { +49 BufferedReader reader = new BufferedReader(new InputStreamReader(input, UTF_8)); +50 StringBuilder buffer = new StringBuilder(); +51 String field = null; +52 int linenr = 0; +53 while (true) { +54 final String line = reader.readLine(); +55 +56 if (line == null) { +57 // flush value of the previous field +58 set(field, buffer.toString()); +59 break; +60 } +61 +62 linenr++; +63 +64 if (line.length() == 0) { +65 throw new ParseException("Empty line", linenr); +66 } +67 +68 final char first = line.charAt(0); +69 if (first == '#') { +70 // ignore commented out lines +71 continue; +72 } +73 +74 if (Character.isLetter(first)) { +75 +76 // new field +77 +78 // flush value of the previous field +79 set(field, buffer.toString()); +80 buffer = new StringBuilder(); +81 +82 +83 final int i = line.indexOf(':'); +84 +85 if (i < 0) { +86 throw new ParseException("Line misses ':' delimiter", linenr); +87 } +88 +89 field = line.substring(0, i); +90 buffer.append(line.substring(i + 1).trim()); +91 +92 continue; +93 } +94 +95 // continuing old value, lines with only a dot are ignored +96 buffer.append('\n'); +97 if (!".".equals(line.substring(1).trim())) { +98 buffer.append(line.substring(1)); +99 } +100 } +101 reader.close(); +102 +103 } +104 +105 public void set(String field, final String value) { +106 if (field != null && isUserDefinedField(field)) { +107 userDefinedFields.put(field, value); +108 String fieldName = getUserDefinedFieldName(field); +109 +110 if (fieldName != null) { +111 userDefinedFieldNames.add(new ControlField(fieldName)); +112 } +113 +114 field = fieldName; +115 } +116 +117 if (field != null && !"".equals(field)) { +118 values.put(field, value); +119 } +120 } +121 +122 public String get(String field) { +123 return values.get(field); +124 } +125 +126 protected abstract ControlField[] getFields(); +127 +128 protected Map<String, String> getUserDefinedFields() { +129 return userDefinedFields; +130 } +131 +132 protected Set<ControlField> getUserDefinedFieldNames() { +133 return userDefinedFieldNames; +134 } +135 +136 public List<String> getMandatoryFields() { +137 List<String> fields = new ArrayList<>(); +138 +139 for (ControlField field : getFields()) { +140 if (field.isMandatory()) { +141 fields.add(field.getName()); +142 } +143 } +144 +145 return fields; +146 } +147 +148 public boolean isValid() { +149 return invalidFields().size() == 0; +150 } +151 +152 public Set<String> invalidFields() { +153 Set<String> invalid = new HashSet<>(); +154 +155 for (ControlField field : getFields()) { +156 if (field.isMandatory() && get(field.getName()) == null) { +157 invalid.add(field.getName()); +158 } +159 } +160 +161 return invalid; +162 } +163 +164 public String toString(ControlField... fields) { +165 StringBuilder s = new StringBuilder(); +166 for (ControlField field : fields) { +167 String value = values.get(field.getName()); +168 s.append(field.format(value)); +169 } +170 return s.toString(); +171 } +172 +173 public String toString() { +174 List<ControlField> fields = new ArrayList<>(); +175 fields.addAll(Arrays.asList(getFields())); +176 fields.addAll(getUserDefinedFieldNames()); +177 return toString(fields.toArray(new ControlField[fields.size()])); +178 } +179 +180 /** +181 * Returns the letter expected in the prefix of a user defined field +182 * in order to include the field in this control file. +183 * +184 * @return The letter returned is: +185 * <ul> +186 * <li>B: for a binary package</li> +187 * <li>S: for a source package</li> +188 * <li>C: for a changes file</li> +189 * </ul> +190 * +191 * @since 1.1 +192 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.7">Debian Policy - User-defined fields</a> +193 */ +194 protected abstract char getUserDefinedFieldLetter(); +195 +196 /** +197 * Tells if the specified field name is a user defined field. +198 * User-defined fields must begin with an 'X', followed by one or more +199 * letters that specify the output file and a hyphen. +200 * +201 * @param field the name of the field +202 * +203 * @since 1.1 +204 * @see <a href="http://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.7">Debian Policy - User-defined fields</a> +205 */ +206 protected boolean isUserDefinedField(String field) { +207 return field.startsWith("X") && field.indexOf("-") > 0; +208 } +209 +210 /** +211 * Returns the user defined field without its prefix. +212 * +213 * @param field the name of the user defined field +214 * @return the user defined field without the prefix, or null if the fields +215 * doesn't apply to this control file. +216 * @since 1.1 +217 */ +218 protected String getUserDefinedFieldName(String field) { +219 int index = field.indexOf('-'); +220 char letter = getUserDefinedFieldLetter(); +221 +222 for (int i = 0; i < index; ++i) { +223 if (field.charAt(i) == letter) { +224 return field.substring(index + 1); +225 } +226 } +227 +228 return null; +229 } +230 } ++
Class | +
---|
+ BinaryPackageControlFile + | +
+ ChangesFile + | +
+ ControlField + | +
+ ControlField.Type + | +
+ ControlFile + | +
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.mapping; +17 +18 import java.io.BufferedReader; +19 import java.io.IOException; +20 import java.io.InputStream; +21 import java.io.InputStreamReader; +22 import java.util.HashMap; +23 import java.util.Map; +24 import java.util.regex.Matcher; +25 import java.util.regex.Pattern; +26 +27 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +28 +29 /** +30 * Reads permissions and ownerships from a "ls -laR > mapping.txt" dump and +31 * maps entries accordingly. +32 */ +33 public final class LsMapper implements Mapper { +34 +35 private final Map<String, TarArchiveEntry> mapping; +36 +37 +38 public final static class ParseError extends Exception { +39 +40 private static final long serialVersionUID = 1L; +41 +42 public ParseError( String message ) { +43 super(message); +44 } +45 } +46 +47 +48 public LsMapper( final InputStream pInput ) throws IOException, ParseError { +49 mapping = parse(pInput); +50 } +51 +52 /* +53 ./trunk/target/test-classes/org/vafer/dependency: +54 total 176 +55 drwxr-xr-x 23 tcurdt tcurdt 782 Jun 25 03:48 . +56 drwxr-xr-x 3 tcurdt tcurdt 102 Jun 25 03:48 .. +57 -rw-r--r-- 1 tcurdt tcurdt 2934 Jun 25 03:48 DependenciesTestCase.class +58 -rw-r--r-- 1 tcurdt tcurdt 786 Jun 25 03:48 JarCombiningTestCase$1.class +59 -rw-r--r-- 1 tcurdt tcurdt 2176 Jun 25 03:48 WarTestCase.class +60 drwxr-xr-x 4 tcurdt tcurdt 136 Jun 25 03:48 classes +61 +62 ./trunk/target/test-classes/org/vafer/dependency/classes: +63 */ +64 +65 final private Pattern basePattern = Pattern.compile("^\\./(.*):$"); +66 final private Pattern totalPattern = Pattern.compile("^total ([0-9]+)$"); +67 final private Pattern dirPattern = Pattern.compile("^d([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+[.]{1,2}$"); +68 final private Pattern filePattern = Pattern.compile("^([d-])([rwx-]{9})\\s+([0-9]+)\\s+(\\S*)\\s+(\\S*)\\s+([0-9]+)\\s+(.*)\\s+(.*)$"); +69 final private Pattern newlinePattern = Pattern.compile("$"); +70 +71 private String readBase( final BufferedReader reader ) throws IOException, ParseError { +72 final String line = reader.readLine(); +73 if (line == null) { +74 return null; +75 } +76 final Matcher matcher = basePattern.matcher(line); +77 if (!matcher.matches()) { +78 throw new ParseError("expected base line but got \"" + line + "\""); +79 } +80 return matcher.group(1); +81 } +82 +83 private String readTotal( final BufferedReader reader ) throws IOException, ParseError { +84 final String line = reader.readLine(); +85 final Matcher matcher = totalPattern.matcher(line); +86 if (!matcher.matches()) { +87 throw new ParseError("expected total line but got \"" + line + "\""); +88 } +89 return matcher.group(1); +90 } +91 +92 private TarArchiveEntry readDir( final BufferedReader reader, final String base ) throws IOException, ParseError { +93 final String current = reader.readLine(); +94 final Matcher currentMatcher = dirPattern.matcher(current); +95 if (!currentMatcher.matches()) { +96 throw new ParseError("expected dirline but got \"" + current + "\""); +97 } +98 +99 final String parent = reader.readLine(); +100 final Matcher parentMatcher = dirPattern.matcher(parent); +101 if (!parentMatcher.matches()) { +102 throw new ParseError("expected dirline but got \"" + parent + "\""); +103 } +104 +105 final TarArchiveEntry entry = new TarArchiveEntry(base, true); +106 +107 entry.setMode(convertModeFromString(currentMatcher.group(1))); +108 entry.setUserName(currentMatcher.group(3)); +109 entry.setGroupName(currentMatcher.group(4)); +110 +111 return entry; +112 } +113 +114 +115 private int convertModeFromString( final String mode ) { +116 +117 final char[] m = mode.toCharArray(); +118 /* +119 -rwxrwxrwx +120 +121 4000 set-user-ID-on-execution bit +122 2000 set-user-ID-on-execution bit +123 1000 sticky bit +124 0400 allow read by owner. +125 0200 allow write by owner. +126 0100 execute / search +127 0040 allow read by group members. +128 0020 allow write by group members. +129 0010 execute / search +130 0004 allow read by others. +131 0002 allow write by others. +132 0001 execute / search +133 */ +134 // TODO: simplified - needs fixing +135 int sum = 0; +136 int bit = 1; +137 for (int i = m.length - 1; i >= 0; i--) { +138 if (m[i] != '-') { +139 sum += bit; +140 } +141 bit += bit; +142 } +143 return sum; +144 } +145 +146 private TarArchiveEntry readFile( final BufferedReader reader, final String base ) throws IOException, ParseError { +147 +148 while (true) { +149 final String line = reader.readLine(); +150 +151 if (line == null) { +152 return null; +153 } +154 +155 final Matcher currentMatcher = filePattern.matcher(line); +156 if (!currentMatcher.matches()) { +157 final Matcher newlineMatcher = newlinePattern.matcher(line); +158 if (newlineMatcher.matches()) { +159 return null; +160 } +161 throw new ParseError("expected file line but got \"" + line + "\""); +162 } +163 +164 final String type = currentMatcher.group(1); +165 if (type.startsWith("-")) { +166 final TarArchiveEntry entry = new TarArchiveEntry(base + "/" + currentMatcher.group(8), true); +167 +168 entry.setMode(convertModeFromString(currentMatcher.group(2))); +169 entry.setUserName(currentMatcher.group(4)); +170 entry.setGroupName(currentMatcher.group(5)); +171 +172 return entry; +173 } +174 } +175 +176 } +177 +178 private Map<String, TarArchiveEntry> parse( final InputStream pInput ) throws IOException, ParseError { +179 final Map<String, TarArchiveEntry> mapping = new HashMap<>(); +180 +181 final BufferedReader reader = new BufferedReader(new InputStreamReader(pInput)); +182 +183 boolean first = true; +184 while (true) { +185 +186 final String base; +187 if (first) { +188 base = ""; +189 first = false; +190 } else { +191 base = readBase(reader); +192 if (base == null) { +193 break; +194 } +195 } +196 +197 readTotal(reader); +198 final TarArchiveEntry dir = readDir(reader, base); +199 mapping.put(dir.getName(), dir); +200 +201 while (true) { +202 final TarArchiveEntry file = readFile(reader, base); +203 +204 if (file == null) { +205 break; +206 } +207 +208 mapping.put(file.getName(), file); +209 } +210 } +211 +212 return mapping; +213 } +214 +215 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +216 final TarArchiveEntry entry = mapping.get(pEntry.getName()); +217 if (entry != null) { +218 return entry; +219 } +220 return pEntry; +221 } +222 +223 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.mapping; +17 +18 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +19 +20 +21 /** +22 * Maps one entry to another. So you modify ownerships permissions etc in a Mapper. +23 */ +24 public interface Mapper { +25 +26 TarArchiveEntry map( final TarArchiveEntry entry ); +27 +28 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.mapping; +17 +18 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +19 +20 public final class NullMapper implements Mapper { +21 +22 public static final Mapper INSTANCE = new NullMapper(); +23 +24 private NullMapper() { +25 } +26 +27 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +28 return pEntry; +29 } +30 +31 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.mapping; +17 +18 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +19 import org.vafer.jdeb.utils.Utils; +20 +21 /** +22 * Applies a uniform set of permissions and ownership to all entries. +23 */ +24 public final class PermMapper implements Mapper { +25 +26 private final int strip; +27 private final String prefix; +28 private int uid = -1; +29 private int gid = -1; +30 private String user; +31 private String group; +32 private int fileMode = -1; +33 private int dirMode = -1; +34 +35 public static int toMode( String modeString ) { +36 int mode = -1; +37 if (modeString != null && modeString.length() > 0) { +38 mode = Integer.parseInt(modeString, 8); +39 } +40 return mode; +41 } +42 +43 public PermMapper( int uid, int gid, String user, String group, int fileMode, int dirMode, int strip, String prefix ) { +44 this.strip = strip; +45 this.prefix = (prefix == null) ? "" : prefix; +46 this.uid = uid; +47 this.gid = gid; +48 this.user = user; +49 this.group = group; +50 this.fileMode = fileMode; +51 this.dirMode = dirMode; +52 } +53 +54 public PermMapper( int uid, int gid, String user, String group, String fileMode, String dirMode, int strip, String prefix ) { +55 this(uid, gid, user, group, toMode(fileMode), toMode(dirMode), strip, prefix); +56 } +57 +58 public TarArchiveEntry map( final TarArchiveEntry entry ) { +59 entry.setName(Utils.stripLeadingSlash(Utils.joinUnixPath( +60 prefix, +61 Utils.stripPath(strip, entry.getName()) +62 ))); +63 +64 // Set ownership +65 if (uid > -1) { +66 entry.setUserId(uid); +67 } +68 if (gid > -1) { +69 entry.setGroupId(gid); +70 } +71 if (user != null) { +72 entry.setUserName(user); +73 } +74 if (group != null) { +75 entry.setGroupName(group); +76 } +77 +78 // Set permissions +79 if (entry.isDirectory()) { +80 if (dirMode > -1) { +81 entry.setMode(dirMode); +82 } +83 } else { +84 if (fileMode > -1) { +85 entry.setMode(fileMode); +86 } +87 } +88 +89 return entry; +90 } +91 } ++
Class | +
---|
+ LsMapper + | +
+ LsMapper.ParseError + | +
+ Mapper + | +
+ NullMapper + | +
+ PermMapper + | +
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.maven; +18 +19 import java.io.File; +20 import java.io.FileNotFoundException; +21 import java.io.IOException; +22 import java.util.ArrayList; +23 import java.util.List; +24 import java.util.StringTokenizer; +25 +26 import org.apache.maven.plugins.annotations.Parameter; +27 import org.vafer.jdeb.DataConsumer; +28 import org.vafer.jdeb.DataProducer; +29 import org.vafer.jdeb.producers.DataProducerArchive; +30 import org.vafer.jdeb.producers.DataProducerDirectory; +31 import org.vafer.jdeb.producers.DataProducerFile; +32 import org.vafer.jdeb.producers.DataProducerFiles; +33 import org.vafer.jdeb.producers.DataProducerLink; +34 import org.vafer.jdeb.producers.DataProducerPathTemplate; +35 +36 import static org.vafer.jdeb.maven.MissingSourceBehavior.*; +37 +38 /** +39 * Maven "data" element acting as a factory for DataProducers. So far Archive and +40 * Directory producers are supported. Both support the usual ant pattern set +41 * matching. +42 */ +43 public final class Data implements DataProducer { +44 +45 @Parameter +46 private File src; +47 +48 public void setSrc( File src ) { +49 this.src = src; +50 } +51 +52 @Parameter +53 private String dst; +54 +55 public void setDst( String dst ) { +56 this.dst = dst; +57 } +58 +59 @Parameter +60 private String type; +61 +62 public void setType( String type ) { +63 this.type = type; +64 } +65 +66 @Parameter +67 private MissingSourceBehavior missingSrc = FAIL; +68 +69 public void setMissingSrc( String missingSrc ) { +70 this.missingSrc = MissingSourceBehavior.valueOf(missingSrc.trim().toUpperCase()); +71 } +72 +73 @Parameter +74 private String linkName; +75 +76 public void setLinkName(String linkName) { +77 this.linkName = linkName; +78 } +79 +80 @Parameter +81 private String linkTarget; +82 +83 public void setLinkTarget(String linkTarget) { +84 this.linkTarget = linkTarget; +85 } +86 +87 @Parameter +88 private boolean symlink = true; +89 +90 public void setSymlink(boolean symlink) { +91 this.symlink = symlink; +92 } +93 +94 private boolean conffile = false; +95 +96 /** +97 * @parameter expression="${conffile}" +98 */ +99 public void setConffile(boolean conffile) { +100 this.conffile = conffile; +101 } +102 +103 public boolean getConffile() { +104 return this.conffile; +105 } +106 +107 @Parameter(alias = "includes") +108 private String[] includePatterns; +109 +110 public void setIncludes( String includes ) { +111 includePatterns = splitPatterns(includes); +112 } +113 +114 @Parameter(alias = "excludes") +115 private String[] excludePatterns; +116 +117 public void setExcludes( String excludes ) { +118 excludePatterns = splitPatterns(excludes); +119 } +120 +121 @Parameter +122 private Mapper mapper; +123 +124 @Parameter +125 private String[] paths; +126 +127 /* For testing only */ +128 void setPaths( String[] paths ) { +129 this.paths = paths; +130 } +131 +132 public String[] splitPatterns( String patterns ) { +133 String[] result = null; +134 if (patterns != null && patterns.length() > 0) { +135 List<String> tokens = new ArrayList<>(); +136 StringTokenizer tok = new StringTokenizer(patterns, ", ", false); +137 while (tok.hasMoreTokens()) { +138 tokens.add(tok.nextToken()); +139 } +140 result = tokens.toArray(new String[tokens.size()]); +141 } +142 return result; +143 } +144 +145 public void produce( final DataConsumer pReceiver ) throws IOException { +146 org.vafer.jdeb.mapping.Mapper[] mappers = null; +147 if (mapper != null) { +148 mappers = new org.vafer.jdeb.mapping.Mapper[] { mapper.createMapper() }; +149 } +150 +151 // link type +152 +153 if (typeIs("link")) { +154 if (linkName == null) { +155 throw new RuntimeException("linkName is not set"); +156 } +157 if (linkTarget == null) { +158 throw new RuntimeException("linkTarget is not set"); +159 } +160 +161 new DataProducerLink(linkName, linkTarget, symlink, includePatterns, excludePatterns, mappers).produce(pReceiver); +162 return; +163 } +164 +165 // template type +166 +167 if (typeIs("template")) { +168 checkPaths(); +169 new DataProducerPathTemplate(paths, includePatterns, excludePatterns, mappers).produce(pReceiver); +170 return; +171 } +172 +173 if (typeIs("files")) { +174 checkPaths(); +175 new DataProducerFiles(paths, dst, mappers).produce(pReceiver); +176 return; +177 } +178 +179 // Types that require src to exist +180 +181 if (src == null || !src.exists()) { +182 if (missingSrc == IGNORE) { +183 return; +184 } else { +185 throw new FileNotFoundException("Data source not found : " + src); +186 } +187 } +188 +189 if (typeIs("file")) { +190 new DataProducerFile(src, dst, includePatterns, excludePatterns, mappers).produce(pReceiver); +191 return; +192 } +193 +194 if (typeIs("archive")) { +195 new DataProducerArchive(src, includePatterns, excludePatterns, mappers).produce(pReceiver); +196 return; +197 } +198 +199 if (typeIs("directory")) { +200 new DataProducerDirectory(src, includePatterns, excludePatterns, mappers).produce(pReceiver); +201 return; +202 } +203 +204 throw new IOException("Unknown type '" + type + "' (file|directory|archive|template|link) for " + src); +205 } +206 +207 private boolean typeIs( final String type ) { +208 return type.equalsIgnoreCase(this.type); +209 } +210 +211 private void checkPaths() { +212 if (paths == null || paths.length == 0) { +213 throw new RuntimeException("paths parameter is not set"); +214 } +215 } +216 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.maven; +18 +19 import java.io.File; +20 import java.io.FileInputStream; +21 import java.io.FileNotFoundException; +22 import java.util.ArrayList; +23 import java.util.Collection; +24 import java.util.Collections; +25 import java.util.HashMap; +26 import java.util.HashSet; +27 import java.util.List; +28 import java.util.Map; +29 import java.util.Set; +30 +31 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +32 import org.apache.commons.compress.archivers.tar.TarConstants; +33 import org.apache.maven.artifact.Artifact; +34 import org.apache.maven.execution.MavenSession; +35 import org.apache.maven.plugin.AbstractMojo; +36 import org.apache.maven.plugin.MojoExecutionException; +37 import org.apache.maven.plugins.annotations.Component; +38 import org.apache.maven.plugins.annotations.LifecyclePhase; +39 import org.apache.maven.plugins.annotations.Mojo; +40 import org.apache.maven.plugins.annotations.Parameter; +41 import org.apache.maven.project.MavenProject; +42 import org.apache.maven.project.MavenProjectHelper; +43 import org.apache.maven.settings.Profile; +44 import org.apache.maven.settings.Settings; +45 import org.apache.tools.tar.TarEntry; +46 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; +47 import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException; +48 import org.vafer.jdeb.Console; +49 import org.vafer.jdeb.DataConsumer; +50 import org.vafer.jdeb.DataProducer; +51 import org.vafer.jdeb.DebMaker; +52 import org.vafer.jdeb.PackagingException; +53 import org.vafer.jdeb.utils.MapVariableResolver; +54 import org.vafer.jdeb.utils.OutputTimestampResolver; +55 import org.vafer.jdeb.utils.SymlinkUtils; +56 import org.vafer.jdeb.utils.Utils; +57 import org.vafer.jdeb.utils.VariableResolver; +58 +59 import static org.vafer.jdeb.utils.Utils.isBlank; +60 import static org.vafer.jdeb.utils.Utils.lookupIfEmpty; +61 +62 /** +63 * Creates Debian package +64 */ +65 @Mojo(name = "jdeb", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true) +66 public class DebMojo extends AbstractMojo { +67 +68 @Component +69 private MavenProjectHelper projectHelper; +70 +71 @Component(hint = "jdeb-sec") +72 private SecDispatcher secDispatcher; +73 +74 /** +75 * Defines the name of deb package. +76 */ +77 @Parameter +78 private String name; +79 +80 /** +81 * Defines the pattern of the name of final artifacts. Possible +82 * substitutions are [[baseDir]] [[buildDir]] [[artifactId]] [[version]] +83 * [[extension]] and [[groupId]]. +84 */ +85 @Parameter(defaultValue = "[[buildDir]]/[[artifactId]]_[[version]]_all.[[extension]]") +86 private String deb; +87 +88 /** +89 * Explicitly defines the path to the control directory. At least the +90 * control file is mandatory. +91 */ +92 @Parameter(defaultValue = "[[baseDir]]/src/deb/control") +93 private String controlDir; +94 +95 /** +96 * Explicitly define the file to read the changes from. +97 */ +98 @Parameter(defaultValue = "[[baseDir]]/CHANGES.txt") +99 private String changesIn; +100 +101 /** +102 * Explicitly define the file where to write the changes to. +103 */ +104 @Parameter(defaultValue = "[[buildDir]]/[[artifactId]]_[[version]]_all.changes") +105 private String changesOut; +106 +107 /** +108 * Explicitly define the file where to write the changes of the changes input to. +109 */ +110 @Parameter(defaultValue = "[[baseDir]]/CHANGES.txt") +111 private String changesSave; +112 +113 /** +114 * The compression method used for the data file (none, gzip, bzip2 or xz) +115 */ +116 @Parameter(defaultValue = "gzip") +117 private String compression; +118 +119 /** +120 * Boolean option whether to attach the artifact to the project +121 */ +122 @Parameter(defaultValue = "true") +123 private String attach; +124 +125 /** +126 * The location where all package files will be installed. By default, all +127 * packages are installed in /opt (see the FHS here: +128 * http://www.pathname.com/ +129 * fhs/pub/fhs-2.3.html#OPTADDONAPPLICATIONSOFTWAREPACKAGES) +130 */ +131 @Parameter(defaultValue = "/opt/[[artifactId]]") +132 private String installDir; +133 +134 /** +135 * The type of attached artifact +136 */ +137 @Parameter(defaultValue = "deb") +138 private String type; +139 +140 /** +141 * The project base directory +142 */ +143 @Parameter(defaultValue = "${basedir}", required = true, readonly = true) +144 private File baseDir; +145 +146 /** +147 * The Maven Session Object +148 */ +149 @Parameter( defaultValue = "${session}", readonly = true ) +150 private MavenSession session; +151 +152 /** +153 * The Maven Project Object +154 */ +155 @Parameter( defaultValue = "${project}", readonly = true ) +156 private MavenProject project; +157 +158 /** +159 * The build directory +160 */ +161 @Parameter(property = "project.build.directory", required = true, readonly = true) +162 private File buildDirectory; +163 +164 /** +165 * The classifier of attached artifact +166 */ +167 @Parameter +168 private String classifier; +169 +170 /** +171 * The digest algorithm to use. +172 * +173 * @see org.bouncycastle.bcpg.HashAlgorithmTags +174 */ +175 @Parameter(defaultValue = "SHA256") +176 private String digest; +177 +178 /** +179 * "data" entries used to determine which files should be added to this deb. +180 * The "data" entries may specify a tarball (tar.gz, tar.bz2, tgz), a +181 * directory, or a normal file. An entry would look something like this in +182 * your pom.xml: +183 * +184 * +185 * <pre> +186 * <build> +187 * <plugins> +188 * <plugin> +189 * <artifactId>jdeb</artifactId> +190 * <groupId>org.vafer</groupId> +191 * ... +192 * <configuration> +193 * ... +194 * <dataSet> +195 * <data> +196 * <src>${project.basedir}/target/my_archive.tar.gz</src> +197 * <include>...</include> +198 * <exclude>...</exclude> +199 * <mapper> +200 * <type>perm</type> +201 * <strip>1</strip> +202 * <prefix>/somewhere/else</prefix> +203 * <user>santbj</user> +204 * <group>santbj</group> +205 * <mode>600</mode> +206 * </mapper> +207 * </data> +208 * <data> +209 * <src>${project.build.directory}/data</src> +210 * <include></include> +211 * <exclude>**/.svn</exclude> +212 * <mapper> +213 * <type>ls</type> +214 * <src>mapping.txt</src> +215 * </mapper> +216 * </data> +217 * <data> +218 * <type>link</type> +219 * <linkName>/a/path/on/the/target/fs</linkName> +220 * <linkTarget>/a/sym/link/to/the/scr/file</linkTarget> +221 * <symlink>true</symlink> +222 * </data> +223 * <data> +224 * <src>${project.basedir}/README.txt</src> +225 * </data> +226 * </dataSet> +227 * </configuration> +228 * </plugins> +229 * </build> +230 * </pre> +231 * +232 */ +233 @Parameter +234 private Data[] dataSet; +235 +236 /** +237 * When enabled SNAPSHOT inside the version gets replaced with current timestamp or +238 * if set a value of a environment variable. +239 */ +240 @Parameter(defaultValue = "false") +241 private boolean snapshotExpand; +242 +243 /** +244 * Which environment variable to check for the SNAPSHOT value. +245 * If the variable is not set/empty it will default to use the timestamp. +246 */ +247 @Parameter(defaultValue = "SNAPSHOT") +248 private String snapshotEnv; +249 +250 /** +251 * Template for replacing the SNAPSHOT value. A timestamp format can be provided in brackets. +252 * prefix[yyMMdd]suffix -> prefix151230suffix +253 */ +254 @Parameter +255 private String snapshotTemplate; +256 +257 /** +258 * If verbose is true more build messages are logged. +259 */ +260 @Parameter(defaultValue = "false") +261 private boolean verbose; +262 +263 /** +264 * Indicates if the execution should be disabled. If <code>true</code>, nothing will occur during execution. +265 * +266 * @since 1.1 +267 */ +268 @Parameter(property = "jdeb.skip", defaultValue = "false") +269 private boolean skip; +270 +271 @Parameter(property = "jdeb.skipPOMs", defaultValue = "true") +272 private boolean skipPOMs; +273 +274 @Parameter(property = "jdeb.skipSubmodules", defaultValue = "false") +275 private boolean skipSubmodules; +276 +277 @Deprecated +278 @Parameter(defaultValue = "true") +279 private boolean submodules; +280 +281 +282 /** +283 * If signPackage is true then a origin signature will be placed +284 * in the generated package. +285 */ +286 @Parameter(defaultValue = "false") +287 private boolean signPackage; +288 +289 /** +290 * If signChanges is true then changes file will be signed. +291 */ +292 @Parameter(defaultValue = "false") +293 private boolean signChanges; +294 +295 /** +296 * Defines which utility is used to verify the signed package +297 */ +298 @Parameter(defaultValue = "debsig-verify") +299 private String signMethod; +300 +301 /** +302 * Defines the role to sign with +303 */ +304 @Parameter(defaultValue = "origin") +305 private String signRole; +306 +307 /** +308 * The keyring to use for signing operations. +309 */ +310 @Parameter +311 private String keyring; +312 +313 /** +314 * The key to use for signing operations. +315 */ +316 @Parameter +317 private String key; +318 +319 /** +320 * The passphrase to use for signing operations. +321 */ +322 @Parameter +323 private String passphrase; +324 +325 /** +326 * The prefix to use when reading signing variables +327 * from settings. +328 */ +329 @Parameter(defaultValue = "jdeb.") +330 private String signCfgPrefix; +331 +332 /** +333 * The settings. +334 */ +335 @Parameter(defaultValue = "${settings}") +336 private Settings settings; +337 +338 @Parameter(defaultValue = "") +339 private String propertyPrefix; +340 +341 /** +342 * Sets the long file mode for the resulting tar file. Valid values are "gnu", "posix", "error" or "truncate" +343 * @see org.apache.commons.compress.archivers.tar.TarArchiveOutputStream#setLongFileMode(int) +344 */ +345 @Parameter(defaultValue = "gnu") +346 private String tarLongFileMode; +347 +348 /** +349 * Sets the big number mode for the resulting tar file. Valid values are "gnu", "posix" or "error" +350 * @see org.apache.commons.compress.archivers.tar.TarArchiveOutputStream#setBigNumberMode(int) +351 */ +352 @Parameter(defaultValue = "gnu") +353 private String tarBigNumberMode; +354 +355 /** +356 * Timestamp for reproducible output archive entries, either formatted as ISO 8601 +357 * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like +358 * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>). +359 * +360 * @since 1.9 +361 */ +362 @Parameter(defaultValue = "${project.build.outputTimestamp}") +363 private String outputTimestamp; +364 +365 /* end of parameters */ +366 +367 private static final String KEY = "key"; +368 private static final String KEYRING = "keyring"; +369 private static final String PASSPHRASE = "passphrase"; +370 +371 private String openReplaceToken = "[["; +372 private String closeReplaceToken = "]]"; +373 private Console console; +374 private Collection<DataProducer> dataProducers = new ArrayList<>(); +375 private Collection<DataProducer> conffileProducers = new ArrayList<>(); +376 +377 public void setOpenReplaceToken( String openReplaceToken ) { +378 this.openReplaceToken = openReplaceToken; +379 } +380 +381 public void setCloseReplaceToken( String closeReplaceToken ) { +382 this.closeReplaceToken = closeReplaceToken; +383 } +384 +385 protected void setData( Data[] dataSet ) { +386 this.dataSet = dataSet; +387 dataProducers.clear(); +388 conffileProducers.clear(); +389 if (dataSet != null) { +390 Collections.addAll(dataProducers, dataSet); +391 +392 for (Data item : dataSet) { +393 if (item.getConffile()) { +394 conffileProducers.add(item); +395 } +396 } +397 } +398 } +399 +400 @SuppressWarnings("unchecked,rawtypes") +401 protected VariableResolver initializeVariableResolver( Map<String, String> variables ) { +402 variables.putAll((Map) getProject().getProperties()); +403 variables.putAll((Map) System.getProperties()); +404 variables.put("name", name != null ? name : getProject().getName()); +405 variables.put("artifactId", getProject().getArtifactId()); +406 variables.put("groupId", getProject().getGroupId()); +407 variables.put("version", getProjectVersion()); +408 variables.put("description", getProject().getDescription()); +409 variables.put("extension", "deb"); +410 variables.put("baseDir", getProject().getBasedir().getAbsolutePath()); +411 variables.put("buildDir", buildDirectory.getAbsolutePath()); +412 variables.put("project.version", getProject().getVersion()); +413 +414 if (getProject().getInceptionYear() != null) { +415 variables.put("project.inceptionYear", getProject().getInceptionYear()); +416 } +417 if (getProject().getOrganization() != null) { +418 if (getProject().getOrganization().getName() != null) { +419 variables.put("project.organization.name", getProject().getOrganization().getName()); +420 } +421 if (getProject().getOrganization().getUrl() != null) { +422 variables.put("project.organization.url", getProject().getOrganization().getUrl()); +423 } +424 } +425 +426 variables.put("url", getProject().getUrl()); +427 +428 return new MapVariableResolver(variables); +429 } +430 +431 /** +432 * Doc some cleanup and conversion on the Maven project version. +433 * <ul> +434 * <li>any "-" is replaced by "+"</li> +435 * <li>"SNAPSHOT" is replaced with the current time and date, prepended by "~"</li> +436 * </ul> +437 * +438 * @return the Maven project version +439 */ +440 private String getProjectVersion() { +441 return Utils.convertToDebianVersion(getProject().getVersion(), this.snapshotExpand, this.snapshotEnv, this.snapshotTemplate, session.getStartTime()); +442 } +443 +444 /** +445 * @return whether the artifact is a POM or not +446 */ +447 private boolean isPOM() { +448 String type = getProject().getArtifact().getType(); +449 return "pom".equalsIgnoreCase(type); +450 } +451 +452 /** +453 * @return whether the artifact is of configured type (i.e. the package to generate is the main artifact) +454 */ +455 private boolean isType() { +456 return type.equals(getProject().getArtifact().getType()); +457 } +458 +459 /** +460 * @return whether or not Maven is currently operating in the execution root +461 */ +462 private boolean isSubmodule() { +463 // FIXME there must be a better way +464 return !session.getExecutionRootDirectory().equalsIgnoreCase(baseDir.toString()); +465 } +466 +467 /** +468 * @return whether or not the main artifact was created +469 */ +470 private boolean hasMainArtifact() { +471 final MavenProject project = getProject(); +472 final Artifact artifact = project.getArtifact(); +473 return artifact.getFile() != null && artifact.getFile().isFile(); +474 } +475 +476 /** +477 * Main entry point +478 * +479 * @throws MojoExecutionException on error +480 */ +481 public void execute() throws MojoExecutionException { +482 +483 final MavenProject project = getProject(); +484 +485 if (skip) { +486 getLog().info("skipping as configured (skip)"); +487 return; +488 } +489 +490 if (skipPOMs && isPOM()) { +491 getLog().info("skipping because artifact is a pom (skipPOMs)"); +492 return; +493 } +494 +495 if (skipSubmodules && isSubmodule()) { +496 getLog().info("skipping submodule (skipSubmodules)"); +497 return; +498 } +499 +500 +501 setData(dataSet); +502 +503 console = new MojoConsole(getLog(), verbose); +504 +505 initializeSignProperties(); +506 +507 final VariableResolver resolver = initializeVariableResolver(new HashMap<String, String>()); +508 +509 final File debFile = new File(Utils.replaceVariables(resolver, deb, openReplaceToken, closeReplaceToken)); +510 final File controlDirFile = new File(Utils.replaceVariables(resolver, controlDir, openReplaceToken, closeReplaceToken)); +511 final File installDirFile = new File(Utils.replaceVariables(resolver, installDir, openReplaceToken, closeReplaceToken)); +512 final File changesInFile = new File(Utils.replaceVariables(resolver, changesIn, openReplaceToken, closeReplaceToken)); +513 final File changesOutFile = new File(Utils.replaceVariables(resolver, changesOut, openReplaceToken, closeReplaceToken)); +514 final File changesSaveFile = new File(Utils.replaceVariables(resolver, changesSave, openReplaceToken, closeReplaceToken)); +515 final File keyringFile = keyring == null ? null : new File(Utils.replaceVariables(resolver, keyring, openReplaceToken, closeReplaceToken)); +516 +517 // if there are no producers defined we try to use the artifacts +518 if (dataProducers.isEmpty()) { +519 +520 if (hasMainArtifact()) { +521 Set<Artifact> artifacts = new HashSet<>(); +522 +523 artifacts.add(project.getArtifact()); +524 +525 @SuppressWarnings("unchecked") +526 final Set<Artifact> projectArtifacts = project.getArtifacts(); +527 +528 artifacts.addAll(projectArtifacts); +529 +530 @SuppressWarnings("unchecked") +531 final List<Artifact> attachedArtifacts = project.getAttachedArtifacts(); +532 +533 artifacts.addAll(attachedArtifacts); +534 +535 for (Artifact artifact : artifacts) { +536 final File file = artifact.getFile(); +537 if (file != null) { +538 dataProducers.add(new DataProducer() { +539 public void produce( final DataConsumer receiver ) { +540 try { +541 final File path = new File(installDirFile.getPath(), file.getName()); +542 final String entryName = path.getPath(); +543 +544 final boolean symbolicLink = SymlinkUtils.isSymbolicLink(path); +545 final TarArchiveEntry e; +546 if (symbolicLink) { +547 e = new TarArchiveEntry(entryName, TarConstants.LF_SYMLINK); +548 e.setLinkName(SymlinkUtils.readSymbolicLink(path)); +549 } else { +550 e = new TarArchiveEntry(entryName, true); +551 } +552 +553 e.setUserId(0); +554 e.setGroupId(0); +555 e.setUserName("root"); +556 e.setGroupName("root"); +557 e.setMode(TarEntry.DEFAULT_FILE_MODE); +558 e.setSize(file.length()); +559 +560 receiver.onEachFile(new FileInputStream(file), e); +561 } catch (Exception e) { +562 getLog().error(e); +563 } +564 } +565 }); +566 } else { +567 getLog().error("No file for artifact " + artifact); +568 } +569 } +570 } +571 } +572 +573 try { +574 DebMaker debMaker = new DebMaker(console, dataProducers, conffileProducers); +575 debMaker.setDeb(debFile); +576 debMaker.setControl(controlDirFile); +577 debMaker.setPackage(getProject().getArtifactId()); +578 debMaker.setDescription(getProject().getDescription()); +579 debMaker.setHomepage(getProject().getUrl()); +580 debMaker.setChangesIn(changesInFile); +581 debMaker.setChangesOut(changesOutFile); +582 debMaker.setChangesSave(changesSaveFile); +583 debMaker.setCompression(compression); +584 debMaker.setKeyring(keyringFile); +585 debMaker.setKey(key); +586 debMaker.setPassphrase(passphrase); +587 debMaker.setSignPackage(signPackage); +588 debMaker.setSignChanges(signChanges); +589 debMaker.setSignMethod(signMethod); +590 debMaker.setSignRole(signRole); +591 debMaker.setResolver(resolver); +592 debMaker.setOpenReplaceToken(openReplaceToken); +593 debMaker.setCloseReplaceToken(closeReplaceToken); +594 debMaker.setDigest(digest); +595 debMaker.setTarBigNumberMode(tarBigNumberMode); +596 debMaker.setTarLongFileMode(tarLongFileMode); +597 Long outputTimestampMs = new OutputTimestampResolver(console).resolveOutputTimestamp(outputTimestamp); +598 debMaker.setOutputTimestampMs(outputTimestampMs); +599 debMaker.validate(); +600 debMaker.makeDeb(); +601 +602 // Always attach unless explicitly set to false +603 if ("true".equalsIgnoreCase(attach)) { +604 console.info("Attaching created debian package " + debFile); +605 if (!isType()) { +606 projectHelper.attachArtifact(project, type, classifier, debFile); +607 } else { +608 project.getArtifact().setFile(debFile); +609 } +610 } +611 +612 } catch (PackagingException e) { +613 getLog().error("Failed to create debian package " + debFile, e); +614 throw new MojoExecutionException("Failed to create debian package " + debFile, e); +615 } +616 +617 if (!isBlank(propertyPrefix)) { +618 project.getProperties().put(propertyPrefix+"version", getProjectVersion() ); +619 project.getProperties().put(propertyPrefix+"deb", debFile.getAbsolutePath()); +620 project.getProperties().put(propertyPrefix+"deb.name", debFile.getName()); +621 project.getProperties().put(propertyPrefix+"changes", changesOutFile.getAbsolutePath()); +622 project.getProperties().put(propertyPrefix+"changes.name", changesOutFile.getName()); +623 project.getProperties().put(propertyPrefix+"changes.txt", changesSaveFile.getAbsolutePath()); +624 project.getProperties().put(propertyPrefix+"changes.txt.name", changesSaveFile.getName()); +625 } +626 +627 } +628 +629 /** +630 * Initializes unspecified sign properties using available defaults +631 * and global settings. +632 */ +633 private void initializeSignProperties() { +634 if (!signPackage && !signChanges) { +635 return; +636 } +637 +638 if (key != null && keyring != null && passphrase != null) { +639 return; +640 } +641 +642 Map<String, String> properties = +643 readPropertiesFromActiveProfiles(signCfgPrefix, KEY, KEYRING, PASSPHRASE); +644 +645 key = lookupIfEmpty(key, properties, KEY); +646 keyring = lookupIfEmpty(keyring, properties, KEYRING); +647 passphrase = decrypt(lookupIfEmpty(passphrase, properties, PASSPHRASE)); +648 +649 if (keyring == null) { +650 try { +651 keyring = Utils.guessKeyRingFile().getAbsolutePath(); +652 console.info("Located keyring at " + keyring); +653 } catch (FileNotFoundException e) { +654 console.warn(e.getMessage()); +655 } +656 } +657 } +658 +659 /** +660 * Decrypts given passphrase if needed using maven security dispatcher. +661 * See http://maven.apache.org/guides/mini/guide-encryption.html for details. +662 * +663 * @param maybeEncryptedPassphrase possibly encrypted passphrase +664 * @return decrypted passphrase +665 */ +666 private String decrypt( final String maybeEncryptedPassphrase ) { +667 if (maybeEncryptedPassphrase == null) { +668 return null; +669 } +670 +671 try { +672 final String decrypted = secDispatcher.decrypt(maybeEncryptedPassphrase); +673 if (maybeEncryptedPassphrase.equals(decrypted)) { +674 console.info("Passphrase was not encrypted"); +675 } else { +676 console.info("Passphrase was successfully decrypted"); +677 } +678 return decrypted; +679 } catch (SecDispatcherException e) { +680 console.warn("Unable to decrypt passphrase: " + e.getMessage()); +681 } +682 +683 return maybeEncryptedPassphrase; +684 } +685 +686 /** +687 * +688 * @return the maven project used by this mojo +689 */ +690 private MavenProject getProject() { +691 if (project.getExecutionProject() != null) { +692 return project.getExecutionProject(); +693 } +694 +695 return project; +696 } +697 +698 +699 +700 /** +701 * Read properties from the active profiles. +702 * +703 * Goes through all active profiles (in the order the +704 * profiles are defined in settings.xml) and extracts +705 * the desired properties (if present). The prefix is +706 * used when looking up properties in the profile but +707 * not in the returned map. +708 * +709 * @param prefix The prefix to use or null if no prefix should be used +710 * @param properties The properties to read +711 * +712 * @return A map containing the values for the properties that were found +713 */ +714 public Map<String, String> readPropertiesFromActiveProfiles( final String prefix, +715 final String... properties ) { +716 if (settings == null) { +717 console.debug("No maven setting injected"); +718 return Collections.emptyMap(); +719 } +720 +721 final List<String> activeProfilesList = settings.getActiveProfiles(); +722 if (activeProfilesList.isEmpty()) { +723 console.debug("No active profiles found"); +724 return Collections.emptyMap(); +725 } +726 +727 final Map<String, String> map = new HashMap<>(); +728 final Set<String> activeProfiles = new HashSet<>(activeProfilesList); +729 +730 // Iterate over all active profiles in order +731 for (final Profile profile : settings.getProfiles()) { +732 // Check if the profile is active +733 final String profileId = profile.getId(); +734 if (activeProfiles.contains(profileId)) { +735 console.debug("Trying active profile " + profileId); +736 for (final String property : properties) { +737 final String propKey = prefix != null ? prefix + property : property; +738 final String value = profile.getProperties().getProperty(propKey); +739 if (value != null) { +740 console.debug("Found property " + property + " in profile " + profileId); +741 map.put(property, value); +742 } +743 } +744 } +745 } +746 +747 return map; +748 } +749 +750 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.maven; +18 +19 import java.io.File; +20 import java.io.FileInputStream; +21 import java.io.IOException; +22 +23 import org.apache.maven.plugins.annotations.Parameter; +24 import org.vafer.jdeb.mapping.LsMapper; +25 import org.vafer.jdeb.mapping.NullMapper; +26 import org.vafer.jdeb.mapping.PermMapper; +27 +28 /** +29 * Maven "mapper" element acting as factory for the entry mapper. +30 * Supported types: ls, perm +31 */ +32 public final class Mapper { +33 +34 @Parameter(required = true) +35 private String type; +36 +37 @Parameter +38 private int uid = -1; +39 +40 @Parameter +41 private int gid = -1; +42 +43 @Parameter +44 private String user; +45 +46 @Parameter +47 private String group; +48 +49 @Parameter +50 private String filemode; +51 +52 @Parameter +53 private String dirmode; +54 +55 @Parameter +56 private String prefix; +57 +58 @Parameter +59 private int strip; +60 +61 @Parameter +62 private File src; +63 +64 +65 public org.vafer.jdeb.mapping.Mapper createMapper() throws IOException { +66 +67 if ("ls".equalsIgnoreCase(type)) { +68 try { +69 return new LsMapper(new FileInputStream(src)); +70 } catch (Exception e) { +71 e.printStackTrace(); +72 } +73 } +74 +75 if ("perm".equalsIgnoreCase(type)) { +76 return new PermMapper(uid, gid, user, group, filemode, dirmode, strip, prefix); +77 } +78 +79 /* NullMapper required for DataProducerPathTemplate */ +80 return NullMapper.INSTANCE; +81 } +82 +83 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.maven; +18 +19 public enum MissingSourceBehavior { +20 IGNORE, FAIL +21 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.maven; +18 +19 import org.apache.maven.plugin.logging.Log; +20 import org.vafer.jdeb.Console; +21 +22 /** +23 * Console implementation for Maven plugins. debug messages are only displayed +24 * when the <tt>verbose</tt> parameter is true. +25 */ +26 class MojoConsole implements Console { +27 +28 private final Log log; +29 private final boolean verbose; +30 +31 public MojoConsole(Log log, boolean verbose) { +32 this.log = log; +33 this.verbose = verbose; +34 } +35 +36 public void debug(String message) { +37 if (verbose) { +38 log.info(message); +39 } +40 } +41 +42 public void info(String message) { +43 log.info(message); +44 } +45 +46 public void warn(String message) { +47 log.warn(message); +48 } +49 } ++
Class | +
---|
+ Data + | +
+ DebMojo + | +
+ Mapper + | +
+ MissingSourceBehavior + | +
+ MojoConsole + | +
Class | +
---|
+ ChangesFileBuilder + | +
+ Compression + | +
+ Console + | +
+ ControlBuilder + | +
+ DataBuilder + | +
+ DataBuilder.Total + | +
+ DataConsumer + | +
+ DataProducer + | +
+ DebMaker + | +
+ PackagingException + | +
+ TarOptions + | +
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +19 import org.apache.tools.ant.types.selectors.SelectorUtils; +20 import org.vafer.jdeb.DataConsumer; +21 import org.vafer.jdeb.DataProducer; +22 import org.vafer.jdeb.mapping.Mapper; +23 +24 import java.io.File; +25 import java.io.FileInputStream; +26 import java.io.IOException; +27 +28 /** +29 * Base Producer class providing including/excluding. +30 */ +31 public abstract class AbstractDataProducer implements DataProducer { +32 +33 private final String[] includes; +34 private final String[] excludes; +35 private final Mapper[] mappers; +36 +37 +38 public AbstractDataProducer( final String[] pIncludes, final String[] pExcludes, final Mapper[] pMapper ) { +39 excludes = (pExcludes != null) ? pExcludes : new String[0]; +40 includes = (pIncludes != null) ? pIncludes : new String[] { "**" }; +41 mappers = (pMapper != null) ? pMapper : new Mapper[0]; +42 } +43 +44 public boolean isIncluded( final String pName ) { +45 if (!isIncluded(pName, includes)) { +46 return false; +47 } +48 if (isExcluded(pName, excludes)) { +49 return false; +50 } +51 return true; +52 } +53 +54 private boolean isIncluded( String name, String[] includes ) { +55 for (String include : includes) { +56 if (SelectorUtils.matchPath(include, name)) { +57 return true; +58 } +59 } +60 return false; +61 } +62 +63 +64 private boolean isExcluded( String name, String[] excludes ) { +65 for (String exclude : excludes) { +66 if (SelectorUtils.matchPath(exclude, name)) { +67 return true; +68 } +69 } +70 return false; +71 } +72 +73 public void produceDir( final DataConsumer consumer, +74 final String dirName ) throws IOException { +75 final String name = dirName.endsWith("/") ? dirName : dirName + "/"; +76 TarArchiveEntry entry = Producers.defaultDirEntryWithName(name); +77 entry = map(entry); +78 entry.setSize(0); +79 Producers.produceDirEntry(consumer, entry); +80 } +81 +82 public void produceFile( final DataConsumer consumer, +83 final File file, +84 final String fileName ) throws IOException { +85 TarArchiveEntry fileEntry = Producers.defaultFileEntryWithName(fileName); +86 fileEntry.setSize(file.length()); +87 fileEntry = map(fileEntry); +88 Producers.produceInputStreamWithEntry(consumer, new FileInputStream(file), fileEntry); +89 } +90 +91 public TarArchiveEntry map( final TarArchiveEntry pEntry ) { +92 +93 TarArchiveEntry entry = pEntry; +94 +95 for (Mapper mapper : mappers) { +96 entry = mapper.map(entry); +97 } +98 +99 return entry; +100 } +101 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import java.io.BufferedInputStream; +19 import java.io.File; +20 import java.io.FileInputStream; +21 import java.io.IOException; +22 import java.io.InputStream; +23 +24 import org.apache.commons.compress.archivers.ArchiveEntry; +25 import org.apache.commons.compress.archivers.ArchiveException; +26 import org.apache.commons.compress.archivers.ArchiveInputStream; +27 import org.apache.commons.compress.archivers.ArchiveStreamFactory; +28 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +29 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +30 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +31 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +32 import org.apache.commons.compress.compressors.CompressorException; +33 import org.apache.commons.compress.compressors.CompressorInputStream; +34 import org.apache.commons.compress.compressors.CompressorStreamFactory; +35 import org.vafer.jdeb.DataConsumer; +36 import org.vafer.jdeb.DataProducer; +37 import org.vafer.jdeb.mapping.Mapper; +38 +39 /** +40 * Providing data from an archive keeping permissions and ownerships. +41 */ +42 public final class DataProducerArchive extends AbstractDataProducer implements DataProducer { +43 +44 private final File archive; +45 +46 public DataProducerArchive( final File pArchive, final String[] pIncludes, final String[] pExcludes, final Mapper[] pMappers ) { +47 super(pIncludes, pExcludes, pMappers); +48 archive = pArchive; +49 } +50 +51 public void produce( final DataConsumer pReceiver ) throws IOException { +52 +53 InputStream is = new BufferedInputStream(new FileInputStream(archive)); +54 +55 CompressorInputStream compressorInputStream = null; +56 +57 try { +58 compressorInputStream = new CompressorStreamFactory().createCompressorInputStream(is); +59 } catch (CompressorException e) { +60 // expected if the input file is a zip archive +61 } +62 +63 if (compressorInputStream != null) { +64 is = new BufferedInputStream(compressorInputStream); +65 } +66 +67 ArchiveInputStream archiveInputStream; +68 +69 try { +70 archiveInputStream = new ArchiveStreamFactory().createArchiveInputStream(is); +71 } catch (ArchiveException e) { +72 throw new IOException("Unsupported archive format: " + archive, e); +73 } +74 +75 EntryConverter converter; +76 +77 if (archiveInputStream instanceof TarArchiveInputStream) { +78 +79 converter = new EntryConverter() { +80 public TarArchiveEntry convert( ArchiveEntry entry ) { +81 return (TarArchiveEntry) entry; +82 } +83 }; +84 +85 } else if (archiveInputStream instanceof ZipArchiveInputStream) { +86 +87 converter = new EntryConverter() { +88 public TarArchiveEntry convert( ArchiveEntry entry ) { +89 ZipArchiveEntry src = (ZipArchiveEntry) entry; +90 final TarArchiveEntry dst = new TarArchiveEntry(src.getName(), true); +91 //TODO: if (src.isUnixSymlink()) { +92 //} +93 +94 dst.setSize(src.getSize()); +95 dst.setMode(src.getUnixMode()); +96 dst.setModTime(src.getTime()); +97 +98 return dst; +99 } +100 }; +101 +102 } else { +103 throw new IOException("Unsupported archive format: " + archive); +104 } +105 +106 +107 try { +108 while (true) { +109 +110 ArchiveEntry archiveEntry = archiveInputStream.getNextEntry(); +111 +112 if (archiveEntry == null) { +113 break; +114 } +115 +116 if (!isIncluded(archiveEntry.getName())) { +117 continue; +118 } +119 +120 TarArchiveEntry entry = converter.convert(archiveEntry); +121 +122 entry = map(entry); +123 +124 if (entry.isSymbolicLink()) { +125 pReceiver.onEachLink(entry); +126 continue; +127 } +128 +129 if (entry.isDirectory()) { +130 pReceiver.onEachDir(entry); +131 continue; +132 } +133 +134 pReceiver.onEachFile(archiveInputStream, entry); +135 } +136 +137 } finally { +138 if (archiveInputStream != null) { +139 archiveInputStream.close(); +140 } +141 } +142 } +143 +144 private interface EntryConverter { +145 TarArchiveEntry convert( ArchiveEntry entry ); +146 } +147 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import java.io.File; +19 import java.io.IOException; +20 +21 import org.apache.tools.ant.DirectoryScanner; +22 import org.vafer.jdeb.DataConsumer; +23 import org.vafer.jdeb.DataProducer; +24 import org.vafer.jdeb.mapping.Mapper; +25 import org.vafer.jdeb.utils.Utils; +26 +27 /** +28 * DataProducer iterating over a directory. +29 * For cross-platform permissions and ownerships you probably want to use a Mapper, too. +30 */ +31 public final class DataProducerDirectory extends AbstractDataProducer implements DataProducer { +32 +33 private final DirectoryScanner scanner = new DirectoryScanner(); +34 +35 public DataProducerDirectory( final File pDir, final String[] pIncludes, final String[] pExcludes, final Mapper[] pMappers ) { +36 super(pIncludes, pExcludes, pMappers); +37 scanner.setBasedir(pDir); +38 scanner.setIncludes(pIncludes); +39 scanner.setExcludes(pExcludes); +40 scanner.setCaseSensitive(true); +41 scanner.setFollowSymlinks(true); +42 } +43 +44 public void produce( final DataConsumer pReceiver ) throws IOException { +45 +46 scanner.scan(); +47 +48 final File baseDir = scanner.getBasedir(); +49 +50 for (String dir : scanner.getIncludedDirectories()) { +51 final File file = new File(baseDir, dir); +52 String dirname = getFilename(baseDir, file); +53 +54 if ("".equals(dirname)) { +55 continue; +56 } +57 +58 if ('/' != File.separatorChar) { +59 dirname = dirname.replace(File.separatorChar, '/'); +60 } +61 +62 if (!isIncluded(dirname)) { +63 continue; +64 } +65 +66 if (!dirname.endsWith("/")) { +67 dirname += "/"; +68 } +69 +70 produceDir(pReceiver, dirname); +71 } +72 +73 +74 for (String f : scanner.getIncludedFiles()) { +75 final File file = new File(baseDir, f); +76 String filename = getFilename(baseDir, file); +77 +78 if ('/' != File.separatorChar) { +79 filename = filename.replace(File.separatorChar, '/'); +80 } +81 +82 if (!isIncluded(filename)) { +83 continue; +84 } +85 +86 produceFile(pReceiver, file, filename); +87 } +88 } +89 +90 private String getFilename( File root, File file ) { +91 +92 final String relativeFilename = file.getAbsolutePath().substring(root.getAbsolutePath().length()); +93 +94 return Utils.stripLeadingSlash(relativeFilename); +95 } +96 +97 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import java.io.File; +19 import java.io.FileInputStream; +20 import java.io.IOException; +21 +22 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +23 import org.vafer.jdeb.DataConsumer; +24 import org.vafer.jdeb.DataProducer; +25 import org.vafer.jdeb.mapping.Mapper; +26 +27 /** +28 * DataProducer representing a single file +29 * For cross-platform permissions and ownerships you probably want to use a Mapper, too. +30 */ +31 public final class DataProducerFile extends AbstractDataProducer implements DataProducer { +32 +33 private final File file; +34 +35 private final String destinationName; +36 +37 public DataProducerFile( final File pFile, String pDestinationName, String[] pIncludes, String[] pExcludes, Mapper[] pMapper ) { +38 super(pIncludes, pExcludes, pMapper); +39 file = pFile; +40 destinationName = pDestinationName; +41 } +42 +43 public void produce( final DataConsumer pReceiver ) throws IOException { +44 String fileName; +45 if (destinationName != null && destinationName.trim().length() > 0) { +46 fileName = destinationName.trim(); +47 } else { +48 fileName = file.getName(); +49 } +50 +51 TarArchiveEntry entry = Producers.defaultFileEntryWithName(fileName); +52 +53 entry = map(entry); +54 +55 entry.setSize(file.length()); +56 +57 Producers.produceInputStreamWithEntry(pReceiver, new FileInputStream(file), entry); +58 } +59 +60 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import java.io.File; +19 import java.io.FileInputStream; +20 import java.io.IOException; +21 import java.io.InputStream; +22 +23 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +24 import org.apache.commons.compress.archivers.tar.TarConstants; +25 import org.apache.tools.ant.DirectoryScanner; +26 import org.apache.tools.ant.taskdefs.Tar; +27 import org.apache.tools.ant.types.FileSet; +28 import org.apache.tools.tar.TarEntry; +29 import org.vafer.jdeb.DataConsumer; +30 import org.vafer.jdeb.DataProducer; +31 import org.vafer.jdeb.utils.SymlinkUtils; +32 +33 /** +34 * DataProducer providing data from an Ant fileset. TarFileSets are also +35 * supported with their permissions. +36 */ +37 public final class DataProducerFileSet implements DataProducer { +38 +39 private final FileSet fileset; +40 +41 public DataProducerFileSet( final FileSet fileset ) { +42 this.fileset = fileset; +43 } +44 +45 public void produce( final DataConsumer pReceiver ) throws IOException { +46 String user = Producers.ROOT_NAME; +47 int uid = Producers.ROOT_UID; +48 String group = Producers.ROOT_NAME; +49 int gid = Producers.ROOT_UID; +50 int filemode = TarEntry.DEFAULT_FILE_MODE; +51 int dirmode = TarEntry.DEFAULT_DIR_MODE; +52 String prefix = ""; +53 String fullpath = ""; +54 +55 if (fileset instanceof Tar.TarFileSet) { +56 Tar.TarFileSet tarfileset = (Tar.TarFileSet) fileset; +57 user = tarfileset.getUserName(); +58 uid = tarfileset.getUid(); +59 group = tarfileset.getGroup(); +60 gid = tarfileset.getGid(); +61 filemode = tarfileset.getMode(); +62 dirmode = tarfileset.getDirMode(tarfileset.getProject()); +63 prefix = tarfileset.getPrefix(tarfileset.getProject()); +64 fullpath = tarfileset.getFullpath(tarfileset.getProject()); +65 } +66 +67 final DirectoryScanner scanner = fileset.getDirectoryScanner(fileset.getProject()); +68 scanner.scan(); +69 +70 final File basedir = scanner.getBasedir(); +71 +72 if (scanner.getIncludedFilesCount() != 1 || scanner.getIncludedDirsCount() != 0) { +73 // the full path attribute only have sense in this context +74 // if it's a single-file fileset, we ignore it otherwise +75 fullpath = ""; +76 } +77 +78 for (String directory : scanner.getIncludedDirectories()) { +79 String name = directory.replace('\\', '/'); +80 +81 final TarArchiveEntry entry = new TarArchiveEntry(prefix + "/" + name); +82 entry.setUserName(user); +83 entry.setUserId(uid); +84 entry.setGroupName(group); +85 entry.setGroupId(gid); +86 entry.setMode(dirmode); +87 +88 pReceiver.onEachDir(entry); +89 } +90 +91 for (String filename : scanner.getIncludedFiles()) { +92 final String name = filename.replace('\\', '/'); +93 final File file = new File(basedir, name); +94 +95 try (InputStream inputStream = new FileInputStream(file)) { +96 final String entryName = "".equals(fullpath) ? prefix + "/" + name : fullpath; +97 +98 final File entryPath = new File(entryName); +99 +100 final boolean symbolicLink = SymlinkUtils.isSymbolicLink(entryPath); +101 final TarArchiveEntry e; +102 if (symbolicLink) { +103 e = new TarArchiveEntry(entryName, TarConstants.LF_SYMLINK); +104 e.setLinkName(SymlinkUtils.readSymbolicLink(entryPath)); +105 } else { +106 e = new TarArchiveEntry(entryName, true); +107 } +108 +109 e.setUserId(uid); +110 e.setGroupId(gid); +111 e.setUserName(user); +112 e.setGroupName(group); +113 e.setMode(filemode); +114 e.setSize(file.length()); +115 +116 pReceiver.onEachFile(inputStream, e); +117 } +118 } +119 } +120 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import org.vafer.jdeb.DataConsumer; +19 import org.vafer.jdeb.mapping.Mapper; +20 import org.vafer.jdeb.utils.Utils; +21 +22 import java.io.File; +23 import java.io.IOException; +24 +25 /** +26 * Data producer that places multiple files into a single +27 * destination directory. +28 */ +29 public class DataProducerFiles extends AbstractDataProducer { +30 +31 private final String[] files; +32 private final String destDir; +33 +34 public DataProducerFiles( final String[] files, +35 final String destDir, +36 final Mapper[] mappers ) { +37 super(null, null, mappers); +38 this.files = files; +39 this.destDir = destDir; +40 } +41 +42 public void produce( DataConsumer receiver ) throws IOException { +43 boolean hasDestDir = !Utils.isNullOrEmpty(destDir); +44 +45 for (String fileName : files) { +46 File f = new File(fileName); +47 +48 if (hasDestDir) { +49 fileName = Utils.movePath(fileName, destDir); +50 } +51 +52 produceFile(receiver, f, fileName); +53 } +54 } +55 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import java.io.IOException; +19 +20 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +21 import org.vafer.jdeb.DataConsumer; +22 import org.vafer.jdeb.DataProducer; +23 import org.vafer.jdeb.mapping.Mapper; +24 +25 /** +26 * DataProducer representing a single file +27 * For cross-platform permissions and ownerships you probably want to use a Mapper, too. +28 */ +29 public final class DataProducerLink extends AbstractDataProducer implements DataProducer { +30 +31 private final String path; +32 private final String linkName; +33 private final boolean symlink; +34 +35 public DataProducerLink(final String path, final String linkName, final boolean symlink, String[] pIncludes, String[] pExcludes, Mapper[] pMapper) { +36 super(pIncludes, pExcludes, pMapper); +37 this.path = path; +38 this.symlink = symlink; +39 this.linkName = linkName; +40 } +41 +42 public void produce( final DataConsumer pReceiver ) throws IOException { +43 TarArchiveEntry entry = new TarArchiveEntry(path, symlink ? TarArchiveEntry.LF_SYMLINK : TarArchiveEntry.LF_LINK); +44 entry.setLinkName(linkName); +45 +46 entry.setUserId(Producers.ROOT_UID); +47 entry.setUserName(Producers.ROOT_NAME); +48 entry.setGroupId(Producers.ROOT_UID); +49 entry.setGroupName(Producers.ROOT_NAME); +50 entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE); +51 +52 entry = map(entry); +53 +54 entry.setName(path); +55 entry.setLinkName(linkName); +56 +57 pReceiver.onEachLink(entry); +58 } +59 +60 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import java.io.IOException; +19 +20 import org.vafer.jdeb.DataConsumer; +21 import org.vafer.jdeb.DataProducer; +22 import org.vafer.jdeb.mapping.Mapper; +23 +24 public class DataProducerPathTemplate extends AbstractDataProducer implements DataProducer { +25 +26 private final String[] literalPaths; +27 +28 public DataProducerPathTemplate( String[] pLiteralPaths, String[] pIncludes, String[] pExcludes, Mapper[] pMapper ) { +29 super(pIncludes, pExcludes, pMapper); +30 literalPaths = pLiteralPaths; +31 } +32 +33 public void produce( DataConsumer pReceiver ) throws IOException { +34 for (String literalPath : literalPaths) { +35 produceDir(pReceiver, literalPath); +36 } +37 } +38 +39 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.producers; +17 +18 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +19 +20 import org.apache.commons.io.IOUtils; +21 import org.vafer.jdeb.DataConsumer; +22 +23 import java.io.IOException; +24 import java.io.InputStream; +25 +26 /** +27 * Package-private utility class with common producers functionality. +28 */ +29 class Producers { +30 +31 final static int ROOT_UID = 0; +32 final static String ROOT_NAME = "root"; +33 +34 private Producers() {} +35 +36 +37 /** +38 * Creates a tar file entry with defaults parameters. +39 * @param fileName the entry name +40 * @return file entry with reasonable defaults +41 */ +42 static TarArchiveEntry defaultFileEntryWithName( final String fileName ) { +43 TarArchiveEntry entry = new TarArchiveEntry(fileName, true); +44 entry.setUserId(ROOT_UID); +45 entry.setUserName(ROOT_NAME); +46 entry.setGroupId(ROOT_UID); +47 entry.setGroupName(ROOT_NAME); +48 entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE); +49 return entry; +50 } +51 +52 /** +53 * Creates a tar directory entry with defaults parameters. +54 * @param dirName the directory name +55 * @return dir entry with reasonable defaults +56 */ +57 static TarArchiveEntry defaultDirEntryWithName( final String dirName ) { +58 TarArchiveEntry entry = new TarArchiveEntry(dirName, true); +59 entry.setUserId(ROOT_UID); +60 entry.setUserName(ROOT_NAME); +61 entry.setGroupId(ROOT_UID); +62 entry.setGroupName(ROOT_NAME); +63 entry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE); +64 return entry; +65 } +66 +67 /** +68 * Forwards tar archive entry entry to a consumer. +69 * @param consumer the consumer +70 * @param dirEntry the entry to pass +71 * @throws IOException +72 */ +73 static void produceDirEntry( final DataConsumer consumer, +74 final TarArchiveEntry dirEntry ) throws IOException { +75 consumer.onEachDir(dirEntry); +76 } +77 +78 +79 /** +80 * Feeds input stream to data consumer using metadata from tar entry. +81 * @param consumer the consumer +82 * @param inputStream the stream to feed +83 * @param entry the entry to use for metadata +84 * @throws IOException on consume error +85 */ +86 static void produceInputStreamWithEntry( final DataConsumer consumer, +87 final InputStream inputStream, +88 final TarArchiveEntry entry ) throws IOException { +89 try { +90 consumer.onEachFile(inputStream, entry); +91 } finally { +92 IOUtils.closeQuietly(inputStream); +93 } +94 } +95 +96 } ++
Class | +
---|
+ AbstractDataProducer + | +
+ DataProducerArchive + | +
+ DataProducerArchive.EntryConverter + | +
+ DataProducerDirectory + | +
+ DataProducerFile + | +
+ DataProducerFileSet + | +
+ DataProducerFiles + | +
+ DataProducerLink + | +
+ DataProducerPathTemplate + | +
+ Producers + | +
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.signing; +18 +19 import java.io.ByteArrayInputStream; +20 import java.io.IOException; +21 import java.io.InputStream; +22 import java.io.InputStreamReader; +23 import java.io.OutputStream; +24 import java.util.Iterator; +25 +26 import org.apache.commons.io.LineIterator; +27 import org.bouncycastle.bcpg.ArmoredOutputStream; +28 import org.bouncycastle.bcpg.BCPGOutputStream; +29 import org.bouncycastle.bcpg.HashAlgorithmTags; +30 import org.bouncycastle.openpgp.PGPException; +31 import org.bouncycastle.openpgp.PGPPrivateKey; +32 import org.bouncycastle.openpgp.PGPSecretKey; +33 import org.bouncycastle.openpgp.PGPSecretKeyRing; +34 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; +35 import org.bouncycastle.openpgp.PGPSignature; +36 import org.bouncycastle.openpgp.PGPSignatureGenerator; +37 import org.bouncycastle.openpgp.PGPUtil; +38 import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +39 import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +40 import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +41 import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +42 +43 import static java.nio.charset.StandardCharsets.*; +44 +45 import org.vafer.jdeb.PackagingException; +46 +47 /** +48 * Signing with OpenPGP. +49 */ +50 public class PGPSigner { +51 +52 private static final byte[] EOL = "\n".getBytes(UTF_8); +53 +54 private PGPSecretKey secretKey; +55 private PGPPrivateKey privateKey; +56 private int digest; +57 +58 private org.bouncycastle.crypto.digests.SHA1Digest keepSHA1; +59 private org.bouncycastle.crypto.digests.MD2Digest keepMD2; +60 private org.bouncycastle.crypto.digests.MD5Digest keepMD5; +61 private org.bouncycastle.crypto.digests.RIPEMD160Digest keepRIPEMD160; +62 private org.bouncycastle.crypto.digests.SHA256Digest keepSHA256; +63 private org.bouncycastle.crypto.digests.SHA384Digest keepSHA384; +64 private org.bouncycastle.crypto.digests.SHA512Digest keepSHA512; +65 private org.bouncycastle.crypto.digests.SHA224Digest keepSHA224; +66 +67 public static int getDigestCode(String digestName) throws PackagingException { +68 if ("SHA1".equals(digestName)) { +69 return HashAlgorithmTags.SHA1; +70 } else if ("MD2".equals(digestName)) { +71 return HashAlgorithmTags.MD2; +72 } else if ("MD5".equals(digestName)) { +73 return HashAlgorithmTags.MD5; +74 } else if ("RIPEMD160".equals(digestName)) { +75 return HashAlgorithmTags.RIPEMD160; +76 } else if ("SHA256".equals(digestName)) { +77 return HashAlgorithmTags.SHA256; +78 } else if ("SHA384".equals(digestName)) { +79 return HashAlgorithmTags.SHA384; +80 } else if ("SHA512".equals(digestName)) { +81 return HashAlgorithmTags.SHA512; +82 } else if ("SHA224".equals(digestName)) { +83 return HashAlgorithmTags.SHA224; +84 } else { +85 throw new PackagingException("unknown hash algorithm tag in digestName: " + digestName); +86 } +87 } +88 +89 public PGPSigner(InputStream keyring, String keyId, String passphrase, int digest) throws IOException, PGPException { +90 secretKey = getSecretKey(keyring, keyId); +91 if(secretKey == null) +92 { +93 throw new PGPException(String.format("Specified key %s does not exist in key ring %s", keyId, keyring)); +94 } +95 privateKey = secretKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(passphrase.toCharArray())); +96 this.digest = digest; +97 } +98 +99 /** +100 * Creates a clear sign signature over the input data. (Not detached) +101 * +102 * @param input the content to be signed +103 * @param output the output destination of the signature +104 */ +105 public void clearSign(String input, OutputStream output) throws IOException, PGPException { +106 clearSign(new ByteArrayInputStream(input.getBytes(UTF_8)), output); +107 } +108 +109 /** +110 * Creates a clear sign signature over the input data. (Not detached) +111 * +112 * @param input the content to be signed +113 * @param output the output destination of the signature +114 */ +115 public void clearSign(InputStream input, OutputStream output) throws IOException, PGPException { +116 +117 PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(privateKey.getPublicKeyPacket().getAlgorithm(), digest)); +118 signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privateKey); +119 +120 ArmoredOutputStream armoredOutput = new ArmoredOutputStream(output); +121 armoredOutput.beginClearText(digest); +122 +123 LineIterator iterator = new LineIterator(new InputStreamReader(input)); +124 +125 while (iterator.hasNext()) { +126 String line = iterator.nextLine(); +127 +128 // trailing spaces must be removed for signature calculation (see http://tools.ietf.org/html/rfc4880#section-7.1) +129 byte[] data = trim(line).getBytes(UTF_8); +130 +131 armoredOutput.write(data); +132 armoredOutput.write(EOL); +133 +134 signatureGenerator.update(data); +135 if (iterator.hasNext()) { +136 signatureGenerator.update(EOL); +137 } +138 } +139 +140 armoredOutput.endClearText(); +141 +142 PGPSignature signature = signatureGenerator.generate(); +143 signature.encode(new BCPGOutputStream(armoredOutput)); +144 +145 armoredOutput.close(); +146 } +147 +148 /** +149 * Returns the secret key. +150 */ +151 public PGPSecretKey getSecretKey() +152 { +153 return secretKey; +154 } +155 +156 /** +157 * Returns the private key. +158 */ +159 public PGPPrivateKey getPrivateKey() +160 { +161 return privateKey; +162 } +163 +164 /** +165 * Returns the secret key matching the specified identifier. +166 * +167 * @param input the input stream containing the keyring collection +168 * @param keyId the 4 bytes identifier of the key +169 */ +170 private PGPSecretKey getSecretKey(InputStream input, String keyId) throws IOException, PGPException { +171 PGPSecretKeyRingCollection keyrings = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); +172 +173 Iterator<PGPSecretKeyRing> rIt = keyrings.getKeyRings(); +174 +175 while (rIt.hasNext()) { +176 PGPSecretKeyRing kRing = rIt.next(); +177 Iterator<PGPSecretKey> kIt = kRing.getSecretKeys(); +178 +179 while (kIt.hasNext()) { +180 PGPSecretKey key = kIt.next(); +181 +182 if (key.isSigningKey() && String.format("%08x", key.getKeyID() & 0xFFFFFFFFL).equals(keyId.toLowerCase())) { +183 return key; +184 } +185 } +186 } +187 +188 return null; +189 } +190 +191 /** +192 * Trim the trailing spaces. +193 * +194 * @param line +195 */ +196 private String trim(String line) { +197 char[] chars = line.toCharArray(); +198 int len = chars.length; +199 +200 while (len > 0) { +201 if (!Character.isWhitespace(chars[len - 1])) { +202 break; +203 } +204 len--; +205 } +206 +207 return line.substring(0, len); +208 } +209 } ++
Class | +
---|
+ PGPSigner + | +
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.utils; +18 +19 import java.io.BufferedReader; +20 import java.io.IOException; +21 import java.io.InputStream; +22 import java.io.InputStreamReader; +23 import java.util.ArrayList; +24 import java.util.List; +25 +26 public class FilteredFile { +27 +28 private String openToken = "[["; +29 private String closeToken = "]]"; +30 private List<String> lines = new ArrayList<>(); +31 +32 public FilteredFile(InputStream in, VariableResolver resolver) throws IOException { +33 parse(in, resolver); +34 } +35 +36 public void setOpenToken(String token) { +37 openToken = token; +38 } +39 +40 public void setCloseToken(String token) { +41 closeToken = token; +42 } +43 +44 private void parse(InputStream in, VariableResolver resolver) throws IOException { +45 try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { +46 String line; +47 while ((line = reader.readLine()) != null) { +48 if (resolver != null) { +49 lines.add(Utils.replaceVariables(resolver, line, openToken, closeToken)); +50 } else { +51 lines.add(line); +52 } +53 } +54 } +55 } +56 +57 public String toString() { +58 StringBuilder builder = new StringBuilder(); +59 for (String line : lines) { +60 builder.append(line).append('\n'); +61 } +62 return builder.toString(); +63 } +64 } ++
+1 /* +2 * Copyright 2007-2024 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 org.vafer.jdeb.utils; +18 +19 import java.io.FilterInputStream; +20 import java.io.IOException; +21 import java.io.InputStream; +22 import java.util.HashMap; +23 import java.util.Map; +24 +25 public final class InformationInputStream extends FilterInputStream { +26 +27 private long i; +28 private long ascii; +29 private long nonascii; +30 private long cr; +31 private long lf; +32 private long zero; +33 +34 private final Map<BOM, Integer> bomPositions = new HashMap<>(); +35 private final Map<Shell, Integer> shellPositions = new HashMap<>(); +36 +37 /** +38 * Byte Order Marks +39 */ +40 private enum BOM { +41 NONE(null), +42 UTF8("UTF-8", 0xEF, 0xBB, 0xBF), +43 UTF16LE("UTF-16LE", 0xFF, 0xFE), +44 UTF16BE("UTF-16BE", 0xFE, 0xFF); +45 +46 int[] sequence; +47 String encoding; +48 +49 BOM( String encoding, int... sequence ) { +50 this.encoding = encoding; +51 this.sequence = sequence; +52 } +53 } +54 +55 /** +56 * Shebang for shell scripts in various encodings. +57 */ +58 private enum Shell { +59 NONE, +60 ASCII(0x23, 0x21), +61 UTF16BE(0x00, 0x23, 0x00, 0x21), +62 UTF16LE(0x23, 0x00, 0x21, 0x00); +63 +64 int[] header; +65 +66 Shell( int... header ) { +67 this.header = header; +68 } +69 } +70 +71 private BOM bom = BOM.NONE; +72 private Shell shell = Shell.NONE; +73 +74 public InformationInputStream( InputStream in ) { +75 super(in); +76 } +77 +78 public boolean hasBom() { +79 return bom != BOM.NONE; +80 } +81 +82 public boolean isShell() { +83 return shell != Shell.NONE; +84 } +85 +86 public boolean hasUnixLineEndings() { +87 return cr == 0; +88 } +89 +90 public String getEncoding() { +91 String encoding = bom.encoding; +92 +93 if (encoding == null) { +94 // guess the encoding from the shebang +95 if (shell == Shell.UTF16BE) { +96 encoding = BOM.UTF16BE.encoding; +97 } else if (shell == Shell.UTF16LE) { +98 encoding = BOM.UTF16LE.encoding; +99 } +100 } +101 +102 return encoding; +103 } +104 +105 private void add( int c ) { +106 if (i < 10) { +107 if (shell == Shell.NONE) { +108 for (Shell shell : Shell.values()) { +109 int position = shellPositions.containsKey(shell) ? shellPositions.get(shell) : 0; +110 if (position < shell.header.length) { +111 if (c == shell.header[position]) { +112 shellPositions.put(shell, position + 1); +113 } else { +114 shellPositions.put(shell, 0); +115 } +116 } else { +117 this.shell = shell; +118 } +119 } +120 } +121 +122 if (bom == BOM.NONE) { +123 for (BOM bom : BOM.values()) { +124 int position = bomPositions.containsKey(bom) ? bomPositions.get(bom) : 0; +125 if (position < bom.sequence.length) { +126 if (c == bom.sequence[position] && position == i) { +127 bomPositions.put(bom, position + 1); +128 } else { +129 bomPositions.put(bom, 0); +130 } +131 } else { +132 this.bom = bom; +133 } +134 } +135 } +136 } +137 +138 i++; +139 +140 if (c == '\n') { +141 lf++; +142 return; +143 } +144 if (c == '\r') { +145 cr++; +146 return; +147 } +148 if (c >= ' ' && c <= '~') { +149 ascii++; +150 return; +151 } +152 if (c == 0) { +153 zero++; +154 return; +155 } +156 nonascii++; +157 } +158 +159 public int read() throws IOException { +160 int b = super.read(); +161 if (b != -1) { +162 add(b & 0xFF); +163 } +164 return b; +165 } +166 +167 public int read( byte[] b, int off, int len ) throws IOException { +168 int length = super.read(b, off, len); +169 for (int i = 0; i < length; i++) { +170 add(b[off + i] & 0xFF); +171 } +172 return length; +173 } +174 +175 public String toString() { +176 StringBuilder sb = new StringBuilder(); +177 sb.append("{"); +178 sb.append("total=").append(i); +179 sb.append(",noascii=").append(nonascii); +180 sb.append(",ascii=").append(ascii); +181 sb.append(",cr=").append(cr); +182 sb.append(",lf=").append(lf); +183 sb.append(",zero=").append(zero); +184 sb.append("}"); +185 return sb.toString(); +186 } +187 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.utils; +17 +18 import java.io.IOException; +19 import java.io.OutputStream; +20 import java.security.DigestOutputStream; +21 import java.security.MessageDigest; +22 +23 /** +24 * Convenience class to provide digest info and length of a stream. +25 * +26 * ATTENTION: don't use outside of jdeb +27 */ +28 public class InformationOutputStream extends DigestOutputStream { +29 +30 private final MessageDigest digest; +31 private long size; +32 +33 public InformationOutputStream( OutputStream pStream, MessageDigest pDigest ) { +34 super(pStream, pDigest); +35 digest = pDigest; +36 size = 0; +37 } +38 +39 public String getHexDigest() { +40 return Utils.toHex(digest.digest()); +41 } +42 +43 public void write( byte[] b, int off, int len ) throws IOException { +44 super.write(b, off, len); +45 size += len; +46 } +47 +48 public void write( int b ) throws IOException { +49 super.write(b); +50 size++; +51 } +52 +53 public long getSize() { +54 return size; +55 } +56 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.utils; +17 +18 import java.util.Map; +19 +20 /** +21 * Resolve variables based on a Map. +22 * +23 * ATTENTION: don't use outside of jdeb +24 */ +25 public final class MapVariableResolver implements VariableResolver { +26 +27 private final Map<String, String> map; +28 +29 public MapVariableResolver( Map<String, String> map ) { +30 this.map = map; +31 } +32 +33 public String get( String key ) { +34 return map.get(key); +35 } +36 +37 } ++
+1 package org.vafer.jdeb.utils; +2 +3 import java.util.Date; +4 import java.util.concurrent.TimeUnit; +5 +6 import org.apache.maven.archiver.MavenArchiver; +7 import org.vafer.jdeb.Console; +8 +9 public class OutputTimestampResolver { +10 private final Console console; +11 private final EnvironmentVariablesReader envReader; +12 +13 public OutputTimestampResolver(Console console) { +14 this(console, new EnvironmentVariablesReader()); +15 } +16 +17 OutputTimestampResolver(Console console, EnvironmentVariablesReader envReader) { +18 this.console = console; +19 this.envReader = envReader; +20 } +21 +22 public Long resolveOutputTimestamp(String paramValue) { +23 if (paramValue != null) { +24 Date outputDate = new MavenArchiver().parseOutputTimestamp(paramValue); +25 if (outputDate != null) { +26 console.info("Accepted outputTimestamp parameter: " + paramValue); +27 return outputDate.getTime(); +28 } +29 } +30 +31 String sourceDate = envReader.getSourceDateEpoch(); +32 if (sourceDate != null && !sourceDate.isEmpty()) { +33 try { +34 long sourceDateVal = Long.parseLong(sourceDate); +35 console.info("Accepted SOURCE_DATE_EPOCH environment variable: " + sourceDate); +36 return sourceDateVal * TimeUnit.SECONDS.toMillis(1); +37 } catch (NumberFormatException e) { +38 throw new IllegalArgumentException("Invalid SOURCE_DATE_EPOCH environment variable value: " + sourceDate, e); +39 } +40 } +41 +42 return null; +43 } +44 +45 static class EnvironmentVariablesReader { +46 String getSourceDateEpoch() { +47 return System.getenv("SOURCE_DATE_EPOCH"); +48 } +49 } +50 } ++
+1 package org.vafer.jdeb.utils; +2 +3 import java.io.ByteArrayOutputStream; +4 import java.io.IOException; +5 import java.io.OutputStream; +6 +7 import org.bouncycastle.bcpg.ArmoredOutputStream; +8 import org.bouncycastle.openpgp.PGPException; +9 import org.bouncycastle.openpgp.PGPSignature; +10 import org.bouncycastle.openpgp.PGPSignatureGenerator; +11 +12 /** +13 * An output stream that calculates the signature of the input data as it +14 * is written +15 */ +16 public class PGPSignatureOutputStream extends OutputStream { +17 private final PGPSignatureGenerator signatureGenerator; +18 +19 public PGPSignatureOutputStream( PGPSignatureGenerator signatureGenerator ) { +20 super(); +21 this.signatureGenerator = signatureGenerator; +22 } +23 +24 public void write( int b ) throws IOException { +25 signatureGenerator.update(new byte[] { (byte)b }); +26 } +27 +28 public void write( byte[] b ) throws IOException { +29 signatureGenerator.update(b); +30 } +31 +32 public void write( byte[] b, int off, int len ) throws IOException { +33 signatureGenerator.update(b, off, len); +34 } +35 +36 public PGPSignature generateSignature() throws PGPException { +37 return signatureGenerator.generate(); +38 } +39 +40 public String generateASCIISignature() throws PGPException { +41 try { +42 PGPSignature signature = generateSignature(); +43 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); +44 ArmoredOutputStream armorStream = new ArmoredOutputStream(buffer); +45 signature.encode(armorStream); +46 armorStream.close(); +47 return buffer.toString(); +48 } catch(IOException e) { +49 //Should never happen since we are just using a memory buffer +50 throw new RuntimeException(e); +51 } +52 } +53 +54 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.utils; +17 +18 import java.io.File; +19 import java.io.IOException; +20 +21 /** +22 * Simple utilities to deal with symbolic links +23 */ +24 public final class SymlinkUtils { +25 private SymlinkUtils() { +26 } +27 +28 public static boolean isSymbolicLink(final File file) throws IOException { +29 final File canon; +30 if (file.getParent() == null) { +31 canon = file; +32 } else { +33 final File canonDir = file.getParentFile().getCanonicalFile(); +34 canon = new File(canonDir, file.getName()); +35 } +36 return !canon.getCanonicalFile().equals(canon.getAbsoluteFile()); +37 } +38 +39 public static String readSymbolicLink(final File file) throws IOException { +40 return file.getCanonicalFile().getPath(); +41 } +42 +43 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.utils; +17 +18 import java.io.ByteArrayOutputStream; +19 import java.io.File; +20 import java.io.FileNotFoundException; +21 import java.io.IOException; +22 import java.io.InputStream; +23 import java.io.InputStreamReader; +24 import java.io.OutputStream; +25 import java.text.SimpleDateFormat; +26 import java.util.Collection; +27 import java.util.Date; +28 import java.util.Iterator; +29 import java.util.LinkedHashSet; +30 import java.util.Map; +31 import java.util.TimeZone; +32 import java.util.regex.Matcher; +33 import java.util.regex.Pattern; +34 +35 import org.apache.tools.ant.filters.FixCrLfFilter; +36 import org.apache.tools.ant.util.ReaderInputStream; +37 +38 /** +39 * Simple utils functions. +40 * +41 * ATTENTION: don't use outside of jdeb +42 */ +43 public final class Utils { +44 private static final Pattern BETA_PATTERN = Pattern.compile("^(?:(?:(.*?)([.\\-_]))|(.*[^a-z]))(alpha|a|beta|b|milestone|m|cr|rc)([^a-z].*)?$", Pattern.CASE_INSENSITIVE); +45 +46 private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(.*)[\\-+]SNAPSHOT"); +47 +48 public static int copy( final InputStream pInput, final OutputStream pOutput ) throws IOException { +49 final byte[] buffer = new byte[2048]; +50 int count = 0; +51 int n; +52 while (-1 != (n = pInput.read(buffer))) { +53 pOutput.write(buffer, 0, n); +54 count += n; +55 } +56 return count; +57 } +58 +59 public static String toHex( final byte[] bytes ) { +60 final StringBuilder sb = new StringBuilder(); +61 +62 for (byte b : bytes) { +63 sb.append(Integer.toHexString((b >> 4) & 0x0f)); +64 sb.append(Integer.toHexString(b & 0x0f)); +65 } +66 +67 return sb.toString(); +68 } +69 +70 public static String stripPath( final int p, final String s ) { +71 +72 if (p <= 0) { +73 return s; +74 } +75 +76 int x = 0; +77 for (int i = 0; i < p; i++) { +78 x = s.indexOf('/', x + 1); +79 if (x < 0) { +80 return s; +81 } +82 } +83 +84 return s.substring(x + 1); +85 } +86 +87 private static String joinPath(char sep, String ...paths) { +88 final StringBuilder sb = new StringBuilder(); +89 for (String p : paths) { +90 if (p == null) continue; +91 if (!p.startsWith("/") && sb.length() > 0) { +92 sb.append(sep); +93 } +94 sb.append(p); +95 } +96 return sb.toString(); +97 } +98 +99 public static String joinUnixPath(String ...paths) { +100 return joinPath('/', paths); +101 } +102 +103 public static String joinLocalPath(String ...paths) { +104 return joinPath(File.separatorChar, paths); +105 } +106 +107 public static String stripLeadingSlash( final String s ) { +108 if (s == null) { +109 return s; +110 } +111 if (s.length() == 0) { +112 return s; +113 } +114 if (s.charAt(0) == '/' || s.charAt(0) == '\\') { +115 return s.substring(1); +116 } +117 return s; +118 } +119 +120 /** +121 * Substitute the variables in the given expression with the +122 * values from the resolver +123 * +124 * @param pResolver +125 * @param pExpression +126 */ +127 public static String replaceVariables( final VariableResolver pResolver, final String pExpression, final String pOpen, final String pClose ) { +128 final char[] open = pOpen.toCharArray(); +129 final char[] close = pClose.toCharArray(); +130 +131 final StringBuilder out = new StringBuilder(); +132 StringBuilder sb = new StringBuilder(); +133 char[] last = null; +134 int wo = 0; +135 int wc = 0; +136 int level = 0; +137 for (char c : pExpression.toCharArray()) { +138 if (c == open[wo]) { +139 if (wc > 0) { +140 sb.append(close, 0, wc); +141 } +142 wc = 0; +143 wo++; +144 if (open.length == wo) { +145 // found open +146 if (last == open) { +147 out.append(open); +148 } +149 level++; +150 out.append(sb); +151 sb = new StringBuilder(); +152 wo = 0; +153 last = open; +154 } +155 } else if (c == close[wc]) { +156 if (wo > 0) { +157 sb.append(open, 0, wo); +158 } +159 wo = 0; +160 wc++; +161 if (close.length == wc) { +162 // found close +163 if (last == open) { +164 final String variable = pResolver.get(sb.toString()); +165 if (variable != null) { +166 out.append(variable); +167 } else { +168 out.append(open); +169 out.append(sb); +170 out.append(close); +171 } +172 } else { +173 out.append(sb); +174 out.append(close); +175 } +176 sb = new StringBuilder(); +177 level--; +178 wc = 0; +179 last = close; +180 } +181 } else { +182 +183 if (wo > 0) { +184 sb.append(open, 0, wo); +185 } +186 +187 if (wc > 0) { +188 sb.append(close, 0, wc); +189 } +190 +191 sb.append(c); +192 +193 wo = wc = 0; +194 } +195 } +196 +197 if (wo > 0) { +198 sb.append(open, 0, wo); +199 } +200 +201 if (wc > 0) { +202 sb.append(close, 0, wc); +203 } +204 +205 if (level > 0) { +206 out.append(open); +207 } +208 out.append(sb); +209 +210 return out.toString(); +211 } +212 +213 /** +214 * Replaces new line delimiters in the input stream with the Unix line feed. +215 * +216 * @param input +217 */ +218 public static byte[] toUnixLineEndings( InputStream input ) throws IOException { +219 String encoding = "ISO-8859-1"; +220 FixCrLfFilter filter = new FixCrLfFilter(new InputStreamReader(input, encoding)); +221 filter.setEol(FixCrLfFilter.CrLf.newInstance("unix")); +222 +223 ByteArrayOutputStream filteredFile = new ByteArrayOutputStream(); +224 Utils.copy(new ReaderInputStream(filter, encoding), filteredFile); +225 +226 return filteredFile.toByteArray(); +227 } +228 +229 private static String formatSnapshotTemplate( String template, Date timestamp ) { +230 int startBracket = template.indexOf('['); +231 int endBracket = template.indexOf(']'); +232 if(startBracket == -1 || endBracket == -1) { +233 return template; +234 } else { +235 // prefix[yyMMdd]suffix +236 final String date = new SimpleDateFormat(template.substring(startBracket + 1, endBracket)).format(timestamp); +237 String datePrefix = startBracket == 0 ? "" : template.substring(0, startBracket); +238 String dateSuffix = endBracket == template.length() ? "" : template.substring(endBracket + 1); +239 return datePrefix + date + dateSuffix; +240 } +241 } +242 +243 /** +244 * Convert the project version to a version suitable for a Debian package. +245 * -SNAPSHOT suffixes are replaced with a timestamp (~yyyyMMddHHmmss). +246 * The separator before a rc, alpha or beta version is replaced with '~' +247 * such that the version is always ordered before the final or GA release. +248 * +249 * @param version the project version to convert to a Debian package version +250 * @param template the template used to replace -SNAPSHOT, the timestamp format is in brackets, +251 * the rest of the string is preserved (prefix[yyMMdd]suffix -> prefix151230suffix) +252 * @param timestamp the UTC date used as the timestamp to replace the SNAPSHOT suffix. +253 */ +254 public static String convertToDebianVersion( String version, boolean apply, String envName, String template, Date timestamp ) { +255 Matcher matcher = SNAPSHOT_PATTERN.matcher(version); +256 if (matcher.matches()) { +257 version = matcher.group(1) + "~"; +258 +259 if (apply) { +260 final String envValue = System.getenv(envName); +261 if(template != null && template.length() > 0) { +262 version += formatSnapshotTemplate(template, timestamp); +263 } else if (envValue != null && envValue.length() > 0) { +264 version += envValue; +265 } else { +266 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); +267 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); +268 version += dateFormat.format(timestamp); +269 } +270 } else { +271 version += "SNAPSHOT"; +272 } +273 } else { +274 matcher = BETA_PATTERN.matcher(version); +275 if (matcher.matches()) { +276 if (matcher.group(1) != null) { +277 version = matcher.group(1) + "~" + matcher.group(4) + matcher.group(5); +278 } else { +279 version = matcher.group(3) + "~" + matcher.group(4) + matcher.group(5); +280 } +281 } +282 } +283 +284 // safest upstream_version should only contain full stop, plus, tilde, and alphanumerics +285 // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version +286 version = version.replaceAll("[^.+~A-Za-z0-9]", "+").replaceAll("\\++", "+"); +287 +288 return version; +289 } +290 +291 /** +292 * Construct new path by replacing file directory part. No +293 * files are actually modified. +294 * @param file path to move +295 * @param target new path directory +296 */ +297 public static String movePath( final String file, +298 final String target ) { +299 final String name = new File(file).getName(); +300 return target.endsWith("/") ? target + name : target + '/' + name; +301 } +302 +303 /** +304 * Extracts value from map if given value is null. +305 * @param value current value +306 * @param props properties to extract value from +307 * @param key property name to extract +308 * @return initial value or value extracted from map +309 */ +310 public static String lookupIfEmpty( final String value, +311 final Map<String, String> props, +312 final String key ) { +313 return value != null ? value : props.get(key); +314 } +315 +316 /** +317 * Get the known locations where the secure keyring can be located. +318 * Looks through known locations of the GNU PG secure keyring. +319 * +320 * @return The location of the PGP secure keyring if it was found, +321 * null otherwise +322 */ +323 public static Collection<String> getKnownPGPSecureRingLocations() { +324 final LinkedHashSet<String> locations = new LinkedHashSet<>(); +325 +326 final String os = System.getProperty("os.name"); +327 final boolean runOnWindows = os == null || os.toLowerCase().contains("win"); +328 +329 if (runOnWindows) { +330 // The user's roaming profile on Windows, via environment +331 final String windowsRoaming = System.getenv("APPDATA"); +332 if (windowsRoaming != null) { +333 locations.add(joinLocalPath(windowsRoaming, "gnupg", "secring.gpg")); +334 } +335 +336 // The user's local profile on Windows, via environment +337 final String windowsLocal = System.getenv("LOCALAPPDATA"); +338 if (windowsLocal != null) { +339 locations.add(joinLocalPath(windowsLocal, "gnupg", "secring.gpg")); +340 } +341 +342 // The Windows installation directory +343 final String windir = System.getProperty("WINDIR"); +344 if (windir != null) { +345 // Local Profile on Windows 98 and ME +346 locations.add(joinLocalPath(windir, "Application Data", "gnupg", "secring.gpg")); +347 } +348 } +349 +350 final String home = System.getProperty("user.home"); +351 +352 if (home != null && runOnWindows) { +353 // These are for various flavours of Windows +354 // if the environment variables above have failed +355 +356 // Roaming profile on Vista and later +357 locations.add(joinLocalPath(home, "AppData", "Roaming", "gnupg", "secring.gpg")); +358 // Local profile on Vista and later +359 locations.add(joinLocalPath(home, "AppData", "Local", "gnupg", "secring.gpg")); +360 // Roaming profile on 2000 and XP +361 locations.add(joinLocalPath(home, "Application Data", "gnupg", "secring.gpg")); +362 // Local profile on 2000 and XP +363 locations.add(joinLocalPath(home, "Local Settings", "Application Data", "gnupg", "secring.gpg")); +364 } +365 +366 // *nix, including OS X +367 if (home != null) { +368 locations.add(joinLocalPath(home, ".gnupg", "secring.gpg")); +369 } +370 +371 return locations; +372 } +373 +374 /** +375 * Tries to guess location of the user secure keyring using various +376 * heuristics. +377 * +378 * @return path to the keyring file +379 * @throws FileNotFoundException if no keyring file found +380 */ +381 public static File guessKeyRingFile() throws FileNotFoundException { +382 final Collection<String> possibleLocations = getKnownPGPSecureRingLocations(); +383 for (final String location : possibleLocations) { +384 final File candidate = new File(location); +385 if (candidate.exists()) { +386 return candidate; +387 } +388 } +389 final StringBuilder message = new StringBuilder("Could not locate secure keyring, locations tried: "); +390 final Iterator<String> it = possibleLocations.iterator(); +391 while (it.hasNext()) { +392 message.append(it.next()); +393 if (it.hasNext()) { +394 message.append(", "); +395 } +396 } +397 throw new FileNotFoundException(message.toString()); +398 } +399 +400 /** +401 * Returns true if string is null or empty. +402 */ +403 public static boolean isNullOrEmpty(final String str) { +404 return str == null || str.length() == 0; +405 } +406 +407 /** +408 * Return fallback if first string is null or empty +409 */ +410 public static String defaultString(final String str, final String fallback) { +411 return isNullOrEmpty(str) ? fallback : str; +412 } +413 +414 +415 /** +416 * Check if a CharSequence is whitespace, empty ("") or null. +417 * +418 * <pre> +419 * StringUtils.isBlank(null) = true +420 * StringUtils.isBlank("") = true +421 * StringUtils.isBlank(" ") = true +422 * StringUtils.isBlank("bob") = false +423 * StringUtils.isBlank(" bob ") = false +424 * </pre> +425 * +426 * @param cs +427 * the CharSequence to check, may be null +428 * @return {@code true} if the CharSequence is null, empty or whitespace +429 */ +430 public static boolean isBlank(final CharSequence cs) { +431 int strLen; +432 if (cs == null || (strLen = cs.length()) == 0) { +433 return true; +434 } +435 for (int i = 0; i < strLen; i++) { +436 if (!Character.isWhitespace(cs.charAt(i))) { +437 return false; +438 } +439 } +440 return true; +441 } +442 } ++
+1 /* +2 * Copyright 2007-2024 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 package org.vafer.jdeb.utils; +17 +18 /** +19 * Interface for resolving variables. +20 * See Utils.replaceVariables() +21 * +22 * ATTENTION: don't use outside of jdeb +23 */ +24 +25 public interface VariableResolver { +26 String get( final String pKey ); +27 } ++
Class | +
---|
+ FilteredFile + | +
+ InformationInputStream + | +
+ InformationInputStream.BOM + | +
+ InformationInputStream.Shell + | +
+ InformationOutputStream + | +
+ MapVariableResolver + | +
+ OutputTimestampResolver + | +
+ OutputTimestampResolver.EnvironmentVariablesReader + | +
+ PGPSignatureOutputStream + | +
+ SymlinkUtils + | +
+ Utils + | +
+ VariableResolver + | +
+ + + diff --git a/release/1.11/xref/overview-summary.html b/release/1.11/xref/overview-summary.html new file mode 100644 index 000000000..9275aa053 --- /dev/null +++ b/release/1.11/xref/overview-summary.html @@ -0,0 +1,132 @@ + + + + + + +
Package | +
---|
+ org.vafer.jdeb + | +
+ org.vafer.jdeb.ant + | +
+ org.vafer.jdeb.changes + | +
+ org.vafer.jdeb.debian + | +
+ org.vafer.jdeb.mapping + | +
+ org.vafer.jdeb.maven + | +
+ org.vafer.jdeb.producers + | +
+ org.vafer.jdeb.signing + | +
+ org.vafer.jdeb.utils + | +