summaryrefslogtreecommitdiff
path: root/src/jwebmail/read_mails.py
blob: 05c6e1036f16259a780e6cc216e33b7df35672d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
from contextlib import closing
from datetime import datetime, timedelta

from flask import current_app, g
from flask_login import UserMixin, current_user

from .model.read_mails import QMailAuthuser

EXPIRATION_SEC = 60 * 60 * 25


class JWebmailUser(UserMixin):
    def __init__(self, mail_addr, password):
        self.id = mail_addr
        self.password = password


class RedisTimeoutSession:
    def __init__(self, username, passwd, timeout, port=6379):
        import redis

        self.timeout = timeout
        self.conn = redis.Redis(
            host="localhost",
            port=port,
            decode_responses=True,
            protocol=3,
            username=username,
            password=passwd,
        )

    def set(self, key, value):
        self.conn.setex(f"jwm:user:{key}", self.timeout, value)

    def get(self, key):
        return self.conn.getex(f"jwm:user:{key}", self.timeout)

    def close(self):
        self.conn.close()


class MysqlTimeoutSession:
    def __init__(self, username, passwd, timeout, database="jwebmaildb1", port=3306):
        import mysql.connector

        self.timeout = timeout
        self.conn = mysql.connector.connect(
            host="localhost",
            port=port,
            username=username,
            password=passwd,
            database=database,
        )

    def set(self, key, value):
        timeout = datetime.now() + timedelta(seconds=self.timeout)

        with closing(self.conn.cursor()) as cur:
            cur.execute("DELETE FROM session WHERE user = %s", [key])
            cur.execute(
                "INSERT INTO session VALUES (%s, %s, %s)", [key, value, timeout]
            )
            self.conn.commit()

    def get(self, key):
        with closing(self.conn.cursor()) as cur:
            cur.execute("DELETE FROM session WHERE timeout < NOW()")
            cur.execute("SELECT password FROM session WHERE user = %s", [key])
            row = cur.fetchone()

            if row is None:
                self.conn.commit()
                return None
            else:
                timeout = datetime.now() + timedelta(seconds=self.timeout)
                cur.execute(
                    "UPDATE session SET timeout = %s WHERE user = %s", [timeout, key]
                )
                self.conn.commit()
                return row[0]

    def close(self):
        self.conn.close()


class SqliteTimeoutSession:
    def __init__(self, _username, _passwd, timeout, database):
        import sqlite3

        self.timeout = timeout

        self.conn = sqlite3.connect(database, autocommit=False)
        cur = self.conn.cursor()
        cur.execute(
            "CREATE TABLE IF NOT EXISTS session (user text PRIMARY KEY, password text, timeout real NOT NULL) STRICT"
        )
        cur.execute("CREATE INDEX IF NOT EXISTS timeout_idx ON session (timeout)")

    def set(self, key, value):
        timeout = datetime.now() + timedelta(seconds=self.timeout)

        with closing(self.conn.cursor()) as cur:
            cur.execute(
                "REPLACE INTO session VALUES (?, ?, unixepoch(?, 'subsec'))",
                [key, value, timeout],
            )
            self.conn.commit()

    def get(self, key):
        with closing(self.conn.cursor()) as cur:
            cur.execute("DELETE FROM session WHERE timeout < unixepoch('subsec')")
            cur.execute("SELECT password FROM session WHERE user = ?", [key])
            row = cur.fetchone()

            if row is None:
                self.conn.commit()
                return None
            else:
                timeout = datetime.now() + timedelta(seconds=self.timeout)
                cur.execute(
                    "UPDATE session SET timeout = unixepoch(?, 'subsec') WHERE user = ?",
                    [timeout, key],
                )
                self.conn.commit()
                return row[0]

    def close(self):
        self.conn.close()


def select_timeout_session():
    session_type = current_app.config["JWEBMAIL"]["READ_MAILS"]["SESSION_TYPE"]

    user = "jwebmail"
    passwd = current_app.config["JWEBMAIL"]["READ_MAILS"]["SESSION_STORE_PASSWD"]
    args = dict()
    db_name = current_app.config["JWEBMAIL"]["READ_MAILS"].get("SESSION_STORE_DB_NAME")
    if db_name:
        args["database"] = db_name

    if session_type == "REDIS":
        return RedisTimeoutSession(user, passwd, EXPIRATION_SEC)
    elif session_type == "MYSQL":
        return MysqlTimeoutSession(user, passwd, EXPIRATION_SEC, **args)
    elif session_type == "SQLITE":
        args.setdefault("database", "/var/local/lib/jwebmail/jwebmail.sqlite3")
        return SqliteTimeoutSession(user, passwd, EXPIRATION_SEC, **args)
    else:
        raise ValueError(f"unknown session_type {session_type!r}")


def build_qma(username, password):
    authenticator = current_app.config["JWEBMAIL"]["READ_MAILS"]["AUTHENTICATOR"]
    backend = current_app.config["JWEBMAIL"]["READ_MAILS"]["BACKEND"]
    mailbox = current_app.config["JWEBMAIL"]["READ_MAILS"]["MAILBOX"]
    mailbox_user = current_app.config["JWEBMAIL"]["READ_MAILS"]["MAILBOX_USER"]

    return QMailAuthuser(
        username, password, backend, mailbox, mailbox_user, authenticator
    )


def login(username, password):
    return build_qma(username, password).verify_user()


def add_user(user: JWebmailUser):
    r = select_timeout_session()
    r.set(user.get_id(), user.password)
    r.close()


def load_user(username: str) -> JWebmailUser:
    r = select_timeout_session()
    passwd = r.get(username)
    r.close()
    if passwd is None:
        return None
    return JWebmailUser(username, passwd)


def get_read_mails_logged_in():
    if "read_mails" in g:
        return g.read_mails

    qma = build_qma(current_user.get_id(), current_user.password)
    g.read_mails = qma
    return qma