#!/usr/bin/env ruby
# frozen_string_literal: true

# Ruby internal
# Project internal
require 'haiti'
# External
require 'docopt'
require 'paint'

# NOTE: `haiti [options] <hash>` needs to be after commands, else their names are caught as hashes
doc = <<~DOCOPT
  #{Paint['HAITI (HAsh IdenTifIer)', '#FF69B4']} v#{Paint[HashIdentifier::VERSION, :bold]}

  #{Paint['Usage:', '#00FFFF']}
    haiti [options] list
    haiti samples (<ref> | <name>)
    haiti [options] file <file>
    haiti [options] <hash>
    haiti --ascii-art
    haiti -h | --help
    haiti --version

  #{Paint['Commands:', '#00FFFF']}
    samples         Display hash samples for the given type
    list            Display a list of all the available hash types
    file            Identify hashes from a file, one hash per line

  #{Paint['Parameters:', '#00FFFF']}
    <hash>          Hash string to identify, read from STDIN if equal to "-"
    <file>          File containing one hash per line
    <ref>           hashcat or john the ripper reference
    <name>          Hash type name

  #{Paint['Options:', '#00FFFF']}
    --no-color        Disable colorized output (NO_COLOR environment variable is respected too)
    -e, --extended    List all possible hash algorithms including ones using salt
    --short           Display in a short format: do not display hashcat and john the ripper references
    --hashcat-only    Show only hashcat references
    --john-only       Show only john the ripper references
    --ascii-art       Display the logo in colored ascii-art
    --debug           Display arguments
    -h, --help        Show this screen
    --version         Show version

  #{Paint['Examples:', '#00FFFF']}
    haiti -e d41d8cd98f00b204e9800998ecf8427e
    haiti --no-color --short d41d8cd98f00b204e9800998ecf8427e
    b2sum /etc/os-release | awk '{print $1}' | haiti -
    haiti file hashes.txt
    haiti samples crc32

  #{Paint['Project:', '#00FFFF']}
    #{Paint['author', :underline]} (https://pwn.by/noraj / https://twitter.com/noraj_rawsec)
    #{Paint['source', :underline]} (https://github.com/noraj/haiti)
    #{Paint['documentation', :underline]} (https://noraj.github.io/haiti)
DOCOPT

# Shared option management logic for the identify and list command
def manage_options(args, types)
  types.each do |type|
    next if type.extended && !args['--extended']

    print Paint[type.name, :bold]
    print Paint[" [HC: #{type.hashcat}]", '#00FFFF'] unless type.hashcat.nil? || args['--short'] || args['--john-only']
    print Paint[" [JtR: #{type.john}]", '#FF69B4'] unless type.john.nil? || args['--short'] || args['--hashcat-only']
    puts
  end
end

def identify_hash(args, hash)
  hi = HashIdentifier.new(hash)
  if hi.type.empty?
    puts 'Unknown hash type'
  else
    manage_options(args, hi.type)
  end
end

begin
  args = Docopt.docopt(doc, version: HashIdentifier::VERSION)
  Paint.mode = 0 if args['--no-color']
  puts args if args['--debug']
  # use case 1, using the tool
  if args['file']
    File.foreach(args['<file>']) do |line|
      hash = line.chomp
      next if hash.empty?

      puts "Hash: #{hash}"
      identify_hash(args, hash)
      puts
    end
  elsif args['<hash>']
    args['<hash>'] = $stdin.read.chomp if args['<hash>'] == '-'
    identify_hash(args, args['<hash>'])
  elsif args['samples']
    input = args['<ref>'] || args['<name>']
    samples = HashIdentifier.samples(input)
    samples.each do |sample|
      puts sample
    end
  elsif args['list']
    types = HashIdentifier.object_list
    manage_options(args, types)
  elsif args['--ascii-art']
    puts File.read(File.join(__dir__, '../docs/_media/logo.ascii'))
  end
  # use case 2, help: already handled by docopt
  # use case 3, version: already handled by docopt
rescue Docopt::Exit => e
  puts e.message
end
