chiark / gitweb /
Merge branch 'implement-gettext' into 'master'
[fdroidserver.git] / fdroidserver / signatures.py
1 #!/usr/bin/env python3
2 #
3 # Copyright (C) 2017, Michael Poehn <michael.poehn@fsfe.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Affero General Public License for more details.
14 #
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 from argparse import ArgumentParser
19
20 import re
21 import os
22 import sys
23 import logging
24
25 from . import _
26 from . import common
27 from . import net
28 from .exception import FDroidException
29
30
31 def extract_signature(apkpath):
32
33     if not os.path.exists(apkpath):
34         raise FDroidException("file APK does not exists '{}'".format(apkpath))
35     if not common.verify_apk_signature(apkpath):
36         raise FDroidException("no valid signature in '{}'".format(apkpath))
37     logging.debug('signature okay: %s', apkpath)
38
39     appid, vercode, _ = common.get_apk_id_aapt(apkpath)
40     sigdir = common.metadata_get_sigdir(appid, vercode)
41     if not os.path.exists(sigdir):
42         os.makedirs(sigdir)
43     common.apk_extract_signatures(apkpath, sigdir)
44
45     return sigdir
46
47
48 def extract(config, options):
49
50     # Create tmp dir if missing...
51     tmp_dir = 'tmp'
52     if not os.path.exists(tmp_dir):
53         os.mkdir(tmp_dir)
54
55     if not options.APK or len(options.APK) <= 0:
56         logging.critical(_('no APK supplied'))
57         sys.exit(1)
58
59     # iterate over supplied APKs downlaod and extract them...
60     httpre = re.compile('https?:\/\/')
61     for apk in options.APK:
62         try:
63             if os.path.isfile(apk):
64                 sigdir = extract_signature(apk)
65                 logging.info(_('fetched signatures for %s -> %s'), apk, sigdir)
66             elif httpre.match(apk):
67                 if apk.startswith('https') or options.no_check_https:
68                     try:
69                         tmp_apk = os.path.join(tmp_dir, 'signed.apk')
70                         net.download_file(apk, tmp_apk)
71                         sigdir = extract_signature(tmp_apk)
72                         logging.info(_('fetched signatures for %s -> %s'), apk, sigdir)
73                     finally:
74                         if tmp_apk and os.path.exists(tmp_apk):
75                             os.remove(tmp_apk)
76                 else:
77                     logging.warn(_('refuse downloading via insecure http connection (use https or specify --no-https-check): %s'), apk)
78         except FDroidException as e:
79             logging.warning(_("failed fetching signatures for '%s': %s"), apk, e)
80             if e.detail:
81                 logging.debug(e.detail)
82
83
84 def main():
85
86     global config, options
87
88     # Parse command line...
89     parser = ArgumentParser(usage="%(prog)s [options] APK [APK...]")
90     common.setup_global_opts(parser)
91     parser.add_argument("APK", nargs='*',
92                         help=_("signed APK, either a file-path or HTTPS URL."))
93     parser.add_argument("--no-check-https", action="store_true", default=False)
94     options = parser.parse_args()
95
96     # Read config.py...
97     config = common.read_config(options)
98
99     extract(config, options)