#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright 2008-2022 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided 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 with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <https://www.gnu.org/licenses/>.
"""
Univention Updater: UCR Repository Server URL
"""
from copy import copy
try:
from typing import Optional, TypeVar, Union # noqa: F401
from univention.config_registry import ConfigRegistry # noqa: F401
_T = TypeVar("_T")
except ImportError:
pass
from six.moves.urllib_parse import urlsplit, quote
[docs]class UcsRepoUrl(object):
"""
UCS repository server base URL.
"""
DEFAULT = 'https://updates.software-univention.de/'
def __init__(self, ucr, prefix, default=None):
# type: (ConfigRegistry, str, Union[None, str, UcsRepoUrl]) -> None
'''
>>> UcsRepoUrl({'_/server': 'hostname'}, '_').path
''
>>> UcsRepoUrl({'_/server': 'hostname', '_/prefix': '/p'}, '_').path
'/p/'
>>> UcsRepoUrl({'_/server': 'hostname', '_/prefix': 'path'}, '_').path
'/path/'
>>> UcsRepoUrl({}, '', UcsRepoUrl({'_/server': 'https://hostname/'}, '_')).private()
'https://hostname/'
>>> UcsRepoUrl({'_/server': 'other'}, '_', UcsRepoUrl({'_/server': 'https://hostname:80/'}, '_')).private()
'http://other/'
>>> UcsRepoUrl({'_/server': 'other'}, '_', UcsRepoUrl.DEFAULT).private()
'http://other/'
>>> UcsRepoUrl({}, '').private() == UcsRepoUrl.DEFAULT
True
'''
def ucrv(key, default=None):
# type: (str, _T) -> _T
return ucr.get('%s/%s' % (prefix, key), default)
server = ucrv('server', '')
url = urlsplit(server)
if url.scheme:
self.scheme = url.scheme
self.username = url.username
self.password = url.password
self.hostname = url.hostname
port = url.port
prefix = url.path
else:
if default is None:
default = self.DEFAULT
elif isinstance(default, UcsRepoUrl):
default = default.private()
defaults = urlsplit(default)
self.username = ucrv('username', defaults.username)
self.password = ucrv('password', defaults.password)
if server:
self.hostname = server
port = ucrv('port', 80)
self.scheme = 'https' if port == 443 else 'http'
prefix = ucrv('prefix', None)
else:
self.hostname = defaults.hostname
port = ucrv('port', defaults.port)
self.scheme = defaults.scheme
prefix = ucrv('prefix', defaults.path)
self.port = int(port if port else 443 if self.scheme == 'https' else 80)
if prefix:
prefix = prefix.strip('/')
if prefix:
self.path = '/%s/' % (prefix,)
else:
self.path = '/'
else:
self.path = ''
@property
def cred(self):
# type: () -> str
if self.username:
# FIXME http://bugs.debian.org/500560: [@:/] don't work
return '%s:%s@' % (quote(self.username), quote(self.password or ''))
return ''
@property
def _port(self):
# type: () -> str
return ':%d' % (self.port) if (self.scheme, self.port) not in (
('http', 80),
('https', 443)
) else ''
@property
def _path(self):
# type: () -> str
return quote('/%s' % (self.path.lstrip('/'),))
[docs] def public(self):
# type: () -> str
"""
URI without credentials.
>>> UcsRepoUrl({'_/server': 'hostname'}, '_').public()
'http://hostname/'
>>> UcsRepoUrl({'_/server': 'hostname', '_/username': 'user', '_/password': 'pass'}, '_').public()
'http://hostname/'
>>> UcsRepoUrl({'_/server': 'https://hostname'}, '_').public()
'https://hostname/'
>>> UcsRepoUrl({'_/server': 'https://user:pass@hostname'}, '_').public()
'https://hostname/'
"""
return '{0.scheme}://{0.hostname}{0._port}{0._path}'.format(self)
[docs] def private(self):
# type: () -> str
"""
URI with credentials.
>>> UcsRepoUrl({'_/server': 'hostname'}, '_').private()
'http://hostname/'
>>> UcsRepoUrl({'_/server': 'hostname', '_/username': 'user', '_/password': 'pass'}, '_').private()
'http://user:pass@hostname/'
>>> UcsRepoUrl({'_/server': 'https://hostname'}, '_').private()
'https://hostname/'
>>> UcsRepoUrl({'_/server': 'https://user:pass@hostname'}, '_').private()
'https://user:pass@hostname/'
"""
return '{0.scheme}://{0.cred}{0.hostname}{0._port}{0._path}'.format(self)
def __repr__(self):
# type: () -> str
"""
>>> repr(UcsRepoUrl({'_/server': 'hostname'}, '_'))
"UcsRepoUrl({}, '', 'http://hostname/')"
"""
return '%s(%r, %r, %r)' % (
self.__class__.__name__,
{},
'',
self.private(),
)
def __eq__(self, other):
# type: (object) -> bool
"""
>>> UcsRepoUrl({}, '') == UcsRepoUrl({}, '')
True
>>> UcsRepoUrl({}, '') == UcsRepoUrl({'_/server': 'other'}, '_')
False
"""
return isinstance(other, UcsRepoUrl) and self.private() == other.private()
def __add__(self, rel):
# type: (str) -> UcsRepoUrl
"""
Append relative path component.
>>> (UcsRepoUrl({'_/server': 'http://hostname'}, '_') + '/b').public()
'http://hostname/b'
>>> (UcsRepoUrl({'_/server': 'http://hostname/'}, '_') + '/b').public()
'http://hostname/b'
>>> (UcsRepoUrl({'_/server': 'http://hostname/a'}, '_') + '/b').public()
'http://hostname/a/b'
>>> (UcsRepoUrl({'_/server': 'http://hostname/a/'}, '_') + '/b').public()
'http://hostname/a/b'
>>> (UcsRepoUrl({'_/server': 'http://hostname/a/'}, '_') + '/b/').public()
'http://hostname/a/b/'
"""
cfg = copy(self)
cfg.path = '%s/%s' % (self._path.rstrip('/'), str(rel).lstrip('/'))
return cfg