#!/usr/bin/python # # 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, see # """ brand_hunter searches spec and source files for possible Red Hat branding issues """ try: import magic except ImportError as e: raise ImportError("%s: please install the 'python-magic' package" % e) import os import re import sys import tarfile from optparse import OptionParser DEVNULL=open('/dev/null', 'w') MIME = magic.open(magic.MAGIC_MIME) MIME.load() RE = re.compile(r'[Rr][Ee][Dd]\s?[Hh][Aa][Tt]', flags=re.M) RE_EMAIL = re.compile(r'<[^@]+@redhat.com>', flags=re.M) TAR_OPEN_MODE = { 'application/x-bzip2' : 'r:bz2', 'application/x-gzip' : 'r:gz', } def main(opts, args): # validate directory for topdir in args: if not ('SPECS' in os.listdir(topdir) and 'SOURCES' in os.listdir(topdir)): print ("Error: The specified directory does not contain SPECS and " "SOURCES folders") sys.exit(1) # setup print "\nprocessing %s" % topdir issues_file = os.path.join(topdir, 'issues.txt') # search files issues = [] for dir in ['SPECS', 'SOURCES']: searchdir = os.path.join(topdir, dir) for path,_,files in os.walk(searchdir, topdown=False): for file in files: fp = os.path.join(path, file) if opts.verbose: print fp find_issues(issues, topdir, fp) # output results if issues: with open(issues_file, 'w') as f: f.write("\n".join(issues) + '\n') print "- issues found: see %s" % issues_file else: print "- no issues found" print "\n" # blank line at end for readability def find_issues(issues, topdir, file): relpath = file.replace(topdir + '/', '') if not os.path.exists(file): return mimetype=MIME.file(file) if 'application/x-empty' in mimetype: return elif not 'charset=binary' in mimetype: with open(file, 'r') as fo: s = fo.read() for m in RE.finditer(s): lineno = s.count('\n',0,m.start()) line = s.split('\n')[lineno] # filter out lines with email only matches if opts.ignore_email: email = RE_EMAIL.findall(line) if email and len(email) == len(RE.findall(line)): continue issues.append('%s:%s:%s' % (relpath, lineno+1, line)) elif ('application/x-bzip2' in mimetype or 'application/x-gzip' in mimetype): try: tf = tarfile.open(file, TAR_OPEN_MODE[mimetype.split(';')[0]]) except tarfile.ReadError: # we can't decompress the file, so treat it as an unknown binary file issues.append('%s:binary file' % relpath) else: tf.extractall(os.path.dirname(file)) os.remove(file) for n in tf.getnames(): mp = os.path.join(os.path.dirname(file), n) if not os.path.isdir(mp): find_issues(issues, topdir, mp) else: issues.append('%s:binary file' % relpath) if __name__ == '__main__': parser = OptionParser("usage: %prog [options] directory...", description=( "Searches SPEC and SOURCE folders in one or more directories for possible " "Red Hat branding issues." )) parser.add_option('--ignore-email', dest='ignore_email', action='store_true', default=False, help="ignore text that matches ''") parser.add_option('--verbose', '-v', dest='verbose', action='store_true', default=False, help="print the names of files as they are processed") opts,args = parser.parse_args(args=sys.argv[1:]) if not args: print "Error: no directory specified" sys.exit(1) main(opts, args) sys.exit()