mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-06-03 06:10:10 +02:00
18f3acc32e
There is no longer any reason for these to be intertwined. This deliberately avoids touching some files as much as possible because they are super tangled and due to be replaced. Those files are: * fdroidserver/build.py * fdroidserver/update.py # Conflicts: # tests/testcommon.py # Conflicts: # fdroidserver/btlog.py # fdroidserver/import_subcommand.py
300 lines
11 KiB
Python
300 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# init.py - part of the FDroid server tools
|
|
# Copyright (C) 2010-2013, Ciaran Gultnieks, ciaran@ciarang.com
|
|
# Copyright (C) 2013-2014 Daniel Martí <mvdan@mvdan.cc>
|
|
# Copyright (C) 2013 Hans-Christoph Steiner <hans@eds.org>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import glob
|
|
import os
|
|
import re
|
|
import shutil
|
|
import socket
|
|
import sys
|
|
from argparse import ArgumentParser
|
|
import logging
|
|
|
|
from . import _
|
|
from . import common
|
|
from .exception import FDroidException
|
|
|
|
config = {}
|
|
|
|
|
|
def disable_in_config(key, value):
|
|
"""Write a key/value to the local config.yml, then comment it out."""
|
|
import yaml
|
|
|
|
with open('config.yml') as f:
|
|
data = f.read()
|
|
pattern = r'\n[\s#]*' + key + r':.*'
|
|
repl = '\n#' + yaml.dump({key: value}, default_flow_style=False)
|
|
data = re.sub(pattern, repl, data)
|
|
with open('config.yml', 'w') as f:
|
|
f.writelines(data)
|
|
|
|
|
|
def main():
|
|
global config
|
|
|
|
# Parse command line...
|
|
parser = ArgumentParser()
|
|
common.setup_global_opts(parser)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--distinguished-name",
|
|
default=None,
|
|
help=_("X.509 'Distinguished Name' used when generating keys"),
|
|
)
|
|
parser.add_argument(
|
|
"--keystore",
|
|
default=None,
|
|
help=_("Path to the keystore for the repo signing key"),
|
|
)
|
|
parser.add_argument(
|
|
"--repo-keyalias",
|
|
default=None,
|
|
help=_("Alias of the repo signing key in the keystore"),
|
|
)
|
|
parser.add_argument(
|
|
"--android-home",
|
|
default=None,
|
|
help=_("Path to the Android SDK (sometimes set in ANDROID_HOME)"),
|
|
)
|
|
parser.add_argument(
|
|
"--no-prompt",
|
|
action="store_true",
|
|
default=False,
|
|
help=_("Do not prompt for Android SDK path, just fail"),
|
|
)
|
|
options = common.parse_args(parser)
|
|
|
|
common.set_console_logging(options.verbose)
|
|
|
|
fdroiddir = os.getcwd()
|
|
test_config = dict()
|
|
examplesdir = common.get_examples_dir()
|
|
common.fill_config_defaults(test_config)
|
|
|
|
# track down where the Android SDK is, the default is to use the path set
|
|
# in ANDROID_HOME if that exists, otherwise None
|
|
if options.android_home is not None:
|
|
test_config['sdk_path'] = options.android_home
|
|
elif not common.test_sdk_exists(test_config):
|
|
# if neither --android-home nor the default sdk_path
|
|
# exist, prompt the user using platform-specific default
|
|
# and if the user leaves it blank, ignore and move on.
|
|
default_sdk_path = ''
|
|
if sys.platform in ('win32', 'cygwin'):
|
|
p = os.path.join(
|
|
os.getenv('USERPROFILE'), 'AppData', 'Local', 'Android', 'android-sdk'
|
|
)
|
|
elif sys.platform == 'darwin':
|
|
# on OSX, Homebrew is common and has an easy path to detect
|
|
p = '/usr/local/opt/android-sdk'
|
|
elif os.path.isdir('/usr/lib/android-sdk'):
|
|
# if the Debian packages are installed, suggest them
|
|
p = '/usr/lib/android-sdk'
|
|
else:
|
|
p = '/opt/android-sdk'
|
|
if os.path.exists(p):
|
|
default_sdk_path = p
|
|
test_config['sdk_path'] = default_sdk_path
|
|
|
|
if not common.test_sdk_exists(test_config):
|
|
del test_config['sdk_path']
|
|
while not options.no_prompt:
|
|
try:
|
|
s = input(
|
|
_('Enter the path to the Android SDK (%s) here:\n> ')
|
|
% default_sdk_path
|
|
)
|
|
except KeyboardInterrupt:
|
|
print('')
|
|
sys.exit(1)
|
|
if re.match(r'^\s*$', s) is not None:
|
|
test_config['sdk_path'] = default_sdk_path
|
|
else:
|
|
test_config['sdk_path'] = s
|
|
if common.test_sdk_exists(test_config):
|
|
break
|
|
default_sdk_path = ''
|
|
|
|
if test_config.get('sdk_path') and not common.test_sdk_exists(test_config):
|
|
raise FDroidException(
|
|
_("Android SDK not found at {path}!").format(path=test_config['sdk_path'])
|
|
)
|
|
|
|
if not os.path.exists('config.yml') and not os.path.exists('config.py'):
|
|
# 'metadata' and 'tmp' are created in fdroid
|
|
if not os.path.exists('repo'):
|
|
os.mkdir('repo')
|
|
example_config_yml = os.path.join(examplesdir, 'config.yml')
|
|
if os.path.exists(example_config_yml):
|
|
shutil.copyfile(example_config_yml, 'config.yml')
|
|
else:
|
|
from pkg_resources import get_distribution
|
|
|
|
versionstr = get_distribution('fdroidserver').version
|
|
if not versionstr:
|
|
versionstr = 'master'
|
|
with open('config.yml', 'w') as fp:
|
|
fp.write('# see https://gitlab.com/fdroid/fdroidserver/blob/')
|
|
fp.write(versionstr)
|
|
fp.write('/examples/config.yml\n')
|
|
os.chmod('config.yml', 0o0600)
|
|
# If android_home is None, test_config['sdk_path'] will be used and
|
|
# "$ANDROID_HOME" may be used if the env var is set up correctly.
|
|
# If android_home is not None, the path given from the command line
|
|
# will be directly written in the config.
|
|
if 'sdk_path' in test_config:
|
|
common.write_to_config(test_config, 'sdk_path', options.android_home)
|
|
else:
|
|
logging.warning(
|
|
'Looks like this is already an F-Droid repo, cowardly refusing to overwrite it...'
|
|
)
|
|
logging.info('Try running `fdroid init` in an empty directory.')
|
|
raise FDroidException('Repository already exists.')
|
|
|
|
# now that we have a local config.yml, read configuration...
|
|
config = common.read_config()
|
|
|
|
# the NDK is optional and there may be multiple versions of it, so it's
|
|
# left for the user to configure
|
|
|
|
# find or generate the keystore for the repo signing key. First try the
|
|
# path written in the default config.yml. Then check if the user has
|
|
# specified a path from the command line, which will trump all others.
|
|
# Otherwise, create ~/.local/share/fdroidserver and stick it in there. If
|
|
# keystore is set to NONE, that means that Java will look for keys in a
|
|
# Hardware Security Module aka Smartcard.
|
|
keystore = config['keystore']
|
|
if options.keystore:
|
|
keystore = os.path.abspath(options.keystore)
|
|
if options.keystore == 'NONE':
|
|
keystore = options.keystore
|
|
else:
|
|
keystore = os.path.abspath(options.keystore)
|
|
if not os.path.exists(keystore):
|
|
logging.info(
|
|
'"' + keystore + '" does not exist, creating a new keystore there.'
|
|
)
|
|
common.write_to_config(test_config, 'keystore', keystore)
|
|
repo_keyalias = None
|
|
keydname = None
|
|
if options.repo_keyalias:
|
|
repo_keyalias = options.repo_keyalias
|
|
common.write_to_config(test_config, 'repo_keyalias', repo_keyalias)
|
|
if options.distinguished_name:
|
|
keydname = options.distinguished_name
|
|
common.write_to_config(test_config, 'keydname', keydname)
|
|
if keystore == 'NONE': # we're using a smartcard
|
|
common.write_to_config(
|
|
test_config, 'repo_keyalias', '1'
|
|
) # seems to be the default
|
|
disable_in_config('keypass', 'never used with smartcard')
|
|
common.write_to_config(
|
|
test_config,
|
|
'smartcardoptions',
|
|
(
|
|
'-storetype PKCS11 '
|
|
+ '-providerClass sun.security.pkcs11.SunPKCS11 '
|
|
+ '-providerArg opensc-fdroid.cfg'
|
|
),
|
|
)
|
|
# find opensc-pkcs11.so
|
|
if not os.path.exists('opensc-fdroid.cfg'):
|
|
if os.path.exists('/usr/lib/opensc-pkcs11.so'):
|
|
opensc_so = '/usr/lib/opensc-pkcs11.so'
|
|
elif os.path.exists('/usr/lib64/opensc-pkcs11.so'):
|
|
opensc_so = '/usr/lib64/opensc-pkcs11.so'
|
|
else:
|
|
files = glob.glob(
|
|
'/usr/lib/' + os.uname()[4] + '-*-gnu/opensc-pkcs11.so'
|
|
)
|
|
if len(files) > 0:
|
|
opensc_so = files[0]
|
|
else:
|
|
opensc_so = '/usr/lib/opensc-pkcs11.so'
|
|
logging.warning(
|
|
'No OpenSC PKCS#11 module found, '
|
|
+ 'install OpenSC then edit "opensc-fdroid.cfg"!'
|
|
)
|
|
with open('opensc-fdroid.cfg', 'w') as f:
|
|
f.write('name = OpenSC\nlibrary = ')
|
|
f.write(opensc_so)
|
|
f.write('\n')
|
|
logging.info(
|
|
"Repo setup using a smartcard HSM. Please edit keystorepass and repo_keyalias in config.yml."
|
|
)
|
|
logging.info(
|
|
"If you want to generate a new repo signing key in the HSM you can do that with 'fdroid update "
|
|
"--create-key'."
|
|
)
|
|
elif os.path.exists(keystore):
|
|
to_set = ['keystorepass', 'keypass', 'repo_keyalias', 'keydname']
|
|
if repo_keyalias:
|
|
to_set.remove('repo_keyalias')
|
|
if keydname:
|
|
to_set.remove('keydname')
|
|
logging.warning(
|
|
'\n'
|
|
+ _('Using existing keystore "{path}"').format(path=keystore)
|
|
+ '\n'
|
|
+ _('Now set these in config.yml:')
|
|
+ ' '
|
|
+ ', '.join(to_set)
|
|
+ '\n'
|
|
)
|
|
else:
|
|
password = common.genpassword()
|
|
c = dict(test_config)
|
|
c['keystorepass'] = password
|
|
c['keypass'] = password
|
|
c['repo_keyalias'] = repo_keyalias or socket.getfqdn()
|
|
c['keydname'] = 'CN=' + c['repo_keyalias'] + ', OU=F-Droid'
|
|
common.write_to_config(test_config, 'keystorepass', password)
|
|
common.write_to_config(test_config, 'keypass', password)
|
|
common.write_to_config(test_config, 'repo_keyalias', c['repo_keyalias'])
|
|
common.write_to_config(test_config, 'keydname', c['keydname'])
|
|
common.genkeystore(c)
|
|
|
|
msg = '\n'
|
|
msg += _('Built repo based in "%s" with this config:') % fdroiddir
|
|
msg += '\n\n Android SDK:\t\t\t' + config['sdk_path']
|
|
msg += '\n ' + _('Keystore for signing key:\t') + keystore
|
|
if repo_keyalias is not None:
|
|
msg += '\n Alias for key in store:\t' + repo_keyalias
|
|
msg += '\n\n'
|
|
msg += (
|
|
_(
|
|
"""To complete the setup, add your APKs to "%s"
|
|
then run "fdroid update -c; fdroid update". You might also want to edit
|
|
"config.yml" to set the URL, repo name, and more. You should also set up
|
|
a signing key (a temporary one might have been automatically generated).
|
|
|
|
For more info: https://f-droid.org/docs/Setup_an_F-Droid_App_Repo
|
|
and https://f-droid.org/docs/Signing_Process"""
|
|
)
|
|
% os.path.join(fdroiddir, 'repo')
|
|
)
|
|
if not options.quiet:
|
|
# normally, INFO is only shown with --verbose, but show this unless --quiet
|
|
logger = logging.getLogger()
|
|
logger.setLevel(logging.INFO)
|
|
logger.info(msg)
|
|
logging.shutdown()
|