#
# Licensed to the Apache Software Foundation (ASF) under one   
# or more contributor license agreements.  See the NOTICE file 
# distributed with this work for additional information        
# regarding copyright ownership.  The ASF licenses this file   
# to you under the Apache License, Version 2.0 (the            
# "License"); you may not use this file except in compliance   
# with the License.  You may obtain a copy of the License at   
#                                                              
#   http://www.apache.org/licenses/LICENSE-2.0                 
#                                                              
# Unless required by applicable law or agreed to in writing,   
# software distributed under the License is distributed on an  
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       
# KIND, either express or implied.  See the License for the    
# specific language governing permissions and limitations      
# under the License.                                           
#

from sys import argv
from os import popen
from os import popen4
from os import spawnl
from os.path import isdir
from os.path import walk
from os.path import split
from os.path import splitext
from os.path import exists
from tempfile import mkdtemp
from os.path import join
from os import listdir
from os import rmdir
from os import makedirs
from shutil import rmtree
import md5
import sha

class UserAbortError(Exception):
    def __str__(self):
        return "Aborted by user. Bye."

def matchExtension(file, dir, extension):
    matches = []
    extension = '.' + extension.lower()
    for element in listdir(dir):
        root, ext = splitext(element)
        if extension == ext.lower() and root == file:
            matches.append(element)
    return matches

def printMatches(file, dir, extension):
    matches = matchExtension(file, dir, extension)
    for match in matches:
        matchFile = open(join(dir,match), 'r')
        text = matchFile.read(); 
        matchFile.close()
        print text
    

def untgz(file, dir):
    popen("tar -xzf " + file + " -C " + dir)
    
def unzip(file, dir):
    popen("unzip -d " + dir + " " + file)

def checkSums(filename, dir):
    file = open(join(dir, filename), 'r')
    contents = file.read()
    file.close()
    md5sum = md5.new()
    md5sum.update(contents)
    print "MD5:"
    print md5sum.hexdigest()
    printMatches(filename, dir, "MD5")
    shaSum = sha.new()
    shaSum.update(contents)
    print "SHA1:"
    print shaSum.hexdigest()
    printMatches(filename, dir, "SHA")
    printMatches(filename, dir, "SHA1")

def checkSignature(filename, dir, gpgCommand):
    file = join(dir, filename)
    signatureFile = file + '.asc'
    if not(exists(signatureFile)):
       signatureFile = file + '.sig'
       if not(exists(signatureFile)):
           print "SIGNATURE IS MISSING"
    command = gpgCommand + ' --verify ' + signatureFile + ' ' + file
    input, output = popen4(command)
    print output.read()

def rat(file, dir, uncompress, ratCommand, baseReportDirectory):
    if baseReportDirectory == None:
        reportdir = mkdtemp()
    else:
        reportdir = join(baseReportDirectory, file.replace('.', '_'))
        if exists(reportdir):
           rmtree(reportdir) 
        makedirs(reportdir)
            
    uncompress(join(dir, file), reportdir)
    command = ratCommand + " " + reportdir
    input, output = popen4(command)
    out = output.read()
    if not baseReportDirectory == None:
        f = open(join(baseReportDirectory, file + '.rat'), 'w')
        f.write(out)
        f.close()
    print out

def checkArchive(file, dir, uncompress, ratCommand, gpgCommand, baseReportDirectory):
    print "."
    print "."
    print "."
    print "###################################################"
    print "Checking ", file
    checkSums(file, dir)
    checkSignature(file, dir, gpgCommand)
    rat(file, dir, uncompress, ratCommand, baseReportDirectory)
    print "###################################################"
    print "."
    print "."
    print "."
        
