Code Repositories xandikos / 501d519
Fix style. Jelmer Vernooń≥ 2 months ago
10 changed file(s) with 55 addition(s) and 45 deletion(s). Raw diff Collapse all Expand all
536536 propstat = davcommon.get_properties_with_data(
537537 self.data_property, href, resource, properties, environ,
538538 requested)
539 yield webdav.Status(href, '200 OK', propstat=[s async for s in propstat])
539 yield webdav.Status(
540 href, '200 OK', propstat=[s async for s in propstat])
540541
541542
542543 class CalendarColorProperty(webdav.Property):
881882 resource_type = CALENDAR_RESOURCE_TYPE
882883
883884 async def report(self, environ, body, resources_by_hrefs,
884 properties, base_href, base_resource, depth):
885 properties, base_href, base_resource, depth):
885886 requested = None
886887 for el in body:
887888 if el.tag == '{urn:ietf:params:xml:ns:caldav}time-range':
312312 data_property = AddressDataProperty()
313313
314314 @webdav.multistatus
315 async def report(self, environ, body, resources_by_hrefs, properties, base_href,
316 base_resource, depth):
315 async def report(self, environ, body, resources_by_hrefs, properties,
316 base_href, base_resource, depth):
317317 requested = None
318318 filter_el = None
319319 limit = None
351351 propstat = davcommon.get_properties_with_data(
352352 self.data_property, href, resource, properties, environ,
353353 requested)
354 yield webdav.Status(href, '200 OK', propstat=[s async for s in propstat])
354 yield webdav.Status(
355 href, '200 OK', propstat=[s async for s in propstat])
355356 i += 1
3939
4040
4141 async def get_properties_with_data(data_property, href, resource, properties,
42 environ, requested):
42 environ, requested):
4343 properties = dict(properties)
4444 properties[data_property.name] = data_property
4545 async for ps in webdav.get_properties(
5656 data_property = None
5757
5858 @webdav.multistatus
59 async def report(self, environ, body, resources_by_hrefs, properties, base_href,
60 resource, depth):
59 async def report(self, environ, body, resources_by_hrefs, properties,
60 base_href, resource, depth):
6161 # TODO(jelmer): Verify that depth == "0"
6262 # TODO(jelmer): Verify that resource is an the right resource type
6363 requested = None
7777 propstat = get_properties_with_data(
7878 self.data_property, href, resource, properties, environ,
7979 requested)
80 yield webdav.Status(href, '200 OK', propstat=[s async for s in propstat])
80 yield webdav.Status(
81 href, '200 OK', propstat=[s async for s in propstat])
8182
8283
8384 # see https://tools.ietf.org/html/rfc4790
3232 Histogram,
3333 generate_latest,
3434 CONTENT_TYPE_LATEST,
35 )
35 )
3636
3737
3838 request_counter = Counter(
5454 name = '{DAV:}sync-collection'
5555
5656 @webdav.multistatus
57 async def report(self, environ, request_body, resources_by_hrefs, properties,
58 href, resource, depth):
57 async def report(self, environ, request_body, resources_by_hrefs,
58 properties, href, resource, depth):
5959 old_token = None
6060 sync_level = None
6161 limit = None
6767 environ = {
6868 'PATH_INFO': path,
6969 'REQUEST_METHOD': 'MKCOL',
70 }
70 }
7171 setup_testing_defaults(environ)
7272 _code = []
7373 _headers = []
1616 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
1717 # MA 02110-1301, USA.
1818
19 import shutil
20 import tempfile
2119 import unittest
2220
2321 from xandikos.wsgi import (
2323 the carddav support, the caldav support and the DAV store.
2424 """
2525
26 import asyncio
2726 from email.utils import parseaddr
2827 import functools
2928 import hashlib
361360 raise NotImplementedError(self.get_body)
362361
363362 async def render(self, self_url, accepted_content_types,
364 accepted_content_languages):
363 accepted_content_languages):
365364 content_types = webdav.pick_content_types(
366365 accepted_content_types, ['text/html'])
367366 assert content_types == ['text/html']
622621 shutil.rmtree(p)
623622
624623 async def render(self, self_url, accepted_content_types,
625 accepted_content_languages):
624 accepted_content_languages):
626625 content_types = webdav.pick_content_types(
627626 accepted_content_types, ['text/html'])
628627 assert content_types == ['text/html']
793792 return p
794793
795794 async def render(self, self_url, accepted_content_types,
796 accepted_content_languages):
795 accepted_content_languages):
797796 content_types = webdav.pick_content_types(
798797 accepted_content_types, ['text/html'])
799798 assert content_types == ['text/html']
11251124
11261125 app = web.Application()
11271126 try:
1128 import prometheus_client
1127 import prometheus_client # noqa: F401
11291128 except ModuleNotFoundError:
11301129 logging.warning(
11311130 'Prometheus client not found; /metrics will not be available.')
11401139 if options.route_prefix.strip('/'):
11411140 xandikos_app = web.Application()
11421141 xandikos_app.router.add_route("*", "/{path_info:.*}", xandikos_handler)
1142
11431143 async def redirect_to_subprefix(request):
11441144 return web.HTTPFound(options.route_prefix)
11451145 app.router.add_route("*", "/", redirect_to_subprefix)
1146 r = app.add_subapp(options.route_prefix, xandikos_app)
1146 app.add_subapp(options.route_prefix, xandikos_app)
11471147 else:
11481148 app.router.add_route("*", "/{path_info:.*}", xandikos_handler)
11491149 web.run_app(app, port=options.port, host=options.listen_address)
119119 return web.Response(
120120 status=self.status, reason=self.reason,
121121 headers=self.headers, body=body)
122
122
123123
124124 def pick_content_types(accepted_content_types, available_content_types):
125125 """Pick best content types for a client.
307307 def multistatus(req_fn):
308308
309309 async def wrapper(self, environ, *args, **kwargs):
310 responses = [resp async for resp in req_fn(self, environ, *args, **kwargs)]
310 responses = [
311 resp async for resp in req_fn(self, environ, *args, **kwargs)]
311312 return _send_dav_responses(responses, DEFAULT_ENCODING)
312313
313314 return wrapper
712713 :return: Iterable over bytestrings."""
713714 raise NotImplementedError(self.get_body)
714715
715 async def render(self, self_url, accepted_content_types, accepted_languages):
716 async def render(self, self_url, accepted_content_types,
717 accepted_languages):
716718 """'Render' this resource in the specified content type.
717719
718720 The default implementation just checks that the
957959 href, resource, properties, environ, ET.Element(name))
958960
959961
960 async def get_property_from_element(href, resource, properties, environ, requested):
962 async def get_property_from_element(href, resource, properties, environ,
963 requested):
961964 """Get a single property on a resource.
962965
963966 :param href: Resource href
10341037 :return: Iterator over PropStatus items
10351038 """
10361039 for name in properties:
1037 ps = await get_property_from_name(href, resource, properties, name, environ)
1040 ps = await get_property_from_name(
1041 href, resource, properties, name, environ)
10381042 if ps.statuscode == '200 OK':
10391043 yield ps
10401044
11101114 return self.resource_type in resource.resource_types
11111115
11121116 async def report(self, environ, request_body, resources_by_hrefs,
1113 properties, href, resource, depth):
1117 properties, href, resource, depth):
11141118 """Send a report.
11151119
11161120 :param environ: wsgi environ
11521156 name = '{DAV:}expand-property'
11531157
11541158 async def _populate(self, prop_list, resources_by_hrefs, properties, href,
1155 resource, environ):
1159 resource, environ):
11561160 """Expand properties for a resource.
11571161
11581162 :param prop_list: DAV:property elements to retrieve and expand
11961200 yield Status(href, '200 OK', propstat=ret)
11971201
11981202 @multistatus
1199 async def report(self, environ, request_body, resources_by_hrefs, properties,
1200 href, resource, depth):
1201 async for resp in self._populate(request_body, resources_by_hrefs, properties,
1202 href, resource, environ):
1203 async def report(self, environ, request_body, resources_by_hrefs,
1204 properties, href, resource, depth):
1205 async for resp in self._populate(
1206 request_body, resources_by_hrefs, properties, href, resource,
1207 environ):
12031208 yield resp
12041209
12051210
13121317 status=status,
13131318 body=body,
13141319 headers={
1315 'Content-Type': body_type,
1316 'Content-Length': str(sum(map(len, body)))})
1320 'Content-Type': body_type,
1321 'Content-Length': str(sum(map(len, body)))})
13171322
13181323
13191324 def _send_dav_responses(responses, out_encoding):
15321537 description=e.description)
15331538 return Response(
15341539 status=201, reason='Created', headers=[
1535 ('ETag', new_etag)])
1540 ('ETag', new_etag)])
15361541
15371542
15381543 class ReportMethod(Method):
15391544
15401545 async def handle(self, request, environ, app):
15411546 # See https://tools.ietf.org/html/rfc3253, section 3.6
1542 base_href, unused_path, r = app._get_resource_from_environ(request, environ)
1547 base_href, unused_path, r = app._get_resource_from_environ(
1548 request, environ)
15431549 if r is None:
15441550 return _send_not_found(request)
15451551 depth = request.headers.get("Depth", "0")
15851591 except ValueError:
15861592 raise BadRequestError(
15871593 'Received more than one element in propfind.')
1588 ret = []
15891594 async for href, resource in traverse_resource(
15901595 base_resource, base_href, depth):
15911596 propstat = []
16111616
16121617 @multistatus
16131618 async def handle(self, request, environ, app):
1614 href, unused_path, resource = app._get_resource_from_environ(request, environ)
1619 href, unused_path, resource = app._get_resource_from_environ(
1620 request, environ)
16151621 if resource is None:
16161622 yield Status(request.url, '404 Not Found')
16171623 return
16971703
16981704
16991705 async def _do_get(request, environ, app, send_body):
1700 unused_href, unused_path, r = app._get_resource_from_environ(request, environ)
1706 unused_href, unused_path, r = app._get_resource_from_environ(
1707 request, environ)
17011708 if r is None:
17021709 return _send_not_found(request)
17031710 accept_content_types = parse_accept_header(
17121719 content_type,
17131720 content_languages
17141721 ) = await r.render(
1715 request.path, accept_content_types, accept_content_languages)
1722 request.path, accept_content_types, accept_content_languages)
17161723
17171724 if_none_match = request.headers.get('If-None-Match', None)
17181725 if (
17481755 self._environ = environ
17491756 self.method = environ['REQUEST_METHOD']
17501757 self.raw_path = environ['SCRIPT_NAME'] + environ['PATH_INFO']
1751 self.path = environ['SCRIPT_NAME'] + path_from_environ(environ, 'PATH_INFO')
1758 self.path = environ['SCRIPT_NAME'] + path_from_environ(
1759 environ, 'PATH_INFO')
17521760 self.content_type = environ.get(
17531761 'CONTENT_TYPE', 'application/octet-stream')
17541762 try:
18601868 return Response(
18611869 status='415 Unsupported Media Type',
18621870 body=[('Unsupported media type %r' % e.content_type)
1863 .encode(DEFAULT_ENCODING)])
1871 .encode(DEFAULT_ENCODING)])
18641872 except UnauthorizedError:
18651873 return Response(
18661874 status='401 Unauthorized',
18731881 environ['SCRIPT_NAME'] = ''
18741882 request = WSGIRequest(environ)
18751883 environ = {'SCRIPT_NAME': environ['SCRIPT_NAME']}
1876 response = loop.run_until_complete(self._handle_request(request, environ))
1884 response = loop.run_until_complete(self._handle_request(
1885 request, environ))
18771886 return response.for_wsgi(start_response)
18781887
18791888 async def aiohttp_handler(self, request, route_prefix='/'):
6464 create_defaults=os.environ['AUTOCREATE'] == 'defaults')
6565 else:
6666 logging.warning(
67 'default user principal \'%s\' does not exist. Create directory %s'
68 ' or set AUTOCREATE variable?',
67 'default user principal \'%s\' does not exist. '
68 'Create directory %s or set AUTOCREATE variable?',
6969 current_user_principal, backend._map_to_file_path(
7070 current_user_principal))
7171