#!/usr/bin/env python
#******************************************************************************\
#* Copyright (C) 2003-2008 Martin Blais <blais@furius.ca>
#*
#* This program is free software; you can redistribute it and/or modify
#* it under the terms of the GNU General Public License as published by
#* the Free Software Foundation; either version 2 of the License, or
#* (at your option) any later version.
#*
#* This program is distributed in the hope that it will be useful,
#* but WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#* GNU General Public License for more details.
#*
#* You should have received a copy of the GNU General Public License
#* along with this program; if not, write to the Free Software
#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#*
#*****************************************************************************/

"""encrypt-hier [<options>] <file>

Walks a hierarchy of files and encrypts the files that have an encrypted
equivalent in the same directory. With the --all option, encrypt all the files
recursively.
"""

__version__ = "Revision: 1.3 "
__author__ = "Martin Blais <blais@furius.ca>"

## This script replaces the following but does a whole lot more and more safely:
##
## find $* -type f \( ! -name \*.gpg -a ! -name \.\*.gpg \) -ls \
##         -exec gpg -e {} \; -exec rm -f {} \;


import sys, os, re, commands
from os.path import *


encrypted_re = re.compile('.*\.(gpg|asc)')

def process(fn, opts):
    """
    Encrypt the filename and delete if requested.
    """
    if opts.regex and not opts.re.match(basename(fn)):
        if opts.verbose:
            print >> sys.stderr, \
                  "%s: ignoring because it does not match regexp" % fn
        return

    if opts.dont_reencrypt and \
       ( exists('%s.gpg' % fn) or exists('%s.asc' % fn) ):
        if opts.verbose:
            print >> sys.stderr, \
                  "%s: ignoring because it has already been encrypted" % fn
        return

    if True: # not opts.encrypt_encrypted:
        if encrypted_re.match(fn):
            if opts.verbose:
                print >> sys.stderr, "%s: ignoring encrypted file" % fn
            return

    print '==========', fn

    flags = ''
    if opts.overwrite:
        flags += ' --yes'
    if opts.armor:
        flags += ' --armor'
    if opts.recipient:
        flags += ' --recipient="%s"' % opts.recipient
    cmd = 'gpg %s --encrypt "%s"' % (flags, fn)
    if opts.dry_run:
        print cmd; return

    s, o = commands.getstatusoutput(cmd)
    print s, o
    if s != 0:
        raise SystemExit("Error: could not encrypt file '%s'" % fn)

    if opts.delete:
        os.unlink(fn)


def complete(parser):
    "Programmable completion support."
    try:
        import optcomplete
        optcomplete.autocomplete(parser, optcomplete.DirCompleter())
    except ImportError:
        pass

def main():
    import optparse
    parser = optparse.OptionParser(__doc__.strip(), version=__version__[1:-1])

    parser.add_option('-v', '--verbose', action='store_true',
                      help="Verbose output. Report skipped files.")

    parser.add_option('-r', '--recipient', action='store',
                      help="Recipient to encrypt for.")

    parser.add_option('-R', '--regex', action='store',
                      help="Match only against specified regular expression.")

    parser.add_option('-d', '--delete', action='store_true',
                      help="Delete files after encrypting them.")

    parser.add_option('-n', '--dry-run', action='store_true',
                      help="Do not actually encrypt the files, just print what "
                      "would be done instead.")

    parser.add_option('-w', '--overwrite', action='store_true',
                      help="Overwrite existing encrypted files.")

    parser.add_option('-W', '--dont-reencrypt', action='store_true',
                      help="Do not re-encrypt already encrypted files.")

    parser.add_option('-a', '--armor', action='store_true',
                      help="Create ASCII armored output.")
    # parser.add_option('-R', '--encrypt-encrypted', action='store_true',
    #                   help="Unconditionally re-encrypt already encrypted "
    #                   "files found in the search.")

    complete(parser)
    opts, args = parser.parse_args()

    if not args:
        raise SystemExit('Error: please specify files to process.')
    if opts.overwrite and opts.dont_reencrypt:
        raise SystemExit('Error: conflicting options for already encrypted '
                         'files.')

    if opts.regex:
        opts.re = re.compile(opts.regex)

    for n in args:
        if isfile(n):
            process(n, opts)
        elif isdir(n):
            for dn, dirs, files in os.walk(n):
                for fn in files:
                    process(join(dn, fn), opts)

if __name__ == '__main__':
    main()
