some code cleanup. added setting to auto accept files. socks5 connections are now threaded
This commit is contained in:
parent
27d5966ac3
commit
7dfe4ae082
|
@ -7,4 +7,16 @@
|
|||
<item>Conversations</item>
|
||||
<item>Android</item>
|
||||
</array>
|
||||
<string-array name="filesizes">
|
||||
<item>never</item>
|
||||
<item>256 KB</item>
|
||||
<item>512 KB</item>
|
||||
<item>1 MB</item>
|
||||
</string-array>
|
||||
<string-array name="filesizes_values">
|
||||
<item>0</item>
|
||||
<item>262144</item>
|
||||
<item>524288</item>
|
||||
<item>1048576</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
|
|
@ -15,6 +15,13 @@
|
|||
android:entries="@array/resources"
|
||||
android:entryValues="@array/resources"
|
||||
android:defaultValue="Mobile"/>
|
||||
<ListPreference
|
||||
android:key="auto_accept_file_size"
|
||||
android:title="Accept files"
|
||||
android:summary="Automatically accept files smaller than"
|
||||
android:entries="@array/filesizes"
|
||||
android:entryValues="@array/filesizes_values"
|
||||
android:defaultValue="524288"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="Notification Settings">
|
||||
|
|
|
@ -38,7 +38,7 @@ public class FileBackend {
|
|||
|
||||
}
|
||||
|
||||
public JingleFile getImageFile(Message message) {
|
||||
public JingleFile getJingleFile(Message message) {
|
||||
Conversation conversation = message.getConversation();
|
||||
String prefix = context.getFilesDir().getAbsolutePath();
|
||||
String path = prefix + "/" + conversation.getAccount().getJid() + "/"
|
||||
|
@ -72,7 +72,7 @@ public class FileBackend {
|
|||
try {
|
||||
InputStream is = context.getContentResolver()
|
||||
.openInputStream(image);
|
||||
JingleFile file = getImageFile(message);
|
||||
JingleFile file = getJingleFile(message);
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
OutputStream os = new FileOutputStream(file);
|
||||
|
@ -98,14 +98,14 @@ public class FileBackend {
|
|||
|
||||
public Bitmap getImageFromMessage(Message message) {
|
||||
return BitmapFactory
|
||||
.decodeFile(getImageFile(message).getAbsolutePath());
|
||||
.decodeFile(getJingleFile(message).getAbsolutePath());
|
||||
}
|
||||
|
||||
public Bitmap getThumbnailFromMessage(Message message, int size) {
|
||||
Bitmap thumbnail = thumbnailCache.get(message.getUuid());
|
||||
if (thumbnail==null) {
|
||||
Log.d("xmppService","creating new thumbnail" + message.getUuid());
|
||||
Bitmap fullsize = BitmapFactory.decodeFile(getImageFile(message)
|
||||
Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message)
|
||||
.getAbsolutePath());
|
||||
thumbnail = resize(fullsize, size);
|
||||
this.thumbnailCache.put(message.getUuid(), thumbnail);
|
||||
|
|
|
@ -124,9 +124,7 @@ public class XmppConnectionService extends Service {
|
|||
MessagePacket packet) {
|
||||
Message message = null;
|
||||
boolean notify = true;
|
||||
if(PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext())
|
||||
.getBoolean("notification_grace_period_after_carbon_received", true)){
|
||||
if(getPreferences().getBoolean("notification_grace_period_after_carbon_received", true)){
|
||||
notify=(SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > CARBON_GRACE_PERIOD;
|
||||
}
|
||||
|
||||
|
@ -625,8 +623,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public XmppConnection createConnection(Account account) {
|
||||
SharedPreferences sharedPref = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences sharedPref = getPreferences();
|
||||
account.setResource(sharedPref.getString("resource", "mobile").toLowerCase(Locale.getDefault()));
|
||||
XmppConnection connection = new XmppConnection(account, this.pm);
|
||||
connection.setOnMessagePacketReceivedListener(this.messageListener);
|
||||
|
@ -1204,8 +1201,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void createContact(Contact contact) {
|
||||
SharedPreferences sharedPref = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences sharedPref = getPreferences();
|
||||
boolean autoGrant = sharedPref.getBoolean("grant_new_contacts", true);
|
||||
if (autoGrant) {
|
||||
contact.setSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
|
||||
|
@ -1396,4 +1392,8 @@ public class XmppConnectionService extends Service {
|
|||
convChangedListener.onConversationListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public SharedPreferences getPreferences() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,7 +419,7 @@ public class UIHelper {
|
|||
mBuilder.setContentText(names.toString());
|
||||
mBuilder.setStyle(style);
|
||||
}
|
||||
if (currentCon!=null) {
|
||||
if ((currentCon!=null)&&(notify)) {
|
||||
targetUuid=currentCon.getUuid();
|
||||
}
|
||||
if (unread.size() != 0) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import android.util.Log;
|
||||
|
@ -39,7 +38,9 @@ public class JingleConnection {
|
|||
private String initiator;
|
||||
private String responder;
|
||||
private List<Element> candidates = new ArrayList<Element>();
|
||||
private List<String> candidatesUsedByCounterpart = new ArrayList<String>();
|
||||
private HashMap<String, SocksConnection> connections = new HashMap<String, SocksConnection>();
|
||||
private Content content = new Content();
|
||||
private JingleFile file = null;
|
||||
|
||||
private OnIqPacketReceived responseListener = new OnIqPacketReceived() {
|
||||
|
@ -100,9 +101,9 @@ public class JingleConnection {
|
|||
this.mJingleConnectionManager.getPrimaryCandidate(account, new OnPrimaryCandidateFound() {
|
||||
|
||||
@Override
|
||||
public void onPrimaryCandidateFound(boolean success, Element canditate) {
|
||||
public void onPrimaryCandidateFound(boolean success, Element candidate) {
|
||||
if (success) {
|
||||
candidates.add(canditate);
|
||||
mergeCandidate(candidate);
|
||||
}
|
||||
sendInitRequest();
|
||||
}
|
||||
|
@ -116,24 +117,40 @@ public class JingleConnection {
|
|||
this.message = new Message(conversation, "receiving image file", Message.ENCRYPTION_NONE);
|
||||
this.message.setType(Message.TYPE_IMAGE);
|
||||
this.message.setStatus(Message.STATUS_RECIEVING);
|
||||
String[] fromParts = packet.getFrom().split("/");
|
||||
this.message.setPresence(fromParts[1]);
|
||||
this.account = account;
|
||||
this.initiator = packet.getFrom();
|
||||
this.responder = this.account.getFullJid();
|
||||
this.sessionId = packet.getSessionId();
|
||||
this.candidates.addAll(packet.getJingleContent().getCanditates());
|
||||
Log.d("xmppService","new incomming jingle session "+this.sessionId+" num canditaes:"+this.candidates.size());
|
||||
this.content = packet.getJingleContent();
|
||||
this.mergeCandidates(this.content.getCanditates());
|
||||
Element fileOffer = packet.getJingleContent().getFileOffer();
|
||||
if (fileOffer!=null) {
|
||||
this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message);
|
||||
Element fileSize = fileOffer.findChild("size");
|
||||
Element fileName = fileOffer.findChild("name");
|
||||
this.file.setExpectedSize(Long.parseLong(fileSize.getContent()));
|
||||
if (this.file.getExpectedSize()>=this.mJingleConnectionManager.getAutoAcceptFileSize()) {
|
||||
Log.d("xmppService","auto accepting file from "+packet.getFrom());
|
||||
this.sendAccept();
|
||||
} else {
|
||||
Log.d("xmppService","not auto accepting new file offer with size: "+this.file.getExpectedSize()+" allowed size:"+this.mJingleConnectionManager.getAutoAcceptFileSize());
|
||||
}
|
||||
} else {
|
||||
Log.d("xmppService","no file offer was attached. aborting");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendInitRequest() {
|
||||
JinglePacket packet = this.bootstrapPacket();
|
||||
packet.setAction("session-initiate");
|
||||
packet.setInitiator(this.account.getFullJid());
|
||||
Content content = new Content();
|
||||
this.content = new Content();
|
||||
if (message.getType() == Message.TYPE_IMAGE) {
|
||||
content.setAttribute("creator", "initiator");
|
||||
content.setAttribute("name", "a-file-offer");
|
||||
this.file = this.mXmppConnectionService.getFileBackend().getImageFile(message);
|
||||
content.offerFile(file,message.getBody());
|
||||
this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message);
|
||||
content.setFileOffer(this.file);
|
||||
content.setCandidates(this.mJingleConnectionManager.nextRandomId(),this.candidates);
|
||||
packet.setContent(content);
|
||||
Log.d("xmppService",packet.toString());
|
||||
|
@ -142,58 +159,103 @@ public class JingleConnection {
|
|||
}
|
||||
}
|
||||
|
||||
private void sendAccept() {
|
||||
this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() {
|
||||
|
||||
@Override
|
||||
public void onPrimaryCandidateFound(boolean success, Element candidate) {
|
||||
if (success) {
|
||||
if (mergeCandidate(candidate)) {
|
||||
content.addCandidate(candidate);
|
||||
}
|
||||
}
|
||||
JinglePacket packet = bootstrapPacket();
|
||||
packet.setAction("session-accept");
|
||||
packet.setContent(content);
|
||||
Log.d("xmppService","sending session accept: "+packet.toString());
|
||||
account.getXmppConnection().sendIqPacket(packet, new OnIqPacketReceived() {
|
||||
|
||||
@Override
|
||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||
if (packet.getType() != IqPacket.TYPE_ERROR) {
|
||||
Log.d("xmppService","opsing side has acked our session-accept");
|
||||
connectWithCandidates();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private JinglePacket bootstrapPacket() {
|
||||
JinglePacket packet = new JinglePacket();
|
||||
packet.setFrom(account.getFullJid());
|
||||
packet.setTo(this.message.getCounterpart()); //fixme, not right in all cases;
|
||||
packet.setSessionId(this.sessionId);
|
||||
packet.setInitiator(this.initiator);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void accept(JinglePacket packet) {
|
||||
Log.d("xmppService","session-accept: "+packet.toString());
|
||||
Content content = packet.getJingleContent();
|
||||
this.candidates.addAll(content.getCanditates());
|
||||
this.mergeCandidates(content.getCanditates());
|
||||
this.status = STATUS_ACCEPTED;
|
||||
this.connectWithCandidates();
|
||||
IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
|
||||
Log.d("xmppService","response "+response.toString());
|
||||
account.getXmppConnection().sendIqPacket(response, null);
|
||||
}
|
||||
|
||||
|
||||
private void transportInfo(JinglePacket packet) {
|
||||
Content content = packet.getJingleContent();
|
||||
Log.d("xmppService","transport info : "+content.toString());
|
||||
String cid = content.getUsedCandidate();
|
||||
if (cid!=null) {
|
||||
final JingleFile file = this.mXmppConnectionService.getFileBackend().getImageFile(this.message);
|
||||
final SocksConnection connection = this.connections.get(cid);
|
||||
final OnFileTransmitted callback = new OnFileTransmitted() {
|
||||
Log.d("xmppService","candidate used by counterpart:"+cid);
|
||||
this.candidatesUsedByCounterpart.add(cid);
|
||||
if (this.connections.containsKey(cid)) {
|
||||
this.connect(this.connections.get(cid));
|
||||
}
|
||||
}
|
||||
IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
|
||||
account.getXmppConnection().sendIqPacket(response, null);
|
||||
}
|
||||
|
||||
private void connect(final SocksConnection connection) {
|
||||
final OnFileTransmitted callback = new OnFileTransmitted() {
|
||||
|
||||
@Override
|
||||
public void onFileTransmitted(JingleFile file) {
|
||||
Log.d("xmppService","sucessfully transmitted file. sha1:"+file.getSha1Sum());
|
||||
}
|
||||
};
|
||||
if (connection.isProxy()) {
|
||||
IqPacket activation = new IqPacket(IqPacket.TYPE_SET);
|
||||
activation.setTo(connection.getJid());
|
||||
activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId());
|
||||
activation.query().addChild("activate").setContent(this.getResponder());
|
||||
Log.d("xmppService","connection is proxy. need to activate "+activation.toString());
|
||||
this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() {
|
||||
|
||||
@Override
|
||||
public void onFileTransmitted(JingleFile file) {
|
||||
Log.d("xmppService","sucessfully transmitted file. sha1:"+file.getSha1Sum());
|
||||
}
|
||||
};
|
||||
final IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
|
||||
if (connection.isProxy()) {
|
||||
IqPacket activation = new IqPacket(IqPacket.TYPE_SET);
|
||||
activation.setTo(connection.getJid());
|
||||
activation.query("http://jabber.org/protocol/bytestreams").setAttribute("sid", this.getSessionId());
|
||||
activation.query().addChild("activate").setContent(this.getResponder());
|
||||
Log.d("xmppService","connection is proxy. need to activate "+activation.toString());
|
||||
this.account.getXmppConnection().sendIqPacket(activation, new OnIqPacketReceived() {
|
||||
|
||||
@Override
|
||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||
account.getXmppConnection().sendIqPacket(response, null);
|
||||
Log.d("xmppService","activation result: "+packet.toString());
|
||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||
Log.d("xmppService","activation result: "+packet.toString());
|
||||
if (initiator.equals(account.getFullJid())) {
|
||||
Log.d("xmppService","we were initiating. sending file");
|
||||
connection.send(file,callback);
|
||||
} else {
|
||||
Log.d("xmppService","we were responding. receiving file");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
account.getXmppConnection().sendIqPacket(response, null);
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (initiator.equals(account.getFullJid())) {
|
||||
Log.d("xmppService","we were initiating. sending file");
|
||||
connection.send(file,callback);
|
||||
} else {
|
||||
Log.d("xmppService","we were responding. receiving file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,13 +274,25 @@ public class JingleConnection {
|
|||
|
||||
private void connectWithCandidates() {
|
||||
for(Element canditate : this.candidates) {
|
||||
|
||||
String host = canditate.getAttribute("host");
|
||||
int port = Integer.parseInt(canditate.getAttribute("port"));
|
||||
String type = canditate.getAttribute("type");
|
||||
String jid = canditate.getAttribute("jid");
|
||||
SocksConnection socksConnection = new SocksConnection(this, host, jid, port,type);
|
||||
socksConnection.connect();
|
||||
this.connections.put(canditate.getAttribute("cid"), socksConnection);
|
||||
connections.put(canditate.getAttribute("cid"), socksConnection);
|
||||
socksConnection.connect(new OnSocksConnection() {
|
||||
|
||||
@Override
|
||||
public void failed() {
|
||||
Log.d("xmppService","socks5 failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void established() {
|
||||
Log.d("xmppService","established socks5");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,4 +320,20 @@ public class JingleConnection {
|
|||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
private boolean mergeCandidate(Element candidate) {
|
||||
for(Element c : this.candidates) {
|
||||
if (c.getAttribute("host").equals(candidate.getAttribute("host"))&&(c.getAttribute("port").equals(candidate.getAttribute("port")))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.candidates.add(candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void mergeCandidates(List<Element> canditates) {
|
||||
for(Element c : canditates) {
|
||||
this.mergeCandidate(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,4 +122,8 @@ public class JingleConnectionManager {
|
|||
public String nextRandomId() {
|
||||
return new BigInteger(50, random).toString(32);
|
||||
}
|
||||
|
||||
public long getAutoAcceptFileSize() {
|
||||
return this.xmppConnectionService.getPreferences().getLong("auto_accept_file_size", 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package eu.siacs.conversations.xmpp.jingle;
|
||||
|
||||
public interface OnSocksConnection {
|
||||
public void failed();
|
||||
public void established();
|
||||
}
|
|
@ -25,6 +25,7 @@ public class SocksConnection {
|
|||
private boolean isProxy = false;
|
||||
private String destination;
|
||||
private OutputStream outputStream;
|
||||
private boolean isEstablished = false;
|
||||
|
||||
public SocksConnection(JingleConnection jingleConnection, String host,
|
||||
String jid, int port, String type) {
|
||||
|
@ -42,40 +43,52 @@ public class SocksConnection {
|
|||
mDigest.reset();
|
||||
this.destination = CryptoHelper.bytesToHex(mDigest
|
||||
.digest(destBuilder.toString().getBytes()));
|
||||
Log.d("xmppService", "host=" + host + ", port=" + port
|
||||
+ ", destination: " + destination);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean connect() {
|
||||
try {
|
||||
this.socket = new Socket(this.host, this.port);
|
||||
InputStream is = socket.getInputStream();
|
||||
this.outputStream = socket.getOutputStream();
|
||||
byte[] login = { 0x05, 0x01, 0x00 };
|
||||
byte[] expectedReply = { 0x05, 0x00 };
|
||||
byte[] reply = new byte[2];
|
||||
this.outputStream.write(login);
|
||||
is.read(reply);
|
||||
if (Arrays.equals(reply, expectedReply)) {
|
||||
String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003'
|
||||
+ '\u0028' + this.destination + '\u0000' + '\u0000';
|
||||
this.outputStream.write(connect.getBytes());
|
||||
byte[] result = new byte[2];
|
||||
is.read(result);
|
||||
int status = result[0];
|
||||
return (status == 0);
|
||||
} else {
|
||||
socket.close();
|
||||
return false;
|
||||
public void connect(final OnSocksConnection callback) {
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
socket = new Socket(host, port);
|
||||
InputStream is = socket.getInputStream();
|
||||
outputStream = socket.getOutputStream();
|
||||
byte[] login = { 0x05, 0x01, 0x00 };
|
||||
byte[] expectedReply = { 0x05, 0x00 };
|
||||
byte[] reply = new byte[2];
|
||||
outputStream.write(login);
|
||||
is.read(reply);
|
||||
if (Arrays.equals(reply, expectedReply)) {
|
||||
String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003'
|
||||
+ '\u0028' + destination + '\u0000' + '\u0000';
|
||||
outputStream.write(connect.getBytes());
|
||||
byte[] result = new byte[2];
|
||||
is.read(result);
|
||||
int status = result[1];
|
||||
if (status == 0) {
|
||||
Log.d("xmppService", "established connection with "+host + ":" + port
|
||||
+ "/" + destination);
|
||||
isEstablished = true;
|
||||
callback.established();
|
||||
} else {
|
||||
callback.failed();
|
||||
}
|
||||
} else {
|
||||
socket.close();
|
||||
callback.failed();
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
callback.failed();
|
||||
} catch (IOException e) {
|
||||
callback.failed();
|
||||
}
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
public void send(final JingleFile file, final OnFileTransmitted callback) {
|
||||
|
@ -141,4 +154,8 @@ public class SocksConnection {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEstablished() {
|
||||
return this.isEstablished;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,25 @@ public class Content extends Element {
|
|||
super("content");
|
||||
}
|
||||
|
||||
public void offerFile(JingleFile actualFile, String hash) {
|
||||
public void setFileOffer(JingleFile actualFile) {
|
||||
Element description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3");
|
||||
Element offer = description.addChild("offer");
|
||||
Element file = offer.addChild("file");
|
||||
file.addChild("size").setContent(""+actualFile.getSize());
|
||||
file.addChild("name").setContent(actualFile.getName());
|
||||
}
|
||||
|
||||
public Element getFileOffer() {
|
||||
Element description = this.findChild("description", "urn:xmpp:jingle:apps:file-transfer:3");
|
||||
if (description==null) {
|
||||
return null;
|
||||
}
|
||||
Element offer = description.findChild("offer");
|
||||
if (offer==null) {
|
||||
return null;
|
||||
}
|
||||
return offer.findChild("file");
|
||||
}
|
||||
|
||||
public void setCandidates(String transportId, List<Element> canditates) {
|
||||
Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1");
|
||||
|
@ -56,4 +68,12 @@ public class Content extends Element {
|
|||
return usedCandidate.getAttribute("cid");
|
||||
}
|
||||
}
|
||||
|
||||
public void addCandidate(Element candidate) {
|
||||
Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1");
|
||||
if (transport==null) {
|
||||
transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1");
|
||||
}
|
||||
transport.addChild(candidate);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue