Update for Joomla 3.7.2
This commit is contained in:
parent
2bd7573770
commit
a405edc8ce
205
checkjoomla.py
205
checkjoomla.py
@ -1,21 +1,22 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from pathlib import Path
|
import argparse
|
||||||
import re
|
import hashlib
|
||||||
import urllib.request
|
|
||||||
from urllib.request import urlretrieve
|
|
||||||
import zipfile
|
|
||||||
import shutil
|
|
||||||
import stat
|
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import hashlib
|
import re
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import socket
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
|
import zipfile
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
## CONFIG
|
# CONFIG
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
# BASE BASE
|
# BASE BASE
|
||||||
@ -27,52 +28,57 @@ csv_file_name = 'joomla_status.csv'
|
|||||||
# TMP DOWNLOAD DIR
|
# TMP DOWNLOAD DIR
|
||||||
tmp_dl_dir = '/tmp'
|
tmp_dl_dir = '/tmp'
|
||||||
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def get_joomla_version(filepath):
|
def get_joomla_version(version_file_path):
|
||||||
fobj = open(filepath, "r", -1, None, 'replace')
|
version_file = open(version_file_path, "r", -1, None, 'replace')
|
||||||
version = -1
|
main_version = -1
|
||||||
dev_version = -1
|
dev_version = -1
|
||||||
for line in fobj:
|
for line in version_file:
|
||||||
if version != -1 and dev_version != -1:
|
if main_version != -1 and dev_version != -1:
|
||||||
break
|
break
|
||||||
|
|
||||||
match = re.search("""RELEASE[\s]*=[\s]*'?"?([0-9\.]+)'?"?""", line)
|
version_match = re.search("""RELEASE[\s]*=[\s]*'?"?([0-9.]+)'?"?""", line)
|
||||||
if match is not None:
|
if version_match is not None:
|
||||||
#print(match.group(1))
|
# print(version_match.group(1))
|
||||||
version = match.group(1)
|
main_version = version_match.group(1)
|
||||||
match = re.search("""DEV_LEVEL[\s]*=[\s]*'?"?([0-9]+)'?"?""", line)
|
|
||||||
if match is not None:
|
|
||||||
#print(match.group(1))
|
|
||||||
dev_version = match.group(1)
|
|
||||||
fobj.close()
|
|
||||||
|
|
||||||
if version != -1 and dev_version != -1:
|
version_match = re.search("""DEV_LEVEL[\s]*=[\s]*'?"?([0-9]+)'?"?""", line)
|
||||||
return str(version + "." + dev_version)
|
if version_match is not None:
|
||||||
|
# print(version_match.group(1))
|
||||||
|
dev_version = version_match.group(1)
|
||||||
|
version_file.close()
|
||||||
|
|
||||||
|
if main_version != -1 and dev_version != -1:
|
||||||
|
return str(main_version + "." + dev_version)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def check_version(version, newest_version):
|
|
||||||
if StrictVersion(newest_version) > StrictVersion(version):
|
def check_version(current_version, latest_version):
|
||||||
|
if StrictVersion(latest_version) > StrictVersion(current_version):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_newest_version():
|
def get_newest_version():
|
||||||
response = urllib.request.urlopen("http://update.joomla.org/core/list.xml")
|
response = urllib.request.urlopen("http://update.joomla.org/core/list.xml")
|
||||||
|
|
||||||
highest_version = "0.0"
|
highest_version = "0.0"
|
||||||
for line in response:
|
for line in response:
|
||||||
match = re.search("""[\s]version="?'?([0-9\.]+)"?'?""", str(line))
|
version_match = re.search("""[\s]version="?'?([0-9.]+)"?'?""", str(line))
|
||||||
if match is not None:
|
if version_match is not None:
|
||||||
if StrictVersion(match.group(1)) > StrictVersion(highest_version):
|
if StrictVersion(version_match.group(1)) > StrictVersion(highest_version):
|
||||||
highest_version = match.group(1)
|
highest_version = version_match.group(1)
|
||||||
|
|
||||||
return highest_version
|
return highest_version
|
||||||
|
|
||||||
def download_joomla_version(version):
|
|
||||||
version_match = re.search("""(\d+)\.(\d+)(\.(\d+))?([ab](\d+))?""", version)
|
def download_joomla_version(joomla_version):
|
||||||
|
version_match = re.search("""(\d+)\.(\d+)(\.(\d+))?([ab](\d+))?""", joomla_version)
|
||||||
|
|
||||||
dst_path = ""
|
dst_path = ""
|
||||||
if version_match is not None:
|
if version_match is not None:
|
||||||
if version_match.group(1) == "2":
|
if version_match.group(1) == "2":
|
||||||
@ -82,40 +88,56 @@ def download_joomla_version(version):
|
|||||||
version_string = version_match.group(1) + "-" + version_match.group(2) + "-" + version_match.group(4)
|
version_string = version_match.group(1) + "-" + version_match.group(2) + "-" + version_match.group(4)
|
||||||
|
|
||||||
# check if file has already been downloaded...
|
# check if file has already been downloaded...
|
||||||
dst_path = tmp_dl_dir + "/orig_joomla_" + version + ".zip"
|
dst_path = tmp_dl_dir + "/orig_joomla_" + joomla_version + ".zip"
|
||||||
dst_file = Path(dst_path)
|
dst_file = Path(dst_path)
|
||||||
if not dst_file.is_file():
|
if not dst_file.is_file():
|
||||||
url = "https://downloads.joomla.org/cms/" + version_path + "/" + version_string + "/joomla_" + version_string + "-stable-full_package-zip?format=zip"
|
if StrictVersion(joomla_version) > StrictVersion('3.7.1'):
|
||||||
|
version_string2 = ".".join(version_string.rsplit('-', 1))
|
||||||
|
|
||||||
|
url = "https://downloads.joomla.org/cms/" + \
|
||||||
|
version_path + "/" + version_string + \
|
||||||
|
"/Joomla_" + version_string2 + \
|
||||||
|
"-Stable-Full_Package.zip?format=zip"
|
||||||
|
else:
|
||||||
|
url = "https://downloads.joomla.org/cms/" + \
|
||||||
|
version_path + "/" + version_string + \
|
||||||
|
"/joomla_" + version_string + \
|
||||||
|
"-stable-full_package-zip?format=zip"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
urllib.request.urlretrieve (url, dst_path)
|
urllib.request.urlretrieve(url, dst_path)
|
||||||
except:
|
except (urllib.error.URLError, urllib.error.HTTPError, socket.error):
|
||||||
|
# print("Download of", url, "failed!")
|
||||||
dst_path = ""
|
dst_path = ""
|
||||||
|
|
||||||
return dst_path
|
return dst_path
|
||||||
|
|
||||||
def extract_downloaded_joomla_version(version, path):
|
|
||||||
dst_path = tmp_dl_dir + "/orig_joomla_" + version
|
def extract_downloaded_joomla_version(joomla_version, zip_file_path):
|
||||||
|
dst_path = tmp_dl_dir + "/orig_joomla_" + joomla_version
|
||||||
|
|
||||||
# extract a fresh copy...
|
# extract a fresh copy...
|
||||||
shutil.rmtree(dst_path, onerror=remove_readonly)
|
shutil.rmtree(dst_path, onerror=remove_readonly)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(path, "r") as zip_ref:
|
with zipfile.ZipFile(zip_file_path, "r") as zip_ref:
|
||||||
zip_ref.extractall(dst_path)
|
zip_ref.extractall(dst_path)
|
||||||
except:
|
except (urllib.error.URLError, urllib.error.HTTPError, socket.error):
|
||||||
dst_path = ""
|
dst_path = ""
|
||||||
|
|
||||||
return dst_path
|
return dst_path
|
||||||
|
|
||||||
|
|
||||||
def remove_readonly(func, path, excinfo):
|
def remove_readonly(func, path, excinfo):
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
os.chmod(path, stat.S_IWRITE)
|
os.chmod(path, stat.S_IWRITE)
|
||||||
func(path)
|
func(path)
|
||||||
|
|
||||||
|
|
||||||
def get_dir_md5(dir_root):
|
def get_dir_md5(dir_root):
|
||||||
exclude_dirs = {"installation", "tmp"}
|
exclude_dirs = {"installation", "tmp"}
|
||||||
|
|
||||||
hash = hashlib.md5()
|
md5_hash = hashlib.md5()
|
||||||
for dirpath, dirnames, filenames in os.walk(dir_root, topdown=True):
|
for dirpath, dirnames, filenames in os.walk(dir_root, topdown=True):
|
||||||
|
|
||||||
dirnames.sort(key=os.path.normcase)
|
dirnames.sort(key=os.path.normcase)
|
||||||
@ -140,57 +162,69 @@ def get_dir_md5(dir_root):
|
|||||||
|
|
||||||
f = open(filepath, 'rb')
|
f = open(filepath, 'rb')
|
||||||
for chunk in iter(lambda: f.read(65536), b''):
|
for chunk in iter(lambda: f.read(65536), b''):
|
||||||
hash.update(chunk)
|
md5_hash.update(chunk)
|
||||||
|
|
||||||
|
return md5_hash.hexdigest()
|
||||||
|
|
||||||
return hash.hexdigest()
|
|
||||||
|
|
||||||
def cmp_joomla_directories(original_root, installation_root):
|
def cmp_joomla_directories(original_root, installation_root):
|
||||||
exclude_dirs = {"installation", "tmp", "logs"}
|
exclude_dirs = {"installation", "tmp", "logs"}
|
||||||
|
|
||||||
check_failures = []
|
check_failures = []
|
||||||
|
|
||||||
for dirpath, dirnames, filenames in os.walk(original_root, topdown=True):
|
|
||||||
|
|
||||||
dirnames.sort(key=os.path.normcase)
|
for dir_path, dir_names, file_names in os.walk(original_root, topdown=True):
|
||||||
filenames.sort(key=os.path.normcase)
|
|
||||||
|
|
||||||
dirnames[:] = [d for d in dirnames if d not in exclude_dirs]
|
dir_names.sort(key=os.path.normcase)
|
||||||
|
file_names.sort(key=os.path.normcase)
|
||||||
|
|
||||||
for filename in filenames:
|
dir_names[:] = [d for d in dir_names if d not in exclude_dirs]
|
||||||
relative_path = os.path.relpath(dirpath, original_root)
|
|
||||||
|
for filename in file_names:
|
||||||
|
relative_path = os.path.relpath(dir_path, original_root)
|
||||||
|
|
||||||
if relative_path == ".":
|
if relative_path == ".":
|
||||||
relative_path = ""
|
relative_path = ""
|
||||||
|
|
||||||
orig_filepath = os.path.join(dirpath, filename)
|
orig_file_path = os.path.join(dir_path, filename)
|
||||||
inst_filepath = os.path.join(installation_root, os.path.join(relative_path, filename))
|
inst_file_path = os.path.join(installation_root, os.path.join(relative_path, filename))
|
||||||
|
|
||||||
if os.path.isfile(inst_filepath):
|
if os.path.isfile(inst_file_path):
|
||||||
hash_orig = hashlib.md5()
|
hash_orig = hashlib.md5()
|
||||||
f = open(orig_filepath, 'rb')
|
f = open(orig_file_path, 'rb')
|
||||||
for chunk in iter(lambda: f.read(65536), b''):
|
for chunk in iter(lambda: f.read(65536), b''):
|
||||||
hash_orig.update(chunk)
|
hash_orig.update(chunk)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
hash_inst = hashlib.md5()
|
hash_inst = hashlib.md5()
|
||||||
f = open(inst_filepath, 'rb')
|
f = open(inst_file_path, 'rb')
|
||||||
for chunk in iter(lambda: f.read(65536), b''):
|
for chunk in iter(lambda: f.read(65536), b''):
|
||||||
hash_inst.update(chunk)
|
hash_inst.update(chunk)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
if hash_orig.hexdigest() == hash_inst.hexdigest():
|
if hash_orig.hexdigest() == hash_inst.hexdigest():
|
||||||
#print("file ok", os.path.join(relative_path, filename), hash_orig.hexdigest(), hash_inst.hexdigest())
|
"""
|
||||||
|
print("file ok",
|
||||||
|
os.path.join(relative_path, filename),
|
||||||
|
hash_orig.hexdigest(),
|
||||||
|
hash_inst.hexdigest())
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
#print("file NOT OK!!!!!!!!!!!!!!!!!!!!!!!", os.path.join(relative_path, filename), hash_orig.hexdigest(), hash_inst.hexdigest())
|
"""
|
||||||
|
print("file NOT OK!",
|
||||||
|
os.path.join(relative_path, filename),
|
||||||
|
hash_orig.hexdigest(),
|
||||||
|
hash_inst.hexdigest())
|
||||||
|
"""
|
||||||
check_failures.append(os.path.join(relative_path, filename))
|
check_failures.append(os.path.join(relative_path, filename))
|
||||||
else:
|
else:
|
||||||
#print("File", os.path.join(relative_path, filename), "is missing!")
|
# print("File", os.path.join(relative_path, filename), "is missing!")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return check_failures
|
return check_failures
|
||||||
|
|
||||||
class bcolors:
|
|
||||||
|
class ConsoleColors:
|
||||||
HEADER = '\033[95m'
|
HEADER = '\033[95m'
|
||||||
OKBLUE = '\033[94m'
|
OKBLUE = '\033[94m'
|
||||||
OKGREEN = '\033[92m'
|
OKGREEN = '\033[92m'
|
||||||
@ -200,6 +234,7 @@ class bcolors:
|
|||||||
BOLD = '\033[1m'
|
BOLD = '\033[1m'
|
||||||
UNDERLINE = '\033[4m'
|
UNDERLINE = '\033[4m'
|
||||||
|
|
||||||
|
|
||||||
# MAIN
|
# MAIN
|
||||||
###########
|
###########
|
||||||
|
|
||||||
@ -211,7 +246,7 @@ args = parser.parse_args()
|
|||||||
|
|
||||||
newest_version = get_newest_version()
|
newest_version = get_newest_version()
|
||||||
|
|
||||||
print(bcolors.HEADER + "Newest Joomla Version: ", bcolors.OKBLUE, newest_version, bcolors.ENDC, "\n")
|
print(ConsoleColors.HEADER + "Newest Joomla Version: ", ConsoleColors.OKBLUE, newest_version, ConsoleColors.ENDC, "\n")
|
||||||
|
|
||||||
fobj = open(csv_file_name, "w")
|
fobj = open(csv_file_name, "w")
|
||||||
fobj.write("Status;Integrity;Actual Version;Newest Version;Domain;Path\n")
|
fobj.write("Status;Integrity;Actual Version;Newest Version;Domain;Path\n")
|
||||||
@ -229,30 +264,37 @@ for file_path in Path(base_path).glob('**/version.php'):
|
|||||||
version_status = "UNKN"
|
version_status = "UNKN"
|
||||||
integrity_status = "UNKN"
|
integrity_status = "UNKN"
|
||||||
if not check_version(version, newest_version):
|
if not check_version(version, newest_version):
|
||||||
print(bcolors.FAIL, "[WARNING]", bcolors.ENDC, "Outdated Joomla version found!\t[", bcolors.FAIL + version + bcolors.ENDC, "] [", bcolors.WARNING + domain + bcolors.ENDC, "] \tin ", file_path)
|
print(ConsoleColors.FAIL, "[WARNING]", ConsoleColors.ENDC, "Outdated Joomla version found!\t[",
|
||||||
|
ConsoleColors.FAIL + version + ConsoleColors.ENDC, "] [",
|
||||||
|
ConsoleColors.WARNING + domain + ConsoleColors.ENDC, "] \tin ",
|
||||||
|
file_path)
|
||||||
version_status = "WARN"
|
version_status = "WARN"
|
||||||
else:
|
else:
|
||||||
print(bcolors.OKGREEN, "[OK] ", bcolors.ENDC, "Up to date Joomla version found!\t[", bcolors.OKGREEN + version + bcolors.ENDC, "] [", bcolors.WARNING + domain + bcolors.ENDC, "] \tin ", file_path)
|
print(ConsoleColors.OKGREEN, "[OK] ", ConsoleColors.ENDC, "Up to date Joomla version found!\t[",
|
||||||
|
ConsoleColors.OKGREEN + version + ConsoleColors.ENDC, "] [",
|
||||||
|
ConsoleColors.WARNING + domain + ConsoleColors.ENDC, "] \tin ",
|
||||||
|
file_path)
|
||||||
version_status = "OKOK"
|
version_status = "OKOK"
|
||||||
|
|
||||||
if not args.nointegrity:
|
if not args.nointegrity:
|
||||||
print(bcolors.HEADER, " -> Checking file integrity: ", bcolors.ENDC, end=" ")
|
print(ConsoleColors.HEADER, " -> Checking file integrity: ", ConsoleColors.ENDC, end=" ")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
dl_path = download_joomla_version(version)
|
dl_path = download_joomla_version(version)
|
||||||
|
|
||||||
if not dl_path:
|
if not dl_path:
|
||||||
print(bcolors.FAIL, "Failed to download joomla source!", bcolors.ENDC)
|
print(ConsoleColors.FAIL, "Failed to download joomla source!", ConsoleColors.ENDC)
|
||||||
else:
|
else:
|
||||||
orig_root = extract_downloaded_joomla_version(version, dl_path)
|
orig_root = extract_downloaded_joomla_version(version, dl_path)
|
||||||
cms_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(str(file_path))))) # strip "libraries/cms/version/version.php" from filename
|
cms_root = os.path.dirname(os.path.dirname(os.path.dirname(
|
||||||
|
os.path.dirname(str(file_path))))) # strip "libraries/cms/version/version.php" from filename
|
||||||
|
|
||||||
if not orig_root:
|
if not orig_root:
|
||||||
print(bcolors.FAIL, "Failed to extract joomla source!", bcolors.ENDC)
|
print(ConsoleColors.FAIL, "Failed to extract joomla source!", ConsoleColors.ENDC)
|
||||||
else:
|
else:
|
||||||
result_list = cmp_joomla_directories(orig_root, cms_root)
|
result_list = cmp_joomla_directories(orig_root, cms_root)
|
||||||
|
|
||||||
if len(result_list) == 0:
|
if len(result_list) == 0:
|
||||||
print(bcolors.OKGREEN, "OK", bcolors.ENDC)
|
print(ConsoleColors.OKGREEN, "OK", ConsoleColors.ENDC)
|
||||||
integrity_status = "OKOK"
|
integrity_status = "OKOK"
|
||||||
else:
|
else:
|
||||||
# check if only image files differ... if so ignore it.
|
# check if only image files differ... if so ignore it.
|
||||||
@ -265,18 +307,21 @@ for file_path in Path(base_path).glob('**/version.php'):
|
|||||||
real_fail_count = real_fail_count + 1
|
real_fail_count = real_fail_count + 1
|
||||||
|
|
||||||
if real_fail_count == 0:
|
if real_fail_count == 0:
|
||||||
print(bcolors.WARNING, "OK", bcolors.ENDC, "Use -v to get details!")
|
print(ConsoleColors.WARNING, "OK", ConsoleColors.ENDC, "Use -v to get details!")
|
||||||
integrity_status = "WARN"
|
integrity_status = "WARN"
|
||||||
else:
|
else:
|
||||||
print(bcolors.FAIL, "FAIL", bcolors.ENDC, "Use -v to get details!")
|
print(ConsoleColors.FAIL, "FAIL", ConsoleColors.ENDC, "Use -v to get details!")
|
||||||
integrity_status = "FAIL"
|
integrity_status = "FAIL"
|
||||||
|
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
if len(result_list) > 0:
|
if len(result_list) > 0:
|
||||||
print('\tMissmatch: %s' % '\n\tMissmatch: '.join(map(str, result_list)))
|
print('\tMissmatch: %s' % '\n\tMissmatch: '.join(map(str, result_list)))
|
||||||
|
|
||||||
fobj.write(version_status + ";" + integrity_status + ";" + version + ";" + newest_version + ";" + domain + ";" + str(file_path) + "\n")
|
fobj.write(
|
||||||
print("") # empty last line
|
version_status + ";" + integrity_status + ";" + version + ";" + newest_version + ";" + domain + ";" + str(
|
||||||
|
file_path) + "\n")
|
||||||
|
print("") # empty last line
|
||||||
|
|
||||||
fobj.close()
|
fobj.close()
|
||||||
print("\n" + bcolors.HEADER + "All versions written to: ", bcolors.OKBLUE, csv_file_name, bcolors.ENDC)
|
print("\n" + ConsoleColors.HEADER + "All versions written to: ",
|
||||||
|
ConsoleColors.OKBLUE, csv_file_name, ConsoleColors.ENDC)
|
||||||
|
Loading…
Reference in New Issue
Block a user