package org.codehaus.mojo.unix.rpm; /* * The MIT License * * Copyright 2009 The Codehaus. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import static fj.data.Option.*; import org.codehaus.mojo.unix.*; import static org.codehaus.mojo.unix.FileAttributes.*; import static org.codehaus.mojo.unix.UnixFsObject.*; import org.codehaus.mojo.unix.util.*; import static org.codehaus.mojo.unix.util.RelativePath.*; import org.codehaus.mojo.unix.util.line.*; import org.joda.time.*; import java.io.*; import static java.lang.Long.*; import java.text.*; import java.util.*; /** * @author <a href="mailto:trygvis@codehaus.org">Trygve Laugstøl</a> * @version $Id$ */ public class RpmUtil { public static final SimpleDateFormat DATE_FORMAT_SHORTER = new SimpleDateFormat( "MMM dd HH:mm" ); public static final SimpleDateFormat DATE_FORMAT_LONGER = new SimpleDateFormat( "MMM dd yyyy" ); public static final class FileInfo implements EqualsIgnoreNull<FileInfo>, LineProducer { public final String path; public final String user; public final String group; public final String mode; public final int size; public final Date date; public FileInfo( String path, String user, String group, String mode, int size, Date date ) { this.path = path; this.user = user; this.group = group; this.mode = mode; this.size = size; this.date = date; } public boolean equalsIgnoreNull( FileInfo that ) { return size == that.size && ( date == null || date.equals( that.date ) ) && group.equals( that.group ) && mode.equals( that.mode ) && path.equals( that.path ) && user.equals( that.user ); } public final boolean equals( Object o ) { if ( this == o ) { return true; } if ( !( o instanceof FileInfo ) ) { return false; } FileInfo that = (FileInfo) o; return path.equals( that.path ); } public final int hashCode() { return path.hashCode(); } public void streamTo( LineStreamWriter stream ) { stream.add( new StringBuffer(). append( mode ).append( " " ). append( user ).append( " " ). append( group ).append( " " ). append( size ).append( " " ). append( date != null ? DATE_FORMAT_SHORTER.format( date ) : "<not set>" ).append( " " ). append( path ).toString() ); } } public static List<FileInfo> queryPackageForFileInfo( File rpm ) throws IOException { RpmQueryParser parser = new RpmQueryParser(); new SystemCommand(). withStdoutConsumer( parser ). setCommand( "rpm" ). addArgument( "--package" ). addArgument( rpm.getAbsolutePath() ). addArgument( "-q" ). addArgument( "-l" ). addArgument( "-v" ). execute(). assertSuccess(); return parser.getList(); } public static SpecFile getSpecFileFromRpm( File rpm ) throws IOException { // rpm -q --queryformat "%{NAME}\n%{SIZE}" -p ./unix-maven-plugin/src/it/jetty/target/jetty-1.1-2.rpm SpecFile specFile = new SpecFile(); RpmSpecParser parser = new RpmSpecParser( specFile ); new SystemCommand(). dumpCommandIf( true ). withStdoutConsumer( parser ). withStderrConsumer( System.err ). setCommand( "rpm" ). addArgument( "--query" ). addArgument( "--queryformat" ). addArgument( "%{NAME}\\n%{VERSION}\\n%{RELEASE}\\n%{SUMMARY}\\n%{LICENSE}\\n%{GROUP}\\n" ). addArgument( "--package" ). addArgument( rpm.getAbsolutePath() ). execute(). assertSuccess(); ByteArrayOutputStream description = new ByteArrayOutputStream(); new SystemCommand(). dumpCommandIf( true ). withStdoutConsumer( description ). withStderrConsumer( System.err ). setCommand( "rpm" ). addArgument( "--query" ). addArgument( "--queryformat" ). addArgument( "%{DESCRIPTION}" ). addArgument( "--package" ). addArgument( rpm.getAbsolutePath() ). execute(). assertSuccess(); RpmDumpParser rpmDumpParser = new RpmDumpParser( specFile ); new SystemCommand(). dumpCommandIf( true ). withStdoutConsumer( rpmDumpParser ). setCommand( "rpm" ). addArgument( "--query" ). addArgument( "--dump" ). addArgument( "--package" ). addArgument( rpm.getAbsolutePath() ). execute(). assertSuccess(); specFile.description = description.toString(); return specFile; } private static class RpmQueryParser implements SystemCommand.LineConsumer { private List<FileInfo> list = new ArrayList<FileInfo>(); public void onLine( String line ) throws IOException { String[] parts = line.replaceAll( " +", " " ).split( " " ); // Dunno what the second element is Date date; try { date = DATE_FORMAT_SHORTER.parse( parts[5] + " " + parts[6] + " " + parts[7] ); } catch ( ParseException e ) { try { date = DATE_FORMAT_LONGER.parse( parts[5] + " " + parts[6] + " " + parts[7] ); } catch ( Exception e2 ) { e2.printStackTrace(); return; } } list.add( new FileInfo( parts[8].trim(), parts[2].trim(), parts[3].trim(), parts[0].trim(), Integer.parseInt( parts[4].trim() ), date ) ); } public List<FileInfo> getList() { return list; } } private static class RpmSpecParser implements SystemCommand.LineConsumer { private final SpecFile specFile; private int count; public RpmSpecParser( SpecFile specFile ) { this.specFile = specFile; } public void onLine( String line ) throws IOException { switch ( count++ ) { case 0: specFile.name = line; break; case 1: specFile.version = line; break; case 2: specFile.release = line; break; case 3: specFile.summary = line; break; case 4: specFile.license = line; break; case 5: specFile.group = line; break; } } } public static class RpmDumpParser implements SystemCommand.LineConsumer { private final SpecFile specFile; public RpmDumpParser( SpecFile specFile ) { this.specFile = specFile; } public void onLine( String line ) { // Each of these lines look like this: // /etc/openldap/schema/dnszone.schema 5114 1232540847 2294a352407600431f736427587345e2 0100644 root root 1 0 0 X // path size time md5 mode user group config? doc? wtf? symlink (not "X" if symlink) // 0 1 2 3 4 5 6 7 8 9 10 String[] parts = line.split( " " ); RelativePath path = relativePath( parts[0] ); long size = parseLong( parts[1] ); LocalDateTime lastModified = new LocalDateTime( parseLong( parts[2] ) ); // #4 is the md5 int mode = Integer.parseInt( parts[4], 8 ); FileAttributes attributes = EMPTY. user( parts[5] ). group( parts[6] ). mode( UnixFileMode.fromInt( mode ) ); if ( "1".equals( parts[7] ) ) { attributes = attributes.addTag( "config" ); } if ( "1".equals( parts[8] ) ) { attributes = attributes.addTag( "doc" ); } if ( (mode & 0x4000) != 0 ) { specFile.addDirectory( directory( path, lastModified, attributes ) ); } else { if ( "X".equals( parts[10] ) ) { specFile.addFile( regularFile( path, lastModified, size, some( attributes ) ) ); } else { specFile.addSymlink( symlink( path, lastModified, some( attributes ), parts[10] ) ); } } } } }