chiark / gitweb /
make sure config exists before writing to it
[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 common
26 from . import net
27 from .exception import FDroidException
28
29
30 def extract_signature(apkpath):
31
32     if not os.path.exists(apkpath):
33         raise FDroidException("file APK does not exists '{}'".format(apkpath))
34     if not common.verify_apk_signature(apkpath):
35         raise FDroidException("no valid signature in '{}'".format(apkpath))
36     logging.debug('signature okay: %s', apkpath)
37
38     appid, vercode, _ = common.get_apk_id_aapt(apkpath)
39     sigdir = common.metadata_get_sigdir(appid, vercode)
40     if not os.path.exists(sigdir):
41         os.makedirs(sigdir)
42     common.apk_extract_signatures(apkpath, sigdir)
43
44     return sigdir
45
46
47 def extract(config, options):
48
49     # Create tmp dir if missing...
50     tmp_dir = 'tmp'
51     if not os.path.exists(tmp_dir):
52         os.mkdir(tmp_dir)
53
54     if not options.APK or len(options.APK) <= 0:
55         logging.critical('no APK supplied')
56         sys.exit(1)
57
58     # iterate over supplied APKs downlaod and extract them...
59     httpre = re.compile('https?:\/\/')
60     for apk in options.APK:
61         try:
62             if os.path.isfile(apk):
63                 sigdir = extract_signature(apk)
64                 logging.info('fetched singatures for %s -> %s', apk, sigdir)
65             elif httpre.match(apk):
66                 if apk.startswith('https') or options.no_check_https:
67                     try:
68                         tmp_apk = os.path.join(tmp_dir, 'signed.apk')
69                         net.download_file(apk, tmp_apk)
70                         sigdir = extract_signature(tmp_apk)
71                         logging.info('fetched singatures for %s -> %s', apk, sigdir)
72                     finally:
73                         if tmp_apk and os.path.exists(tmp_apk):
74                             os.remove(tmp_apk)
75                 else:
76                     logging.warn('refuse downloading via insecure http connection (use https or specify --no-https-check): %s', apk)
77         except FDroidException as e:
78             logging.warning("failed fetching signatures for '%s': %s", apk, e)
79             if e.detail:
80                 logging.debug(e.detail)
81
82
83 def main():
84
85     global config, options
86
87     # Parse command line...
88     parser = ArgumentParser(usage="%(prog)s [options] APK [APK...]")
89     common.setup_global_opts(parser)
90     parser.add_argument("APK", nargs='*',
91                         help="signed APK, either a file-path or Https-URL are fine here.")
92     parser.add_argument("--no-check-https", action="store_true", default=False)
93     options = parser.parse_args()
94
95     # Read config.py...
96     config = common.read_config(options)
97
98     extract(config, options)