Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
79 lines
2.8 KiB
Python
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()
|