001package org.apache.maven.plugins.enforcer;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.IOException;
025import java.io.InputStream;
026
027import org.apache.commons.codec.digest.DigestUtils;
028import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
029import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
030
031/**
032 * Rule to validate a binary file to match the specified checksum.
033 *
034 * @author Edward Samson
035 * @author Lyubomyr Shaydariv
036 * @see RequireTextFileChecksum
037 */
038public class RequireFileChecksum
039    extends AbstractNonCacheableEnforcerRule
040{
041
042    protected File file;
043
044    private String checksum;
045
046    private String type;
047
048    private String nonexistentFileMessage;
049
050    @Override
051    public void execute( EnforcerRuleHelper helper )
052        throws EnforcerRuleException
053    {
054        if ( this.file == null )
055        {
056            throw new EnforcerRuleException( "Input file unspecified" );
057        }
058
059        if ( this.type == null )
060        {
061            throw new EnforcerRuleException( "Hash type unspecified" );
062        }
063
064        if ( this.checksum == null )
065        {
066            throw new EnforcerRuleException( "Checksum unspecified" );
067        }
068
069        if ( !this.file.exists() )
070        {
071            String message = nonexistentFileMessage;
072            if ( message == null )
073            {
074                message = "File does not exist: " + this.file.getAbsolutePath();
075            }
076            throw new EnforcerRuleException( message );
077        }
078
079        if ( this.file.isDirectory() )
080        {
081            throw new EnforcerRuleException( "Cannot calculate the checksum of directory: "
082                + this.file.getAbsolutePath() );
083        }
084
085        if ( !this.file.canRead() )
086        {
087            throw new EnforcerRuleException( "Cannot read file: " + this.file.getAbsolutePath() );
088        }
089
090        String checksum = calculateChecksum();
091
092        if ( !checksum.equalsIgnoreCase( this.checksum ) )
093        {
094            String exceptionMessage = getMessage();
095            if ( exceptionMessage == null )
096            {
097                exceptionMessage = this.type + " hash of " + this.file + " was " + checksum
098                    + " but expected " + this.checksum;
099            }
100            throw new EnforcerRuleException( exceptionMessage );
101        }
102    }
103
104    /**
105     * The file to check.
106     *
107     * @param file file
108     */
109    public void setFile( File file )
110    {
111        this.file = file;
112    }
113
114    /**
115     * The expected checksum value.
116     *
117     * @param checksum checksum
118     */
119    public void setChecksum( String checksum )
120    {
121        this.checksum = checksum;
122    }
123
124    /**
125     * The checksum algorithm to use. Possible values: "md5", "sha1", "sha256", "sha384", "sha512".
126     *
127     * @param type algorithm
128     */
129    public void setType( String type )
130    {
131        this.type = type;
132    }
133
134    /**
135     * The friendly message to use when the file does not exist.
136     *
137     * @param nonexistentFileMessage message
138     */
139    public void setNonexistentFileMessage( String nonexistentFileMessage )
140    {
141        this.nonexistentFileMessage = nonexistentFileMessage;
142    }
143
144    protected String calculateChecksum()
145        throws EnforcerRuleException
146    {
147        try ( InputStream inputStream = new FileInputStream( this.file ) )
148        {
149            return calculateChecksum( inputStream );
150        }
151        catch ( IOException e )
152        {
153            throw new EnforcerRuleException( "Unable to calculate checksum", e );
154        }
155    }
156
157    protected String calculateChecksum( InputStream inputStream )
158        throws IOException, EnforcerRuleException
159    {
160        String checksum;
161        if ( "md5".equals( this.type ) )
162        {
163            checksum = DigestUtils.md5Hex( inputStream );
164        }
165        else if ( "sha1".equals( this.type ) )
166        {
167            checksum = DigestUtils.sha1Hex( inputStream );
168        }
169        else if ( "sha256".equals( this.type ) )
170        {
171            checksum = DigestUtils.sha256Hex( inputStream );
172        }
173        else if ( "sha384".equals( this.type ) )
174        {
175            checksum = DigestUtils.sha384Hex( inputStream );
176        }
177        else if ( "sha512".equals( this.type ) )
178        {
179            checksum = DigestUtils.sha512Hex( inputStream );
180        }
181        else
182        {
183            throw new EnforcerRuleException( "Unsupported hash type: " + this.type );
184        }
185        return checksum;
186    }
187}