diff --git a/chatmaild/src/chatmaild/metadata.py b/chatmaild/src/chatmaild/metadata.py index 5d886cb8..40ebafec 100644 --- a/chatmaild/src/chatmaild/metadata.py +++ b/chatmaild/src/chatmaild/metadata.py @@ -18,6 +18,16 @@ class Metadata: def get_metadata_dict(self, addr): return FileDict(self.vmail_dir / addr / "metadata.json") + def write_login_timestamp(self, addr, timestamp): + # day resolution is enough for timestamp + timestamp = int(timestamp) // 86400 * 86400 + target_file = self.vmail_dir.joinpath(addr, "last-login") + try: + target_file.write_text(str(timestamp)) + except FileNotFoundError: + target_file.parent.mkdir() + target_file.write_text(str(timestamp)) + def add_token_to_addr(self, addr, token): with self.get_metadata_dict(addr).modify() as data: tokens = data.setdefault(self.DEVICETOKEN_KEY, []) diff --git a/chatmaild/src/chatmaild/tests/test_metadata.py b/chatmaild/src/chatmaild/tests/test_metadata.py index 7a0573d2..de5fa7a3 100644 --- a/chatmaild/src/chatmaild/tests/test_metadata.py +++ b/chatmaild/src/chatmaild/tests/test_metadata.py @@ -92,8 +92,26 @@ def test_notifier_remove_without_set(metadata, testaddr): assert not metadata.get_tokens_for_addr(testaddr) +<<<<<<< HEAD def test_handle_dovecot_request_lookup_fails(dictproxy, testaddr): res = dictproxy.handle_dovecot_request(f"Lpriv/123/chatmail\t{testaddr}") +======= +def test_metadata_login_timestamp(metadata, testaddr): + timestamp = metadata.vmail_dir.joinpath(testaddr).mkdir() + metadata.write_login_timestamp(testaddr, timestamp=100000) + timestamp = metadata.vmail_dir.joinpath(testaddr, "last-login").read_text() + assert int(timestamp) == 86400 + + metadata.write_login_timestamp(testaddr, timestamp=200000) + timestamp = metadata.vmail_dir.joinpath(testaddr, "last-login").read_text() + assert int(timestamp) == 86400 * 2 + + +def test_handle_dovecot_request_lookup_fails(notifier, metadata, testaddr): + res = handle_dovecot_request( + f"Lpriv/123/chatmail\t{testaddr}", {}, notifier, metadata + ) +>>>>>>> 317d30f (write last login differently) assert res == "N\n" @@ -133,7 +151,37 @@ def test_handle_dovecot_request_happy_path(dictproxy, testaddr, token): assert queue_item.path.exists() +<<<<<<< HEAD def test_handle_dovecot_protocol_set_devicetoken(dictproxy): +======= +def test_handle_dovecot_request_last_login(notifier, metadata, testaddr, token): + transactions = {} + + userdir = metadata.vmail_dir.joinpath(testaddr) + + # set last-login info for user + tx = "1111" + msg = f"B{tx}\t{testaddr}" + res = handle_dovecot_request(msg, transactions, notifier, metadata) + assert not res + assert transactions == {tx: dict(addr=testaddr, res="O\n")} + + timestamp = int(time.time()) + msg = f"S{tx}\tshared/last-login/{testaddr}\t{timestamp}" + res = handle_dovecot_request(msg, transactions, notifier, metadata) + assert not res + assert len(transactions) == 1 + read_timestamp = int(userdir.joinpath("last-login").read_text()) + assert read_timestamp == timestamp // 86400 * 86400 + + msg = f"C{tx}" + res = handle_dovecot_request(msg, transactions, notifier, metadata) + assert res == "O\n" + assert len(transactions) == 0 + + +def test_handle_dovecot_protocol_set_devicetoken(metadata, notifier): +>>>>>>> 317d30f (write last login differently) rfile = io.BytesIO( b"\n".join( [ diff --git a/cmdeploy/src/cmdeploy/dovecot/dovecot.conf.j2 b/cmdeploy/src/cmdeploy/dovecot/dovecot.conf.j2 index 0eb7ba6a..581ce9a6 100644 --- a/cmdeploy/src/cmdeploy/dovecot/dovecot.conf.j2 +++ b/cmdeploy/src/cmdeploy/dovecot/dovecot.conf.j2 @@ -106,10 +106,16 @@ mail_attribute_dict = proxy:/run/chatmail-metadata/metadata.socket:metadata # `imap_zlib` enables IMAP COMPRESS (RFC 4978). # protocol imap { - mail_plugins = $mail_plugins imap_zlib imap_quota + mail_plugins = $mail_plugins imap_zlib imap_quota last_login imap_metadata = yes } +plugin { + last_login_dict = proxy:/run/chatmail-metadata/metadata.socket:metadata + #last_login_key = last-login/%u # default + last_login_precision = s +} + protocol lmtp { # notify plugin is a dependency of push_notification plugin: #