Ring can be used to transfer files. The current implementation pass
through a TURN server to pass through the NAT.
How to use it?
For sending a file on gnome, in a conversation you need to click to the
send file icon at the bottom right of the conversation:
Then you will see your image as soon as the transfer is finished (and
show images is activated)
On the contrary if you receive a file (and if it's not a picture < 20
Mb), you will have to accept the transfer:
And then the file will be sent. You have the possibility to cancel in a
middle of a transfer.
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
The following method is used:
1. A client will call DataTransferFacade::sendFile()
2. The method DhtPeerConnector: requestConnection() is triggered and
creates a connection between all connected devices of the peer. This
will create a ClientConnector and launch the process() method.
3. This method put a PeerConnectionMsg on the DHT and waits for a
response (DhtPeerConnector::Impl::onResponseMsg). The reponse will
contains informations to connect to the peer.
4. Then a response is received from the DHT, which contains public
address of the contact. A TcpSocketEndpoint is created and wrapped
in a TlsSocketEndpoint wrapped in a PeerConnection object (and
5. This connection is given to
DataTransferFacade::Impl::onConnectionRequestReply and attached to a
6. PeerConnection::PeerConnectionImpl::eventLoop() as soon as the
output is connected will send headers (from SubOutgoingFileTransfer)
to the peer. And will wait for the "GO" message (or "NGO"). Then if
headers are sent and the file accepted, it will send the rest of the
file until the connection is closed or the file is finished.
1. Because the sender will send a PeerConnectionMsg on the DHT, the
first thing to do is to listen for incoming PeerConnectionMsg
(messages will come on the hash peer:device_id)
2. When a message arrives,
DhtPeerConnector::Impl::onTrustedRequestMsg is triggered. 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. 3. Then, it
will send a response containing public ips where the peer should use to
send the file on the DHT and waits for a connection from the other side.
4. As soon as a peer is successfully connected,
DhtPeerConnector::Impl::onTurnPeerConnection is called and the
negotiation of a TLS over TURN session is launched. Then, a
PeerConnection is created and attached to a local FTPServer (see
ftp_server.h) used to retrieve the file.
5. The PeerConnection::PeerConnectionImpl::eventLoop() will detect
that a new output stream is attached (the ftp_server) and calls
FtpServer::write. This method will retrieve headers of the incoming
file and call DataTransferFacade::onIncomingFileRequest.
6. onIncomingFileRequest will create a new IncomingFile and block
the transfer until the client accepts this transfer (or refuse).
7. As soon as the client accepts (or not) the transfer, the FTPServer
sends "GO" (or "NGO"), retrieve the file until the file is finished, or
canceled (by killing the connection)
A RING 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.
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.
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
Now (since https://gerrit-ring.savoirfairelinux.com/#/c/9327/), 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 turn.ring.cx. But you can host
your own TURN server. For example by running a
sudo turnserver -a -v -n -u user:password -r "realm"
Then, you can configure the TURN server in the advanced settings of
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
For now, if a file transfer fails when ongoing, the sender can't resume
the transfer and must relaunch the whole transfer. In the future, there
will be a retry button for resuming the transfer.
Moreover, the current implementation must pass through a TURN server.
Ring should supports in a distant future a true peer-to-peer
implementation with TURN fallback. Ideas and PR welcome.
Finally, because Ring do not support text conferences (just video
conferences, where there is one master merging slaves SIP calls), there
is no real file transfer in conferences. For now, when you are in a
conference on the gnome client for example: A master, B and C slave. B
will be able to send a file to A the master (C same) A will be able to
send a file to B or to C (just have to select the correct conversation).