1# Copyright (C) 2018-2019 Garmin Ltd.
2#
3# SPDX-License-Identifier: GPL-2.0-only
4#
5
6import asyncio
7from contextlib import closing
8import re
9import itertools
10import json
11from collections import namedtuple
12from urllib.parse import urlparse
13
14UNIX_PREFIX = "unix://"
15WS_PREFIX = "ws://"
16WSS_PREFIX = "wss://"
17
18ADDR_TYPE_UNIX = 0
19ADDR_TYPE_TCP = 1
20ADDR_TYPE_WS = 2
21
22User = namedtuple("User", ("username", "permissions"))
23
24
25def parse_address(addr):
26    if addr.startswith(UNIX_PREFIX):
27        return (ADDR_TYPE_UNIX, (addr[len(UNIX_PREFIX) :],))
28    elif addr.startswith(WS_PREFIX) or addr.startswith(WSS_PREFIX):
29        return (ADDR_TYPE_WS, (addr,))
30    else:
31        m = re.match(r"\[(?P<host>[^\]]*)\]:(?P<port>\d+)$", addr)
32        if m is not None:
33            host = m.group("host")
34            port = m.group("port")
35        else:
36            host, port = addr.split(":")
37
38        return (ADDR_TYPE_TCP, (host, int(port)))
39
40
41def create_server(
42    addr,
43    dbname,
44    *,
45    sync=True,
46    upstream=None,
47    read_only=False,
48    db_username=None,
49    db_password=None,
50    anon_perms=None,
51    admin_username=None,
52    admin_password=None,
53):
54    def sqlite_engine():
55        from .sqlite import DatabaseEngine
56
57        return DatabaseEngine(dbname, sync)
58
59    def sqlalchemy_engine():
60        from .sqlalchemy import DatabaseEngine
61
62        return DatabaseEngine(dbname, db_username, db_password)
63
64    from . import server
65
66    if "://" in dbname:
67        db_engine = sqlalchemy_engine()
68    else:
69        db_engine = sqlite_engine()
70
71    if anon_perms is None:
72        anon_perms = server.DEFAULT_ANON_PERMS
73
74    s = server.Server(
75        db_engine,
76        upstream=upstream,
77        read_only=read_only,
78        anon_perms=anon_perms,
79        admin_username=admin_username,
80        admin_password=admin_password,
81    )
82
83    (typ, a) = parse_address(addr)
84    if typ == ADDR_TYPE_UNIX:
85        s.start_unix_server(*a)
86    elif typ == ADDR_TYPE_WS:
87        url = urlparse(a[0])
88        s.start_websocket_server(url.hostname, url.port)
89    else:
90        s.start_tcp_server(*a)
91
92    return s
93
94
95def create_client(addr, username=None, password=None):
96    from . import client
97
98    c = client.Client(username, password)
99
100    try:
101        (typ, a) = parse_address(addr)
102        if typ == ADDR_TYPE_UNIX:
103            c.connect_unix(*a)
104        elif typ == ADDR_TYPE_WS:
105            c.connect_websocket(*a)
106        else:
107            c.connect_tcp(*a)
108        return c
109    except Exception as e:
110        c.close()
111        raise e
112
113
114async def create_async_client(addr, username=None, password=None):
115    from . import client
116
117    c = client.AsyncClient(username, password)
118
119    try:
120        (typ, a) = parse_address(addr)
121        if typ == ADDR_TYPE_UNIX:
122            await c.connect_unix(*a)
123        elif typ == ADDR_TYPE_WS:
124            await c.connect_websocket(*a)
125        else:
126            await c.connect_tcp(*a)
127
128        return c
129    except Exception as e:
130        await c.close()
131        raise e
132