2014-04-08 21:15:55 +00:00
package eu.siacs.conversations.xmpp.jingle ;
2014-04-07 18:05:45 +00:00
2017-05-03 09:03:04 +00:00
import android.util.Base64 ;
2015-07-20 12:26:29 +00:00
import android.util.Log ;
2015-07-31 23:19:16 +00:00
import android.util.Pair ;
2015-07-20 12:26:29 +00:00
2015-08-11 14:50:00 +00:00
import java.io.FileNotFoundException ;
2017-10-26 16:42:10 +00:00
import java.io.IOException ;
2015-07-31 23:19:16 +00:00
import java.io.InputStream ;
import java.io.OutputStream ;
2014-04-07 18:05:45 +00:00
import java.util.ArrayList ;
2017-05-03 09:03:04 +00:00
import java.util.Arrays ;
2014-04-13 09:32:45 +00:00
import java.util.Iterator ;
2014-04-07 18:05:45 +00:00
import java.util.List ;
2014-06-22 19:44:17 +00:00
import java.util.Locale ;
2014-04-13 09:32:45 +00:00
import java.util.Map.Entry ;
2014-08-19 13:06:50 +00:00
import java.util.concurrent.ConcurrentHashMap ;
2014-04-07 18:05:45 +00:00
2014-08-31 14:28:21 +00:00
import eu.siacs.conversations.Config ;
2015-07-31 23:19:16 +00:00
import eu.siacs.conversations.crypto.axolotl.AxolotlService ;
import eu.siacs.conversations.crypto.axolotl.OnMessageCreatedCallback ;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage ;
2014-04-07 18:05:45 +00:00
import eu.siacs.conversations.entities.Account ;
2014-04-13 09:32:45 +00:00
import eu.siacs.conversations.entities.Conversation ;
2014-10-14 10:02:48 +00:00
import eu.siacs.conversations.entities.DownloadableFile ;
2014-04-07 18:05:45 +00:00
import eu.siacs.conversations.entities.Message ;
2016-06-29 15:16:34 +00:00
import eu.siacs.conversations.entities.Presence ;
2016-07-04 17:29:46 +00:00
import eu.siacs.conversations.entities.ServiceDiscoveryResult ;
2015-07-20 12:26:29 +00:00
import eu.siacs.conversations.entities.Transferable ;
import eu.siacs.conversations.entities.TransferablePlaceholder ;
2016-10-26 10:26:04 +00:00
import eu.siacs.conversations.parser.IqParser ;
2015-07-31 23:19:16 +00:00
import eu.siacs.conversations.persistance.FileBackend ;
import eu.siacs.conversations.services.AbstractConnectionManager ;
2014-04-07 18:05:45 +00:00
import eu.siacs.conversations.services.XmppConnectionService ;
2017-05-03 09:03:04 +00:00
import eu.siacs.conversations.utils.CryptoHelper ;
2014-04-07 18:05:45 +00:00
import eu.siacs.conversations.xml.Element ;
2014-04-08 21:15:55 +00:00
import eu.siacs.conversations.xmpp.OnIqPacketReceived ;
import eu.siacs.conversations.xmpp.jingle.stanzas.Content ;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket ;
2014-04-13 09:32:45 +00:00
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason ;
2014-04-08 21:15:55 +00:00
import eu.siacs.conversations.xmpp.stanzas.IqPacket ;
2018-03-05 17:30:40 +00:00
import rocks.xmpp.addr.Jid ;
2014-04-07 18:05:45 +00:00
2015-07-10 13:11:03 +00:00
public class JingleConnection implements Transferable {
2014-04-07 18:05:45 +00:00
private JingleConnectionManager mJingleConnectionManager ;
private XmppConnectionService mXmppConnectionService ;
2014-08-08 09:49:23 +00:00
2017-08-18 12:33:56 +00:00
private static final int JINGLE_STATUS_OFFERED = - 1 ;
2014-10-15 17:32:12 +00:00
protected static final int JINGLE_STATUS_INITIATED = 0 ;
protected static final int JINGLE_STATUS_ACCEPTED = 1 ;
protected static final int JINGLE_STATUS_FINISHED = 4 ;
protected static final int JINGLE_STATUS_TRANSMITTING = 5 ;
protected static final int JINGLE_STATUS_FAILED = 99 ;
2014-08-08 09:49:23 +00:00
2016-06-29 15:16:34 +00:00
private Content . Version ftVersion = Content . Version . FT_3 ;
2015-07-17 11:06:51 +00:00
private int ibbBlockSize = 8192 ;
2014-08-08 09:49:23 +00:00
2017-08-18 12:33:56 +00:00
private int mJingleStatus = JINGLE_STATUS_OFFERED ;
2015-07-10 13:11:03 +00:00
private int mStatus = Transferable . STATUS_UNKNOWN ;
2014-04-08 21:15:55 +00:00
private Message message ;
2014-04-07 18:05:45 +00:00
private String sessionId ;
private Account account ;
2014-11-06 19:10:03 +00:00
private Jid initiator ;
private Jid responder ;
private List < JingleCandidate > candidates = new ArrayList < > ( ) ;
private ConcurrentHashMap < String , JingleSocks5Transport > connections = new ConcurrentHashMap < > ( ) ;
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private String transportId ;
private Element fileOffer ;
2014-10-13 23:06:45 +00:00
private DownloadableFile file = null ;
2014-08-08 09:49:23 +00:00
2014-05-03 16:47:53 +00:00
private String contentName ;
private String contentCreator ;
2014-08-08 09:49:23 +00:00
2014-11-13 20:04:05 +00:00
private int mProgress = 0 ;
2014-04-18 09:57:28 +00:00
private boolean receivedCandidate = false ;
private boolean sentCandidate = false ;
2014-08-08 09:49:23 +00:00
2014-04-22 16:46:40 +00:00
private boolean acceptedAutomatically = false ;
2014-08-08 09:49:23 +00:00
2015-07-31 23:19:16 +00:00
private XmppAxolotlMessage mXmppAxolotlMessage ;
2014-04-20 20:34:27 +00:00
private JingleTransport transport = null ;
2014-08-08 09:49:23 +00:00
2015-07-31 23:19:16 +00:00
private OutputStream mFileOutputStream ;
private InputStream mFileInputStream ;
2014-04-08 21:15:55 +00:00
private OnIqPacketReceived responseListener = new OnIqPacketReceived ( ) {
2014-08-08 09:49:23 +00:00
2014-04-08 21:15:55 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-08-23 15:53:23 +00:00
if ( packet . getType ( ) ! = IqPacket . TYPE . RESULT ) {
2016-10-26 10:26:04 +00:00
fail ( IqParser . extractErrorMessage ( packet ) ) ;
2014-04-10 12:12:08 +00:00
}
2014-04-08 21:15:55 +00:00
}
} ;
2017-05-03 09:03:04 +00:00
private byte [ ] expectedHash = new byte [ 0 ] ;
2014-08-08 09:49:23 +00:00
2017-05-03 09:03:04 +00:00
private boolean responding ( ) {
2017-11-16 11:49:15 +00:00
return responder ! = null & & responder . equals ( account . getJid ( ) ) ;
2017-05-03 09:03:04 +00:00
}
private boolean initiating ( ) {
return initiator . equals ( account . getJid ( ) ) ;
}
final OnFileTransmissionStatusChanged onFileTransmissionStatusChanged = new OnFileTransmissionStatusChanged ( ) {
2014-08-08 09:49:23 +00:00
2014-04-20 20:34:27 +00:00
@Override
2014-10-13 23:06:45 +00:00
public void onFileTransmitted ( DownloadableFile file ) {
2017-05-03 09:03:04 +00:00
if ( responding ( ) ) {
if ( expectedHash . length > 0 & & ! Arrays . equals ( expectedHash , file . getSha1Sum ( ) ) ) {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " : hashes did not match " ) ;
2017-05-03 09:03:04 +00:00
}
2014-04-20 20:34:27 +00:00
sendSuccess ( ) ;
2015-08-08 15:19:40 +00:00
mXmppConnectionService . getFileBackend ( ) . updateFileParams ( message ) ;
mXmppConnectionService . databaseBackend . createMessage ( message ) ;
mXmppConnectionService . markMessage ( message , Message . STATUS_RECEIVED ) ;
2014-04-22 16:46:40 +00:00
if ( acceptedAutomatically ) {
message . markUnread ( ) ;
2016-06-15 10:44:29 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_PGP ) {
account . getPgpDecryptionService ( ) . decrypt ( message , true ) ;
} else {
JingleConnection . this . mXmppConnectionService . getNotificationService ( ) . push ( message ) ;
}
2014-04-22 16:46:40 +00:00
}
2014-11-14 00:29:56 +00:00
} else {
2017-05-03 09:03:04 +00:00
if ( ftVersion = = Content . Version . FT_5 ) { //older Conversations will break when receiving a session-info
sendHash ( ) ;
}
2016-06-15 10:44:29 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_PGP ) {
account . getPgpDecryptionService ( ) . decrypt ( message , false ) ;
}
2014-11-14 13:04:34 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_PGP | | message . getEncryption ( ) = = Message . ENCRYPTION_DECRYPTED ) {
2014-11-14 11:31:57 +00:00
file . delete ( ) ;
}
2014-08-08 09:49:23 +00:00
}
2017-05-03 09:03:04 +00:00
Log . d ( Config . LOGTAG , " successfully transmitted file: " + file . getAbsolutePath ( ) + " ( " + CryptoHelper . bytesToHex ( file . getSha1Sum ( ) ) + " ) " ) ;
2014-08-31 14:28:21 +00:00
if ( message . getEncryption ( ) ! = Message . ENCRYPTION_PGP ) {
2016-03-23 18:23:22 +00:00
mXmppConnectionService . getFileBackend ( ) . updateMediaScanner ( file ) ;
2014-04-20 20:34:27 +00:00
}
}
2014-06-29 11:44:59 +00:00
@Override
public void onFileTransferAborted ( ) {
2014-07-03 20:55:20 +00:00
JingleConnection . this . sendCancel ( ) ;
2014-11-15 11:37:09 +00:00
JingleConnection . this . fail ( ) ;
2014-06-29 11:44:59 +00:00
}
2014-04-20 20:34:27 +00:00
} ;
2014-08-08 09:49:23 +00:00
2018-09-30 11:48:11 +00:00
InputStream getFileInputStream ( ) {
2015-07-31 23:19:16 +00:00
return this . mFileInputStream ;
}
2018-09-30 11:48:11 +00:00
OutputStream getFileOutputStream ( ) throws IOException {
2017-10-26 16:42:10 +00:00
if ( this . file = = null ) {
Log . d ( Config . LOGTAG , " file object was not assigned " ) ;
return null ;
}
this . file . getParentFile ( ) . mkdirs ( ) ;
this . file . createNewFile ( ) ;
2018-09-30 11:48:11 +00:00
this . mFileOutputStream = AbstractConnectionManager . createOutputStream ( this . file ) ;
2015-07-31 23:19:16 +00:00
return this . mFileOutputStream ;
}
2014-04-20 20:34:27 +00:00
private OnProxyActivated onProxyActivated = new OnProxyActivated ( ) {
2014-08-08 09:49:23 +00:00
2014-04-20 20:34:27 +00:00
@Override
public void success ( ) {
2014-11-09 15:57:22 +00:00
if ( initiator . equals ( account . getJid ( ) ) ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " we were initiating. sending file " ) ;
2017-05-03 09:03:04 +00:00
transport . send ( file , onFileTransmissionStatusChanged ) ;
2014-04-20 20:34:27 +00:00
} else {
2017-05-03 09:03:04 +00:00
transport . receive ( file , onFileTransmissionStatusChanged ) ;
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " we were responding. receiving file " ) ;
2014-04-20 20:34:27 +00:00
}
}
2014-08-08 09:49:23 +00:00
2014-04-20 20:34:27 +00:00
@Override
public void failed ( ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " proxy activation failed " ) ;
2014-04-20 20:34:27 +00:00
}
} ;
2014-08-08 09:49:23 +00:00
2014-04-08 21:15:55 +00:00
public JingleConnection ( JingleConnectionManager mJingleConnectionManager ) {
2014-04-07 18:05:45 +00:00
this . mJingleConnectionManager = mJingleConnectionManager ;
2014-08-08 09:49:23 +00:00
this . mXmppConnectionService = mJingleConnectionManager
. getXmppConnectionService ( ) ;
2014-04-07 18:05:45 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-07 18:05:45 +00:00
public String getSessionId ( ) {
return this . sessionId ;
}
2014-08-08 09:49:23 +00:00
2014-10-21 17:43:34 +00:00
public Account getAccount ( ) {
return this . account ;
2014-04-10 12:12:08 +00:00
}
2014-08-08 09:49:23 +00:00
2014-11-06 19:10:03 +00:00
public Jid getCounterPart ( ) {
2014-04-10 12:12:08 +00:00
return this . message . getCounterpart ( ) ;
}
2014-08-08 09:49:23 +00:00
2014-04-10 12:12:08 +00:00
public void deliverPacket ( JinglePacket packet ) {
2014-05-13 12:49:09 +00:00
boolean returnResult = true ;
2014-04-11 19:13:09 +00:00
if ( packet . isAction ( " session-terminate " ) ) {
2014-04-13 09:32:45 +00:00
Reason reason = packet . getReason ( ) ;
2014-08-08 09:49:23 +00:00
if ( reason ! = null ) {
2014-04-16 10:50:53 +00:00
if ( reason . hasChild ( " cancel " ) ) {
2014-11-15 11:37:09 +00:00
this . fail ( ) ;
2014-04-16 10:50:53 +00:00
} else if ( reason . hasChild ( " success " ) ) {
2014-04-23 19:19:56 +00:00
this . receiveSuccess ( ) ;
2014-05-13 12:49:09 +00:00
} else {
2014-11-15 11:37:09 +00:00
this . fail ( ) ;
2014-04-16 10:50:53 +00:00
}
} else {
2014-11-15 11:37:09 +00:00
this . fail ( ) ;
2014-04-11 19:13:09 +00:00
}
2014-05-13 12:49:09 +00:00
} else if ( packet . isAction ( " session-accept " ) ) {
returnResult = receiveAccept ( packet ) ;
2017-05-03 09:03:04 +00:00
} else if ( packet . isAction ( " session-info " ) ) {
returnResult = true ;
Element checksum = packet . getChecksum ( ) ;
Element file = checksum = = null ? null : checksum . findChild ( " file " ) ;
Element hash = file = = null ? null : file . findChild ( " hash " , " urn:xmpp:hashes:2 " ) ;
if ( hash ! = null & & " sha-1 " . equalsIgnoreCase ( hash . getAttribute ( " algo " ) ) ) {
try {
this . expectedHash = Base64 . decode ( hash . getContent ( ) , Base64 . DEFAULT ) ;
} catch ( Exception e ) {
this . expectedHash = new byte [ 0 ] ;
}
}
2014-04-11 20:49:26 +00:00
} else if ( packet . isAction ( " transport-info " ) ) {
2014-05-13 12:49:09 +00:00
returnResult = receiveTransportInfo ( packet ) ;
2014-04-22 11:11:53 +00:00
} else if ( packet . isAction ( " transport-replace " ) ) {
if ( packet . getJingleContent ( ) . hasIbbTransport ( ) ) {
2014-05-13 12:49:09 +00:00
returnResult = this . receiveFallbackToIbb ( packet ) ;
2014-04-22 11:11:53 +00:00
} else {
2014-05-13 12:49:09 +00:00
returnResult = false ;
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " trying to fallback to something unknown "
2014-08-08 09:49:23 +00:00
+ packet . toString ( ) ) ;
2014-04-22 11:11:53 +00:00
}
} else if ( packet . isAction ( " transport-accept " ) ) {
2014-05-13 12:49:09 +00:00
returnResult = this . receiveTransportAccept ( packet ) ;
2014-04-11 19:13:09 +00:00
} else {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " packet arrived in connection. action was "
2014-08-08 09:49:23 +00:00
+ packet . getAction ( ) ) ;
2014-05-13 12:49:09 +00:00
returnResult = false ;
}
IqPacket response ;
if ( returnResult ) {
2014-12-30 13:16:25 +00:00
response = packet . generateResponse ( IqPacket . TYPE . RESULT ) ;
2014-08-08 09:49:23 +00:00
2014-05-13 12:49:09 +00:00
} else {
2014-12-30 13:16:25 +00:00
response = packet . generateResponse ( IqPacket . TYPE . ERROR ) ;
2014-04-10 12:12:08 +00:00
}
2015-04-26 18:27:30 +00:00
mXmppConnectionService . sendIqPacket ( account , response , null ) ;
2014-04-10 12:12:08 +00:00
}
2014-08-08 09:49:23 +00:00
2015-07-31 23:19:16 +00:00
public void init ( final Message message ) {
if ( message . getEncryption ( ) = = Message . ENCRYPTION_AXOLOTL ) {
2018-04-26 11:22:31 +00:00
Conversation conversation = ( Conversation ) message . getConversation ( ) ;
2016-02-29 12:18:07 +00:00
conversation . getAccount ( ) . getAxolotlService ( ) . prepareKeyTransportMessage ( conversation , new OnMessageCreatedCallback ( ) {
2015-07-31 23:19:16 +00:00
@Override
public void run ( XmppAxolotlMessage xmppAxolotlMessage ) {
2015-11-26 05:52:46 +00:00
if ( xmppAxolotlMessage ! = null ) {
init ( message , xmppAxolotlMessage ) ;
} else {
fail ( ) ;
}
2015-07-31 23:19:16 +00:00
}
} ) ;
} else {
init ( message , null ) ;
}
}
private void init ( Message message , XmppAxolotlMessage xmppAxolotlMessage ) {
this . mXmppAxolotlMessage = xmppAxolotlMessage ;
2014-05-03 16:47:53 +00:00
this . contentCreator = " initiator " ;
this . contentName = this . mJingleConnectionManager . nextRandomId ( ) ;
2014-04-08 21:15:55 +00:00
this . message = message ;
2016-06-29 15:16:34 +00:00
this . account = message . getConversation ( ) . getAccount ( ) ;
upgradeNamespace ( ) ;
2015-07-10 13:11:03 +00:00
this . message . setTransferable ( this ) ;
this . mStatus = Transferable . STATUS_UPLOADING ;
2014-11-09 15:57:22 +00:00
this . initiator = this . account . getJid ( ) ;
2014-04-11 19:13:09 +00:00
this . responder = this . message . getCounterpart ( ) ;
2014-04-13 09:32:45 +00:00
this . sessionId = this . mJingleConnectionManager . nextRandomId ( ) ;
2016-06-29 15:16:34 +00:00
this . transportId = this . mJingleConnectionManager . nextRandomId ( ) ;
2014-04-11 19:13:09 +00:00
if ( this . candidates . size ( ) > 0 ) {
2014-04-08 21:15:55 +00:00
this . sendInitRequest ( ) ;
} else {
2014-08-08 09:49:23 +00:00
this . mJingleConnectionManager . getPrimaryCandidate ( account ,
new OnPrimaryCandidateFound ( ) {
@Override
public void onPrimaryCandidateFound ( boolean success ,
2015-05-18 01:17:14 +00:00
final JingleCandidate candidate ) {
2014-08-08 09:49:23 +00:00
if ( success ) {
final JingleSocks5Transport socksConnection = new JingleSocks5Transport (
JingleConnection . this , candidate ) ;
connections . put ( candidate . getCid ( ) ,
socksConnection ) ;
socksConnection
. connect ( new OnTransportConnected ( ) {
@Override
public void failed ( ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG ,
2014-08-08 09:49:23 +00:00
" connection to our own primary candidete failed " ) ;
sendInitRequest ( ) ;
}
@Override
public void established ( ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG ,
2016-05-04 08:29:29 +00:00
" successfully connected to our own primary candidate " ) ;
2014-08-08 09:49:23 +00:00
mergeCandidate ( candidate ) ;
sendInitRequest ( ) ;
}
} ) ;
2014-04-18 09:57:28 +00:00
mergeCandidate ( candidate ) ;
2014-08-08 09:49:23 +00:00
} else {
2015-08-10 10:55:37 +00:00
Log . d ( Config . LOGTAG , " no primary candidate of our own was found " ) ;
2014-04-18 09:57:28 +00:00
sendInitRequest ( ) ;
}
2014-08-08 09:49:23 +00:00
}
} ) ;
2014-04-08 21:15:55 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-08 21:15:55 +00:00
}
2014-08-08 09:49:23 +00:00
2016-06-29 15:16:34 +00:00
private void upgradeNamespace ( ) {
Jid jid = this . message . getCounterpart ( ) ;
2018-03-05 17:30:40 +00:00
String resource = jid ! = null ? jid . getResource ( ) : null ;
2016-06-29 15:16:34 +00:00
if ( resource ! = null ) {
Presence presence = this . account . getRoster ( ) . getContact ( jid ) . getPresences ( ) . getPresences ( ) . get ( resource ) ;
2016-07-04 17:29:46 +00:00
ServiceDiscoveryResult result = presence ! = null ? presence . getServiceDiscoveryResult ( ) : null ;
if ( result ! = null ) {
List < String > features = result . getFeatures ( ) ;
2017-04-30 14:21:13 +00:00
if ( features . contains ( Content . Version . FT_5 . getNamespace ( ) ) ) {
this . ftVersion = Content . Version . FT_5 ;
} else if ( features . contains ( Content . Version . FT_4 . getNamespace ( ) ) ) {
2016-06-29 15:16:34 +00:00
this . ftVersion = Content . Version . FT_4 ;
}
}
}
}
2014-04-13 09:32:45 +00:00
public void init ( Account account , JinglePacket packet ) {
2014-10-15 17:32:12 +00:00
this . mJingleStatus = JINGLE_STATUS_INITIATED ;
2014-08-08 09:49:23 +00:00
Conversation conversation = this . mXmppConnectionService
. findOrCreateConversation ( account ,
2018-03-05 17:30:40 +00:00
packet . getFrom ( ) . asBareJid ( ) , false , false ) ;
2014-04-25 21:06:20 +00:00
this . message = new Message ( conversation , " " , Message . ENCRYPTION_NONE ) ;
2014-10-15 17:32:12 +00:00
this . message . setStatus ( Message . STATUS_RECEIVED ) ;
2015-07-10 13:11:03 +00:00
this . mStatus = Transferable . STATUS_OFFER ;
this . message . setTransferable ( this ) ;
2014-11-06 19:10:03 +00:00
final Jid from = packet . getFrom ( ) ;
2014-11-09 15:21:13 +00:00
this . message . setCounterpart ( from ) ;
2014-04-13 09:32:45 +00:00
this . account = account ;
this . initiator = packet . getFrom ( ) ;
2014-11-09 15:57:22 +00:00
this . responder = this . account . getJid ( ) ;
2014-04-13 09:32:45 +00:00
this . sessionId = packet . getSessionId ( ) ;
2014-04-17 12:52:10 +00:00
Content content = packet . getJingleContent ( ) ;
2014-05-03 16:47:53 +00:00
this . contentCreator = content . getAttribute ( " creator " ) ;
this . contentName = content . getAttribute ( " name " ) ;
2014-04-17 12:52:10 +00:00
this . transportId = content . getTransportId ( ) ;
2015-07-31 23:19:16 +00:00
this . mergeCandidates ( JingleCandidate . parse ( content . socks5transport ( ) . getChildren ( ) ) ) ;
2016-06-29 15:16:34 +00:00
this . ftVersion = content . getVersion ( ) ;
if ( ftVersion = = null ) {
this . sendCancel ( ) ;
this . fail ( ) ;
return ;
}
this . fileOffer = content . getFileOffer ( this . ftVersion ) ;
2015-05-16 02:12:53 +00:00
mXmppConnectionService . sendIqPacket ( account , packet . generateResponse ( IqPacket . TYPE . RESULT ) , null ) ;
2014-08-08 09:49:23 +00:00
if ( fileOffer ! = null ) {
2015-07-31 23:19:16 +00:00
Element encrypted = fileOffer . findChild ( " encrypted " , AxolotlService . PEP_PREFIX ) ;
if ( encrypted ! = null ) {
2018-03-05 17:30:40 +00:00
this . mXmppAxolotlMessage = XmppAxolotlMessage . fromElement ( encrypted , packet . getFrom ( ) . asBareJid ( ) ) ;
2015-07-31 23:19:16 +00:00
}
2014-04-13 16:09:40 +00:00
Element fileSize = fileOffer . findChild ( " size " ) ;
2014-04-23 19:19:56 +00:00
Element fileNameElement = fileOffer . findChild ( " name " ) ;
2014-08-08 09:49:23 +00:00
if ( fileNameElement ! = null ) {
String [ ] filename = fileNameElement . getContent ( )
2015-01-04 22:04:23 +00:00
. toLowerCase ( Locale . US ) . toLowerCase ( ) . split ( " \\ . " ) ;
2015-01-11 14:19:36 +00:00
String extension = filename [ filename . length - 1 ] ;
2016-03-04 10:24:53 +00:00
if ( VALID_IMAGE_EXTENSIONS . contains ( extension ) ) {
2014-11-13 20:04:05 +00:00
message . setType ( Message . TYPE_IMAGE ) ;
2015-01-11 14:19:36 +00:00
message . setRelativeFilePath ( message . getUuid ( ) + " . " + extension ) ;
2016-03-04 10:24:53 +00:00
} else if ( VALID_CRYPTO_EXTENSIONS . contains (
2014-08-08 09:49:23 +00:00
filename [ filename . length - 1 ] ) ) {
2014-04-23 19:19:56 +00:00
if ( filename . length = = 3 ) {
2015-01-11 14:19:36 +00:00
extension = filename [ filename . length - 2 ] ;
2016-03-04 10:24:53 +00:00
if ( VALID_IMAGE_EXTENSIONS . contains ( extension ) ) {
2014-11-13 20:04:05 +00:00
message . setType ( Message . TYPE_IMAGE ) ;
2015-01-11 14:19:36 +00:00
message . setRelativeFilePath ( message . getUuid ( ) + " . " + extension ) ;
2014-11-13 20:04:05 +00:00
} else {
message . setType ( Message . TYPE_FILE ) ;
2014-04-23 19:19:56 +00:00
}
2018-02-19 12:55:48 +00:00
message . setEncryption ( Message . ENCRYPTION_PGP ) ;
2014-06-20 15:30:19 +00:00
}
2014-04-23 19:19:56 +00:00
} else {
2014-11-13 20:04:05 +00:00
message . setType ( Message . TYPE_FILE ) ;
}
if ( message . getType ( ) = = Message . TYPE_FILE ) {
String suffix = " " ;
if ( ! fileNameElement . getContent ( ) . isEmpty ( ) ) {
String parts [ ] = fileNameElement . getContent ( ) . split ( " / " ) ;
suffix = parts [ parts . length - 1 ] ;
2018-02-19 12:55:48 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_PGP & & ( suffix . endsWith ( " .pgp " ) | | suffix . endsWith ( " .gpg " ) ) ) {
2014-11-14 02:27:18 +00:00
suffix = suffix . substring ( 0 , suffix . length ( ) - 4 ) ;
2014-11-13 21:59:00 +00:00
}
2014-11-13 20:04:05 +00:00
}
message . setRelativeFilePath ( message . getUuid ( ) + " _ " + suffix ) ;
2014-04-22 16:46:40 +00:00
}
2014-11-13 20:04:05 +00:00
long size = Long . parseLong ( fileSize . getContent ( ) ) ;
message . setBody ( Long . toString ( size ) ) ;
conversation . add ( message ) ;
2017-01-12 15:02:09 +00:00
mJingleConnectionManager . updateConversationUi ( true ) ;
2015-12-06 17:23:59 +00:00
if ( mJingleConnectionManager . hasStoragePermission ( )
2016-08-27 11:35:52 +00:00
& & size < this . mJingleConnectionManager . getAutoAcceptFileSize ( )
& & mXmppConnectionService . isDataSaverDisabled ( ) ) {
2015-07-31 23:19:16 +00:00
Log . d ( Config . LOGTAG , " auto accepting file from " + packet . getFrom ( ) ) ;
2014-11-13 20:04:05 +00:00
this . acceptedAutomatically = true ;
this . sendAccept ( ) ;
} else {
message . markUnread ( ) ;
Log . d ( Config . LOGTAG ,
" not auto accepting new file offer with size: "
+ size
+ " allowed size: "
+ this . mJingleConnectionManager
. getAutoAcceptFileSize ( ) ) ;
2015-07-31 23:19:16 +00:00
this . mXmppConnectionService . getNotificationService ( ) . push ( message ) ;
2014-11-13 20:04:05 +00:00
}
2015-07-31 23:19:16 +00:00
this . file = this . mXmppConnectionService . getFileBackend ( ) . getFile ( message , false ) ;
if ( mXmppAxolotlMessage ! = null ) {
2018-01-18 19:58:55 +00:00
XmppAxolotlMessage . XmppAxolotlKeyTransportMessage transportMessage = account . getAxolotlService ( ) . processReceivingKeyTransportMessage ( mXmppAxolotlMessage , false ) ;
2015-07-31 23:19:16 +00:00
if ( transportMessage ! = null ) {
message . setEncryption ( Message . ENCRYPTION_AXOLOTL ) ;
this . file . setKey ( transportMessage . getKey ( ) ) ;
this . file . setIv ( transportMessage . getIv ( ) ) ;
2016-03-31 19:15:49 +00:00
message . setFingerprint ( transportMessage . getFingerprint ( ) ) ;
2015-07-31 23:19:16 +00:00
} else {
Log . d ( Config . LOGTAG , " could not process KeyTransportMessage " ) ;
}
2014-11-13 20:04:05 +00:00
}
2017-07-18 10:43:53 +00:00
this . file . setExpectedSize ( size ) ;
2017-08-04 09:58:12 +00:00
message . resetFileParams ( ) ;
2015-07-31 23:19:16 +00:00
Log . d ( Config . LOGTAG , " receiving file: expecting size of " + this . file . getExpectedSize ( ) ) ;
2014-04-23 19:19:56 +00:00
} else {
2014-07-03 20:55:20 +00:00
this . sendCancel ( ) ;
2014-11-15 11:37:09 +00:00
this . fail ( ) ;
2014-04-13 16:09:40 +00:00
}
} else {
2014-07-03 20:55:20 +00:00
this . sendCancel ( ) ;
2014-11-15 11:37:09 +00:00
this . fail ( ) ;
2014-04-13 16:09:40 +00:00
}
2014-04-13 09:32:45 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-08 21:15:55 +00:00
private void sendInitRequest ( ) {
2014-04-16 10:50:53 +00:00
JinglePacket packet = this . bootstrapPacket ( " session-initiate " ) ;
2014-08-08 09:49:23 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-11-13 20:04:05 +00:00
if ( message . getType ( ) = = Message . TYPE_IMAGE | | message . getType ( ) = = Message . TYPE_FILE ) {
2014-04-20 20:34:27 +00:00
content . setTransportId ( this . transportId ) ;
2015-07-31 23:19:16 +00:00
this . file = this . mXmppConnectionService . getFileBackend ( ) . getFile ( message , false ) ;
Pair < InputStream , Integer > pair ;
2015-08-11 14:50:00 +00:00
try {
2018-02-19 12:55:48 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_AXOLOTL ) {
2015-08-11 14:50:00 +00:00
this . file . setKey ( mXmppAxolotlMessage . getInnerKey ( ) ) ;
this . file . setIv ( mXmppAxolotlMessage . getIV ( ) ) ;
2018-10-03 10:50:54 +00:00
pair = AbstractConnectionManager . createInputStream ( this . file ) ;
2015-08-11 14:50:00 +00:00
this . file . setExpectedSize ( pair . second ) ;
2016-06-29 15:16:34 +00:00
content . setFileOffer ( this . file , false , this . ftVersion ) . addChild ( mXmppAxolotlMessage . toElement ( ) ) ;
2015-08-11 14:50:00 +00:00
} else {
2018-10-03 10:50:54 +00:00
pair = AbstractConnectionManager . createInputStream ( this . file ) ;
2015-08-11 14:50:00 +00:00
this . file . setExpectedSize ( pair . second ) ;
2016-06-29 15:16:34 +00:00
content . setFileOffer ( this . file , false , this . ftVersion ) ;
2015-07-19 12:51:04 +00:00
}
2015-08-11 14:50:00 +00:00
} catch ( FileNotFoundException e ) {
cancel ( ) ;
return ;
2014-06-20 15:30:19 +00:00
}
2017-08-04 09:58:12 +00:00
message . resetFileParams ( ) ;
2015-07-31 23:19:16 +00:00
this . mFileInputStream = pair . first ;
2014-04-21 10:03:26 +00:00
content . setTransportId ( this . transportId ) ;
2014-04-20 20:34:27 +00:00
content . socks5transport ( ) . setChildren ( getCandidatesAsElements ( ) ) ;
2014-04-07 18:05:45 +00:00
packet . setContent ( content ) ;
2015-01-25 15:32:59 +00:00
this . sendJinglePacket ( packet , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-08-23 15:53:23 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " : other party received offer " ) ;
2017-08-18 12:33:56 +00:00
if ( mJingleStatus = = JINGLE_STATUS_OFFERED ) {
mJingleStatus = JINGLE_STATUS_INITIATED ;
mXmppConnectionService . markMessage ( message , Message . STATUS_OFFERED ) ;
} else {
Log . d ( Config . LOGTAG , " received ack for offer when status was " + mJingleStatus ) ;
}
2015-01-25 15:32:59 +00:00
} else {
2016-10-26 10:26:04 +00:00
fail ( IqParser . extractErrorMessage ( packet ) ) ;
2015-01-25 15:32:59 +00:00
}
}
} ) ;
2014-04-07 18:05:45 +00:00
}
}
2014-08-08 09:49:23 +00:00
2017-05-03 09:03:04 +00:00
private void sendHash ( ) {
JinglePacket packet = this . bootstrapPacket ( " session-info " ) ;
packet . addChecksum ( file . getSha1Sum ( ) , ftVersion . getNamespace ( ) ) ;
this . sendJinglePacket ( packet ) ;
}
2014-04-17 12:52:10 +00:00
private List < Element > getCandidatesAsElements ( ) {
2014-11-06 19:10:03 +00:00
List < Element > elements = new ArrayList < > ( ) ;
2014-08-08 09:49:23 +00:00
for ( JingleCandidate c : this . candidates ) {
2015-05-18 01:17:14 +00:00
if ( c . isOurs ( ) ) {
elements . add ( c . toElement ( ) ) ;
}
2014-04-17 12:52:10 +00:00
}
return elements ;
}
2014-08-08 09:49:23 +00:00
2014-04-13 16:09:40 +00:00
private void sendAccept ( ) {
2014-10-15 17:32:12 +00:00
mJingleStatus = JINGLE_STATUS_ACCEPTED ;
2015-07-10 13:11:03 +00:00
this . mStatus = Transferable . STATUS_DOWNLOADING ;
2017-01-12 15:02:09 +00:00
this . mJingleConnectionManager . updateConversationUi ( true ) ;
2015-01-09 13:42:58 +00:00
this . mJingleConnectionManager . getPrimaryCandidate ( this . account , new OnPrimaryCandidateFound ( ) {
@Override
public void onPrimaryCandidateFound ( boolean success , final JingleCandidate candidate ) {
final JinglePacket packet = bootstrapPacket ( " session-accept " ) ;
final Content content = new Content ( contentCreator , contentName ) ;
2016-06-29 15:16:34 +00:00
content . setFileOffer ( fileOffer , ftVersion ) ;
2015-01-09 13:42:58 +00:00
content . setTransportId ( transportId ) ;
if ( success & & candidate ! = null & & ! equalCandidateExists ( candidate ) ) {
final JingleSocks5Transport socksConnection = new JingleSocks5Transport (
JingleConnection . this ,
candidate ) ;
connections . put ( candidate . getCid ( ) , socksConnection ) ;
socksConnection . connect ( new OnTransportConnected ( ) {
@Override
public void failed ( ) {
Log . d ( Config . LOGTAG , " connection to our own primary candidate failed " ) ;
content . socks5transport ( ) . setChildren ( getCandidatesAsElements ( ) ) ;
2014-04-18 09:57:28 +00:00
packet . setContent ( content ) ;
2014-04-18 23:14:30 +00:00
sendJinglePacket ( packet ) ;
2014-04-21 18:39:57 +00:00
connectNextCandidate ( ) ;
2014-04-18 09:57:28 +00:00
}
2014-08-08 09:49:23 +00:00
2015-01-09 13:42:58 +00:00
@Override
public void established ( ) {
Log . d ( Config . LOGTAG , " connected to primary candidate " ) ;
mergeCandidate ( candidate ) ;
content . socks5transport ( ) . setChildren ( getCandidatesAsElements ( ) ) ;
packet . setContent ( content ) ;
sendJinglePacket ( packet ) ;
connectNextCandidate ( ) ;
}
} ) ;
} else {
Log . d ( Config . LOGTAG , " did not find a primary candidate for ourself " ) ;
content . socks5transport ( ) . setChildren ( getCandidatesAsElements ( ) ) ;
packet . setContent ( content ) ;
sendJinglePacket ( packet ) ;
connectNextCandidate ( ) ;
}
}
} ) ;
2014-04-13 16:09:40 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-16 10:50:53 +00:00
private JinglePacket bootstrapPacket ( String action ) {
2014-04-07 18:05:45 +00:00
JinglePacket packet = new JinglePacket ( ) ;
2014-04-16 10:50:53 +00:00
packet . setAction ( action ) ;
2014-11-09 15:57:22 +00:00
packet . setFrom ( account . getJid ( ) ) ;
2014-04-18 09:57:28 +00:00
packet . setTo ( this . message . getCounterpart ( ) ) ;
2014-04-07 18:05:45 +00:00
packet . setSessionId ( this . sessionId ) ;
2014-04-13 16:09:40 +00:00
packet . setInitiator ( this . initiator ) ;
2014-04-07 18:05:45 +00:00
return packet ;
}
2014-08-08 09:49:23 +00:00
2014-04-18 23:14:30 +00:00
private void sendJinglePacket ( JinglePacket packet ) {
2015-04-26 18:27:30 +00:00
mXmppConnectionService . sendIqPacket ( account , packet , responseListener ) ;
2015-01-25 15:32:59 +00:00
}
private void sendJinglePacket ( JinglePacket packet , OnIqPacketReceived callback ) {
2015-04-26 18:27:30 +00:00
mXmppConnectionService . sendIqPacket ( account , packet , callback ) ;
2014-04-18 23:14:30 +00:00
}
2014-08-08 09:49:23 +00:00
2014-05-13 12:49:09 +00:00
private boolean receiveAccept ( JinglePacket packet ) {
2014-04-11 19:13:09 +00:00
Content content = packet . getJingleContent ( ) ;
2014-08-08 09:49:23 +00:00
mergeCandidates ( JingleCandidate . parse ( content . socks5transport ( )
. getChildren ( ) ) ) ;
2014-10-15 17:32:12 +00:00
this . mJingleStatus = JINGLE_STATUS_ACCEPTED ;
2014-04-22 16:46:40 +00:00
mXmppConnectionService . markMessage ( message , Message . STATUS_UNSEND ) ;
2014-04-14 19:21:13 +00:00
this . connectNextCandidate ( ) ;
2014-05-13 12:49:09 +00:00
return true ;
2014-04-11 19:13:09 +00:00
}
2014-04-13 16:09:40 +00:00
2014-05-13 12:49:09 +00:00
private boolean receiveTransportInfo ( JinglePacket packet ) {
2014-04-11 20:49:26 +00:00
Content content = packet . getJingleContent ( ) ;
2014-04-20 20:34:27 +00:00
if ( content . hasSocks5Transport ( ) ) {
if ( content . socks5transport ( ) . hasChild ( " activated " ) ) {
2015-01-09 13:42:58 +00:00
if ( ( this . transport ! = null ) & & ( this . transport instanceof JingleSocks5Transport ) ) {
2014-04-23 19:19:56 +00:00
onProxyActivated . success ( ) ;
} else {
2015-01-09 13:42:58 +00:00
String cid = content . socks5transport ( ) . findChild ( " activated " ) . getAttribute ( " cid " ) ;
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " received proxy activated ( " + cid
2014-08-08 09:49:23 +00:00
+ " )prior to choosing our own transport " ) ;
2015-01-09 13:42:58 +00:00
JingleSocks5Transport connection = this . connections . get ( cid ) ;
2014-08-08 09:49:23 +00:00
if ( connection ! = null ) {
2014-04-23 19:19:56 +00:00
connection . setActivated ( true ) ;
} else {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " activated connection not found " ) ;
2014-07-03 20:55:20 +00:00
this . sendCancel ( ) ;
2014-11-15 11:37:09 +00:00
this . fail ( ) ;
2014-04-23 19:19:56 +00:00
}
}
2014-05-13 12:49:09 +00:00
return true ;
} else if ( content . socks5transport ( ) . hasChild ( " proxy-error " ) ) {
2014-04-20 20:34:27 +00:00
onProxyActivated . failed ( ) ;
2014-05-13 12:49:09 +00:00
return true ;
2014-04-20 20:34:27 +00:00
} else if ( content . socks5transport ( ) . hasChild ( " candidate-error " ) ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " received candidate error " ) ;
2014-04-20 20:34:27 +00:00
this . receivedCandidate = true ;
2017-08-18 12:33:56 +00:00
if ( mJingleStatus = = JINGLE_STATUS_ACCEPTED & & this . sentCandidate ) {
2014-04-20 20:34:27 +00:00
this . connect ( ) ;
}
2014-05-13 12:49:09 +00:00
return true ;
2014-08-08 09:49:23 +00:00
} else if ( content . socks5transport ( ) . hasChild ( " candidate-used " ) ) {
String cid = content . socks5transport ( )
. findChild ( " candidate-used " ) . getAttribute ( " cid " ) ;
if ( cid ! = null ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " candidate used by counterpart: " + cid ) ;
2014-04-20 20:34:27 +00:00
JingleCandidate candidate = getCandidate ( cid ) ;
2016-11-01 09:27:01 +00:00
if ( candidate = = null ) {
Log . d ( Config . LOGTAG , " could not find candidate with cid= " + cid ) ;
return false ;
}
2014-04-20 20:34:27 +00:00
candidate . flagAsUsedByCounterpart ( ) ;
this . receivedCandidate = true ;
2017-08-18 12:33:56 +00:00
if ( mJingleStatus = = JINGLE_STATUS_ACCEPTED & & this . sentCandidate ) {
2014-04-20 20:34:27 +00:00
this . connect ( ) ;
} else {
2017-08-18 12:33:56 +00:00
Log . d ( Config . LOGTAG , " ignoring because file is already in transmission or we haven't sent our candidate yet status= " + mJingleStatus + " sentCandidate= " + Boolean . toString ( sentCandidate ) ) ;
2014-04-20 20:34:27 +00:00
}
2014-05-13 12:49:09 +00:00
return true ;
2014-04-20 20:34:27 +00:00
} else {
2014-05-13 12:49:09 +00:00
return false ;
2014-04-20 20:34:27 +00:00
}
2014-04-13 19:10:36 +00:00
} else {
2014-05-13 12:49:09 +00:00
return false ;
2014-04-13 16:09:40 +00:00
}
2014-05-13 12:49:09 +00:00
} else {
return true ;
2014-04-13 16:09:40 +00:00
}
}
2014-04-17 12:52:10 +00:00
private void connect ( ) {
2014-04-22 11:11:53 +00:00
final JingleSocks5Transport connection = chooseConnection ( ) ;
2014-04-20 20:34:27 +00:00
this . transport = connection ;
2014-08-08 09:49:23 +00:00
if ( connection = = null ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " could not find suitable candidate " ) ;
2014-11-15 11:37:09 +00:00
this . disconnectSocks5Connections ( ) ;
2017-05-03 09:03:04 +00:00
if ( initiating ( ) ) {
2014-04-22 11:11:53 +00:00
this . sendFallbackToIbb ( ) ;
}
2014-04-18 23:14:30 +00:00
} else {
2014-10-15 17:32:12 +00:00
this . mJingleStatus = JINGLE_STATUS_TRANSMITTING ;
2014-04-23 19:19:56 +00:00
if ( connection . needsActivation ( ) ) {
2014-04-20 20:34:27 +00:00
if ( connection . getCandidate ( ) . isOurs ( ) ) {
2016-06-29 15:16:34 +00:00
final String sid ;
if ( ftVersion = = Content . Version . FT_3 ) {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " : use session ID instead of transport ID to activate proxy " ) ;
2016-06-29 15:16:34 +00:00
sid = getSessionId ( ) ;
} else {
sid = getTransportId ( ) ;
}
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " candidate "
2014-08-08 09:49:23 +00:00
+ connection . getCandidate ( ) . getCid ( )
+ " was our proxy. going to activate " ) ;
2014-12-30 13:16:25 +00:00
IqPacket activation = new IqPacket ( IqPacket . TYPE . SET ) ;
2014-04-20 20:34:27 +00:00
activation . setTo ( connection . getCandidate ( ) . getJid ( ) ) ;
2014-08-08 09:49:23 +00:00
activation . query ( " http://jabber.org/protocol/bytestreams " )
2016-06-29 15:16:34 +00:00
. setAttribute ( " sid " , sid ) ;
2014-08-08 09:49:23 +00:00
activation . query ( ) . addChild ( " activate " )
2014-11-06 19:10:03 +00:00
. setContent ( this . getCounterPart ( ) . toString ( ) ) ;
2015-04-26 18:27:30 +00:00
mXmppConnectionService . sendIqPacket ( account , activation ,
2014-08-08 09:49:23 +00:00
new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account ,
IqPacket packet ) {
2015-08-23 15:53:23 +00:00
if ( packet . getType ( ) ! = IqPacket . TYPE . RESULT ) {
2014-08-08 09:49:23 +00:00
onProxyActivated . failed ( ) ;
} else {
onProxyActivated . success ( ) ;
2015-08-23 15:53:23 +00:00
sendProxyActivated ( connection . getCandidate ( ) . getCid ( ) ) ;
2014-08-08 09:49:23 +00:00
}
}
} ) ;
2014-04-25 21:06:20 +00:00
} else {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG ,
2014-08-08 09:49:23 +00:00
" candidate "
+ connection . getCandidate ( ) . getCid ( )
+ " was a proxy. waiting for other party to activate " ) ;
2014-04-20 20:34:27 +00:00
}
2014-04-13 16:09:40 +00:00
} else {
2017-05-03 09:03:04 +00:00
if ( initiating ( ) ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " we were initiating. sending file " ) ;
2017-05-03 09:03:04 +00:00
connection . send ( file , onFileTransmissionStatusChanged ) ;
2014-04-18 23:14:30 +00:00
} else {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG , " we were responding. receiving file " ) ;
2017-05-03 09:03:04 +00:00
connection . receive ( file , onFileTransmissionStatusChanged ) ;
2014-04-18 23:14:30 +00:00
}
2014-04-11 20:49:26 +00:00
}
}
}
2014-08-08 09:49:23 +00:00
2014-04-22 11:11:53 +00:00
private JingleSocks5Transport chooseConnection ( ) {
JingleSocks5Transport connection = null ;
2014-08-08 09:49:23 +00:00
for ( Entry < String , JingleSocks5Transport > cursor : connections
. entrySet ( ) ) {
JingleSocks5Transport currentConnection = cursor . getValue ( ) ;
2014-08-31 14:28:21 +00:00
// Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
2014-08-08 09:49:23 +00:00
if ( currentConnection . isEstablished ( )
& & ( currentConnection . getCandidate ( ) . isUsedByCounterpart ( ) | | ( ! currentConnection
. getCandidate ( ) . isOurs ( ) ) ) ) {
2014-08-31 14:28:21 +00:00
// Log.d(Config.LOGTAG,"is usable");
2014-08-08 09:49:23 +00:00
if ( connection = = null ) {
connection = currentConnection ;
} else {
if ( connection . getCandidate ( ) . getPriority ( ) < currentConnection
. getCandidate ( ) . getPriority ( ) ) {
connection = currentConnection ;
} else if ( connection . getCandidate ( ) . getPriority ( ) = = currentConnection
. getCandidate ( ) . getPriority ( ) ) {
2014-08-31 14:28:21 +00:00
// Log.d(Config.LOGTAG,"found two candidates with same priority");
2017-05-03 09:03:04 +00:00
if ( initiating ( ) ) {
2014-08-08 09:49:23 +00:00
if ( currentConnection . getCandidate ( ) . isOurs ( ) ) {
connection = currentConnection ;
}
} else {
if ( ! currentConnection . getCandidate ( ) . isOurs ( ) ) {
connection = currentConnection ;
}
}
}
}
}
}
2014-04-17 12:52:10 +00:00
return connection ;
}
2014-04-16 10:50:53 +00:00
private void sendSuccess ( ) {
JinglePacket packet = bootstrapPacket ( " session-terminate " ) ;
Reason reason = new Reason ( ) ;
reason . addChild ( " success " ) ;
packet . setReason ( reason ) ;
2014-04-18 23:14:30 +00:00
this . sendJinglePacket ( packet ) ;
2014-11-15 11:37:09 +00:00
this . disconnectSocks5Connections ( ) ;
2014-10-15 17:32:12 +00:00
this . mJingleStatus = JINGLE_STATUS_FINISHED ;
this . message . setStatus ( Message . STATUS_RECEIVED ) ;
2015-07-10 13:11:03 +00:00
this . message . setTransferable ( null ) ;
2018-05-10 08:47:28 +00:00
this . mXmppConnectionService . updateMessage ( message , false ) ;
2014-05-13 12:49:09 +00:00
this . mJingleConnectionManager . finishConnection ( this ) ;
2014-04-16 10:50:53 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-22 11:11:53 +00:00
private void sendFallbackToIbb ( ) {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " : sending fallback to ibb " ) ;
2014-04-22 11:11:53 +00:00
JinglePacket packet = this . bootstrapPacket ( " transport-replace " ) ;
2014-08-08 09:49:23 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-04-22 11:11:53 +00:00
this . transportId = this . mJingleConnectionManager . nextRandomId ( ) ;
content . setTransportId ( this . transportId ) ;
2014-08-08 09:49:23 +00:00
content . ibbTransport ( ) . setAttribute ( " block-size " ,
2014-09-01 08:40:45 +00:00
Integer . toString ( this . ibbBlockSize ) ) ;
2014-04-22 11:11:53 +00:00
packet . setContent ( content ) ;
this . sendJinglePacket ( packet ) ;
}
2014-08-08 09:49:23 +00:00
2017-01-20 21:37:50 +00:00
OnTransportConnected onIbbTransportConnected = new OnTransportConnected ( ) {
@Override
public void failed ( ) {
Log . d ( Config . LOGTAG , " ibb open failed " ) ;
}
@Override
public void established ( ) {
2017-05-03 09:03:04 +00:00
JingleConnection . this . transport . send ( file , onFileTransmissionStatusChanged ) ;
2017-01-20 21:37:50 +00:00
}
} ;
2014-05-13 12:49:09 +00:00
private boolean receiveFallbackToIbb ( JinglePacket packet ) {
2014-10-20 19:08:33 +00:00
Log . d ( Config . LOGTAG , " receiving fallack to ibb " ) ;
2014-08-08 09:49:23 +00:00
String receivedBlockSize = packet . getJingleContent ( ) . ibbTransport ( )
. getAttribute ( " block-size " ) ;
if ( receivedBlockSize ! = null ) {
2014-04-22 11:11:53 +00:00
int bs = Integer . parseInt ( receivedBlockSize ) ;
2014-08-08 09:49:23 +00:00
if ( bs > this . ibbBlockSize ) {
2014-04-22 11:11:53 +00:00
this . ibbBlockSize = bs ;
}
}
this . transportId = packet . getJingleContent ( ) . getTransportId ( ) ;
2014-11-14 13:04:34 +00:00
this . transport = new JingleInbandTransport ( this , this . transportId , this . ibbBlockSize ) ;
2017-01-20 21:37:50 +00:00
2014-04-22 11:11:53 +00:00
JinglePacket answer = bootstrapPacket ( " transport-accept " ) ;
Content content = new Content ( " initiator " , " a-file-offer " ) ;
content . setTransportId ( this . transportId ) ;
2015-10-12 10:36:54 +00:00
content . ibbTransport ( ) . setAttribute ( " block-size " , this . ibbBlockSize ) ;
2014-04-22 11:11:53 +00:00
answer . setContent ( content ) ;
2017-01-20 21:37:50 +00:00
2017-05-03 09:03:04 +00:00
if ( initiating ( ) ) {
2017-01-20 21:37:50 +00:00
this . sendJinglePacket ( answer , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " recipient ACKed our transport-accept. creating ibb " ) ;
2017-01-20 21:37:50 +00:00
transport . connect ( onIbbTransportConnected ) ;
}
}
} ) ;
} else {
2017-05-03 09:03:04 +00:00
this . transport . receive ( file , onFileTransmissionStatusChanged ) ;
2017-01-20 21:37:50 +00:00
this . sendJinglePacket ( answer ) ;
}
2014-05-13 12:49:09 +00:00
return true ;
2014-04-22 11:11:53 +00:00
}
2014-08-08 09:49:23 +00:00
2014-05-13 12:49:09 +00:00
private boolean receiveTransportAccept ( JinglePacket packet ) {
2014-04-22 11:11:53 +00:00
if ( packet . getJingleContent ( ) . hasIbbTransport ( ) ) {
2014-08-08 09:49:23 +00:00
String receivedBlockSize = packet . getJingleContent ( ) . ibbTransport ( )
. getAttribute ( " block-size " ) ;
if ( receivedBlockSize ! = null ) {
2014-04-22 11:11:53 +00:00
int bs = Integer . parseInt ( receivedBlockSize ) ;
2014-08-08 09:49:23 +00:00
if ( bs > this . ibbBlockSize ) {
2014-04-22 11:11:53 +00:00
this . ibbBlockSize = bs ;
}
}
2014-11-14 13:04:34 +00:00
this . transport = new JingleInbandTransport ( this , this . transportId , this . ibbBlockSize ) ;
2014-08-08 09:49:23 +00:00
2017-01-20 21:37:50 +00:00
//might be receive instead if we are not initiating
2017-05-03 09:03:04 +00:00
if ( initiating ( ) ) {
2017-01-20 21:37:50 +00:00
this . transport . connect ( onIbbTransportConnected ) ;
} else {
2017-05-03 09:03:04 +00:00
this . transport . receive ( file , onFileTransmissionStatusChanged ) ;
2017-01-20 21:37:50 +00:00
}
2014-05-13 12:49:09 +00:00
return true ;
2014-04-22 11:11:53 +00:00
} else {
2014-05-13 12:49:09 +00:00
return false ;
2014-04-22 11:11:53 +00:00
}
}
2014-08-08 09:49:23 +00:00
2014-04-23 19:19:56 +00:00
private void receiveSuccess ( ) {
2017-05-03 09:03:04 +00:00
if ( initiating ( ) ) {
this . mJingleStatus = JINGLE_STATUS_FINISHED ;
this . mXmppConnectionService . markMessage ( this . message , Message . STATUS_SEND_RECEIVED ) ;
this . disconnectSocks5Connections ( ) ;
if ( this . transport ! = null & & this . transport instanceof JingleInbandTransport ) {
this . transport . disconnect ( ) ;
}
this . message . setTransferable ( null ) ;
this . mJingleConnectionManager . finishConnection ( this ) ;
} else {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " : received session-terminate/success while responding " ) ;
2014-11-15 13:40:43 +00:00
}
2014-04-13 09:32:45 +00:00
}
2014-08-08 09:49:23 +00:00
2014-07-03 20:55:20 +00:00
public void cancel ( ) {
2014-11-15 11:37:09 +00:00
this . disconnectSocks5Connections ( ) ;
if ( this . transport ! = null & & this . transport instanceof JingleInbandTransport ) {
this . transport . disconnect ( ) ;
}
this . sendCancel ( ) ;
this . mJingleConnectionManager . finishConnection ( this ) ;
2017-05-03 09:03:04 +00:00
if ( responding ( ) ) {
2015-07-10 13:11:03 +00:00
this . message . setTransferable ( new TransferablePlaceholder ( Transferable . STATUS_FAILED ) ) ;
2014-11-15 13:52:51 +00:00
if ( this . file ! = null ) {
file . delete ( ) ;
}
2017-01-12 15:02:09 +00:00
this . mJingleConnectionManager . updateConversationUi ( true ) ;
2014-11-15 11:37:09 +00:00
} else {
2017-11-16 11:49:15 +00:00
this . mXmppConnectionService . markMessage ( this . message , Message . STATUS_SEND_FAILED ) ;
2015-07-10 13:11:03 +00:00
this . message . setTransferable ( null ) ;
2014-11-15 11:37:09 +00:00
}
}
private void fail ( ) {
2016-10-26 10:26:04 +00:00
fail ( null ) ;
}
private void fail ( String errorMessage ) {
2014-11-15 11:37:09 +00:00
this . mJingleStatus = JINGLE_STATUS_FAILED ;
this . disconnectSocks5Connections ( ) ;
2014-11-15 13:40:43 +00:00
if ( this . transport ! = null & & this . transport instanceof JingleInbandTransport ) {
this . transport . disconnect ( ) ;
}
2015-07-31 23:19:16 +00:00
FileBackend . close ( mFileInputStream ) ;
FileBackend . close ( mFileOutputStream ) ;
2014-08-08 09:49:23 +00:00
if ( this . message ! = null ) {
2017-05-03 09:03:04 +00:00
if ( responding ( ) ) {
2015-07-10 13:11:03 +00:00
this . message . setTransferable ( new TransferablePlaceholder ( Transferable . STATUS_FAILED ) ) ;
2014-11-15 13:52:51 +00:00
if ( this . file ! = null ) {
file . delete ( ) ;
}
2017-01-12 15:02:09 +00:00
this . mJingleConnectionManager . updateConversationUi ( true ) ;
2014-06-29 11:44:59 +00:00
} else {
2014-10-29 10:31:03 +00:00
this . mXmppConnectionService . markMessage ( this . message ,
2016-10-26 10:26:04 +00:00
Message . STATUS_SEND_FAILED ,
errorMessage ) ;
2015-07-10 13:11:03 +00:00
this . message . setTransferable ( null ) ;
2014-06-29 11:44:59 +00:00
}
}
2014-05-13 12:49:09 +00:00
this . mJingleConnectionManager . finishConnection ( this ) ;
2014-04-13 09:32:45 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-23 19:19:56 +00:00
private void sendCancel ( ) {
JinglePacket packet = bootstrapPacket ( " session-terminate " ) ;
Reason reason = new Reason ( ) ;
reason . addChild ( " cancel " ) ;
packet . setReason ( reason ) ;
this . sendJinglePacket ( packet ) ;
}
2014-04-18 23:14:30 +00:00
2014-04-14 19:21:13 +00:00
private void connectNextCandidate ( ) {
2014-08-08 09:49:23 +00:00
for ( JingleCandidate candidate : this . candidates ) {
if ( ( ! connections . containsKey ( candidate . getCid ( ) ) & & ( ! candidate
. isOurs ( ) ) ) ) {
2014-04-14 19:21:13 +00:00
this . connectWithCandidate ( candidate ) ;
2014-04-17 12:52:10 +00:00
return ;
2014-04-14 19:21:13 +00:00
}
}
2014-04-17 12:52:10 +00:00
this . sendCandidateError ( ) ;
2014-04-14 19:21:13 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private void connectWithCandidate ( final JingleCandidate candidate ) {
2014-08-08 09:49:23 +00:00
final JingleSocks5Transport socksConnection = new JingleSocks5Transport (
this , candidate ) ;
2014-04-17 12:52:10 +00:00
connections . put ( candidate . getCid ( ) , socksConnection ) ;
2014-04-22 11:11:53 +00:00
socksConnection . connect ( new OnTransportConnected ( ) {
2014-08-08 09:49:23 +00:00
2014-04-14 19:21:13 +00:00
@Override
public void failed ( ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG ,
2014-08-08 09:49:23 +00:00
" connection failed with " + candidate . getHost ( ) + " : "
+ candidate . getPort ( ) ) ;
2014-04-14 19:21:13 +00:00
connectNextCandidate ( ) ;
}
2014-08-08 09:49:23 +00:00
2014-04-14 19:21:13 +00:00
@Override
public void established ( ) {
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG ,
2014-08-08 09:49:23 +00:00
" established connection with " + candidate . getHost ( )
+ " : " + candidate . getPort ( ) ) ;
2014-04-17 12:52:10 +00:00
sendCandidateUsed ( candidate . getCid ( ) ) ;
2014-04-14 19:21:13 +00:00
}
} ) ;
2014-04-11 19:13:09 +00:00
}
2014-04-14 19:21:13 +00:00
2014-11-15 11:37:09 +00:00
private void disconnectSocks5Connections ( ) {
2014-08-08 09:49:23 +00:00
Iterator < Entry < String , JingleSocks5Transport > > it = this . connections
. entrySet ( ) . iterator ( ) ;
while ( it . hasNext ( ) ) {
Entry < String , JingleSocks5Transport > pairs = it . next ( ) ;
pairs . getValue ( ) . disconnect ( ) ;
it . remove ( ) ;
}
}
2014-04-20 20:34:27 +00:00
private void sendProxyActivated ( String cid ) {
JinglePacket packet = bootstrapPacket ( " transport-info " ) ;
2014-08-08 09:49:23 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-04-20 20:34:27 +00:00
content . setTransportId ( this . transportId ) ;
2014-08-08 09:49:23 +00:00
content . socks5transport ( ) . addChild ( " activated " )
. setAttribute ( " cid " , cid ) ;
2014-04-20 20:34:27 +00:00
packet . setContent ( content ) ;
this . sendJinglePacket ( packet ) ;
}
2014-08-08 09:49:23 +00:00
2014-04-14 18:35:11 +00:00
private void sendCandidateUsed ( final String cid ) {
2014-04-16 10:50:53 +00:00
JinglePacket packet = bootstrapPacket ( " transport-info " ) ;
2014-08-08 09:49:23 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-04-20 20:34:27 +00:00
content . setTransportId ( this . transportId ) ;
2017-08-18 12:33:56 +00:00
content . socks5transport ( ) . addChild ( " candidate-used " ) . setAttribute ( " cid " , cid ) ;
2014-04-13 19:10:36 +00:00
packet . setContent ( content ) ;
2014-04-18 09:57:28 +00:00
this . sentCandidate = true ;
2014-10-15 17:32:12 +00:00
if ( ( receivedCandidate ) & & ( mJingleStatus = = JINGLE_STATUS_ACCEPTED ) ) {
2014-04-19 09:58:35 +00:00
connect ( ) ;
}
2014-04-21 18:39:57 +00:00
this . sendJinglePacket ( packet ) ;
2014-04-17 12:52:10 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private void sendCandidateError ( ) {
JinglePacket packet = bootstrapPacket ( " transport-info " ) ;
2014-08-08 09:49:23 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-04-20 20:34:27 +00:00
content . setTransportId ( this . transportId ) ;
2014-04-21 18:39:57 +00:00
content . socks5transport ( ) . addChild ( " candidate-error " ) ;
2014-04-17 12:52:10 +00:00
packet . setContent ( content ) ;
2014-04-18 09:57:28 +00:00
this . sentCandidate = true ;
2017-08-18 12:33:56 +00:00
if ( receivedCandidate & & mJingleStatus = = JINGLE_STATUS_ACCEPTED ) {
2014-04-19 09:58:35 +00:00
connect ( ) ;
}
2014-04-21 18:39:57 +00:00
this . sendJinglePacket ( packet ) ;
2014-04-11 19:13:09 +00:00
}
2014-04-07 18:05:45 +00:00
2014-10-15 17:32:12 +00:00
public int getJingleStatus ( ) {
return this . mJingleStatus ;
2014-04-13 09:32:45 +00:00
}
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private boolean equalCandidateExists ( JingleCandidate candidate ) {
2014-08-08 09:49:23 +00:00
for ( JingleCandidate c : this . candidates ) {
2014-04-17 12:52:10 +00:00
if ( c . equalValues ( candidate ) ) {
2014-04-14 18:35:11 +00:00
return true ;
}
}
return false ;
}
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private void mergeCandidate ( JingleCandidate candidate ) {
2014-08-08 09:49:23 +00:00
for ( JingleCandidate c : this . candidates ) {
2014-04-17 12:52:10 +00:00
if ( c . equals ( candidate ) ) {
2014-04-14 18:35:11 +00:00
return ;
2014-04-13 16:09:40 +00:00
}
}
this . candidates . add ( candidate ) ;
}
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private void mergeCandidates ( List < JingleCandidate > candidates ) {
2014-08-08 09:49:23 +00:00
for ( JingleCandidate c : candidates ) {
2014-04-14 18:35:11 +00:00
mergeCandidate ( c ) ;
2014-04-13 16:09:40 +00:00
}
}
2014-08-08 09:49:23 +00:00
2014-04-17 12:52:10 +00:00
private JingleCandidate getCandidate ( String cid ) {
2014-08-08 09:49:23 +00:00
for ( JingleCandidate c : this . candidates ) {
2014-04-17 12:52:10 +00:00
if ( c . getCid ( ) . equals ( cid ) ) {
2014-04-14 19:21:13 +00:00
return c ;
}
}
return null ;
}
2014-08-08 09:49:23 +00:00
2014-11-13 20:04:05 +00:00
public void updateProgress ( int i ) {
this . mProgress = i ;
2017-01-12 15:02:09 +00:00
mJingleConnectionManager . updateConversationUi ( false ) ;
2014-11-13 20:04:05 +00:00
}
2016-06-29 15:16:34 +00:00
public String getTransportId ( ) {
return this . transportId ;
}
public Content . Version getFtVersion ( ) {
return this . ftVersion ;
}
2014-04-20 20:34:27 +00:00
interface OnProxyActivated {
2017-05-03 09:03:04 +00:00
void success ( ) ;
2014-08-08 09:49:23 +00:00
2017-05-03 09:03:04 +00:00
void failed ( ) ;
2014-04-20 20:34:27 +00:00
}
2014-04-22 11:11:53 +00:00
public boolean hasTransportId ( String sid ) {
return sid . equals ( this . transportId ) ;
}
2014-08-08 09:49:23 +00:00
2014-04-22 11:11:53 +00:00
public JingleTransport getTransport ( ) {
return this . transport ;
}
2014-04-22 16:46:40 +00:00
2014-10-17 11:09:02 +00:00
public boolean start ( ) {
2014-11-15 16:09:02 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2014-10-17 11:09:02 +00:00
if ( mJingleStatus = = JINGLE_STATUS_INITIATED ) {
new Thread ( new Runnable ( ) {
2014-10-20 19:08:33 +00:00
2014-10-17 11:09:02 +00:00
@Override
public void run ( ) {
sendAccept ( ) ;
}
} ) . start ( ) ;
}
return true ;
2014-04-22 16:46:40 +00:00
} else {
2014-10-17 11:09:02 +00:00
return false ;
2014-10-15 17:32:12 +00:00
}
}
@Override
public int getStatus ( ) {
return this . mStatus ;
}
@Override
public long getFileSize ( ) {
if ( this . file ! = null ) {
return this . file . getExpectedSize ( ) ;
} else {
return 0 ;
2014-04-22 16:46:40 +00:00
}
}
2014-11-13 20:04:05 +00:00
@Override
public int getProgress ( ) {
return this . mProgress ;
}
2015-08-10 17:48:36 +00:00
public AbstractConnectionManager getConnectionManager ( ) {
return this . mJingleConnectionManager ;
}
2014-04-07 18:05:45 +00:00
}