Skip to content

Commit

Permalink
Merge pull request #272 from jelmer/dupe-ports
Browse files Browse the repository at this point in the history
Error if the same port is used for metrics and general serving
  • Loading branch information
jelmer authored Nov 7, 2023
2 parents fa94adf + f42a8ee commit 1cac071
Show file tree
Hide file tree
Showing 28 changed files with 539 additions and 585 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ docker:
buildah push ghcr.io/jelmer/xandikos

reformat:
isort .
ruff format .
15 changes: 7 additions & 8 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,20 @@

# -- Project information -----------------------------------------------------

project = 'Xandikos'
copyright = '2022 Jelmer Vernooij et al'
author = 'Jelmer Vernooij'
project = "Xandikos"
copyright = "2022 Jelmer Vernooij et al"
author = "Jelmer Vernooij"


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
extensions = []

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand All @@ -44,9 +43,9 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'furo'
html_theme = "furo"

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
16 changes: 8 additions & 8 deletions examples/gunicorn.conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#
# Execute: 'gunicorn'
#
wsgi_app = 'xandikos.wsgi:app'
wsgi_app = "xandikos.wsgi:app"

# Server Mechanics
# ========================================
Expand All @@ -18,14 +18,14 @@

# enviroment variables
raw_env = [
'XANDIKOSPATH=./data',
'CURRENT_USER_PRINCIPAL=/user/',
'AUTOCREATE=defaults'
"XANDIKOSPATH=./data",
"CURRENT_USER_PRINCIPAL=/user/",
"AUTOCREATE=defaults",
]

# Server Socket
# ========================================
bind = '0.0.0.0:8000'
bind = "0.0.0.0:8000"

# Worker Processes
# ========================================
Expand All @@ -34,9 +34,9 @@
# Logging
# ========================================
# access log
accesslog = './logs/access.log'
accesslog = "./logs/access.log"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'

# gunicorn log
errorlog = '-'
loglevel = 'info'
errorlog = "-"
loglevel = "info"
1 change: 0 additions & 1 deletion xandikos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,3 @@

__version__ = (0, 2, 10)
version_string = ".".join(map(str, __version__))

48 changes: 17 additions & 31 deletions xandikos/caldav.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
from icalendar.prop import LocalTimezone, vDDDTypes, vPeriod

from . import davcommon, webdav
from .icalendar import (apply_time_range_vevent, as_tz_aware_ts,
expand_calendar_rrule)
from .icalendar import apply_time_range_vevent, as_tz_aware_ts, expand_calendar_rrule

ET = webdav.ET

Expand All @@ -58,9 +57,7 @@


class Calendar(webdav.Collection):

resource_types = webdav.Collection.resource_types + [
CALENDAR_RESOURCE_TYPE]
resource_types = webdav.Collection.resource_types + [CALENDAR_RESOURCE_TYPE]

def get_calendar_description(self) -> str:
"""Return the calendar description."""
Expand Down Expand Up @@ -171,9 +168,7 @@ def get_xmpp_uri(self):


class Subscription:

resource_types = webdav.Collection.resource_types + [
SUBSCRIPTION_RESOURCE_TYPE]
resource_types = webdav.Collection.resource_types + [SUBSCRIPTION_RESOURCE_TYPE]

def get_source_url(self):
"""Get the source URL for this calendar."""
Expand Down Expand Up @@ -261,8 +256,7 @@ async def set_value(self, href, resource, el):
raise NotImplementedError