def checkFile(file, dir, ratCommand, gpgCommand, baseReportDirectory):
    '''Checks the given file'''
    if file.endswith('.jar'):
        print "Checking jar ", file
        checkArchive(file, dir, lambda a,b:unzip(a,b), ratCommand, gpgCommand, baseReportDirectory)
    elif file.endswith('.zip'):
        print "Checking zip ", file
        checkArchive(file, dir, lambda a,b:unzip(a,b), ratCommand, gpgCommand, baseReportDirectory)
    elif file.endswith('.tar.gz') or file.endswith('.tgz'):
        print "Checking tgz ", file
        checkArchive(file, dir, lambda a,b:untgz(a,b), ratCommand, gpgCommand, baseReportDirectory)

def ignoreDirectory(dir):
    '''Is this directory to be ignored?'''
    if dir == "":
        return False
    else:
        head, tail = split(dir)
        if tail.startswith('.') and not(tail.startswith('..')):
            return True
        elif tail == "":
            return False
        else:
            return ignoreDirectory(head)

def checkDirectory(ratCommand, gpgCommand, dir, files, baseReportDir, check=lambda a,b,c,d: lambda x: checkFile(x,a,b,c,d)):
    '''Checks a directory'''
    if not(ignoreDirectory(dir)):
       print 'Checking directory ', dir
       map(check(dir, ratCommand, gpgCommand, baseReportDir), files)

def walkDirectories(base, ratCommand, gpgCommand, baseReportDir = None, f=lambda a,b,c,d,e: checkDirectory(a,b,c,d,e)):
    '''Traverses directories under base and executes checks'''
    print 'Starting traversal at ', base 
    walk(base, lambda dummy, dir, files: f(ratCommand, gpgCommand, dir, files, baseReportDir), None)
    
def enterRatCommand(rat = None):
    '''If necessary, prompts user for an acceptable Rat command'''
    if rat == None:
        try:
            rat = raw_input("Please enter Rat command (including path): ")
        except EOFError:
            raise UserAbortError()
        
        if rat == "":
            print "Rat command is required"
            rat = enterRatCommand()       
             
    ratResult = popen(rat).close()
    if not(ratResult == None):
        print "Cannot execute " + rat
        rat = enterRatCommand()
    return rat
    
def enterBaseDirectory(base = None):
    '''If necessary prompts user for an acceptable base directory'''
    if base == None:
        try:
            base = raw_input("Please enter base directory for recursive traversal: ")
        except EOFError:
            raise UserAbortError()
    if not(isdir(base)):
        print "'" + base + "' is not a directory."
        base = enterBaseDirectory()
    return base

def enterGPGCommand(gpgCommand = None):
    '''If necessary prompts user for the command used to test signatures'''
    if gpgCommand == None:
        try:
            gpgCommand = raw_input("GnuPG is required. Please enter GPG command: ")
        except EOFError:
            raise UserAbortError()
    result = popen(gpgCommand + " --version").close()
    if not(result == None):
        print "Cannot execute " + gpgCommand
        rat = enterGPGCommand()
    return gpgCommand

def getRatCommand():
    '''Gets an acceptable value for the Rat command'''
    if len(argv) == 1:
        ratCommand = enterRatCommand() 
    else:
        ratCommand = enterRatCommand(argv[1])
    return ratCommand

def getBaseDirectory():
    '''Gets an acceptable value for the base directory'''
    if len(argv) < 3:
        base = enterBaseDirectory()
    else:
        base = enterBaseDirectory(argv[2])    
    return base
    
def getGPGCommand():
    '''Gets an acceptable value for the command used to test signatures'''
    if len(argv) < 5:
        gpgCommand = enterGPGCommand('gpg')
    else:
        gpgCommand = enterGPGCommand(argv[4])
    return gpgCommand

def getBaseReportDirectory():
    if len(argv) < 4:
        return None
    else:
        return argv[3]

def checkArgs(f=lambda a,b,c,d: walkDirectories(a, b, c, d)):
    '''Prompts user for arguments missing from the command line. Check argument values.'''
    try:
        ratCommand = getRatCommand()
        base = getBaseDirectory()
        gpgCommand = getGPGCommand()
        baseReportDirectory = getBaseReportDirectory()
        
        f(base, ratCommand, gpgCommand, baseReportDirectory)
    except UserAbortError, e:
        print '\n'
        print e
    
checkArgs()
