File transfer

THIS PAGE IS DEPRECATED: READ developer/swarm:File transfer

How to use it?


When you are talking to somebody on Android, you have the possibility to send a picture on your device or take a photo with these buttons:


Note: when you send a file, the other has to accept it. At this moment you will see ‘awaiting peer’:


How it works? (technical)

How it works


Jami is a distributed application and has to work without any internet connectivity. So, file transfer too! Basically, we use the same method to perform file transfer and calls, but in TCP. To summarize how it works, we can imagine a situation where Alice (A) wants to transfer a file to Bob (B).

First, Alice will request a connection to Bob. To do that, Jami is using ICE (RFC 6544), a protocol used to negotiate links between peers. Alice will send, into an encrypted packet via the DHT the ip of its device. So, when Bob receives the ips of Alice, they will be able to negotiate a transport where Bob will be able to send packets to Alice. The negotiation can be successful, but if it fails, a TURN server will be used (the one configured into the settings) to perform the transfer. If the negotiation succeeds, Bob will send its ips to Alice to perform the negotiation in the other direction. Note that the link is still not secure, so Bob will send the ips through the DHT in an encrypted message. If the second negotiation fails, the TURN will be used as a fallback.

Now that the bidirectionnal TCP link is here, the next step will be to negotiate a TLS 1.3 (generally a (TLS1.3)-(DHE-FFDHE8192)-(RSA-PSS-RSAE-SHA384)-(AES-256-GCM) when I write these lines) between Alice an Bob, then Alice will start to transfer the file.

The first part will be a small header to describe the content of the file. Then, after Bob accepts the transfer, the full file will be transmitted.


Sending a file

The following method is used:

1. A client will call DataTransferFacade::sendFile(). DataTransferFacade is the class corresponding to the API exposed for the clients. It is used to manage a view of the file transfers (the corresponding classes are DataTransfer, IncomingFileTransfer, OutgoingFileTransfer and SubOutgoingFileTransfer). This method will ask the linked JamiAccount to request a connection.

![Diagram: DataTransfer class diagram]images/file-transfer-datatransfer-class-diagram.png)

2. The method DhtPeerConnector: requestConnection() is triggered and creates a connection between all connected devices of the peer (found on the DHT). DhtPeerConnector is used to manage the main event loop which manage connections. When a device is found, the event loop will create a ClientConnector (which manage the connection for one device) and launch the process() method.

3. This method is used to initialize the ICE transport and put a PeerConnectionMsg (which contains the SDP message, see below) on the DHT and waits for a response (DhtPeerConnector::Impl::onResponseMsg).

4. Then a response is received from the DHT, which contains public addresses of the peer device. We can now negotiate a TLS link (directly via ICE, or via TURN as a fallback). This TlsSocketEndpoint is given to the PeerConnection object as an output and the transfer can start.

5.\ When the TLS socket is ready, the callback DataTransferFacade::Impl::onConnectionRequestReply is called, and a OutgoingFileTransfer is linked to the PeerConnection as an input. This OutgoingFileTransfer contains a list of SubOutgoingFileTransfer (one per device) where each sub transfer is a transfer to one device. We do that to be able to furnish the most optimistic view of the transfer (if a contact as 3 devices, where the contact cancel the transfer on one device, but accepted the transfer on the two others, the most advanced transfer will be shown).

6. The SubOutgoingFileTransfer will first transfer the header of the file, wait the peer acceptance (A “GO\n” message on the socket) and then will send the file.

7. If a cancel is received from the peer or the client or if the file transfer finish, the connection will be closed via a CANCEL message on the DhtPeerConnector::eventLoop() and the resources will be released.


Receiving a file

