Error handling for mailbox uploads
When communicating via mailboxes, the max latency and thus the retransmission interval are long, so we need to be careful about any circumstances that could cause messages to be lost.
On the sender side, if an error (such as an IO error, app shutdown, app crash or device crash) occurs while we're writing messages and acks to a file, we need to ensure that those messages and acks can be sent again after recovering from the error.
We can do this by generating a unique ID for the sync session and recording the IDs of any messages sent or acked in that session. This state can be deleted when the session completes successfully. At the next startup, if we find this session state in the DB we know that the session didn't complete successfully, so we mark the messages and acks as unsent.
We also need to handle errors that occur after the file has been created but before it's been uploaded. We can do this by storing the contact ID and filename in the DB before starting the session and removing them when the upload completes successfully (#2230 (closed)). Next time we come online, if we find any upload state in the DB we know that the corresponding uploads didn't complete successfully, so we queue the files for upload. We should use the DB for this, rather storing the files in a separate upload directory per contact, to avoid leaking metadata to the filesystem about which uploads are destined for which contacts.
We should store the contact ID and filename before starting the sync session, so that there's no gap where the DB doesn't contain a record of the incomplete session/upload. If a crash happens during the session we may end up marking the messages and acks in the session as unsent as well as queueing the (possibly incomplete) file for upload. This is acceptable - the receiver should be able to handle the (possibly incomplete) file and another file containing the same messages and acks. Similarly, if the upload completes successfully but a crash happens before we remove the contact ID and filename from the DB, it's acceptable to upload the file again at the next startup, as the receiver should be able to handle the duplicate file.
It's still possible for the file to be lost after a successful upload, but losses beyond that point are handled by different mechanisms (#2191 (closed), #2192 (closed), #2225 (closed)).
Depends on #2230 (closed).