Merge pull request #297 from gfyoung/camel-underscore-consistency

Standardize underscore names in updateHostsFile.py
This commit is contained in:
Steven Black 2017-05-15 14:50:07 -04:00 committed by GitHub
commit 799510b46d

View File

@ -37,25 +37,26 @@ else: # Python 2
from urllib2 import urlopen from urllib2 import urlopen
def getFileByUrl(url): def get_file_by_url(url):
try: try:
f = urlopen(url) f = urlopen(url)
return f.read().decode("UTF-8") return f.read().decode("UTF-8")
except: except:
print("Problem getting file: ", url) print("Problem getting file: ", url)
# raise
def writeData(f, data): def write_data(f, data):
if PY3: if PY3:
f.write(bytes(data, "UTF-8")) f.write(bytes(data, "UTF-8"))
else: else:
f.write(str(data).encode("UTF-8")) f.write(str(data).encode("UTF-8"))
# This function doesn't list hidden files
def listdir_nohidden(path): def list_dir_no_hidden(path):
# This function doesn't list hidden files
return glob(os.path.join(path, "*")) return glob(os.path.join(path, "*"))
# Project Settings # Project Settings
BASEDIR_PATH = os.path.dirname(os.path.realpath(__file__)) BASEDIR_PATH = os.path.dirname(os.path.realpath(__file__))
@ -86,8 +87,8 @@ defaults = {
"blacklistfile" : os.path.join(BASEDIR_PATH, "blacklist"), "blacklistfile" : os.path.join(BASEDIR_PATH, "blacklist"),
"whitelistfile" : os.path.join(BASEDIR_PATH, "whitelist")} "whitelistfile" : os.path.join(BASEDIR_PATH, "whitelist")}
def main():
def main():
parser = argparse.ArgumentParser(description="Creates a unified hosts file from hosts stored in data subfolders.") parser = argparse.ArgumentParser(description="Creates a unified hosts file from hosts stored in data subfolders.")
parser.add_argument("--auto", "-a", dest="auto", default=False, action="store_true", help="Run without prompting.") parser.add_argument("--auto", "-a", dest="auto", default=False, action="store_true", help="Run without prompting.")
parser.add_argument("--backup", "-b", dest="backup", default=False, action="store_true", help="Backup the hosts files before they are overridden.") parser.add_argument("--backup", "-b", dest="backup", default=False, action="store_true", help="Backup the hosts files before they are overridden.")
@ -101,80 +102,90 @@ def main():
parser.add_argument("--replace", "-r", dest="replace", default=False, action="store_true", help="Replace your active hosts file with this new hosts file.") parser.add_argument("--replace", "-r", dest="replace", default=False, action="store_true", help="Replace your active hosts file with this new hosts file.")
parser.add_argument("--flush-dns-cache", "-f", dest="flushdnscache", default=False, action="store_true", help="Attempt to flush DNS cache after replacing the hosts file.") parser.add_argument("--flush-dns-cache", "-f", dest="flushdnscache", default=False, action="store_true", help="Attempt to flush DNS cache after replacing the hosts file.")
global settings global settings
options = vars(parser.parse_args()) options = vars(parser.parse_args())
options["outputpath"] = os.path.join(BASEDIR_PATH, options["outputsubfolder"]) options["outputpath"] = os.path.join(BASEDIR_PATH,
options["outputsubfolder"])
options["freshen"] = not options["noupdate"] options["freshen"] = not options["noupdate"]
settings = {} settings = {}
settings.update(defaults) settings.update(defaults)
settings.update(options) settings.update(options)
settings["sources"] = listdir_nohidden(settings["datapath"]) settings["sources"] = list_dir_no_hidden(settings["datapath"])
settings["extensionsources"] = listdir_nohidden(settings["extensionspath"]) settings["extensionsources"] = list_dir_no_hidden(
settings["extensionspath"])
# All our extensions folders... # All our extensions folders...
settings["extensions"] = [os.path.basename(item) for item in listdir_nohidden(settings["extensionspath"])] settings["extensions"] = [os.path.basename(item) for item in
list_dir_no_hidden(settings["extensionspath"])]
# ... intersected with the extensions passed-in as arguments, then sorted. # ... intersected with the extensions passed-in as arguments, then sorted.
settings["extensions"] = sorted( list(set(options["extensions"]).intersection(settings["extensions"])) ) settings["extensions"] = sorted(list(
set(options["extensions"]).intersection(settings["extensions"])))
with open(settings["readmedatafilename"], "r") as f: with open(settings["readmedatafilename"], "r") as f:
settings["readmedata"] = json.load(f) settings["readmedata"] = json.load(f)
promptForUpdate() prompt_for_update()
promptForExclusions() prompt_for_exclusions()
mergeFile = createInitialFile()
removeOldHostsFile() merge_file = create_initial_file()
finalFile = removeDupsAndExcl(mergeFile) remove_old_hosts_file()
finalizeFile(finalFile)
final_file = remove_dups_and_excl(merge_file)
finalize_file(final_file)
if settings["ziphosts"]: if settings["ziphosts"]:
zf = zipfile.ZipFile(os.path.join(settings["outputsubfolder"], "hosts.zip"), mode='w') zf = zipfile.ZipFile(os.path.join(settings["outputsubfolder"],
zf.write(os.path.join(settings["outputsubfolder"], "hosts"), compress_type=zipfile.ZIP_DEFLATED, arcname='hosts') "hosts.zip"), mode='w')
zf.write(os.path.join(settings["outputsubfolder"], "hosts"),
compress_type=zipfile.ZIP_DEFLATED, arcname='hosts')
zf.close() zf.close()
updateReadmeData() update_readme_data()
printSuccess("Success! The hosts file has been saved in folder " + settings["outputsubfolder"] + "\nIt contains " + print_success("Success! The hosts file has been saved in folder " +
"{:,}".format(settings["numberofrules"]) + " unique entries.") settings["outputsubfolder"] + "\nIt contains " +
"{:,}".format(settings["numberofrules"]) +
" unique entries.")
promptForMove(finalFile) prompt_for_move(final_file)
# Prompt the User # Prompt the User
def promptForUpdate(): def prompt_for_update():
# Create hosts file if it doesn't exists # Create hosts file if it doesn't exists
if not os.path.isfile(os.path.join(BASEDIR_PATH, "hosts")): if not os.path.isfile(os.path.join(BASEDIR_PATH, "hosts")):
try: try:
open(os.path.join(BASEDIR_PATH, "hosts"), "w+").close() open(os.path.join(BASEDIR_PATH, "hosts"), "w+").close()
except: except:
printFailure("ERROR: No 'hosts' file in the folder," print_failure("ERROR: No 'hosts' file in the folder,"
"try creating one manually") "try creating one manually")
if not settings["freshen"]: if not settings["freshen"]:
return return
prompt = "Do you want to update all data sources?" prompt = "Do you want to update all data sources?"
if settings["auto"] or query_yes_no(prompt): if settings["auto"] or query_yes_no(prompt):
updateAllSources() update_all_sources()
elif not settings["auto"]: elif not settings["auto"]:
print("OK, we'll stick with what we've got locally.") print("OK, we'll stick with what we've got locally.")
def promptForExclusions(): def prompt_for_exclusions():
prompt = ("Do you want to exclude any domains?\n" prompt = ("Do you want to exclude any domains?\n"
"For example, hulu.com video streaming must be able to access " "For example, hulu.com video streaming must be able to access "
"its tracking and ad servers in order to play video.") "its tracking and ad servers in order to play video.")
if not settings["auto"]: if not settings["auto"]:
if query_yes_no(prompt): if query_yes_no(prompt):
displayExclusionOptions() display_exclusion_options()
else: else:
print("OK, we'll only exclude domains in the whitelist.") print("OK, we'll only exclude domains in the whitelist.")
def promptForFlushDnsCache(): def prompt_for_flush_dns_cache():
if settings["flushdnscache"]: if settings["flushdnscache"]:
flush_dns_cache() flush_dns_cache()
@ -183,7 +194,7 @@ def promptForFlushDnsCache():
flush_dns_cache() flush_dns_cache()
def promptForMove(finalFile): def prompt_for_move(final_file):
if settings["replace"] and not settings["skipstatichosts"]: if settings["replace"] and not settings["skipstatichosts"]:
move_file = True move_file = True
elif settings["auto"] or settings["skipstatichosts"]: elif settings["auto"] or settings["skipstatichosts"]:
@ -194,20 +205,20 @@ def promptForMove(finalFile):
move_file = query_yes_no(prompt) move_file = query_yes_no(prompt)
if move_file: if move_file:
move_hosts_file_into_place(finalFile) move_hosts_file_into_place(final_file)
promptForFlushDnsCache() prompt_for_flush_dns_cache()
else: else:
return False return False
# End Prompt the User # End Prompt the User
# Exclusion logic # Exclusion logic
def displayExclusionOptions(): def display_exclusion_options():
for exclusionOption in settings["commonexclusions"]: for exclusion_option in settings["commonexclusions"]:
prompt = "Do you want to exclude the domain " + exclusionOption + " ?" prompt = "Do you want to exclude the domain " + exclusion_option + " ?"
if query_yes_no(prompt): if query_yes_no(prompt):
excludeDomain(exclusionOption) exclude_domain(exclusion_option)
else: else:
continue continue
@ -227,87 +238,100 @@ def gather_custom_exclusions():
"to exclude (e.g. facebook.com): ") "to exclude (e.g. facebook.com): ")
user_domain = raw_input(domain_prompt) user_domain = raw_input(domain_prompt)
if isValidDomainFormat(user_domain): if is_valid_domain_format(user_domain):
excludeDomain(user_domain) exclude_domain(user_domain)
continue_prompt = "Do you have more domains you want to enter?" continue_prompt = "Do you have more domains you want to enter?"
if not query_yes_no(continue_prompt): if not query_yes_no(continue_prompt):
return return
def excludeDomain(domain): def exclude_domain(domain):
settings["exclusionregexs"].append(re.compile(settings["exclusionpattern"] + domain)) settings["exclusionregexs"].append(re.compile(
settings["exclusionpattern"] + domain))
def matchesExclusions(strippedRule):
strippedDomain = strippedRule.split()[1] def matches_exclusions(stripped_rule):
stripped_domain = stripped_rule.split()[1]
for exclusionRegex in settings["exclusionregexs"]: for exclusionRegex in settings["exclusionregexs"]:
if exclusionRegex.search(strippedDomain): if exclusionRegex.search(stripped_domain):
return True return True
return False return False
# End Exclusion Logic # End Exclusion Logic
# Update Logic # Update Logic
def updateAllSources(): def update_all_sources():
# Update all hosts files regardless of folder depth # Update all hosts files regardless of folder depth
# allsources = glob('*/**/' + settings["sourcedatafilename"], recursive=True) all_sources = recursive_glob("*", settings["sourcedatafilename"])
allsources = recursiveGlob("*", settings["sourcedatafilename"])
for source in allsources:
updateFile = open(source, "r")
updateData = json.load(updateFile)
updateURL = updateData["url"]
updateFile.close()
print("Updating source " + os.path.dirname(source) + " from " + updateURL) for source in all_sources:
update_file = open(source, "r")
update_data = json.load(update_file)
update_url = update_data["url"]
update_file.close()
updatedFile = getFileByUrl(updateURL) print("Updating source " + os.path.dirname(
source) + " from " + update_url)
updated_file = get_file_by_url(update_url)
try: try:
updatedFile = updatedFile.replace("\r", "") #get rid of carriage-return symbols # get rid of carriage-return symbols
updated_file = updated_file.replace("\r", "")
hostsFile = open(os.path.join(BASEDIR_PATH, os.path.dirname(source), settings["hostfilename"]), "wb") hosts_file = open(os.path.join(BASEDIR_PATH,
writeData(hostsFile, updatedFile) os.path.dirname(source),
hostsFile.close() settings["hostfilename"]), "wb")
write_data(hosts_file, updated_file)
hosts_file.close()
except: except:
print("Skipping.") print("Skipping.")
# End Update Logic # End Update Logic
# File Logic # File Logic
def createInitialFile(): def create_initial_file():
mergeFile = tempfile.NamedTemporaryFile() merge_file = tempfile.NamedTemporaryFile()
# spin the sources for the base file # spin the sources for the base file
for source in recursiveGlob(settings["datapath"], settings["hostfilename"]): for source in recursive_glob(settings["datapath"],
settings["hostfilename"]):
with open(source, "r") as curFile: with open(source, "r") as curFile:
writeData(mergeFile, curFile.read()) write_data(merge_file, curFile.read())
for source in recursiveGlob(settings["datapath"], settings["sourcedatafilename"]): for source in recursive_glob(settings["datapath"],
updateFile = open(source, "r") settings["sourcedatafilename"]):
updateData = json.load(updateFile) update_file = open(source, "r")
settings["sourcesdata"].append(updateData) update_data = json.load(update_file)
updateFile.close() settings["sourcesdata"].append(update_data)
update_file.close()
# spin the sources for extensions to the base file # spin the sources for extensions to the base file
for source in settings["extensions"]: for source in settings["extensions"]:
# filename = os.path.join(settings["extensionspath"], source, settings["hostfilename"]) for filename in recursive_glob(os.path.join(
for filename in recursiveGlob(os.path.join(settings["extensionspath"], source), settings["hostfilename"]): settings["extensionspath"], source), settings["hostfilename"]):
with open(filename, "r") as curFile: with open(filename, "r") as curFile:
writeData(mergeFile, curFile.read()) write_data(merge_file, curFile.read())
# updateFilePath = os.path.join(settings["extensionspath"], source, settings["sourcedatafilename"]) for update_file_path in recursive_glob(os.path.join(
for updateFilePath in recursiveGlob( os.path.join(settings["extensionspath"], source), settings["sourcedatafilename"]): settings["extensionspath"], source),
updateFile = open(updateFilePath, "r") settings["sourcedatafilename"]):
updateData = json.load(updateFile) update_file = open(update_file_path, "r")
settings["sourcesdata"].append(updateData) update_data = json.load(update_file)
updateFile.close()
settings["sourcesdata"].append(update_data)
update_file.close()
if os.path.isfile(settings["blacklistfile"]): if os.path.isfile(settings["blacklistfile"]):
with open(settings["blacklistfile"], "r") as curFile: with open(settings["blacklistfile"], "r") as curFile:
writeData(mergeFile, curFile.read()) write_data(merge_file, curFile.read())
return mergeFile return merge_file
def removeDupsAndExcl(mergeFile):
numberOfRules = settings["numberofrules"] def remove_dups_and_excl(merge_file):
number_of_rules = settings["numberofrules"]
if os.path.isfile(settings["whitelistfile"]): if os.path.isfile(settings["whitelistfile"]):
with open(settings["whitelistfile"], "r") as ins: with open(settings["whitelistfile"], "r") as ins:
for line in ins: for line in ins:
@ -319,117 +343,144 @@ def removeDupsAndExcl(mergeFile):
os.makedirs(settings["outputpath"]) os.makedirs(settings["outputpath"])
# Another mode is required to read and write the file in Python 3 # Another mode is required to read and write the file in Python 3
finalFile = open(os.path.join(settings["outputpath"], "hosts"), final_file = open(os.path.join(settings["outputpath"], "hosts"),
"w+b" if PY3 else "w+") "w+b" if PY3 else "w+")
mergeFile.seek(0) # reset file pointer merge_file.seek(0) # reset file pointer
hostnames = set(["localhost", "localhost.localdomain", "local", "broadcasthost"]) hostnames = {"localhost", "localhost.localdomain",
"local", "broadcasthost"}
exclusions = settings["exclusions"] exclusions = settings["exclusions"]
for line in mergeFile.readlines():
for line in merge_file.readlines():
write = "true" write = "true"
# Explicit encoding # Explicit encoding
line = line.decode("UTF-8") line = line.decode("UTF-8")
# replace tabs with space # replace tabs with space
line = line.replace("\t+", " ") line = line.replace("\t+", " ")
# Trim trailing whitespace, periods -- (Issue #271 - https://github.com/StevenBlack/hosts/issues/271)
# see gh-271: trim trailing whitespace, periods
line = line.rstrip(' .') + "\n" line = line.rstrip(' .') + "\n"
# Testing the first character doesn't require startswith # Testing the first character doesn't require startswith
if line[0] == "#" or re.match(r'^\s*$', line[0]): if line[0] == "#" or re.match(r'^\s*$', line[0]):
writeData(finalFile, line) write_data(final_file, line)
continue continue
if "::1" in line: if "::1" in line:
continue continue
strippedRule = stripRule(line) #strip comments stripped_rule = strip_rule(line) # strip comments
if not strippedRule or matchesExclusions(strippedRule): if not stripped_rule or matches_exclusions(stripped_rule):
continue continue
hostname, normalizedRule = normalizeRule(strippedRule) # normalize rule
# Normalize rule
hostname, normalized_rule = normalize_rule(stripped_rule)
for exclude in exclusions: for exclude in exclusions:
if exclude in line: if exclude in line:
write = "false" write = "false"
break break
if normalizedRule and (hostname not in hostnames) and (write == "true"):
writeData(finalFile, normalizedRule) if (normalized_rule and (hostname not in hostnames)
and (write == "true")):
write_data(final_file, normalized_rule)
hostnames.add(hostname) hostnames.add(hostname)
numberOfRules += 1 number_of_rules += 1
settings["numberofrules"] = numberOfRules settings["numberofrules"] = number_of_rules
mergeFile.close() merge_file.close()
return finalFile return final_file
def normalizeRule(rule):
def normalize_rule(rule):
result = re.search(r'^[ \t]*(\d+\.\d+\.\d+\.\d+)\s+([\w\.-]+)(.*)', rule) result = re.search(r'^[ \t]*(\d+\.\d+\.\d+\.\d+)\s+([\w\.-]+)(.*)', rule)
if result: if result:
hostname, suffix = result.group(2,3) hostname, suffix = result.group(2, 3)
hostname = hostname.lower().strip() # explicitly lowercase and trim the hostname
# Explicitly lowercase and trim the hostname
hostname = hostname.lower().strip()
if suffix and settings["keepdomaincomments"]: if suffix and settings["keepdomaincomments"]:
# add suffix as comment only, not as a separate host # add suffix as comment only, not as a separate host
return hostname, "%s %s #%s\n" % (settings["targetip"], hostname, suffix) return hostname, "%s %s #%s\n" % (settings["targetip"],
hostname, suffix)
else: else:
return hostname, "%s %s\n" % (settings["targetip"], hostname) return hostname, "%s %s\n" % (settings["targetip"], hostname)
print("==>%s<==" % rule) print("==>%s<==" % rule)
return None, None return None, None
def finalizeFile(finalFile):
writeOpeningHeader(finalFile)
finalFile.close()
# Some sources put comments around their rules, for accuracy we need to strip them def finalize_file(final_file):
# the comments are preserved in the output hosts file write_opening_header(final_file)
def stripRule(line): final_file.close()
splitLine = line.split()
if len(splitLine) < 2 :
# Some sources put comments around their rules, for accuracy we need
# to strip them the comments are preserved in the output hosts file
def strip_rule(line):
split_line = line.split()
if len(split_line) < 2:
# just return blank # just return blank
return "" return ""
else: else:
return splitLine[0] + " " + splitLine[1] return split_line[0] + " " + split_line[1]
def writeOpeningHeader(finalFile):
finalFile.seek(0) #reset file pointer def write_opening_header(final_file):
fileContents = finalFile.read() #save content final_file.seek(0) # reset file pointer
finalFile.seek(0) #write at the top file_contents = final_file.read() # save content
writeData(finalFile, "# This hosts file is a merged collection of hosts from reputable sources,\n") final_file.seek(0) # write at the top
writeData(finalFile, "# with a dash of crowd sourcing via Github\n#\n") write_data(final_file, "# This hosts file is a merged collection "
writeData(finalFile, "# Date: " + time.strftime("%B %d %Y", time.gmtime()) + "\n") "of hosts from reputable sources,\n")
write_data(final_file, "# with a dash of crowd sourcing via Github\n#\n")
write_data(final_file, "# Date: " + time.strftime(
"%B %d %Y", time.gmtime()) + "\n")
if settings["extensions"]: if settings["extensions"]:
writeData(finalFile, "# Extensions added to this file: " + ", ".join(settings["extensions"]) + "\n") write_data(final_file, "# Extensions added to this file: " + ", ".join(
writeData(finalFile, "# Number of unique domains: " + "{:,}\n#\n".format(settings["numberofrules"])) settings["extensions"]) + "\n")
writeData(finalFile, "# Fetch the latest version of this file: https://raw.githubusercontent.com/StevenBlack/hosts/master/"+ os.path.join(settings["outputsubfolder"],"") + "hosts\n") write_data(final_file, "# Number of unique domains: " + "{:,}\n#\n".format(
writeData(finalFile, "# Project home page: https://github.com/StevenBlack/hosts\n#\n") settings["numberofrules"]))
writeData(finalFile, "# ===============================================================\n") write_data(final_file, "# Fetch the latest version of this file: "
writeData(finalFile, "\n") "https://raw.githubusercontent.com/"
"StevenBlack/hosts/master/" +
os.path.join(settings["outputsubfolder"], "") + "hosts\n")
write_data(final_file, "# Project home page: https://github.com/"
"StevenBlack/hosts\n#\n")
write_data(final_file, "# ==============================="
"================================\n")
write_data(final_file, "\n")
if not settings["skipstatichosts"]: if not settings["skipstatichosts"]:
writeData(finalFile, "127.0.0.1 localhost\n") write_data(final_file, "127.0.0.1 localhost\n")
writeData(finalFile, "127.0.0.1 localhost.localdomain\n") write_data(final_file, "127.0.0.1 localhost.localdomain\n")
writeData(finalFile, "127.0.0.1 local\n") write_data(final_file, "127.0.0.1 local\n")
writeData(finalFile, "255.255.255.255 broadcasthost\n") write_data(final_file, "255.255.255.255 broadcasthost\n")
writeData(finalFile, "::1 localhost\n") write_data(final_file, "::1 localhost\n")
writeData(finalFile, "fe80::1%lo0 localhost\n") write_data(final_file, "fe80::1%lo0 localhost\n")
writeData(finalFile, "0.0.0.0 0.0.0.0\n") write_data(final_file, "0.0.0.0 0.0.0.0\n")
if platform.system() == "Linux": if platform.system() == "Linux":
writeData(finalFile, "127.0.1.1 " + socket.gethostname() + "\n") write_data(final_file, "127.0.1.1 " + socket.gethostname() + "\n")
writeData(finalFile, "127.0.0.53 " + socket.gethostname() + "\n") write_data(final_file, "127.0.0.53 " + socket.gethostname() + "\n")
writeData(finalFile, "\n") write_data(final_file, "\n")
preamble = os.path.join(BASEDIR_PATH, "myhosts") preamble = os.path.join(BASEDIR_PATH, "myhosts")
if os.path.isfile(preamble): if os.path.isfile(preamble):
with open(preamble, "r") as f: with open(preamble, "r") as f:
writeData(finalFile, f.read()) write_data(final_file, f.read())
finalFile.write(fileContents) final_file.write(file_contents)
def updateReadmeData():
extensionsKey = "base" def update_readme_data():
hostsLocation = "" extensions_key = "base"
if settings["extensions"]: if settings["extensions"]:
extensionsKey = "-".join(settings["extensions"]) extensions_key = "-".join(settings["extensions"])
generationData = {"location": os.path.join(settings["outputsubfolder"], ""), generation_data = {"location": os.path.join(
"entries": settings["numberofrules"], settings["outputsubfolder"], ""),
"sourcesdata": settings["sourcesdata"]} "entries": settings["numberofrules"],
settings["readmedata"][extensionsKey] = generationData "sourcesdata": settings["sourcesdata"]}
settings["readmedata"][extensions_key] = generation_data
with open(settings["readmedatafilename"], "w") as f: with open(settings["readmedatafilename"], "w") as f:
json.dump(settings["readmedata"], f) json.dump(settings["readmedata"], f)
@ -439,7 +490,7 @@ def move_hosts_file_into_place(final_file):
Move the newly-created hosts file into its correct location on the OS. Move the newly-created hosts file into its correct location on the OS.
For UNIX systems, the hosts file is "etc/hosts." On Windows, it's For UNIX systems, the hosts file is "etc/hosts." On Windows, it's
"C:\Windows\system32\drivers\etc\hosts." "C:\Windows\System32\drivers\etc\hosts."
For this move to work, you must have administrator privileges to do this. For this move to work, you must have administrator privileges to do this.
On UNIX systems, this means having "sudo" access, and on Windows, it On UNIX systems, this means having "sudo" access, and on Windows, it
@ -447,8 +498,8 @@ def move_hosts_file_into_place(final_file):
Parameters Parameters
---------- ----------
final_file : str final_file : file object
The name of the newly-created hosts file to move. The newly-created hosts file to move.
""" """
filename = os.path.abspath(final_file.name) filename = os.path.abspath(final_file.name)
@ -457,7 +508,7 @@ def move_hosts_file_into_place(final_file):
print("Moving the file requires administrative privileges. " print("Moving the file requires administrative privileges. "
"You might need to enter your password.") "You might need to enter your password.")
if subprocess.call(["/usr/bin/sudo", "cp", filename, "/etc/hosts"]): if subprocess.call(["/usr/bin/sudo", "cp", filename, "/etc/hosts"]):
printFailure("Moving the file failed.") print_failure("Moving the file failed.")
elif os.name == "nt": elif os.name == "nt":
print("Automatically moving the hosts file " print("Automatically moving the hosts file "
"in place is not yet supported.") "in place is not yet supported.")
@ -479,7 +530,7 @@ def flush_dns_cache():
if platform.system() == "Darwin": if platform.system() == "Darwin":
if subprocess.call(["/usr/bin/sudo", "killall", if subprocess.call(["/usr/bin/sudo", "killall",
"-HUP", "mDNSResponder"]): "-HUP", "mDNSResponder"]):
printFailure("Flushing the DNS cache failed.") print_failure("Flushing the DNS cache failed.")
elif os.name == "nt": elif os.name == "nt":
print("Automatically flushing the DNS cache is not yet supported.") print("Automatically flushing the DNS cache is not yet supported.")
print("Please copy and paste the command 'ipconfig /flushdns' in " print("Please copy and paste the command 'ipconfig /flushdns' in "
@ -490,66 +541,73 @@ def flush_dns_cache():
if subprocess.call(["/usr/bin/sudo", "/etc/rc.d/init.d/nscd", if subprocess.call(["/usr/bin/sudo", "/etc/rc.d/init.d/nscd",
"restart"]): "restart"]):
printFailure("Flushing the DNS cache failed.") print_failure("Flushing the DNS cache failed.")
else: else:
printSuccess("Flushing DNS by restarting nscd succeeded") print_success("Flushing DNS by restarting nscd succeeded")
if os.path.isfile("/usr/lib/systemd/system/NetworkManager.service"): if os.path.isfile("/usr/lib/systemd/system/NetworkManager.service"):
dns_cache_found = True dns_cache_found = True
if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl", if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl",
"restart", "NetworkManager.service"]): "restart", "NetworkManager.service"]):
printFailure("Flushing the DNS cache failed.") print_failure("Flushing the DNS cache failed.")
else: else:
printSuccess("Flushing DNS by restarting " print_success("Flushing DNS by restarting "
"NetworkManager succeeded") "NetworkManager succeeded")
if os.path.isfile("/usr/lib/systemd/system/wicd.service"): if os.path.isfile("/usr/lib/systemd/system/wicd.service"):
dns_cache_found = True dns_cache_found = True
if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl", if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl",
"restart", "wicd.service"]): "restart", "wicd.service"]):
printFailure("Flushing the DNS cache failed.") print_failure("Flushing the DNS cache failed.")
else: else:
printSuccess("Flushing DNS by restarting wicd succeeded") print_success("Flushing DNS by restarting wicd succeeded")
if os.path.isfile("/usr/lib/systemd/system/dnsmasq.service"): if os.path.isfile("/usr/lib/systemd/system/dnsmasq.service"):
dns_cache_found = True dns_cache_found = True
if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl", if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl",
"restart", "dnsmasq.service"]): "restart", "dnsmasq.service"]):
printFailure("Flushing the DNS cache failed.") print_failure("Flushing the DNS cache failed.")
else: else:
printSuccess("Flushing DNS by restarting dnsmasq succeeded") print_success("Flushing DNS by restarting dnsmasq succeeded")
if os.path.isfile("/usr/lib/systemd/system/networking.service"): if os.path.isfile("/usr/lib/systemd/system/networking.service"):
dns_cache_found = True dns_cache_found = True
if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl", if subprocess.call(["/usr/bin/sudo", "/usr/bin/systemctl",
"restart", "networking.service"]): "restart", "networking.service"]):
printFailure("Flushing the DNS cache failed.") print_failure("Flushing the DNS cache failed.")
else: else:
printSuccess("Flushing DNS by restarting " print_success("Flushing DNS by restarting "
"networking.service succeeded") "networking.service succeeded")
if not dns_cache_found: if not dns_cache_found:
printFailure("Unable to determine DNS management tool.") print_failure("Unable to determine DNS management tool.")
def removeOldHostsFile(): # hotfix since merging with an already existing hosts file leads to artefacts and duplicates # Hotfix since merging with an already existing
oldFilePath = os.path.join(BASEDIR_PATH, "hosts") # hosts file leads to artifacts and duplicates
open(oldFilePath, "a").close() # create if already removed, so remove wont raise an error def remove_old_hosts_file():
old_file_path = os.path.join(BASEDIR_PATH, "hosts")
# create if already removed, so remove wont raise an error
open(old_file_path, "a").close()
if settings["backup"]: if settings["backup"]:
backupFilePath = os.path.join(BASEDIR_PATH, "hosts-{}".format(time.strftime("%Y-%m-%d-%H-%M-%S"))) backup_file_path = os.path.join(BASEDIR_PATH, "hosts-{}".format(
shutil.copy(oldFilePath, backupFilePath) # make a backup copy, marking the date in which the list was updated time.strftime("%Y-%m-%d-%H-%M-%S")))
os.remove(oldFilePath) # Make a backup copy, marking the date in which the list was updated
open(oldFilePath, "a").close() # create new empty hostsfile shutil.copy(old_file_path, backup_file_path)
os.remove(old_file_path)
# Create new empty hosts file
open(old_file_path, "a").close()
# End File Logic # End File Logic
# Helper Functions # Helper Functions
def query_yes_no(question, default="yes"): def query_yes_no(question, default="yes"):
""" """
@ -584,7 +642,7 @@ def query_yes_no(question, default="yes"):
reply = None reply = None
while not reply: while not reply:
sys.stdout.write(colorize(question, colors.PROMPT) + prompt) sys.stdout.write(colorize(question, Colors.PROMPT) + prompt)
choice = raw_input().lower() choice = raw_input().lower()
reply = None reply = None
@ -594,18 +652,20 @@ def query_yes_no(question, default="yes"):
elif choice in valid: elif choice in valid:
reply = valid[choice] reply = valid[choice]
else: else:
printFailure("Please respond with 'yes' or 'no' " print_failure("Please respond with 'yes' or 'no' "
"(or 'y' or 'n').\n") "(or 'y' or 'n').\n")
return reply == "yes" return reply == "yes"
def isValidDomainFormat(domain): def is_valid_domain_format(domain):
if domain == "": if domain == "":
print("You didn't enter a domain. Try again.") print("You didn't enter a domain. Try again.")
return False return False
domainRegex = re.compile("www\d{0,3}[.]|https?")
if domainRegex.match(domain): domain_regex = re.compile("www\d{0,3}[.]|https?")
if domain_regex.match(domain):
print("The domain " + domain + print("The domain " + domain +
" is not valid. Do not include " " is not valid. Do not include "
"www.domain.com or http(s)://domain.com. Try again.") "www.domain.com or http(s)://domain.com. Try again.")
@ -613,35 +673,39 @@ def isValidDomainFormat(domain):
else: else:
return True return True
# A version-independent glob( ... "/**/" ... ) # A version-independent glob( ... "/**/" ... )
def recursiveGlob(stem, filepattern): def recursive_glob(stem, file_pattern):
if sys.version_info >= (3,5): if sys.version_info >= (3, 5):
return glob(stem + "/**/" + filepattern, recursive=True) return glob(stem + "/**/" + file_pattern, recursive=True)
else: else:
if stem == "*": if stem == "*":
stem = "." stem = "."
matches = [] matches = []
for root, dirnames, filenames in os.walk(stem): for root, dirnames, filenames in os.walk(stem):
for filename in fnmatch.filter(filenames, filepattern): for filename in fnmatch.filter(filenames, file_pattern):
matches.append(os.path.join(root, filename)) matches.append(os.path.join(root, filename))
return matches return matches
# Colors # Colors
class colors: class Colors(object):
PROMPT = "\033[94m" PROMPT = "\033[94m"
SUCCESS = "\033[92m" SUCCESS = "\033[92m"
FAIL = "\033[91m" FAIL = "\033[91m"
ENDC = "\033[0m" ENDC = "\033[0m"
def colorize(text, color): def colorize(text, color):
return color + text + colors.ENDC return color + text + Colors.ENDC
def printSuccess(text):
print(colorize(text, colors.SUCCESS))
def printFailure(text): def print_success(text):
print(colorize(text, colors.FAIL)) print(colorize(text, Colors.SUCCESS))
def print_failure(text):
print(colorize(text, Colors.FAIL))
# End Helper Functions # End Helper Functions
if __name__ == "__main__": if __name__ == "__main__":