VulpesOS/tundra/runtime/sms-inbox.py
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

79 lines
2.8 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: MPL-2.0
"""Keep oFono incoming messages until Gecko durably acknowledges them."""
import fcntl
import json
import os
from pathlib import Path
import time
import uuid
ROOT = Path('/var/lib/tundra/sms')
def transaction(change, root=ROOT, filename="inbox.json"):
if filename not in ("inbox.json", "outgoing.json"): raise ValueError("INVALID_SMS_STORE")
root.mkdir(parents=True, mode=0o700, exist_ok=True)
with (root/'lock').open('a') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
path = root/filename
rows = json.loads(path.read_text()) if path.exists() else []
result = change(rows)
temporary = path.with_suffix('.tmp')
with open(temporary, 'w', opener=lambda p, f: os.open(p, f, 0o600)) as stream:
os.fchmod(stream.fileno(),0o600)
json.dump(rows,stream)
stream.flush(); os.fsync(stream.fileno())
temporary.replace(path)
return result
def pending():
return transaction(lambda rows: rows[:100])
def acknowledge(ids):
if not isinstance(ids,list) or len(ids)>100 or any(not isinstance(i,str) or len(i)>40 for i in ids):
raise ValueError('INVALID_SMS_IDS')
def remove(rows):
rows[:] = [row for row in rows if row['receipt'] not in ids]
transaction(remove)
return True
def outgoing_state(path):
rows = transaction(lambda rows: list(rows), filename='outgoing.json')
return next((row['state'] for row in reversed(rows) if row['path'] == path), None)
def record_outgoing(path, state):
if state not in ('sent','failed'): return
def update(rows):
rows[:] = [row for row in rows if row['path'] != path][-999:]
rows.append({'path':path,'state':state,'timestamp':round(time.time()*1000)})
transaction(update, filename='outgoing.json')
def main():
import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
os.umask(0o077)
DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
def incoming(body, properties):
row = {'receipt':str(uuid.uuid4()),'sender':str(properties.get('Sender','')),
'body':str(body),'timestamp':round(time.time()*1000)}
transaction(lambda rows:rows.append(row))
bus.add_signal_receiver(incoming,signal_name='IncomingMessage',
dbus_interface='org.ofono.MessageManager',bus_name='org.ofono',path='/ril_0')
def outgoing(name, value, path=None):
if name == 'State' and path and str(path).startswith('/ril_0/message'):
record_outgoing(str(path), str(value))
bus.add_signal_receiver(outgoing, signal_name='PropertyChanged',
dbus_interface='org.ofono.Message', bus_name='org.ofono', path_keyword='path')
GLib.MainLoop().run()
if __name__ == '__main__':
main()