'r13b': None,
'r14b': None,
'r15c': None,
+ 'r16': None,
},
'qt_sdk_path': None,
'build_tools': "25.0.2",
def has_extension(filename, ext):
- _, f_ext = get_extension(filename)
+ _ignored, f_ext = get_extension(filename)
return ext == f_ext
else:
remote = app.Repo
vcs = getvcs(app.RepoType, remote, build_dir)
- logging.debug("Using %s" % vcs.clientversion())
return vcs, build_dir
def clientversioncmd(self):
return ['git', '--version']
+ def GitFetchFDroidPopen(self, gitargs, envs=dict(), cwd=None, output=True):
+ '''Prevent git fetch/clone/submodule from hanging at the username/password prompt
+
+ While fetch/pull/clone respect the command line option flags,
+ it seems that submodule commands do not. They do seem to
+ follow whatever is in env vars, if the version of git is new
+ enough. So we just throw the kitchen sink at it to see what
+ sticks.
+
+ '''
+ if cwd is None:
+ cwd = self.local
+ git_config = []
+ for domain in ('bitbucket.org', 'github.com', 'gitlab.com'):
+ git_config.append('-c')
+ git_config.append('url.https://u:p@' + domain + '/.insteadOf=git@' + domain + ':')
+ git_config.append('-c')
+ git_config.append('url.https://u:p@' + domain + '.insteadOf=git://' + domain)
+ git_config.append('-c')
+ git_config.append('url.https://u:p@' + domain + '.insteadOf=https://' + domain)
+ # add helpful tricks supported in git >= 2.3
+ ssh_command = 'ssh -oBatchMode=yes -oStrictHostKeyChecking=yes'
+ git_config.append('-c')
+ git_config.append('core.sshCommand="' + ssh_command + '"') # git >= 2.10
+ envs.update({
+ 'GIT_TERMINAL_PROMPT': '0',
+ 'GIT_SSH_COMMAND': ssh_command, # git >= 2.3
+ })
+ return FDroidPopen(['git', ] + git_config + gitargs,
+ envs=envs, cwd=cwd, output=output)
+
def checkrepo(self):
"""If the local directory exists, but is somehow not a git repository,
git will traverse up the directory tree until it finds one
def gotorevisionx(self, rev):
if not os.path.exists(self.local):
# Brand new checkout
- p = FDroidPopen(['git', 'clone', self.remote, self.local])
+ p = FDroidPopen(['git', 'clone', self.remote, self.local], cwd=None)
if p.returncode != 0:
self.clone_failed = True
raise VCSException("Git clone failed", p.output)
raise VCSException(_("Git clean failed"), p.output)
if not self.refreshed:
# Get latest commits and tags from remote
- p = FDroidPopen(['git', 'fetch', 'origin'], cwd=self.local)
+ p = self.GitFetchFDroidPopen(['fetch', 'origin'])
if p.returncode != 0:
raise VCSException(_("Git fetch failed"), p.output)
- p = FDroidPopen(['git', 'fetch', '--prune', '--tags', 'origin'], cwd=self.local, output=False)
+ p = self.GitFetchFDroidPopen(['fetch', '--prune', '--tags', 'origin'], output=False)
if p.returncode != 0:
raise VCSException(_("Git fetch failed"), p.output)
# Recreate origin/HEAD as git clone would do it, in case it disappeared
lines = f.readlines()
with open(submfile, 'w') as f:
for line in lines:
- if 'git@github.com' in line:
- line = line.replace('git@github.com:', 'https://github.com/')
- if 'git@gitlab.com' in line:
- line = line.replace('git@gitlab.com:', 'https://gitlab.com/')
+ for domain in ('bitbucket.org', 'github.com', 'gitlab.com'):
+ line = re.sub('git@' + domain + ':', 'https://u:p@' + domain + '/', line)
f.write(line)
p = FDroidPopen(['git', 'submodule', 'sync'], cwd=self.local, output=False)
if p.returncode != 0:
raise VCSException(_("Git submodule sync failed"), p.output)
- p = FDroidPopen(['git', 'submodule', 'update', '--init', '--force', '--recursive'], cwd=self.local)
+ p = self.GitFetchFDroidPopen(['submodule', 'update', '--init', '--force', '--recursive'])
if p.returncode != 0:
raise VCSException(_("Git submodule update failed"), p.output)
dest = os.path.join(build_dir, part)
logging.info("Removing {0}".format(part))
if os.path.lexists(dest):
- if os.path.islink(dest):
- FDroidPopen(['unlink', dest], output=False)
+ # rmtree can only handle directories that are not symlinks, so catch anything else
+ if not os.path.isdir(dest) or os.path.islink(dest):
+ os.remove(dest)
else:
- FDroidPopen(['rm', '-rf', dest], output=False)
+ shutil.rmtree(dest)
else:
logging.info("...but it didn't exist")
default_date = datetime.utcnow()
self.apks[apkName] = (app, default_date)
self.changed = True
- _, added = self.apks[apkName]
+ _ignored, added = self.apks[apkName]
return added
def getapp(self, apkname):
os.rename(signed_apk, tmp_apk)
with ZipFile(tmp_apk, 'r') as in_apk:
with ZipFile(signed_apk, 'w') as out_apk:
- for f in in_apk.infolist():
- if not apk_sigfile.match(f.filename):
+ for info in in_apk.infolist():
+ if not apk_sigfile.match(info.filename):
if strip_manifest:
- if f.filename != 'META-INF/MANIFEST.MF':
- buf = in_apk.read(f.filename)
- out_apk.writestr(f.filename, buf)
+ if info.filename != 'META-INF/MANIFEST.MF':
+ buf = in_apk.read(info.filename)
+ out_apk.writestr(info, buf)
else:
- buf = in_apk.read(f.filename)
- out_apk.writestr(f.filename, buf)
+ buf = in_apk.read(info.filename)
+ out_apk.writestr(info, buf)
def apk_implant_signatures(apkpath, signaturefile, signedfile, manifest):
"""
# get list of available signature files in metadata
with tempfile.TemporaryDirectory() as tmpdir:
- # orig_apk = os.path.join(tmpdir, 'orig.apk')
- # os.rename(apkpath, orig_apk)
apkwithnewsig = os.path.join(tmpdir, 'newsig.apk')
with ZipFile(apkpath, 'r') as in_apk:
with ZipFile(apkwithnewsig, 'w') as out_apk:
for sig_file in [signaturefile, signedfile, manifest]:
- out_apk.write(sig_file, arcname='META-INF/' +
- os.path.basename(sig_file))
- for f in in_apk.infolist():
- if not apk_sigfile.match(f.filename):
- if f.filename != 'META-INF/MANIFEST.MF':
- buf = in_apk.read(f.filename)
- out_apk.writestr(f.filename, buf)
+ with open(sig_file, 'rb') as fp:
+ buf = fp.read()
+ info = zipfile.ZipInfo('META-INF/' + os.path.basename(sig_file))
+ info.compress_type = zipfile.ZIP_DEFLATED
+ info.create_system = 0 # "Windows" aka "FAT", what Android SDK uses
+ out_apk.writestr(info, buf)
+ for info in in_apk.infolist():
+ if not apk_sigfile.match(info.filename):
+ if info.filename != 'META-INF/MANIFEST.MF':
+ buf = in_apk.read(info.filename)
+ out_apk.writestr(info, buf)
os.remove(apkpath)
p = SdkToolsPopen(['zipalign', '-v', '4', apkwithnewsig, apkpath])
if p.returncode != 0:
return False
+def local_rsync(options, fromdir, todir):
+ '''Rsync method for local to local copying of things
+
+ This is an rsync wrapper with all the settings for safe use within
+ the various fdroidserver use cases. This uses stricter rsync
+ checking on all files since people using offline mode are already
+ prioritizing security above ease and speed.
+
+ '''
+ rsyncargs = ['rsync', '--recursive', '--safe-links', '--times', '--perms',
+ '--one-file-system', '--delete', '--chmod=Da+rx,Fa-x,a+r,u+w']
+ if not options.no_checksum:
+ rsyncargs.append('--checksum')
+ if options.verbose:
+ rsyncargs += ['--verbose']
+ if options.quiet:
+ rsyncargs += ['--quiet']
+ logging.debug(' '.join(rsyncargs + [fromdir, todir]))
+ if subprocess.call(rsyncargs + [fromdir, todir]) != 0:
+ raise FDroidException()
+
+
def get_per_app_repos():
'''per-app repos are dirs named with the packageName of a single app'''
b'index-v1.json',
b'categories.txt',
]
+
+
+def get_examples_dir():
+ '''Return the dir where the fdroidserver example files are available'''
+ examplesdir = None
+ tmp = os.path.dirname(sys.argv[0])
+ if os.path.basename(tmp) == 'bin':
+ egg_links = glob.glob(os.path.join(tmp, '..',
+ 'local/lib/python3.*/site-packages/fdroidserver.egg-link'))
+ if egg_links:
+ # installed from local git repo
+ examplesdir = os.path.join(open(egg_links[0]).readline().rstrip(), 'examples')
+ else:
+ # try .egg layout
+ examplesdir = os.path.dirname(os.path.dirname(__file__)) + '/share/doc/fdroidserver/examples'
+ if not os.path.exists(examplesdir): # use UNIX layout
+ examplesdir = os.path.dirname(tmp) + '/share/doc/fdroidserver/examples'
+ else:
+ # we're running straight out of the git repo
+ prefix = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
+ examplesdir = prefix + '/examples'
+
+ return examplesdir