From 84bb41a91f71ef6d3e2acfb09232f4e26c7c4421 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 15 May 2017 17:27:48 +0200 Subject: [PATCH 1/6] metadata: switch from deprecated cgi.escape to html.escape cgi.escape is deprecated in Python 3.x and has security issues: https://bugs.python.org/issue26398 html.escape() differs from cgi.escape() by its defaults to quote=True: s = html.escape( """& < " ' >""" ) # s = '& < " ' >' --- fdroidserver/metadata.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fdroidserver/metadata.py b/fdroidserver/metadata.py index c650ddbc..301a2de0 100644 --- a/fdroidserver/metadata.py +++ b/fdroidserver/metadata.py @@ -21,7 +21,7 @@ import json import os import re import glob -import cgi +import html import logging import textwrap import io @@ -492,10 +492,10 @@ class DescriptionFormatter: self.laststate = self.state self.state = self.stNONE - def formatted(self, txt, html): + def formatted(self, txt, htmlbody): res = '' - if html: - txt = cgi.escape(txt) + if htmlbody: + txt = html.escape(txt, quote=False) while True: index = txt.find("''") if index == -1: @@ -503,7 +503,7 @@ class DescriptionFormatter: res += txt[:index] txt = txt[index:] if txt.startswith("'''"): - if html: + if htmlbody: if self.bold: res += '' else: @@ -511,7 +511,7 @@ class DescriptionFormatter: self.bold = not self.bold txt = txt[3:] else: - if html: + if htmlbody: if self.ital: res += '' else: @@ -538,7 +538,7 @@ class DescriptionFormatter: url, urltext = self.linkResolver(url) else: urltext = url - res_html += '' + cgi.escape(urltext) + '' + res_html += '' + html.escape(urltext, quote=False) + '' res_plain += urltext txt = txt[index + 2:] else: @@ -554,7 +554,7 @@ class DescriptionFormatter: url = url[:index2] if url == urltxt: warn_or_exception("Url title is just the URL - use [url]") - res_html += '' + cgi.escape(urltxt) + '' + res_html += '' + html.escape(urltxt, quote=False) + '' res_plain += urltxt if urltxt != url: res_plain += ' (' + url + ')' From 5d705452f581b851bcc2f8aa3e8cdc7d50b61de7 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 15 May 2017 19:47:31 +0200 Subject: [PATCH 2/6] update: allow repo files to use _ in the file names _ is a valid character for Java package names, so it should also work in the repo file naming scheme. This makes it so it only splits the file name based on the last _. --- fdroidserver/update.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 1cb2fc4e..41d0bd14 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -849,12 +849,13 @@ def scan_repo_files(apkcache, repodir, knownapks, use_date_from_file=False): repo_file['versionName'] = shasum # the static ID is the SHA256 unless it is set in the metadata repo_file['packageName'] = shasum - n = name_utf8.split('_') + + n = name_utf8.rsplit('_', maxsplit=1) if len(n) == 2: packageName = n[0] versionCode = n[1].split('.')[0] if re.match('^-?[0-9]+$', versionCode) \ - and common.is_valid_package_name(name_utf8.split('_')[0]): + and common.is_valid_package_name(n[0]): repo_file['packageName'] = packageName repo_file['versionCode'] = int(versionCode) srcfilename = name + b'_src.tar.gz' From a17b95a860e6762c2a8c48989775ac4f25d9d9e2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 15 May 2017 19:48:20 +0200 Subject: [PATCH 3/6] update: improved logging the aapt warnings were very verbose, and the other now includes the file name type for screenshots, rather than the filename twice. --- fdroidserver/update.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 41d0bd14..4d9777b8 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -783,7 +783,7 @@ def insert_localized_app_metadata(apps): graphics[base] = filename elif screenshotdir in SCREENSHOT_DIRS: # there can any number of these per locale - logging.debug('adding ' + base + ':' + f) + logging.debug('adding to ' + screenshotdir + ': ' + f) if screenshotdir not in graphics: graphics[screenshotdir] = [] graphics[screenshotdir].append(filename) @@ -1126,10 +1126,8 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk): try: if common.set_command_in_config('aapt'): - logging.warning("Using AAPT for metadata") scan_apk_aapt(apk, apkfile) else: - logging.warning("Using androguard for metadata") scan_apk_androguard(apk, apkfile) except BuildException: return True, None, False From 77a65be9db71e59f46c75aaa6e5ab09249ace276 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 15 May 2017 20:11:41 +0200 Subject: [PATCH 4/6] update: skip packages without metadata in index V1 If a package does not have a matching "app" metadata file, then it should not be added to the index. index V0 already does this properly. --- fdroidserver/index.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fdroidserver/index.py b/fdroidserver/index.py index aae2e1b6..6eda4a53 100644 --- a/fdroidserver/index.py +++ b/fdroidserver/index.py @@ -169,7 +169,7 @@ def make_v1(apps, packages, repodir, repodict, requestsdict): appslist = [] output['apps'] = appslist - for appid, appdict in apps.items(): + for packageName, appdict in apps.items(): d = collections.OrderedDict() appslist.append(d) for k, v in sorted(appdict.items()): @@ -190,7 +190,7 @@ def make_v1(apps, packages, repodir, repodict, requestsdict): elif k == 'CurrentVersion': # TODO make SuggestedVersionName the canonical name k = 'suggestedVersionName' elif k == 'AutoName': - if 'Name' not in apps[appid]: + if 'Name' not in apps[packageName]: d['name'] = v continue else: @@ -201,6 +201,9 @@ def make_v1(apps, packages, repodir, repodict, requestsdict): output['packages'] = output_packages for package in packages: packageName = package['packageName'] + if packageName not in apps: + logging.info('Ignoring package without metadata: ' + package['apkName']) + continue if packageName in output_packages: packagelist = output_packages[packageName] else: From c348186ad65daefbcffdf79e2111fcc9bad00760 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 16 May 2017 12:25:42 +0200 Subject: [PATCH 5/6] update: fix fastlane scraping, it uses a subdir called 'images' https://commons.wikimedia.org/wiki/File:GetChromium_FeatureGraphic_1024x500.png --- .gitignore | 1 + fdroidserver/update.py | 13 ++++++++----- .../en-US/images/featureGraphic.png | Bin 0 -> 37877 bytes .../en-US/images/icon.png | Bin 0 -> 1413 bytes tests/update.TestCase | 11 ++++++++++- 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 tests/metadata/info.guardianproject.urzip/en-US/images/featureGraphic.png create mode 100644 tests/metadata/info.guardianproject.urzip/en-US/images/icon.png diff --git a/.gitignore b/.gitignore index 69c27f93..0ad4ccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,6 @@ makebuildserver.config.py /tests/archive/index-v1.jar /tests/repo/index.jar /tests/repo/index-v1.jar +/tests/repo/info.guardianproject.urzip/ /tests/urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234.apk /unsigned/ diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 4d9777b8..2c590ac9 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -676,14 +676,14 @@ def insert_localized_app_metadata(apps): must be PNG or JPEG files ending with ".png", ".jpg", or ".jpeg" and must be in the following layout: # TODO replace these docs with link to All_About_Descriptions_Graphics_and_Screenshots - # TODO mention that the 'localized' section is not in metadata.yml, so key names are like Java vars: camelCase with first letter lowercase. + repo/packageName/locale/featureGraphic.png repo/packageName/locale/phoneScreenshots/1.png repo/packageName/locale/phoneScreenshots/2.png The changelog files must be text files named with the versionCode ending with ".txt" and must be in the following layout: - https://github.com/fastlane/fastlane/blob/1.109.0/supply/README.md#changelogs-whats-new + https://github.com/fastlane/fastlane/blob/2.28.7/supply/README.md#changelogs-whats-new repo/packageName/locale/changelogs/12345.txt @@ -701,7 +701,7 @@ def insert_localized_app_metadata(apps): metadata/ folder and the apps' source repos for standard locations of graphic and screenshot files. If it finds them, it will copy them into the repo. The fastlane files follow this pattern: - https://github.com/fastlane/fastlane/blob/1.109.0/supply/README.md#images-and-screenshots + https://github.com/fastlane/fastlane/blob/2.28.7/supply/README.md#images-and-screenshots """ @@ -718,7 +718,6 @@ def insert_localized_app_metadata(apps): logging.debug(packageName + ' does not have app metadata, skipping l18n scan.') continue locale = segments[-1] - destdir = os.path.join('repo', packageName, locale) for f in files: if f == 'full_description.txt': _set_localized_text_entry(apps[packageName], locale, 'description', @@ -737,11 +736,15 @@ def insert_localized_app_metadata(apps): os.path.join(root, f)) continue elif f == str(apps[packageName]['CurrentVersionCode']) + '.txt': - _set_localized_text_entry(apps[packageName], segments[-2], 'whatsNew', + locale = segments[-2] + _set_localized_text_entry(apps[packageName], locale, 'whatsNew', os.path.join(root, f)) continue base, extension = common.get_extension(f) + if locale == 'images': + locale = segments[-2] + destdir = os.path.join('repo', packageName, locale) if base in GRAPHIC_NAMES and extension in ALLOWED_EXTENSIONS: os.makedirs(destdir, mode=0o755, exist_ok=True) logging.debug('copying ' + os.path.join(root, f) + ' ' + destdir) diff --git a/tests/metadata/info.guardianproject.urzip/en-US/images/featureGraphic.png b/tests/metadata/info.guardianproject.urzip/en-US/images/featureGraphic.png new file mode 100644 index 0000000000000000000000000000000000000000..cb6720cbda546f57b5325d6791c92c644857a945 GIT binary patch literal 37877 zcmYIw2RxSV+x~4l2-&N$vMX6dM4@FBMP-!8NTifxWYbWRy$Kc4K+!;ES&?0+(2$iq zlKnq!-}in0&+qf;+fSbRzV7Qfuk$>P<2&OXhf?n%R4b|<;oyXnU}&0PnJgALOeTJ~j$ zm;^C&=xr~!wfK*62EqbjLRQne@gp*bpHZku`F(7IltNZ%?otb(0^5v(`pN|_Gw=b9W_4VDo zdv|oD{nMwJ7NO(`hYlSwU;P|AcFf$oc*R0-`}VfBw)vH(j~{Iie^Yin6|GKVkbvSn}F(JXs#DsZ+Qhj~>(^9$I++4XGJ9a23oyqIGvr=uN zu&|Y?P=Bti|-Te$;rv$;^Nn@U#CzgbE}{9^z@mT85c?J>$h*;e*XM9 z#ugtR9}*H085xkTef-!l zZhW}7xOjMY@E0DQ<42Evi>#_$|zIgG%86Ovy;g!tfWPzLSjvP61`nk!{%03

