1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-14 19:10:11 +01:00

Merge branch 'scan-single-apk' into 'master'

Allow for scanning single APKs

See merge request !227
This commit is contained in:
Hans-Christoph Steiner 2017-03-17 14:47:30 +00:00
commit 9b12aa6f71

View File

@ -52,6 +52,17 @@ from .metadata import MetaDataException
METADATA_VERSION = 18 METADATA_VERSION = 18
APK_NAME_PAT = re.compile(".*name='([a-zA-Z0-9._]*)'.*")
APK_VERCODE_PAT = re.compile(".*versionCode='([0-9]*)'.*")
APK_VERNAME_PAT = re.compile(".*versionName='([^']*)'.*")
APK_LABEL_PAT = re.compile(".*label='(.*?)'(\n| [a-z]*?=).*")
APK_ICON_PAT = re.compile(".*application-icon-([0-9]+):'([^']+?)'.*")
APK_ICON_PAT_NODPI = re.compile(".*icon='([^']+?)'.*")
APK_SDK_VERSION_PAT = re.compile(".*'([0-9]*)'.*")
APK_PERMISSION_PAT = \
re.compile(".*(name='(?P<name>.*?)')(.*maxSdkVersion='(?P<maxSdkVersion>.*?)')?.*")
APK_FEATURE_PAT = re.compile(".*name='([^']*)'.*")
screen_densities = ['640', '480', '320', '240', '160', '120'] screen_densities = ['640', '480', '320', '240', '160', '120']
all_screen_densities = ['0'] + screen_densities all_screen_densities = ['0'] + screen_densities
@ -716,49 +727,29 @@ def scan_repo_files(apkcache, repodir, knownapks, use_date_from_file=False):
return repo_files, cachechanged return repo_files, cachechanged
def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False): def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
"""Scan the apks in the given repo directory. """Scan the apk with the given filename in the given repo directory.
This also extracts the icons. This also extracts the icons.
:param apkcache: current apk cache information :param apkcache: current apk cache information
:param apkfilename: the filename of the apk to scan
:param repodir: repo directory to scan :param repodir: repo directory to scan
:param knownapks: known apks info :param knownapks: known apks info
:param use_date_from_apk: use date from APK (instead of current date) :param use_date_from_apk: use date from APK (instead of current date)
for newly added APKs for newly added APKs
:returns: (apks, cachechanged) where apks is a list of apk information, :returns: (skip, apk, cachechanged) where skip is a boolean indicating whether to skip this apk,
and cachechanged is True if the apkcache got changed. apk is the scanned apk information, and cachechanged is True if the apkcache got changed.
""" """
cachechanged = False
for icon_dir in get_all_icon_dirs(repodir):
if os.path.exists(icon_dir):
if options.clean:
shutil.rmtree(icon_dir)
os.makedirs(icon_dir)
else:
os.makedirs(icon_dir)
apks = []
name_pat = re.compile(".*name='([a-zA-Z0-9._]*)'.*")
vercode_pat = re.compile(".*versionCode='([0-9]*)'.*")
vername_pat = re.compile(".*versionName='([^']*)'.*")
label_pat = re.compile(".*label='(.*?)'(\n| [a-z]*?=).*")
icon_pat = re.compile(".*application-icon-([0-9]+):'([^']+?)'.*")
icon_pat_nodpi = re.compile(".*icon='([^']+?)'.*")
sdkversion_pat = re.compile(".*'([0-9]*)'.*")
permission_pat = re.compile(".*(name='(?P<name>.*?)')(.*maxSdkVersion='(?P<maxSdkVersion>.*?)')?.*")
feature_pat = re.compile(".*name='([^']*)'.*")
for apkfile in glob.glob(os.path.join(repodir, '*.apk')):
apkfilename = apkfile[len(repodir) + 1:]
if ' ' in apkfilename: if ' ' in apkfilename:
logging.critical("Spaces in filenames are not allowed.") logging.critical("Spaces in filenames are not allowed.")
sys.exit(1) sys.exit(1)
apkfile = os.path.join(repodir, apkfilename)
shasum = sha256sum(apkfile) shasum = sha256sum(apkfile)
cachechanged = False
usecache = False usecache = False
if apkfilename in apkcache: if apkfilename in apkcache:
apk = apkcache[apkfilename] apk = apkcache[apkfilename]
@ -796,39 +787,39 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
logging.error("Could not find {0} to remove it".format(apkfile)) logging.error("Could not find {0} to remove it".format(apkfile))
else: else:
logging.error("Failed to get apk information, skipping " + apkfile) logging.error("Failed to get apk information, skipping " + apkfile)
continue return True
for line in p.output.splitlines(): for line in p.output.splitlines():
if line.startswith("package:"): if line.startswith("package:"):
try: try:
apk['packageName'] = re.match(name_pat, line).group(1) apk['packageName'] = re.match(APK_NAME_PAT, line).group(1)
apk['versionCode'] = int(re.match(vercode_pat, line).group(1)) apk['versionCode'] = int(re.match(APK_VERCODE_PAT, line).group(1))
apk['versionName'] = re.match(vername_pat, line).group(1) apk['versionName'] = re.match(APK_VERNAME_PAT, line).group(1)
except Exception as e: except Exception as e:
logging.error("Package matching failed: " + str(e)) logging.error("Package matching failed: " + str(e))
logging.info("Line was: " + line) logging.info("Line was: " + line)
sys.exit(1) sys.exit(1)
elif line.startswith("application:"): elif line.startswith("application:"):
apk['name'] = re.match(label_pat, line).group(1) apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
# Keep path to non-dpi icon in case we need it # Keep path to non-dpi icon in case we need it
match = re.match(icon_pat_nodpi, line) match = re.match(APK_ICON_PAT_NODPI, line)
if match: if match:
apk['icons_src']['-1'] = match.group(1) apk['icons_src']['-1'] = match.group(1)
elif line.startswith("launchable-activity:"): elif line.startswith("launchable-activity:"):
# Only use launchable-activity as fallback to application # Only use launchable-activity as fallback to application
if not apk['name']: if not apk['name']:
apk['name'] = re.match(label_pat, line).group(1) apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
if '-1' not in apk['icons_src']: if '-1' not in apk['icons_src']:
match = re.match(icon_pat_nodpi, line) match = re.match(APK_ICON_PAT_NODPI, line)
if match: if match:
apk['icons_src']['-1'] = match.group(1) apk['icons_src']['-1'] = match.group(1)
elif line.startswith("application-icon-"): elif line.startswith("application-icon-"):
match = re.match(icon_pat, line) match = re.match(APK_ICON_PAT, line)
if match: if match:
density = match.group(1) density = match.group(1)
path = match.group(2) path = match.group(2)
apk['icons_src'][density] = path apk['icons_src'][density] = path
elif line.startswith("sdkVersion:"): elif line.startswith("sdkVersion:"):
m = re.match(sdkversion_pat, line) m = re.match(APK_SDK_VERSION_PAT, line)
if m is None: if m is None:
logging.error(line.replace('sdkVersion:', '') logging.error(line.replace('sdkVersion:', '')
+ ' is not a valid minSdkVersion!') + ' is not a valid minSdkVersion!')
@ -838,20 +829,20 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
if 'targetSdkVersion' not in apk: if 'targetSdkVersion' not in apk:
apk['targetSdkVersion'] = m.group(1) apk['targetSdkVersion'] = m.group(1)
elif line.startswith("targetSdkVersion:"): elif line.startswith("targetSdkVersion:"):
m = re.match(sdkversion_pat, line) m = re.match(APK_SDK_VERSION_PAT, line)
if m is None: if m is None:
logging.error(line.replace('targetSdkVersion:', '') logging.error(line.replace('targetSdkVersion:', '')
+ ' is not a valid targetSdkVersion!') + ' is not a valid targetSdkVersion!')
else: else:
apk['targetSdkVersion'] = m.group(1) apk['targetSdkVersion'] = m.group(1)
elif line.startswith("maxSdkVersion:"): elif line.startswith("maxSdkVersion:"):
apk['maxSdkVersion'] = re.match(sdkversion_pat, line).group(1) apk['maxSdkVersion'] = re.match(APK_SDK_VERSION_PAT, line).group(1)
elif line.startswith("native-code:"): elif line.startswith("native-code:"):
apk['nativecode'] = [] apk['nativecode'] = []
for arch in line[13:].split(' '): for arch in line[13:].split(' '):
apk['nativecode'].append(arch[1:-1]) apk['nativecode'].append(arch[1:-1])
elif line.startswith('uses-permission:'): elif line.startswith('uses-permission:'):
perm_match = re.match(permission_pat, line).groupdict() perm_match = re.match(APK_PERMISSION_PAT, line).groupdict()
if perm_match['maxSdkVersion']: if perm_match['maxSdkVersion']:
perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion']) perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion'])
permission = UsesPermission( permission = UsesPermission(
@ -861,7 +852,7 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
apk['uses-permission'].add(permission) apk['uses-permission'].add(permission)
elif line.startswith('uses-permission-sdk-23:'): elif line.startswith('uses-permission-sdk-23:'):
perm_match = re.match(permission_pat, line).groupdict() perm_match = re.match(APK_PERMISSION_PAT, line).groupdict()
if perm_match['maxSdkVersion']: if perm_match['maxSdkVersion']:
perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion']) perm_match['maxSdkVersion'] = int(perm_match['maxSdkVersion'])
permission_sdk_23 = UsesPermissionSdk23( permission_sdk_23 = UsesPermissionSdk23(
@ -872,7 +863,7 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
apk['uses-permission-sdk-23'].add(permission_sdk_23) apk['uses-permission-sdk-23'].add(permission_sdk_23)
elif line.startswith('uses-feature:'): elif line.startswith('uses-feature:'):
feature = re.match(feature_pat, line).group(1) feature = re.match(APK_FEATURE_PAT, line).group(1)
# Filter out this, it's only added with the latest SDK tools and # Filter out this, it's only added with the latest SDK tools and
# causes problems for lots of apps. # causes problems for lots of apps.
if feature != "android.hardware.screen.portrait" \ if feature != "android.hardware.screen.portrait" \
@ -1032,13 +1023,47 @@ def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
default_date_param = None default_date_param = None
# Record in known apks, getting the added date at the same time.. # Record in known apks, getting the added date at the same time..
added = knownapks.recordapk(apk['apkName'], apk['packageName'], default_date=default_date_param) added = knownapks.recordapk(apk['apkName'], apk['packageName'],
default_date=default_date_param)
if added: if added:
apk['added'] = added apk['added'] = added
apkcache[apkfilename] = apk apkcache[apkfilename] = apk
cachechanged = True cachechanged = True
return False, apk, cachechanged
def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
"""Scan the apks in the given repo directory.
This also extracts the icons.
:param apkcache: current apk cache information
:param repodir: repo directory to scan
:param knownapks: known apks info
:param use_date_from_apk: use date from APK (instead of current date)
for newly added APKs
:returns: (apks, cachechanged) where apks is a list of apk information,
and cachechanged is True if the apkcache got changed.
"""
cachechanged = False
for icon_dir in get_all_icon_dirs(repodir):
if os.path.exists(icon_dir):
if options.clean:
shutil.rmtree(icon_dir)
os.makedirs(icon_dir)
else:
os.makedirs(icon_dir)
apks = []
for apkfile in glob.glob(os.path.join(repodir, '*.apk')):
apkfilename = apkfile[len(repodir) + 1:]
(skip, apk, cachechanged) = scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk)
if skip:
continue
apks.append(apk) apks.append(apk)
return apks, cachechanged return apks, cachechanged