def _extract_from_component(
incomp: Component, outcomp: Component, requested) -> None:
def _extract_from_component(incomp: Component, outcomp: Component, requested) -> None:
"""Extract specific properties from a calendar event.
Args:
Expand Down Expand Up @@ -310,12 +304,10 @@ def extract_from_calendar(incal, requested):
incal = expand_calendar_rrule(incal, start, end)
elif tag.tag == ("{%s}limit-recurrence-set" % NAMESPACE):
# TODO(jelmer): https://github.com/jelmer/xandikos/issues/103
raise NotImplementedError(
"limit-recurrence-set is not yet implemented")
raise NotImplementedError("limit-recurrence-set is not yet implemented")
elif tag.tag == ("{%s}limit-freebusy-set" % NAMESPACE):
# TODO(jelmer): https://github.com/jelmer/xandikos/issues/104
raise NotImplementedError(
"limit-freebusy-set is not yet implemented")
raise NotImplementedError("limit-freebusy-set is not yet implemented")
else:
raise AssertionError(f"invalid element {tag!r}")
return incal
Expand Down Expand Up @@ -363,7 +355,6 @@ async def set_value(self, href, resource, el):


class CalendarMultiGetReporter(davcommon.MultiGetReporter):

name = "{%s}calendar-multiget" % NAMESPACE
resource_type = (CALENDAR_RESOURCE_TYPE, SCHEDULE_INBOX_RESOURCE_TYPE)
data_property = CalendarDataProperty()
Expand Down Expand Up @@ -506,7 +497,6 @@ def get_calendar_timezone(resource):


class CalendarQueryReporter(webdav.Reporter):

name = "{%s}calendar-query" % NAMESPACE
resource_type = (CALENDAR_RESOURCE_TYPE, SCHEDULE_INBOX_RESOURCE_TYPE)
data_property = CalendarDataProperty()
Expand All @@ -521,7 +511,7 @@ async def report(
base_href,
base_resource,
depth,
strict
strict,
):
# TODO(jelmer): Verify that resource is a calendar
requested = None
Expand All @@ -536,14 +526,13 @@ async def report(
tztext = el.text
else:
webdav.nonfatal_bad_request(
f"Unknown tag {el.tag} in report {self.name}",
strict
f"Unknown tag {el.tag} in report {self.name}", strict
)
if requested is None:
# The CalDAV RFC says that behaviour mimicks that of PROPFIND,
# and the WebDAV RFC says that no body implies {DAV}allprop
# This isn't exactly an empty body, but close enough.
requested = ET.Element('{DAV:}allprop')
requested = ET.Element("{DAV:}allprop")
if tztext is not None:
tz = get_pytz_from_text(tztext)
else:
Expand All @@ -558,7 +547,7 @@ def members(collection):
collection.subcollections(),
)

async for (href, resource) in webdav.traverse_resource(
async for href, resource in webdav.traverse_resource(
base_resource, base_href, depth, members=members
):
# Ideally traverse_resource would only return the right things.
Expand Down Expand Up @@ -635,8 +624,7 @@ async def get_value(self, href, resource, el, environ):
content_type,
version,
) in resource.get_supported_calendar_data_types():
subel = ET.SubElement(
el, "{urn:ietf:params:xml:ns:caldav}calendar-data")
subel = ET.SubElement(el, "{urn:ietf:params:xml:ns:caldav}calendar-data")
subel.set("content-type", content_type)
subel.set("version", version)

Expand Down Expand Up @@ -909,7 +897,7 @@ def extract_freebusy(comp, tzify):


async def iter_freebusy(resources, start, end, tzify):
async for (href, resource) in resources:
async for href, resource in resources:
c = await calendar_from_resource(resource)
if c is None:
continue
Expand Down Expand Up @@ -941,7 +929,7 @@ async def report(
base_href,
base_resource,
depth,
strict
strict,
):
requested = None
for el in body:
Expand Down Expand Up @@ -1005,8 +993,7 @@ async def handle(self, request, environ, app):
href, resource, el, environ
)
ET.SubElement(el, "{urn:ietf:params:xml:ns:caldav}calendar")
await app.properties["{DAV:}resourcetype"].set_value(
href, resource, el)
await app.properties["{DAV:}resourcetype"].set_value(href, resource, el)
if base_content_type in ("text/xml", "application/xml"):
et = await webdav._readXmlBody(
request,
Expand All @@ -1017,8 +1004,8 @@ async def handle(self, request, environ, app):
for el in et:
if el.tag != "{DAV:}set":
webdav.nonfatal_bad_request(
f"Unknown tag {el.tag} in mkcalendar",
app.strict)
f"Unknown tag {el.tag} in mkcalendar", app.strict
)
continue
propstat.extend(
[
Expand All @@ -1028,8 +1015,7 @@ async def handle(self, request, environ, app):
)
]
)
ret = ET.Element(
"{urn:ietf:params:xml:ns:carldav:}mkcalendar-response")
ret = ET.Element("{urn:ietf:params:xml:ns:carldav:}mkcalendar-response")
for propstat_el in webdav.propstat_as_xml(propstat):
ret.append(propstat_el)
return webdav._send_xml_response(
Expand Down
34 changes: 13 additions & 21 deletions xandikos/carddav.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,13 @@ async def set_value(self, href, resource, el):


class AddressbookMultiGetReporter(davcommon.MultiGetReporter):

name = "{%s}addressbook-multiget" % NAMESPACE
resource_type = ADDRESSBOOK_RESOURCE_TYPE
data_property = AddressDataProperty()


class Addressbook(webdav.Collection):

resource_types = webdav.Collection.resource_types + [
ADDRESSBOOK_RESOURCE_TYPE]
resource_types = webdav.Collection.resource_types + [ADDRESSBOOK_RESOURCE_TYPE]

def get_addressbook_description(self) -> str:
raise NotImplementedError(self.get_addressbook_description)
Expand Down Expand Up @@ -229,8 +226,7 @@ def apply_text_match(el: ET.Element, value: str) -> bool:
collation = el.get("collation", "i;ascii-casemap")
negate_condition = el.get("negate-condition", "no")
match_type = el.get("match-type", "contains")
matches = _mod_collation.collations[collation](
value, el.text or '', match_type)
matches = _mod_collation.collations[collation](value, el.text or "", match_type)

if negate_condition == "yes":
return not matches
Expand All @@ -240,8 +236,7 @@ def apply_text_match(el: ET.Element, value: str) -> bool:

def apply_param_filter(el, prop):
name = el.get("name")
if (len(el) == 1
and el[0].tag == "{urn:ietf:params:xml:ns:carddav}is-not-defined"):
if len(el) == 1 and el[0].tag == "{urn:ietf:params:xml:ns:carddav}is-not-defined":
return name not in prop.params

try:
Expand All @@ -266,8 +261,7 @@ def apply_prop_filter(el, ab):
# The CARDDAV:prop-filter XML element contains a CARDDAV:is-not-defined XML
# element and no property of the type specified by the "name" attribute
# exists in the enclosing calendar component;
if (len(el) == 1
and el[0].tag == "{urn:ietf:params:xml:ns:carddav}is-not-defined"):
if len(el) == 1 and el[0].tag == "{urn:ietf:params:xml:ns:carddav}is-not-defined":
return name not in ab

try:
Expand Down Expand Up @@ -305,7 +299,6 @@ async def apply_filter(el, resource):


class AddressbookQueryReporter(webdav.Reporter):

name = "{%s}addressbook-query" % NAMESPACE
resource_type = ADDRESSBOOK_RESOURCE_TYPE
data_property = AddressDataProperty()
Expand All @@ -320,7 +313,7 @@ async def report(
base_href,
base_resource,
depth,
strict
strict,
):
requested = None
filter_el = None
Expand All @@ -334,32 +327,32 @@ async def report(
limit = el
else:
webdav.nonfatal_bad_request(
f"Unknown tag {el.tag} in report {self.name}",
strict)
f"Unknown tag {el.tag} in report {self.name}", strict
)
if requested is None:
# The CardDAV RFC says that behaviour mimicks that of PROPFIND,
# and the WebDAV RFC says that no body implies {DAV}allprop
# This isn't exactly an empty body, but close enough.
requested = ET.Element('{DAV:}allprop')
requested = ET.Element("{DAV:}allprop")
if limit is not None:
try:
[nresults_el] = list(limit)
except ValueError:
webdav.nonfatal_bad_request(
"Invalid number of subelements in limit", strict)
"Invalid number of subelements in limit", strict
)
nresults = None
else:
try:
nresults = int(nresults_el.text)
except ValueError:
webdav.nonfatal_bad_request(
"nresults not a number", strict)
webdav.nonfatal_bad_request("nresults not a number", strict)
nresults = None
else:
nresults = None

i = 0
async for (href, resource) in webdav.traverse_resource(
async for href, resource in webdav.traverse_resource(
base_resource, base_href, depth
):
if not await apply_filter(filter_el, resource):
Expand All @@ -374,6 +367,5 @@ async def report(
environ,
requested,
)
yield webdav.Status(
href, "200 OK", propstat=[s async for s in propstat])
yield webdav.Status(href, "200 OK", propstat=[s async for s in propstat])
i += 1
11 changes: 5 additions & 6 deletions xandikos/collation.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@

class UnknownCollation(Exception):
def __init__(self, collation: str) -> None:
super().__init__(
f"Collation {collation!r} is not supported"
)
super().__init__(f"Collation {collation!r} is not supported")
self.collation = collation


Expand All @@ -51,9 +49,10 @@ def _match(a, b, k):
# TODO(jelmer): Follow all rules as specified in
# https://datatracker.ietf.org/doc/html/rfc5051
"i;unicode-casemap": lambda a, b, k: _match(
a.encode('utf-8', 'surrogateescape').upper(),
b.encode('utf-8', 'surrogateescape').upper(),
k),
a.encode("utf-8", "surrogateescape").upper(),
b.encode("utf-8", "surrogateescape").upper(),
k,
),
}


Expand Down
Loading

0 comments on commit 1cac071

Please sign in to comment.