Digest.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.codec.cli;

  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.nio.charset.Charset;
  21. import java.security.MessageDigest;
  22. import java.util.Arrays;
  23. import java.util.Locale;
  24. import java.util.Objects;

  25. import org.apache.commons.codec.binary.Hex;
  26. import org.apache.commons.codec.digest.DigestUtils;
  27. import org.apache.commons.codec.digest.MessageDigestAlgorithms;

  28. /**
  29.  * A minimal command line to run digest over files, directories or a string.
  30.  *
  31.  * @see #main(String[])
  32.  * @since 1.11
  33.  */
  34. public class Digest {

  35.     /**
  36.      * Runs the digest algorithm in {@code args[0]} on the file in {@code args[1]}. If there is no {@code args[1]}, use
  37.      * standard input.
  38.      *
  39.      * <p>
  40.      * The algorithm can also be {@code ALL} or {@code *} to output one line for each known algorithm.
  41.      * </p>
  42.      *
  43.      * @param args
  44.      *            {@code args[0]} is one of {@link MessageDigestAlgorithms} name,
  45.      *            {@link MessageDigest} name, {@code ALL}, or {@code *}.
  46.      *            {@code args[1+]} is a FILE/DIRECTORY/String.
  47.      * @throws IOException if an error occurs
  48.      */
  49.     public static void main(final String[] args) throws IOException {
  50.         new Digest(args).run();
  51.     }

  52.     private final String algorithm;
  53.     private final String[] args;
  54.     private final String[] inputs;

  55.     private Digest(final String[] args) {
  56.         Objects.requireNonNull(args);
  57.         final int argsLength = args.length;
  58.         if (argsLength == 0) {
  59.             throw new IllegalArgumentException(
  60.                     String.format("Usage: java %s [algorithm] [FILE|DIRECTORY|string] ...", Digest.class.getName()));
  61.         }
  62.         this.args = args;
  63.         this.algorithm = args[0];
  64.         if (argsLength <= 1) {
  65.             this.inputs = null;
  66.         } else {
  67.             this.inputs = Arrays.copyOfRange(args, 1, argsLength);
  68.         }
  69.     }

  70.     private void println(final String prefix, final byte[] digest) {
  71.         println(prefix, digest, null);
  72.     }

  73.     private void println(final String prefix, final byte[] digest, final String fileName) {
  74.         // The standard appears to be to print
  75.         // hex, space, then either space or '*' followed by file name
  76.         // where '*' is used for binary files
  77.         // shasum(1) has a -b option which generates " *" separator
  78.         // we don't distinguish binary files at present
  79.         System.out.println(prefix + Hex.encodeHexString(digest) + (fileName != null ? "  " + fileName : ""));
  80.     }

  81.     private void run() throws IOException {
  82.         if (algorithm.equalsIgnoreCase("ALL") || algorithm.equals("*")) {
  83.             run(MessageDigestAlgorithms.values());
  84.             return;
  85.         }
  86.         final MessageDigest messageDigest = DigestUtils.getDigest(algorithm, null);
  87.         if (messageDigest != null) {
  88.             run("", messageDigest);
  89.         } else {
  90.             run("", DigestUtils.getDigest(algorithm.toUpperCase(Locale.ROOT)));
  91.         }
  92.     }

  93.     private void run(final String prefix, final MessageDigest messageDigest) throws IOException {
  94.         if (inputs == null) {
  95.             println(prefix, DigestUtils.digest(messageDigest, System.in));
  96.             return;
  97.         }
  98.         for (final String source : inputs) {
  99.             final File file = new File(source);
  100.             if (file.isFile()) {
  101.                 println(prefix, DigestUtils.digest(messageDigest, file), source);
  102.             } else if (file.isDirectory()) {
  103.                 final File[] listFiles = file.listFiles();
  104.                 if (listFiles != null) {
  105.                     run(prefix, messageDigest, listFiles);
  106.                 }
  107.             } else {
  108.                 // use the default charset for the command-line parameter
  109.                 final byte[] bytes = source.getBytes(Charset.defaultCharset());
  110.                 println(prefix, DigestUtils.digest(messageDigest, bytes));
  111.             }
  112.         }
  113.     }

  114.     private void run(final String prefix, final MessageDigest messageDigest, final File[] files) throws IOException {
  115.         for (final File file : files) {
  116.             if (file.isFile()) {
  117.                 println(prefix, DigestUtils.digest(messageDigest, file), file.getName());
  118.             }
  119.         }
  120.     }

  121.     private void run(final String prefix, final String messageDigestAlgorithm) throws IOException {
  122.         run(prefix, DigestUtils.getDigest(messageDigestAlgorithm));
  123.     }

  124.     private void run(final String[] digestAlgorithms) throws IOException {
  125.         for (final String messageDigestAlgorithm : digestAlgorithms) {
  126.             if (DigestUtils.isAvailable(messageDigestAlgorithm)) {
  127.                 run(messageDigestAlgorithm + " ", messageDigestAlgorithm);
  128.             }
  129.         }
  130.     }

  131.     @Override
  132.     public String toString() {
  133.         return String.format("%s %s", super.toString(), Arrays.toString(args));
  134.     }
  135. }