The same structure is used to receive files, but the method changes a bit:

  1. The JamiAccount class is used to receives messages from the DHT, because the first thing received will be the DHT request.

  2. Then, this message is given to DhtPeerConnector: onRequestMessage() through the eventLoop.

  3. The DhtPeerConnector::Impl::answerToRequest will try to connect to the TURN server (if not connected) and initialize the ICE transport. This method open 2 control connections to a TURN server (one to authorize IPv4 peers, another one for IPv6 peers, due to RFC 6156) if it’s not already open and permit Peer public addresses to connect. Then, if the SDP received doesn’t contains ICE candidates, will use the TURN and craft the SDP answer to wait for the peer. If the SDP contains ICE candidates, the method will try to negotiate the link (or fallback on the TURN) and then answer the SDP (with ICE candidates or not).

  4. Once the links are ready, like the sender, a TLS link is negotiated and given to the PeerConnection given to the IncomingFileTransfer as an input. The headers of the file will come and the client is now able to accept or cancel the transfer.

Re-ask for a previous file transfer

As specified in developer/swarm:Other mime types, the data-transfer interactions are now synced and stored into conversations. So, a device can easily detects if a file was downloaded or not. If not, it can asks all members in the conversation to transmits the file again.

To do this, the device will send a json with the mime-type: application/data-transfer-request+json containing conversation (the conversation’s id), interaction (related interaction), deviceId the device receiving the file.

The sender now checks if the device is a device from the announced peer and that the device is a member of the conversation, and can sends the file via a classic file transfer.

The receiver can now accepts the first incoming transfer, download the file and verify that the sha3sum is correct.


Diagram: main schema diagram

SDP sent over the DHT
H6e6ca682 1 TCP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 50693 typ host tcptype passive
H6e6ca682 1 TCP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 9 typ host tcptype active
H42c1b577 1 TCP 2130706431 fe80::9eb6:d0ff:fee7:1412 50693 typ host tcptype passive
H42c1b577 1 TCP 2130706431 fe80::9eb6:d0ff:fee7:1412 9 typ host tcptype active
Hc0a8007e 1 TCP 2130706431 42751 typ host tcptype passive
Hc0a8007e 1 TCP 2130706431 9 typ host tcptype active
Sc0a8007e 1 TCP 1694498815 X.X.X.X 42751 typ srflx tcptype passive

Where 0d04b932 is the ufrag and 7c33834e7cf944bf0e367b47 the password of the ICE session. 2130706431 and 1694498815 are the priority of the candidates. 42751 typ host tcptype passive is a passive host candidate and 1694498815 X.X.X.X 42751 typ srflx tcptype passive a passive host reflecting the public ip (mapped via UPnP for example).

Multi devices

A user can link its account to several devices. So, we need to implement the transfer when a user send a file to a contact who have multiple devices linked to this account.

First approach

The first approach was to send a request through the DHT to all devices and the first devices which answers get the file to transfer. This is bad for your contact because they will not know which device will receives will get the transfer.

Current approach

Now, we still send a request to all devices. The difference is that all devices will have the notification for receiving a file and can accept/refuse the transfer. The major part of the code for that is in data_transfer.cpp.

Now (since, when a user send a file, it will request a PeerConnection with all peer devices. And for all connections, we attach a new input stream to have the ability to accept/refuse/cancel each transfer separately.

In data_transfer.cpp we define the OptimisticMetaOutgoingInfo class which represent the optimistic view to show to the client. It’s optimistic because if a contact accept a transfer on one device and refuse on others, this class will show the ongoing file transfer. And it will only show an error if all devices refuse the transfer.

This class is linked to SubOutgoingFileTransfer which represent the state of a transfer with one device. Clients will have the ability to show a sub transfer instead the optimistic later (see TODO list).

Using another TURN server

Actually the default TURN server is But you can host your own TURN server. For example by running a coturn server.

sudo turnserver -a -v -n -u user:password -r "realm"

Then, you can configure the TURN server in the advanced settings of the app.

Note: this needs some technical knowledges. Moreover, the TURN server should see the same ip address of your node as the destination node or the peer connection will fail (because the authorization will be incorrect)


  1. Use libtorrent?

  2. Show subtransfers status for outgoing files