import zipfile
import hashlib
import pickle
-import urlparse
+import urllib.parse
from datetime import datetime, timedelta
from xml.dom.minidom import Document
from argparse import ArgumentParser
from pyasn1.error import PyAsn1Error
from pyasn1.codec.der import decoder, encoder
from pyasn1_modules import rfc2315
-from hashlib import md5
from binascii import hexlify, unhexlify
from PIL import Image
import logging
-import common
-import metadata
-from common import FDroidPopen, SdkToolsPopen
-from metadata import MetaDataException
+from . import common
+from . import metadata
+from .common import FDroidPopen, FDroidPopenBytes, SdkToolsPopen
+from .metadata import MetaDataException
+
+METADATA_VERSION = 16
screen_densities = ['640', '480', '320', '240', '160', '120']
cert_encoded = encoder.encode(certificates)[4:]
- return md5(cert_encoded.encode('hex')).hexdigest()
+ return hashlib.md5(hexlify(cert_encoded)).hexdigest()
def scan_apks(apps, apkcache, repodir, knownapks, use_date_from_apk=False):
icon_pat = re.compile(".*application-icon-([0-9]+):'([^']+?)'.*")
icon_pat_nodpi = re.compile(".*icon='([^']+?)'.*")
sdkversion_pat = re.compile(".*'([0-9]*)'.*")
- string_pat = re.compile(".*'([^']*)'.*")
+ string_pat = re.compile(".* name='([^']*)'.*")
for apkfile in glob.glob(os.path.join(repodir, '*.apk')):
apkfilename = apkfile[len(repodir) + 1:]
logging.error(line.replace('sdkVersion:', '')
+ ' is not a valid minSdkVersion!')
else:
- apk['sdkversion'] = m.group(1)
+ apk['minSdkVersion'] = m.group(1)
+ # if target not set, default to min
+ if 'targetSdkVersion' not in apk:
+ apk['targetSdkVersion'] = m.group(1)
+ elif line.startswith("targetSdkVersion:"):
+ m = re.match(sdkversion_pat, line)
+ if m is None:
+ logging.error(line.replace('targetSdkVersion:', '')
+ + ' is not a valid targetSdkVersion!')
+ else:
+ apk['targetSdkVersion'] = m.group(1)
elif line.startswith("maxSdkVersion:"):
- apk['maxsdkversion'] = re.match(sdkversion_pat, line).group(1)
+ apk['maxSdkVersion'] = re.match(sdkversion_pat, line).group(1)
elif line.startswith("native-code:"):
apk['nativecode'] = []
for arch in line[13:].split(' '):
perm = perm[16:]
apk['features'].add(perm)
- if 'sdkversion' not in apk:
+ if 'minSdkVersion' not in apk:
logging.warn("No SDK version information found in {0}".format(apkfile))
- apk['sdkversion'] = 0
+ apk['minSdkVersion'] = 1
# Check for debuggable apks...
if common.isApkDebuggable(apkfile, config):
def cert_fingerprint(data):
digest = hashlib.sha256(data).digest()
ret = []
- ret.append(' '.join("%02X" % ord(b) for b in digest))
+ ret.append(' '.join("%02X" % b for b in bytearray(digest)))
return " ".join(ret)
if 'repo_pubkey' in config:
pubkey = unhexlify(config['repo_pubkey'])
else:
- p = FDroidPopen([config['keytool'], '-exportcert',
- '-alias', config['repo_keyalias'],
- '-keystore', config['keystore'],
- '-storepass:file', config['keystorepassfile']]
- + config['smartcardoptions'],
- output=False, stderr_to_stdout=False)
+ p = FDroidPopenBytes([config['keytool'], '-exportcert',
+ '-alias', config['repo_keyalias'],
+ '-keystore', config['keystore'],
+ '-storepass:file', config['keystorepassfile']]
+ + config['smartcardoptions'],
+ output=False, stderr_to_stdout=False)
if p.returncode != 0 or len(p.output) < 20:
msg = "Failed to get repo pubkey!"
if config['keystore'] == 'NONE':
mirrorcheckfailed = False
for mirror in config.get('mirrors', []):
- base = os.path.basename(urlparse.urlparse(mirror).path.rstrip('/'))
+ base = os.path.basename(urllib.parse.urlparse(mirror).path.rstrip('/'))
if config.get('nonstandardwebroot') is not True and base != 'fdroid':
logging.error("mirror '" + mirror + "' does not end with 'fdroid'!")
mirrorcheckfailed = True
repoel.setAttribute("icon", os.path.basename(config['archive_icon']))
repoel.setAttribute("url", config['archive_url'])
addElement('description', config['archive_description'], doc, repoel)
- urlbasepath = os.path.basename(urlparse.urlparse(config['archive_url']).path)
+ urlbasepath = os.path.basename(urllib.parse.urlparse(config['archive_url']).path)
for mirror in config.get('mirrors', []):
- addElement('mirror', urlparse.urljoin(mirror, urlbasepath), doc, repoel)
+ addElement('mirror', urllib.parse.urljoin(mirror, urlbasepath), doc, repoel)
else:
repoel.setAttribute("name", config['repo_name'])
repoel.setAttribute("icon", os.path.basename(config['repo_icon']))
repoel.setAttribute("url", config['repo_url'])
addElement('description', config['repo_description'], doc, repoel)
- urlbasepath = os.path.basename(urlparse.urlparse(config['repo_url']).path)
+ urlbasepath = os.path.basename(urllib.parse.urlparse(config['repo_url']).path)
for mirror in config.get('mirrors', []):
- addElement('mirror', urlparse.urljoin(mirror, urlbasepath), doc, repoel)
+ addElement('mirror', urllib.parse.urljoin(mirror, urlbasepath), doc, repoel)
- repoel.setAttribute("version", "15")
+ repoel.setAttribute("version", str(METADATA_VERSION))
repoel.setAttribute("timestamp", str(int(time.time())))
nosigningkey = False
logging.warning("\tfdroid update --create-key")
sys.exit(1)
- repoel.setAttribute("pubkey", extract_pubkey())
+ repoel.setAttribute("pubkey", extract_pubkey().decode('utf-8'))
root.appendChild(repoel)
for appid in sortedids:
apkel.appendChild(hashel)
addElement('sig', apk['sig'], doc, apkel)
addElement('size', str(apk['size']), doc, apkel)
- addElement('sdkver', str(apk['sdkversion']), doc, apkel)
- if 'maxsdkversion' in apk:
- addElement('maxsdkver', str(apk['maxsdkversion']), doc, apkel)
+ addElement('sdkver', str(apk['minSdkVersion']), doc, apkel)
+ if 'targetSdkVersion' in apk:
+ addElement('targetSdkVersion', str(apk['targetSdkVersion']), doc, apkel)
+ if 'maxSdkVersion' in apk:
+ addElement('maxsdkver', str(apk['maxSdkVersion']), doc, apkel)
if 'added' in apk:
addElement('added', time.strftime('%Y-%m-%d', apk['added']), doc, apkel)
addElementNonEmpty('permissions', ','.join(apk['permissions']), doc, apkel)
os.symlink(sigfile_path, siglinkname)
if options.pretty:
- output = doc.toprettyxml()
+ output = doc.toprettyxml(encoding='utf-8')
else:
- output = doc.toxml()
+ output = doc.toxml(encoding='utf-8')
with open(os.path.join(repodir, 'index.xml'), 'wb') as f:
f.write(output)
catdata = ''
for cat in categories:
catdata += cat + '\n'
- with open(os.path.join(repodir, 'categories.txt'), 'w') as f:
+ with open(os.path.join(repodir, 'categories.txt'), 'w', encoding='utf8') as f:
f.write(catdata)
to_path = os.path.join(to_dir, filename)
shutil.move(from_path, to_path)
+ logging.debug("Checking archiving for {0} - apks:{1}, keepversions:{2}, archapks:{3}"
+ .format(appid, len(apks), keepversions, len(archapks)))
+
if len(apks) > keepversions:
apklist = filter_apk_list_sorted(apks)
# Move back the ones we don't want.
# Generate a list of categories...
categories = set()
- for app in apps.itervalues():
+ for app in apps.values():
categories.update(app.Categories)
# Read known apks data (will be updated and written back when we've finished)
apkcachefile = os.path.join('tmp', 'apkcache')
if not options.clean and os.path.exists(apkcachefile):
with open(apkcachefile, 'rb') as cf:
- apkcache = pickle.load(cf)
+ apkcache = pickle.load(cf, encoding='utf-8')
+ if apkcache.get("METADATA_VERSION") != METADATA_VERSION:
+ apkcache = {}
else:
apkcache = {}
if 'name' not in apk:
logging.error(apk['id'] + ' does not have a name! Skipping...')
continue
- f = open(os.path.join('metadata', apk['id'] + '.txt'), 'w')
+ f = open(os.path.join('metadata', apk['id'] + '.txt'), 'w', encoding='utf8')
f.write("License:Unknown\n")
f.write("Web Site:\n")
f.write("Source Code:\n")
# Sort the app list by name, then the web site doesn't have to by default.
# (we had to wait until we'd scanned the apks to do this, because mostly the
# name comes from there!)
- sortedids = sorted(apps.iterkeys(), key=lambda appid: apps[appid].Name.upper())
+ sortedids = sorted(apps.keys(), key=lambda appid: apps[appid].Name.upper())
# APKs are placed into multiple repos based on the app package, providing
# per-app subscription feeds for nightly builds and things like it
# Generate latest apps data for widget
if os.path.exists(os.path.join('stats', 'latestapps.txt')):
data = ''
- with open(os.path.join('stats', 'latestapps.txt'), 'r') as f:
+ with open(os.path.join('stats', 'latestapps.txt'), 'r', encoding='utf8') as f:
for line in f:
appid = line.rstrip()
data += appid + "\t"
if app.icon is not None:
data += app.icon + "\t"
data += app.License + "\n"
- with open(os.path.join(repodirs[0], 'latestapps.dat'), 'w') as f:
+ with open(os.path.join(repodirs[0], 'latestapps.dat'), 'w', encoding='utf8') as f:
f.write(data)
if cachechanged:
+ apkcache["METADATA_VERSION"] = METADATA_VERSION
with open(apkcachefile, 'wb') as cf:
pickle.dump(apkcache, cf)