PJ&k 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 VbS$l3WQl8zb98n%Y45e}{{TFR5r+T( literal 0 HcmV?d00001 diff --git a/tests/metadata/info.guardianproject.urzip/en-US/images/icon.png b/tests/metadata/info.guardianproject.urzip/en-US/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..21439b7b84cf9900c9c44cf62e177688d5560fb1 GIT binary patch literal 1413 zcmV;01$z34P)J%d*P?3pm8#f{3Uf1cDbfGT=A@qNNUb6mo`;Am;c_Lrpc7 zR$iFOp@?FW#t~T=*5sJl0qY_#f~W{AEU0_D{ozECP*~5O@AsYO^FH5;!8Fv?dH|u( znQ)g0m)#v}wj6e# z*-AnG2-uvq6u5JRFP-Mp^vQXA zdwe&PuFeSYa}QFu$=goW)=|+LdE_XRoCu9e7EW%y;fcB0O_UrI6x4g@a0S)Xou<-~ z9TXlmi}(Ujt2$_&yUZ&P9TqFzEldXhc*um=0Re&Jq4w-^^VNn)RDP39zT%0wT$CIf z9Nc@Mp^2VW|NVPZR9HX~nWEZ%cC1XH3Bo85qO=;shWUB%?YN!x0&!h>$w6*HM&8us zT5U*gV{;1;5e*Cs(AdL=WHy^=XlRHGLqqgtQ6b3`s%q{0#WGL-P`p!+iW}9XY?+Io z&e>I23IIpG(?2mDHFP4-M}<|(qIoKrC{3%?_BOX%BqAb<*+jd`N@;a!8s%hfqtx^@ z)YIEXW1~jeUXVv3iTt=g86Z}8`GbfUm0WyOBvWUPPwk;}ix3H&7dkrFW!l+tPfJ`~ zhtHn-V#2oE((7o`tC_TJ?OLj=J~du9jtovGXSyBsfER28N(yy zQrN5zN?ZF1JvN!?@7n+22edTtB^oh~PN z>TccMN)}5to6WUbyl4?PY}PY}uTZ!_Bocwg<4veL@EuWFwu>=aEUAZ&RWp1p%LM@a zxo)G9yY!W8N!i(T^Opm^wY9@XqnY&TG7JL-05i!N-_Z$WyWV0lRwu6-FpR_!U>OJe zs_Vp=#vcSGvsJ<2+Ja}t!+(*Jz!?eg@hmQf%^?~O0QLjJ=#q|}{;g3l3*!xg1L*4N zMxg(U=LUTJRTD~fzID@vVNAp@HscBU2aUyJk4*j@di|JA*N*!7dRVRW+~!%?x%&3* zzD&Ly=SKj40fa)qa+#|*vZuQv(J=U+{7h|4Pu0m&(>HH!L{@gL{>IIoje~vN4MfCD z+L)3M0irI(*FzfP=j;1>W%bDk*YWeSwX`N<9a${a@mfn$18KEdJ=aMZ2Vl~NHXfz= z{3Yz$T~2MCea$S>@Wvg*h3!=}wNp;s(%gt`xdq*~?;E!~HuN>@eedn*Mc%h32LLd` zL%P7%*Qc$r=G24)W{ZWI8ZOYxnKL^@Zk|g3;N|>1=(zaVPDlgsu0Pu1XF7Wly zw4JD`p(BSrrRbO!DL*fl)SjLL0;$Ie07zV)4*<^x0D!Ws$p8RvH(`R*RnjGs$tZlz z9P;+^BzuAQXln7tocT!`#`~uN*h~f3@qP``6X#=7>e4UywoKBP(NM2YxRY2SF>@UR zXR6C{OqB=s;(zPmz;%SqP$3~AKqeA6G}+oY^s58s2B=;LpU~Iw%`p%Wyp-~MsnREj z0W*Ebm2)4#md(IlEnV*DB=||KjaK=D#ZBlj-O~ds7c7p8;Y7wQVTDCR0s#I1LCmDA T+X8aK00000NkvXXu0mjfAS<@z literal 0 HcmV?d00001 diff --git a/tests/update.TestCase b/tests/update.TestCase index 4e204ae6..be1a7266 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -37,6 +37,8 @@ class UpdateTest(unittest.TestCase): fdroidserver.update.options = fdroidserver.common.options os.chdir(os.path.join(localmodule, 'tests')) + shutil.rmtree(os.path.join('repo', 'info.guardianproject.urzip'), ignore_errors=True) + apps = dict() for packageName in ('info.guardianproject.urzip', 'org.videolan.vlc', 'obb.mainpatch.current'): apps[packageName] = dict() @@ -45,17 +47,23 @@ class UpdateTest(unittest.TestCase): apps['info.guardianproject.urzip']['CurrentVersionCode'] = 100 fdroidserver.update.insert_localized_app_metadata(apps) + appdir = os.path.join('repo', 'info.guardianproject.urzip', 'en-US') + self.assertTrue(os.path.isfile(os.path.join(appdir, 'icon.png'))) + self.assertTrue(os.path.isfile(os.path.join(appdir, 'featureGraphic.png'))) + self.assertEqual(3, len(apps)) for packageName, app in apps.items(): self.assertTrue('localized' in app) self.assertTrue('en-US' in app['localized']) self.assertEqual(1, len(app['localized'])) if packageName == 'info.guardianproject.urzip': - self.assertEqual(5, len(app['localized']['en-US'])) + self.assertEqual(7, len(app['localized']['en-US'])) self.assertEqual('full description\n', app['localized']['en-US']['description']) self.assertEqual('title\n', app['localized']['en-US']['name']) self.assertEqual('short description\n', app['localized']['en-US']['summary']) self.assertEqual('video\n', app['localized']['en-US']['video']) + self.assertEqual('icon.png', app['localized']['en-US']['icon']) + self.assertEqual('featureGraphic.png', app['localized']['en-US']['featureGraphic']) self.assertEqual('100\n', app['localized']['en-US']['whatsNew']) elif packageName == 'org.videolan.vlc': self.assertEqual('icon.png', app['localized']['en-US']['icon']) @@ -244,6 +252,7 @@ class UpdateTest(unittest.TestCase): fdroidserver.update.options = type('', (), {})() fdroidserver.update.options.clean = True + fdroidserver.update.options.rename_apks = False fdroidserver.update.options.delete_unknown = True for icon_dir in fdroidserver.update.get_all_icon_dirs('repo'): From 0345d2c79f11501110b69307a4f9922dd8bb1d7b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 18 May 2017 17:20:24 +0200 Subject: [PATCH 6/6] update: find aapt when it is not in the PATH This is some very messy logic built up since 2010. This will all go away once we have a python3 version of androguard available. The removed imports and `dir(APK)` is to silence pyflakes closes #303 --- fdroidserver/build.py | 2 +- fdroidserver/common.py | 2 +- fdroidserver/update.py | 2 +- tests/androguard_test.py | 9 +++------ 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/fdroidserver/build.py b/fdroidserver/build.py index 5b21939b..bf790970 100644 --- a/fdroidserver/build.py +++ b/fdroidserver/build.py @@ -544,7 +544,7 @@ def get_apk_metadata_androguard(apkfile): def get_metadata_from_apk(app, build, apkfile): """get the required metadata from the built APK""" - if common.set_command_in_config('aapt'): + if common.SdkToolsPopen(['aapt', 'version'], output=False): vercode, version, foundid, nativecode = get_apk_metadata_aapt(apkfile) else: vercode, version, foundid, nativecode = get_apk_metadata_androguard(apkfile) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index df90e5f4..cc1df84e 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -1727,7 +1727,7 @@ def isApkAndDebuggable(apkfile, config): if get_file_extension(apkfile) != 'apk': return False - if set_command_in_config('aapt'): + if SdkToolsPopen(['aapt', 'version'], output=False): return get_apk_debuggable_aapt(apkfile) else: return get_apk_debuggable_androguard(apkfile) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 2c590ac9..71277c3e 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -1128,7 +1128,7 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk): apk['antiFeatures'].add('KnownVuln') try: - if common.set_command_in_config('aapt'): + if SdkToolsPopen(['aapt', 'version'], output=False): scan_apk_aapt(apk, apkfile) else: scan_apk_androguard(apk, apkfile) diff --git a/tests/androguard_test.py b/tests/androguard_test.py index 9e5d845a..c708b85f 100644 --- a/tests/androguard_test.py +++ b/tests/androguard_test.py @@ -4,12 +4,8 @@ import inspect import logging import optparse import os -import shutil import sys -import tempfile import unittest -import yaml -from binascii import unhexlify localmodule = os.path.realpath( os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..')) @@ -53,9 +49,10 @@ class UpdateTest(unittest.TestCase): fdroidserver.update.options.clean = True fdroidserver.update.options.delete_unknown = True - self.assertTrue(fdroidserver.common.set_command_in_config('aapt')) + self.assertTrue(fdroidserver.common.SdkToolsPopen('aapt')) try: from androguard.core.bytecodes.apk import APK + dir(APK) except ImportError: raise Exception("androguard not installed!") @@ -89,4 +86,4 @@ if __name__ == "__main__": newSuite = unittest.TestSuite() newSuite.addTest(unittest.makeSuite(UpdateTest)) - unittest.main() \ No newline at end of file + unittest.main()