108902b01SBrad Bishop# Copyright (C) 2018-2019 Garmin Ltd.
219323693SBrad Bishop#
3c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only
419323693SBrad Bishop#
519323693SBrad Bishop
66ce62a20SAndrew Geisslerimport asyncio
7a34c030eSBrad Bishopfrom contextlib import closing
8a34c030eSBrad Bishopimport re
9475cb72dSAndrew Geisslerimport itertools
10475cb72dSAndrew Geisslerimport json
11*ac13d5f3SPatrick Williamsfrom collections import namedtuple
12*ac13d5f3SPatrick Williamsfrom urllib.parse import urlparse
1319323693SBrad Bishop
14a34c030eSBrad BishopUNIX_PREFIX = "unix://"
15*ac13d5f3SPatrick WilliamsWS_PREFIX = "ws://"
16*ac13d5f3SPatrick WilliamsWSS_PREFIX = "wss://"
1719323693SBrad Bishop
18a34c030eSBrad BishopADDR_TYPE_UNIX = 0
19a34c030eSBrad BishopADDR_TYPE_TCP = 1
20*ac13d5f3SPatrick WilliamsADDR_TYPE_WS = 2
2119323693SBrad Bishop
22*ac13d5f3SPatrick WilliamsUser = namedtuple("User", ("username", "permissions"))
23a34c030eSBrad Bishop
24a34c030eSBrad Bishop
25a34c030eSBrad Bishopdef parse_address(addr):
26a34c030eSBrad Bishop    if addr.startswith(UNIX_PREFIX):
27a34c030eSBrad Bishop        return (ADDR_TYPE_UNIX, (addr[len(UNIX_PREFIX) :],))
28*ac13d5f3SPatrick Williams    elif addr.startswith(WS_PREFIX) or addr.startswith(WSS_PREFIX):
29*ac13d5f3SPatrick Williams        return (ADDR_TYPE_WS, (addr,))
30a34c030eSBrad Bishop    else:
31*ac13d5f3SPatrick Williams        m = re.match(r"\[(?P<host>[^\]]*)\]:(?P<port>\d+)$", addr)
32a34c030eSBrad Bishop        if m is not None:
33*ac13d5f3SPatrick Williams            host = m.group("host")
34*ac13d5f3SPatrick Williams            port = m.group("port")
35a34c030eSBrad Bishop        else:
36*ac13d5f3SPatrick Williams            host, port = addr.split(":")
37a34c030eSBrad Bishop
38a34c030eSBrad Bishop        return (ADDR_TYPE_TCP, (host, int(port)))
39a34c030eSBrad Bishop
40a34c030eSBrad Bishop
41*ac13d5f3SPatrick Williamsdef create_server(
42*ac13d5f3SPatrick Williams    addr,
43*ac13d5f3SPatrick Williams    dbname,
44*ac13d5f3SPatrick Williams    *,
45*ac13d5f3SPatrick Williams    sync=True,
46*ac13d5f3SPatrick Williams    upstream=None,
47*ac13d5f3SPatrick Williams    read_only=False,
48*ac13d5f3SPatrick Williams    db_username=None,
49*ac13d5f3SPatrick Williams    db_password=None,
50*ac13d5f3SPatrick Williams    anon_perms=None,
51*ac13d5f3SPatrick Williams    admin_username=None,
52*ac13d5f3SPatrick Williams    admin_password=None,
53*ac13d5f3SPatrick Williams):
54*ac13d5f3SPatrick Williams    def sqlite_engine():
55*ac13d5f3SPatrick Williams        from .sqlite import DatabaseEngine
56475cb72dSAndrew Geissler
57*ac13d5f3SPatrick Williams        return DatabaseEngine(dbname, sync)
58475cb72dSAndrew Geissler
59*ac13d5f3SPatrick Williams    def sqlalchemy_engine():
60*ac13d5f3SPatrick Williams        from .sqlalchemy import DatabaseEngine
61475cb72dSAndrew Geissler
62*ac13d5f3SPatrick Williams        return DatabaseEngine(dbname, db_username, db_password)
63*ac13d5f3SPatrick Williams
64a34c030eSBrad Bishop    from . import server
65*ac13d5f3SPatrick Williams
66*ac13d5f3SPatrick Williams    if "://" in dbname:
67*ac13d5f3SPatrick Williams        db_engine = sqlalchemy_engine()
68*ac13d5f3SPatrick Williams    else:
69*ac13d5f3SPatrick Williams        db_engine = sqlite_engine()
70*ac13d5f3SPatrick Williams
71*ac13d5f3SPatrick Williams    if anon_perms is None:
72*ac13d5f3SPatrick Williams        anon_perms = server.DEFAULT_ANON_PERMS
73*ac13d5f3SPatrick Williams
74*ac13d5f3SPatrick Williams    s = server.Server(
75*ac13d5f3SPatrick Williams        db_engine,
76*ac13d5f3SPatrick Williams        upstream=upstream,
77*ac13d5f3SPatrick Williams        read_only=read_only,
78*ac13d5f3SPatrick Williams        anon_perms=anon_perms,
79*ac13d5f3SPatrick Williams        admin_username=admin_username,
80*ac13d5f3SPatrick Williams        admin_password=admin_password,
81*ac13d5f3SPatrick Williams    )
82a34c030eSBrad Bishop
83a34c030eSBrad Bishop    (typ, a) = parse_address(addr)
84a34c030eSBrad Bishop    if typ == ADDR_TYPE_UNIX:
85a34c030eSBrad Bishop        s.start_unix_server(*a)
86*ac13d5f3SPatrick Williams    elif typ == ADDR_TYPE_WS:
87*ac13d5f3SPatrick Williams        url = urlparse(a[0])
88*ac13d5f3SPatrick Williams        s.start_websocket_server(url.hostname, url.port)
89a34c030eSBrad Bishop    else:
90a34c030eSBrad Bishop        s.start_tcp_server(*a)
91a34c030eSBrad Bishop
92a34c030eSBrad Bishop    return s
93a34c030eSBrad Bishop
94a34c030eSBrad Bishop
95*ac13d5f3SPatrick Williamsdef create_client(addr, username=None, password=None):
96a34c030eSBrad Bishop    from . import client
97a34c030eSBrad Bishop
98*ac13d5f3SPatrick Williams    c = client.Client(username, password)
99*ac13d5f3SPatrick Williams
100*ac13d5f3SPatrick Williams    try:
101a34c030eSBrad Bishop        (typ, a) = parse_address(addr)
102a34c030eSBrad Bishop        if typ == ADDR_TYPE_UNIX:
103a34c030eSBrad Bishop            c.connect_unix(*a)
104*ac13d5f3SPatrick Williams        elif typ == ADDR_TYPE_WS:
105*ac13d5f3SPatrick Williams            c.connect_websocket(*a)
106a34c030eSBrad Bishop        else:
107a34c030eSBrad Bishop            c.connect_tcp(*a)
108a34c030eSBrad Bishop        return c
109*ac13d5f3SPatrick Williams    except Exception as e:
110*ac13d5f3SPatrick Williams        c.close()
111*ac13d5f3SPatrick Williams        raise e
1126ce62a20SAndrew Geissler
113*ac13d5f3SPatrick Williams
114*ac13d5f3SPatrick Williamsasync def create_async_client(addr, username=None, password=None):
1156ce62a20SAndrew Geissler    from . import client
1166ce62a20SAndrew Geissler
117*ac13d5f3SPatrick Williams    c = client.AsyncClient(username, password)
118*ac13d5f3SPatrick Williams
119*ac13d5f3SPatrick Williams    try:
1206ce62a20SAndrew Geissler        (typ, a) = parse_address(addr)
1216ce62a20SAndrew Geissler        if typ == ADDR_TYPE_UNIX:
1226ce62a20SAndrew Geissler            await c.connect_unix(*a)
123*ac13d5f3SPatrick Williams        elif typ == ADDR_TYPE_WS:
124*ac13d5f3SPatrick Williams            await c.connect_websocket(*a)
1256ce62a20SAndrew Geissler        else:
1266ce62a20SAndrew Geissler            await c.connect_tcp(*a)
1276ce62a20SAndrew Geissler
1286ce62a20SAndrew Geissler        return c
129*ac13d5f3SPatrick Williams    except Exception as e:
130*ac13d5f3SPatrick Williams        await c.close()
131*ac13d5f3SPatrick Williams        raise e
132