chiark / gitweb /
Make permission parsing more specific
[fdroidserver.git] / fdroidserver / update.py
index fdf9653600e8b877ac651bbbf414ed0c68d1c1f3..cb1cb10f0c9e0607cf6117753dd696671b193af1 100644 (file)
@@ -41,9 +41,11 @@ import logging
 
 from . import common
 from . import metadata
-from .common import FDroidPopen, SdkToolsPopen
+from .common import FDroidPopen, FDroidPopenBytes, SdkToolsPopen
 from .metadata import MetaDataException
 
+METADATA_VERSION = 16
+
 screen_densities = ['640', '480', '320', '240', '160', '120']
 
 all_screen_densities = ['0'] + screen_densities
@@ -400,7 +402,7 @@ def getsig(apkpath):
 
     cert_encoded = encoder.encode(certificates)[4:]
 
-    return hashlib.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):
@@ -436,7 +438,7 @@ 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:]
@@ -523,9 +525,19 @@ def scan_apks(apps, apkcache, repodir, knownapks, use_date_from_apk=False):
                         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(' '):
@@ -545,9 +557,9 @@ def scan_apks(apps, apkcache, repodir, knownapks, use_date_from_apk=False):
                             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):
@@ -720,12 +732,12 @@ def extract_pubkey():
     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':
@@ -801,7 +813,7 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
         for mirror in config.get('mirrors', []):
             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
@@ -826,7 +838,7 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
             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:
@@ -936,9 +948,11 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
                 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)
@@ -966,9 +980,9 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
                     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)
@@ -1017,7 +1031,7 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
     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)
 
 
@@ -1046,6 +1060,9 @@ def archive_old_apks(apps, apks, archapks, repodir, archivedir, defaultkeepversi
             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.
@@ -1208,7 +1225,9 @@ def main():
     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 = {}
 
@@ -1226,7 +1245,7 @@ def main():
                 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")
@@ -1301,7 +1320,7 @@ def main():
     # 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
@@ -1336,7 +1355,7 @@ def main():
         # 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"
@@ -1345,10 +1364,11 @@ def main():
                     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)