2014-04-08 21:15:55 +00:00
package eu.siacs.conversations.xmpp.jingle ;
2014-04-07 18:05:45 +00:00
import java.util.ArrayList ;
2014-04-23 19:19:56 +00:00
import java.util.Arrays ;
2014-04-11 19:13:09 +00:00
import java.util.HashMap ;
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-04-13 09:32:45 +00:00
import java.util.Map.Entry ;
2014-04-07 18:05:45 +00:00
2014-04-25 21:06:20 +00:00
import android.graphics.BitmapFactory ;
2014-04-07 18:05:45 +00:00
import android.util.Log ;
import eu.siacs.conversations.entities.Account ;
2014-04-13 09:32:45 +00:00
import eu.siacs.conversations.entities.Conversation ;
2014-04-07 18:05:45 +00:00
import eu.siacs.conversations.entities.Message ;
import eu.siacs.conversations.services.XmppConnectionService ;
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 ;
2014-04-07 18:05:45 +00:00
public class JingleConnection {
2014-04-23 19:19:56 +00:00
private final String [ ] extensions = { " webp " , " jpeg " , " jpg " , " png " } ;
private final String [ ] cryptoExtensions = { " pgp " , " gpg " } ;
2014-04-07 18:05:45 +00:00
private JingleConnectionManager mJingleConnectionManager ;
private XmppConnectionService mXmppConnectionService ;
2014-04-10 12:12:08 +00:00
public static final int STATUS_INITIATED = 0 ;
public static final int STATUS_ACCEPTED = 1 ;
2014-04-11 19:13:09 +00:00
public static final int STATUS_TERMINATED = 2 ;
2014-04-13 09:32:45 +00:00
public static final int STATUS_CANCELED = 3 ;
public static final int STATUS_FINISHED = 4 ;
2014-04-13 19:10:36 +00:00
public static final int STATUS_TRANSMITTING = 5 ;
2014-04-10 12:12:08 +00:00
public static final int STATUS_FAILED = 99 ;
2014-04-22 11:11:53 +00:00
private int ibbBlockSize = 4096 ;
2014-04-10 12:12:08 +00:00
private int status = - 1 ;
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-04-08 21:15:55 +00:00
private String initiator ;
private String responder ;
2014-04-17 12:52:10 +00:00
private List < JingleCandidate > candidates = new ArrayList < JingleCandidate > ( ) ;
2014-04-22 11:11:53 +00:00
private HashMap < String , JingleSocks5Transport > connections = new HashMap < String , JingleSocks5Transport > ( ) ;
2014-04-17 12:52:10 +00:00
private String transportId ;
private Element fileOffer ;
2014-04-13 09:32:45 +00:00
private JingleFile file = null ;
2014-04-07 18:05:45 +00:00
2014-05-03 16:47:53 +00:00
private String contentName ;
private String contentCreator ;
2014-04-18 09:57:28 +00:00
private boolean receivedCandidate = false ;
private boolean sentCandidate = false ;
2014-04-17 12:52:10 +00:00
2014-04-22 16:46:40 +00:00
private boolean acceptedAutomatically = false ;
2014-04-20 20:34:27 +00:00
private JingleTransport transport = null ;
2014-04-08 21:15:55 +00:00
private OnIqPacketReceived responseListener = new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2014-04-10 12:12:08 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE_ERROR ) {
2014-04-11 19:13:09 +00:00
mXmppConnectionService . markMessage ( message , Message . STATUS_SEND_FAILED ) ;
2014-04-10 12:12:08 +00:00
status = STATUS_FAILED ;
}
2014-04-08 21:15:55 +00:00
}
} ;
2014-04-20 20:34:27 +00:00
final OnFileTransmitted onFileTransmitted = new OnFileTransmitted ( ) {
@Override
public void onFileTransmitted ( JingleFile file ) {
if ( responder . equals ( account . getFullJid ( ) ) ) {
sendSuccess ( ) ;
2014-04-22 16:46:40 +00:00
if ( acceptedAutomatically ) {
message . markUnread ( ) ;
}
2014-04-25 21:06:20 +00:00
BitmapFactory . Options options = new BitmapFactory . Options ( ) ;
options . inJustDecodeBounds = true ;
BitmapFactory . decodeFile ( file . getAbsolutePath ( ) , options ) ;
int imageHeight = options . outHeight ;
int imageWidth = options . outWidth ;
message . setBody ( " " + file . getSize ( ) + " , " + imageWidth + " , " + imageHeight ) ;
2014-04-23 19:19:56 +00:00
mXmppConnectionService . databaseBackend . createMessage ( message ) ;
2014-04-20 20:34:27 +00:00
mXmppConnectionService . markMessage ( message , Message . STATUS_RECIEVED ) ;
}
2014-04-25 21:06:20 +00:00
Log . d ( " xmppService " , " sucessfully transmitted file. sha1: " + file . getSha1Sum ( ) + " " + message . getBody ( ) ) ;
2014-04-20 20:34:27 +00:00
}
} ;
private OnProxyActivated onProxyActivated = new OnProxyActivated ( ) {
@Override
public void success ( ) {
if ( initiator . equals ( account . getFullJid ( ) ) ) {
Log . d ( " xmppService " , " we were initiating. sending file " ) ;
transport . send ( file , onFileTransmitted ) ;
} else {
transport . receive ( file , onFileTransmitted ) ;
Log . d ( " xmppService " , " we were responding. receiving file " ) ;
}
}
@Override
public void failed ( ) {
Log . d ( " xmppService " , " proxy activation failed " ) ;
}
} ;
2014-04-08 21:15:55 +00:00
public JingleConnection ( JingleConnectionManager mJingleConnectionManager ) {
2014-04-07 18:05:45 +00:00
this . mJingleConnectionManager = mJingleConnectionManager ;
this . mXmppConnectionService = mJingleConnectionManager . getXmppConnectionService ( ) ;
}
public String getSessionId ( ) {
return this . sessionId ;
}
2014-04-10 12:12:08 +00:00
public String getAccountJid ( ) {
2014-04-13 19:10:36 +00:00
return this . account . getFullJid ( ) ;
2014-04-10 12:12:08 +00:00
}
public String getCounterPart ( ) {
return this . message . getCounterpart ( ) ;
}
public void deliverPacket ( JinglePacket packet ) {
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-04-16 10:50:53 +00:00
if ( reason ! = null ) {
if ( reason . hasChild ( " cancel " ) ) {
2014-04-23 19:19:56 +00:00
this . receiveCancel ( ) ;
2014-04-16 10:50:53 +00:00
} else if ( reason . hasChild ( " success " ) ) {
2014-04-23 19:19:56 +00:00
this . receiveSuccess ( ) ;
2014-04-16 10:50:53 +00:00
}
} else {
Log . d ( " xmppService " , " remote terminated for no reason " ) ;
2014-04-23 19:19:56 +00:00
this . receiveCancel ( ) ;
2014-04-11 19:13:09 +00:00
}
2014-04-16 10:50:53 +00:00
} else if ( packet . isAction ( " session-accept " ) ) {
2014-04-22 16:46:40 +00:00
receiveAccept ( packet ) ;
2014-04-11 20:49:26 +00:00
} else if ( packet . isAction ( " transport-info " ) ) {
2014-04-22 11:11:53 +00:00
receiveTransportInfo ( packet ) ;
} else if ( packet . isAction ( " transport-replace " ) ) {
if ( packet . getJingleContent ( ) . hasIbbTransport ( ) ) {
this . receiveFallbackToIbb ( packet ) ;
} else {
Log . d ( " xmppService " , " trying to fallback to something unknown " + packet . toString ( ) ) ;
}
} else if ( packet . isAction ( " transport-accept " ) ) {
this . receiveTransportAccept ( packet ) ;
2014-04-11 19:13:09 +00:00
} else {
Log . d ( " xmppService " , " packet arrived in connection. action was " + packet . getAction ( ) ) ;
2014-04-10 12:12:08 +00:00
}
}
2014-04-07 18:05:45 +00:00
public void init ( Message message ) {
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 ;
this . account = message . getConversation ( ) . getAccount ( ) ;
this . initiator = this . account . getFullJid ( ) ;
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 ( ) ;
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-04-11 19:13:09 +00:00
this . mJingleConnectionManager . getPrimaryCandidate ( account , new OnPrimaryCandidateFound ( ) {
2014-04-08 21:15:55 +00:00
@Override
2014-04-18 09:57:28 +00:00
public void onPrimaryCandidateFound ( boolean success , final JingleCandidate candidate ) {
2014-04-08 21:15:55 +00:00
if ( success ) {
2014-04-22 11:11:53 +00:00
final JingleSocks5Transport socksConnection = new JingleSocks5Transport ( JingleConnection . this , candidate ) ;
2014-04-18 09:57:28 +00:00
connections . put ( candidate . getCid ( ) , socksConnection ) ;
2014-04-22 11:11:53 +00:00
socksConnection . connect ( new OnTransportConnected ( ) {
2014-04-18 09:57:28 +00:00
@Override
public void failed ( ) {
2014-04-19 00:19:26 +00:00
Log . d ( " xmppService " , " connection to our own primary candidete failed " ) ;
2014-04-18 09:57:28 +00:00
sendInitRequest ( ) ;
}
@Override
public void established ( ) {
2014-04-19 00:19:26 +00:00
Log . d ( " xmppService " , " succesfully connected to our own primary candidate " ) ;
2014-04-18 09:57:28 +00:00
mergeCandidate ( candidate ) ;
sendInitRequest ( ) ;
}
} ) ;
2014-04-13 16:09:40 +00:00
mergeCandidate ( candidate ) ;
2014-04-18 09:57:28 +00:00
} else {
2014-04-19 00:19:26 +00:00
Log . d ( " xmppService " , " no primary candidate of our own was found " ) ;
2014-04-18 09:57:28 +00:00
sendInitRequest ( ) ;
2014-04-08 21:15:55 +00:00
}
}
} ) ;
}
}
2014-04-13 09:32:45 +00:00
public void init ( Account account , JinglePacket packet ) {
2014-04-13 19:10:36 +00:00
this . status = STATUS_INITIATED ;
2014-04-13 09:32:45 +00:00
Conversation conversation = this . mXmppConnectionService . findOrCreateConversation ( account , packet . getFrom ( ) . split ( " / " ) [ 0 ] , false ) ;
2014-04-25 21:06:20 +00:00
this . message = new Message ( conversation , " " , Message . ENCRYPTION_NONE ) ;
2014-04-13 09:32:45 +00:00
this . message . setType ( Message . TYPE_IMAGE ) ;
2014-04-22 16:46:40 +00:00
this . message . setStatus ( Message . STATUS_RECEIVED_OFFER ) ;
this . message . setJingleConnection ( this ) ;
2014-04-13 16:09:40 +00:00
String [ ] fromParts = packet . getFrom ( ) . split ( " / " ) ;
this . message . setPresence ( fromParts [ 1 ] ) ;
2014-04-13 09:32:45 +00:00
this . account = account ;
this . initiator = packet . getFrom ( ) ;
this . responder = this . account . getFullJid ( ) ;
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 ( ) ;
2014-04-20 20:34:27 +00:00
this . mergeCandidates ( JingleCandidate . parse ( content . socks5transport ( ) . getChildren ( ) ) ) ;
2014-04-17 12:52:10 +00:00
this . fileOffer = packet . getJingleContent ( ) . getFileOffer ( ) ;
2014-04-13 16:09:40 +00:00
if ( fileOffer ! = null ) {
this . file = this . mXmppConnectionService . getFileBackend ( ) . getJingleFile ( message ) ;
Element fileSize = fileOffer . findChild ( " size " ) ;
2014-04-23 19:19:56 +00:00
Element fileNameElement = fileOffer . findChild ( " name " ) ;
if ( fileNameElement ! = null ) {
boolean supportedFile = false ;
String [ ] filename = fileNameElement . getContent ( ) . toLowerCase ( ) . split ( " \\ . " ) ;
if ( Arrays . asList ( this . extensions ) . contains ( filename [ filename . length - 1 ] ) ) {
supportedFile = true ;
} else if ( Arrays . asList ( this . cryptoExtensions ) . contains ( filename [ filename . length - 1 ] ) ) {
if ( filename . length = = 3 ) {
if ( Arrays . asList ( this . extensions ) . contains ( filename [ filename . length - 2 ] ) ) {
supportedFile = true ;
this . message . setEncryption ( Message . ENCRYPTION_PGP ) ;
}
}
}
if ( supportedFile ) {
this . file . setExpectedSize ( Long . parseLong ( fileSize . getContent ( ) ) ) ;
2014-04-25 21:06:20 +00:00
message . setBody ( " " + this . file . getExpectedSize ( ) ) ;
2014-04-23 19:19:56 +00:00
conversation . getMessages ( ) . add ( message ) ;
if ( this . file . getExpectedSize ( ) < = this . mJingleConnectionManager . getAutoAcceptFileSize ( ) ) {
Log . d ( " xmppService " , " auto accepting file from " + packet . getFrom ( ) ) ;
this . acceptedAutomatically = true ;
this . sendAccept ( ) ;
} else {
message . markUnread ( ) ;
Log . d ( " xmppService " , " not auto accepting new file offer with size: " + this . file . getExpectedSize ( ) + " allowed size: " + this . mJingleConnectionManager . getAutoAcceptFileSize ( ) ) ;
if ( this . mXmppConnectionService . convChangedListener ! = null ) {
this . mXmppConnectionService . convChangedListener . onConversationListChanged ( ) ;
}
}
} else {
this . sendCancel ( ) ;
2014-04-22 16:46:40 +00:00
}
2014-04-23 19:19:56 +00:00
} else {
this . sendCancel ( ) ;
2014-04-13 16:09:40 +00:00
}
} else {
2014-04-23 19:19:56 +00:00
this . sendCancel ( ) ;
2014-04-13 16:09:40 +00:00
}
2014-04-13 09:32:45 +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-05-03 16:47:53 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-04-07 18:05:45 +00:00
if ( message . getType ( ) = = Message . TYPE_IMAGE ) {
2014-04-20 20:34:27 +00:00
content . setTransportId ( this . transportId ) ;
2014-04-13 16:09:40 +00:00
this . file = this . mXmppConnectionService . getFileBackend ( ) . getJingleFile ( message ) ;
content . setFileOffer ( this . file ) ;
2014-04-17 12:52:10 +00:00
this . transportId = this . mJingleConnectionManager . nextRandomId ( ) ;
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 ) ;
2014-04-18 23:14:30 +00:00
this . sendJinglePacket ( packet ) ;
2014-04-10 12:12:08 +00:00
this . status = STATUS_INITIATED ;
2014-04-07 18:05:45 +00:00
}
}
2014-04-17 12:52:10 +00:00
private List < Element > getCandidatesAsElements ( ) {
List < Element > elements = new ArrayList < Element > ( ) ;
for ( JingleCandidate c : this . candidates ) {
elements . add ( c . toElement ( ) ) ;
}
return elements ;
}
2014-04-13 16:09:40 +00:00
private void sendAccept ( ) {
2014-04-18 09:57:28 +00:00
status = STATUS_ACCEPTED ;
2014-04-22 16:46:40 +00:00
mXmppConnectionService . markMessage ( message , Message . STATUS_RECIEVING ) ;
2014-04-13 16:09:40 +00:00
this . mJingleConnectionManager . getPrimaryCandidate ( this . account , new OnPrimaryCandidateFound ( ) {
@Override
2014-04-18 09:57:28 +00:00
public void onPrimaryCandidateFound ( boolean success , final JingleCandidate candidate ) {
final JinglePacket packet = bootstrapPacket ( " session-accept " ) ;
2014-05-03 16:47:53 +00:00
final Content content = new Content ( contentCreator , contentName ) ;
2014-04-17 12:52:10 +00:00
content . setFileOffer ( fileOffer ) ;
2014-04-20 20:34:27 +00:00
content . setTransportId ( transportId ) ;
2014-04-18 09:57:28 +00:00
if ( ( success ) & & ( ! equalCandidateExists ( candidate ) ) ) {
2014-04-22 11:11:53 +00:00
final JingleSocks5Transport socksConnection = new JingleSocks5Transport ( JingleConnection . this , candidate ) ;
2014-04-18 09:57:28 +00:00
connections . put ( candidate . getCid ( ) , socksConnection ) ;
2014-04-22 11:11:53 +00:00
socksConnection . connect ( new OnTransportConnected ( ) {
2014-04-18 09:57:28 +00:00
@Override
public void failed ( ) {
2014-04-19 00:19:26 +00:00
Log . d ( " xmppService " , " connection to our own primary candidate failed " ) ;
2014-04-20 20:34:27 +00:00
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-13 16:09:40 +00:00
}
2014-04-18 09:57:28 +00:00
@Override
public void established ( ) {
2014-04-19 00:19:26 +00:00
Log . d ( " xmppService " , " connected to primary candidate " ) ;
2014-04-18 09:57:28 +00:00
mergeCandidate ( candidate ) ;
2014-04-20 20:34:27 +00:00
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
}
} ) ;
} else {
2014-04-19 00:19:26 +00:00
Log . d ( " xmppService " , " did not find a primary candidate for ourself " ) ;
2014-04-20 20:34:27 +00:00
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-04-13 16:09:40 +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-04-07 18:05:45 +00:00
packet . setFrom ( account . getFullJid ( ) ) ;
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-04-11 19:13:09 +00:00
2014-04-18 23:14:30 +00:00
private void sendJinglePacket ( JinglePacket packet ) {
2014-04-20 20:34:27 +00:00
//Log.d("xmppService",packet.toString());
2014-04-18 23:14:30 +00:00
account . getXmppConnection ( ) . sendIqPacket ( packet , responseListener ) ;
}
2014-04-22 16:46:40 +00:00
private void receiveAccept ( JinglePacket packet ) {
2014-04-11 19:13:09 +00:00
Content content = packet . getJingleContent ( ) ;
2014-04-20 20:34:27 +00:00
mergeCandidates ( JingleCandidate . parse ( content . socks5transport ( ) . getChildren ( ) ) ) ;
2014-04-11 19:13:09 +00:00
this . status = 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-04-11 19:13:09 +00:00
IqPacket response = packet . generateRespone ( IqPacket . TYPE_RESULT ) ;
account . getXmppConnection ( ) . sendIqPacket ( response , null ) ;
}
2014-04-13 16:09:40 +00:00
2014-04-22 11:11:53 +00:00
private void 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 " ) ) {
2014-04-23 19:19:56 +00:00
if ( ( this . transport ! = null ) & & ( this . transport instanceof JingleSocks5Transport ) ) {
onProxyActivated . success ( ) ;
} else {
String cid = content . socks5transport ( ) . findChild ( " activated " ) . getAttribute ( " cid " ) ;
Log . d ( " xmppService " , " received proxy activated ( " + cid + " )prior to choosing our own transport " ) ;
JingleSocks5Transport connection = this . connections . get ( cid ) ;
if ( connection ! = null ) {
connection . setActivated ( true ) ;
} else {
Log . d ( " xmppService " , " activated connection not found " ) ;
this . sendCancel ( ) ;
}
}
2014-04-20 20:34:27 +00:00
} else if ( content . socks5transport ( ) . hasChild ( " activated " ) ) {
onProxyActivated . failed ( ) ;
} else if ( content . socks5transport ( ) . hasChild ( " candidate-error " ) ) {
Log . d ( " xmppService " , " received candidate error " ) ;
this . receivedCandidate = true ;
2014-04-26 15:26:22 +00:00
if ( ( status = = STATUS_ACCEPTED ) & & ( this . sentCandidate ) ) {
2014-04-20 20:34:27 +00:00
this . connect ( ) ;
}
} else if ( content . socks5transport ( ) . hasChild ( " candidate-used " ) ) {
String cid = content . socks5transport ( ) . findChild ( " candidate-used " ) . getAttribute ( " cid " ) ;
if ( cid ! = null ) {
Log . d ( " xmppService " , " candidate used by counterpart: " + cid ) ;
JingleCandidate candidate = getCandidate ( cid ) ;
candidate . flagAsUsedByCounterpart ( ) ;
this . receivedCandidate = true ;
if ( ( status = = STATUS_ACCEPTED ) & & ( this . sentCandidate ) ) {
this . connect ( ) ;
} else {
Log . d ( " xmppService " , " ignoring because file is already in transmission or we havent sent our candidate yet " ) ;
}
} else {
Log . d ( " xmppService " , " couldn't read used candidate " ) ;
}
2014-04-13 19:10:36 +00:00
} else {
2014-04-20 20:34:27 +00:00
Log . d ( " xmppService " , " empty transport " ) ;
2014-04-13 16:09:40 +00:00
}
}
2014-04-20 20:34:27 +00:00
IqPacket response = packet . generateRespone ( IqPacket . TYPE_RESULT ) ;
2014-04-13 16:09:40 +00:00
account . getXmppConnection ( ) . sendIqPacket ( response , null ) ;
}
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-04-18 23:14:30 +00:00
if ( connection = = null ) {
Log . d ( " xmppService " , " could not find suitable candidate " ) ;
this . disconnect ( ) ;
2014-04-22 11:11:53 +00:00
if ( this . initiator . equals ( account . getFullJid ( ) ) ) {
this . sendFallbackToIbb ( ) ;
}
2014-04-18 23:14:30 +00:00
} else {
this . status = 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 ( ) ) {
2014-04-25 21:06:20 +00:00
Log . d ( " xmppService " , " candidate " + connection . getCandidate ( ) . getCid ( ) + " was our proxy. going to activate " ) ;
2014-04-20 20:34:27 +00:00
IqPacket activation = new IqPacket ( IqPacket . TYPE_SET ) ;
activation . setTo ( connection . getCandidate ( ) . getJid ( ) ) ;
activation . query ( " http://jabber.org/protocol/bytestreams " ) . setAttribute ( " sid " , this . getSessionId ( ) ) ;
activation . query ( ) . addChild ( " activate " ) . setContent ( this . getCounterPart ( ) ) ;
this . account . getXmppConnection ( ) . sendIqPacket ( activation , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE_ERROR ) {
onProxyActivated . failed ( ) ;
} else {
onProxyActivated . success ( ) ;
sendProxyActivated ( connection . getCandidate ( ) . getCid ( ) ) ;
}
2014-04-18 23:14:30 +00:00
}
2014-04-20 20:34:27 +00:00
} ) ;
2014-04-25 21:06:20 +00:00
} else {
Log . d ( " xmppService " , " 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 {
2014-04-18 23:14:30 +00:00
if ( initiator . equals ( account . getFullJid ( ) ) ) {
Log . d ( " xmppService " , " we were initiating. sending file " ) ;
2014-04-20 20:34:27 +00:00
connection . send ( file , onFileTransmitted ) ;
2014-04-18 23:14:30 +00:00
} else {
Log . d ( " xmppService " , " we were responding. receiving file " ) ;
2014-04-20 20:34:27 +00:00
connection . receive ( file , onFileTransmitted ) ;
2014-04-18 23:14:30 +00:00
}
2014-04-11 20:49:26 +00:00
}
}
}
2014-04-22 11:11:53 +00:00
private JingleSocks5Transport chooseConnection ( ) {
JingleSocks5Transport connection = null ;
Iterator < Entry < String , JingleSocks5Transport > > it = this . connections . entrySet ( ) . iterator ( ) ;
2014-04-17 12:52:10 +00:00
while ( it . hasNext ( ) ) {
2014-04-22 11:11:53 +00:00
Entry < String , JingleSocks5Transport > pairs = it . next ( ) ;
JingleSocks5Transport currentConnection = pairs . getValue ( ) ;
2014-04-18 23:14:30 +00:00
//Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString());
2014-04-17 12:52:10 +00:00
if ( currentConnection . isEstablished ( ) & & ( currentConnection . getCandidate ( ) . isUsedByCounterpart ( ) | | ( ! currentConnection . getCandidate ( ) . isOurs ( ) ) ) ) {
2014-04-18 23:14:30 +00:00
//Log.d("xmppService","is usable");
2014-04-17 12:52:10 +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-04-18 23:14:30 +00:00
//Log.d("xmppService","found two candidates with same priority");
2014-04-17 12:52:10 +00:00
if ( initiator . equals ( account . getFullJid ( ) ) ) {
if ( currentConnection . getCandidate ( ) . isOurs ( ) ) {
connection = currentConnection ;
}
} else {
if ( ! currentConnection . getCandidate ( ) . isOurs ( ) ) {
connection = currentConnection ;
}
}
}
}
}
it . remove ( ) ;
2014-04-18 09:57:28 +00:00
}
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-04-16 10:50:53 +00:00
this . disconnect ( ) ;
this . status = STATUS_FINISHED ;
this . mXmppConnectionService . markMessage ( this . message , Message . STATUS_RECIEVED ) ;
}
2014-04-22 11:11:53 +00:00
private void sendFallbackToIbb ( ) {
JinglePacket packet = this . bootstrapPacket ( " transport-replace " ) ;
2014-05-03 16:47:53 +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 ) ;
content . ibbTransport ( ) . setAttribute ( " block-size " , " " + this . ibbBlockSize ) ;
packet . setContent ( content ) ;
this . sendJinglePacket ( packet ) ;
}
private void receiveFallbackToIbb ( JinglePacket packet ) {
String receivedBlockSize = packet . getJingleContent ( ) . ibbTransport ( ) . getAttribute ( " block-size " ) ;
if ( receivedBlockSize ! = null ) {
int bs = Integer . parseInt ( receivedBlockSize ) ;
if ( bs > this . ibbBlockSize ) {
this . ibbBlockSize = bs ;
}
}
this . transportId = packet . getJingleContent ( ) . getTransportId ( ) ;
this . transport = new JingleInbandTransport ( this . account , this . responder , this . transportId , this . ibbBlockSize ) ;
this . transport . receive ( file , onFileTransmitted ) ;
JinglePacket answer = bootstrapPacket ( " transport-accept " ) ;
Content content = new Content ( " initiator " , " a-file-offer " ) ;
content . setTransportId ( this . transportId ) ;
content . ibbTransport ( ) . setAttribute ( " block-size " , " " + this . ibbBlockSize ) ;
answer . setContent ( content ) ;
this . sendJinglePacket ( answer ) ;
}
private void receiveTransportAccept ( JinglePacket packet ) {
if ( packet . getJingleContent ( ) . hasIbbTransport ( ) ) {
String receivedBlockSize = packet . getJingleContent ( ) . ibbTransport ( ) . getAttribute ( " block-size " ) ;
if ( receivedBlockSize ! = null ) {
int bs = Integer . parseInt ( receivedBlockSize ) ;
if ( bs > this . ibbBlockSize ) {
this . ibbBlockSize = bs ;
}
}
this . transport = new JingleInbandTransport ( this . account , this . responder , this . transportId , this . ibbBlockSize ) ;
this . transport . connect ( new OnTransportConnected ( ) {
@Override
public void failed ( ) {
Log . d ( " xmppService " , " ibb open failed " ) ;
}
@Override
public void established ( ) {
JingleConnection . this . transport . send ( file , onFileTransmitted ) ;
}
} ) ;
} else {
Log . d ( " xmppService " , " invalid transport accept " ) ;
}
}
2014-04-23 19:19:56 +00:00
private void receiveSuccess ( ) {
2014-04-13 09:32:45 +00:00
this . status = STATUS_FINISHED ;
this . mXmppConnectionService . markMessage ( this . message , Message . STATUS_SEND ) ;
this . disconnect ( ) ;
}
2014-04-23 19:19:56 +00:00
private void receiveCancel ( ) {
2014-04-13 09:32:45 +00:00
this . disconnect ( ) ;
this . status = STATUS_CANCELED ;
this . mXmppConnectionService . markMessage ( this . message , Message . STATUS_SEND_REJECTED ) ;
}
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-04-17 12:52:10 +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-04-17 12:52:10 +00:00
private void connectWithCandidate ( final JingleCandidate candidate ) {
2014-04-22 11:11:53 +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-04-14 19:21:13 +00:00
@Override
public void failed ( ) {
2014-04-18 23:14:30 +00:00
Log . d ( " xmppService " , " connection failed with " + candidate . getHost ( ) + " : " + candidate . getPort ( ) ) ;
2014-04-14 19:21:13 +00:00
connectNextCandidate ( ) ;
}
@Override
public void established ( ) {
2014-04-18 23:14:30 +00:00
Log . d ( " xmppService " , " 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-04-13 09:32:45 +00:00
private void disconnect ( ) {
2014-04-22 11:11:53 +00:00
Iterator < Entry < String , JingleSocks5Transport > > it = this . connections . entrySet ( ) . iterator ( ) ;
2014-04-13 09:32:45 +00:00
while ( it . hasNext ( ) ) {
2014-04-22 11:11:53 +00:00
Entry < String , JingleSocks5Transport > pairs = it . next ( ) ;
2014-04-13 09:32:45 +00:00
pairs . getValue ( ) . disconnect ( ) ;
it . remove ( ) ;
}
}
2014-04-20 20:34:27 +00:00
private void sendProxyActivated ( String cid ) {
JinglePacket packet = bootstrapPacket ( " transport-info " ) ;
2014-05-03 16:47:53 +00:00
Content content = new Content ( this . contentCreator , this . contentName ) ;
2014-04-20 20:34:27 +00:00
content . setTransportId ( this . transportId ) ;
content . socks5transport ( ) . addChild ( " activated " ) . setAttribute ( " cid " , cid ) ;
packet . setContent ( content ) ;
this . sendJinglePacket ( packet ) ;
}
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-05-03 16:47:53 +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-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-04-19 09:58:35 +00:00
if ( ( receivedCandidate ) & & ( status = = STATUS_ACCEPTED ) ) {
connect ( ) ;
}
2014-04-21 18:39:57 +00:00
this . sendJinglePacket ( packet ) ;
2014-04-17 12:52:10 +00:00
}
private void sendCandidateError ( ) {
JinglePacket packet = bootstrapPacket ( " transport-info " ) ;
2014-05-03 16:47:53 +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 ;
2014-04-19 09:58:35 +00:00
if ( ( receivedCandidate ) & & ( status = = STATUS_ACCEPTED ) ) {
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-04-11 19:13:09 +00:00
public String getInitiator ( ) {
return this . initiator ;
}
public String getResponder ( ) {
return this . responder ;
}
2014-04-13 09:32:45 +00:00
public int getStatus ( ) {
return this . status ;
}
2014-04-13 16:09:40 +00:00
2014-04-17 12:52:10 +00:00
private boolean equalCandidateExists ( JingleCandidate candidate ) {
for ( JingleCandidate c : this . candidates ) {
if ( c . equalValues ( candidate ) ) {
2014-04-14 18:35:11 +00:00
return true ;
}
}
return false ;
}
2014-04-17 12:52:10 +00:00
private void mergeCandidate ( JingleCandidate candidate ) {
for ( JingleCandidate c : this . candidates ) {
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-04-17 12:52:10 +00:00
private void mergeCandidates ( List < JingleCandidate > candidates ) {
for ( JingleCandidate c : candidates ) {
2014-04-14 18:35:11 +00:00
mergeCandidate ( c ) ;
2014-04-13 16:09:40 +00:00
}
}
2014-04-14 19:21:13 +00:00
2014-04-17 12:52:10 +00:00
private JingleCandidate getCandidate ( String cid ) {
for ( JingleCandidate c : this . candidates ) {
if ( c . getCid ( ) . equals ( cid ) ) {
2014-04-14 19:21:13 +00:00
return c ;
}
}
return null ;
}
2014-04-20 20:34:27 +00:00
interface OnProxyActivated {
public void success ( ) ;
public void failed ( ) ;
}
2014-04-22 11:11:53 +00:00
public boolean hasTransportId ( String sid ) {
return sid . equals ( this . transportId ) ;
}
public JingleTransport getTransport ( ) {
return this . transport ;
}
2014-04-22 16:46:40 +00:00
public void accept ( ) {
if ( status = = STATUS_INITIATED ) {
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
sendAccept ( ) ;
}
} ) . start ( ) ;
} else {
Log . d ( " xmppService " , " status ( " + status + " ) was not ok " ) ;
}
}
2014-04-07 18:05:45 +00:00
}