basic image over http downloading
This commit is contained in:
parent
7e373bc89f
commit
bbb0693f4a
|
@ -0,0 +1,25 @@
|
||||||
|
package eu.siacs.conversations;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
|
||||||
|
public class AbstractConnectionManager {
|
||||||
|
protected XmppConnectionService mXmppConnectionService;
|
||||||
|
|
||||||
|
public AbstractConnectionManager(XmppConnectionService service) {
|
||||||
|
this.mXmppConnectionService = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmppConnectionService getXmppConnectionService() {
|
||||||
|
return this.mXmppConnectionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAutoAcceptFileSize() {
|
||||||
|
String config = this.mXmppConnectionService.getPreferences().getString(
|
||||||
|
"auto_accept_file_size", "524288");
|
||||||
|
try {
|
||||||
|
return Long.parseLong(config);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 524288;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
package eu.siacs.conversations;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class DownloadableFile extends File {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2247012619505115863L;
|
||||||
|
|
||||||
|
private long expectedSize = 0;
|
||||||
|
private String sha1sum;
|
||||||
|
private Key aeskey;
|
||||||
|
|
||||||
|
private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
|
||||||
|
|
||||||
|
public DownloadableFile(String path) {
|
||||||
|
super(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return super.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpectedSize() {
|
||||||
|
if (this.aeskey != null) {
|
||||||
|
return (this.expectedSize / 16 + 1) * 16;
|
||||||
|
} else {
|
||||||
|
return this.expectedSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpectedSize(long size) {
|
||||||
|
this.expectedSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSha1Sum() {
|
||||||
|
return this.sha1sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSha1Sum(String sum) {
|
||||||
|
this.sha1sum = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(byte[] key) {
|
||||||
|
if (key.length >= 32) {
|
||||||
|
byte[] secretKey = new byte[32];
|
||||||
|
System.arraycopy(key, 0, secretKey, 0, 32);
|
||||||
|
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
||||||
|
} else if (key.length >= 16) {
|
||||||
|
byte[] secretKey = new byte[16];
|
||||||
|
System.arraycopy(key, 0, secretKey, 0, 16);
|
||||||
|
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG, "weird key");
|
||||||
|
}
|
||||||
|
Log.d(Config.LOGTAG,
|
||||||
|
"using aes key "
|
||||||
|
+ CryptoHelper.bytesToHex(this.aeskey.getEncoded()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key getKey() {
|
||||||
|
return this.aeskey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream createInputStream() {
|
||||||
|
if (this.getKey() == null) {
|
||||||
|
try {
|
||||||
|
return new FileInputStream(this);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
IvParameterSpec ips = new IvParameterSpec(iv);
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, this.getKey(), ips);
|
||||||
|
Log.d(Config.LOGTAG, "opening encrypted input stream");
|
||||||
|
return new CipherInputStream(new FileInputStream(this), cipher);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (NoSuchPaddingException e) {
|
||||||
|
Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (InvalidAlgorithmParameterException e) {
|
||||||
|
Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream createOutputStream() {
|
||||||
|
if (this.getKey() == null) {
|
||||||
|
try {
|
||||||
|
return new FileOutputStream(this);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
IvParameterSpec ips = new IvParameterSpec(iv);
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, this.getKey(), ips);
|
||||||
|
Log.d(Config.LOGTAG, "opening encrypted output stream");
|
||||||
|
return new CipherOutputStream(new FileOutputStream(this),
|
||||||
|
cipher);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (NoSuchPaddingException e) {
|
||||||
|
Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (InvalidKeyException e) {
|
||||||
|
Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (InvalidAlgorithmParameterException e) {
|
||||||
|
Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import org.openintents.openpgp.util.OpenPgpApi;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
|
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
|
@ -22,7 +23,6 @@ import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.ui.UiCallback;
|
import eu.siacs.conversations.ui.UiCallback;
|
||||||
import eu.siacs.conversations.xmpp.jingle.JingleFile;
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
@ -86,10 +86,10 @@ public class PgpEngine {
|
||||||
});
|
});
|
||||||
} else if (message.getType() == Message.TYPE_IMAGE) {
|
} else if (message.getType() == Message.TYPE_IMAGE) {
|
||||||
try {
|
try {
|
||||||
final JingleFile inputFile = this.mXmppConnectionService
|
final DownloadableFile inputFile = this.mXmppConnectionService
|
||||||
.getFileBackend().getJingleFile(message, false);
|
.getFileBackend().getConversationsFile(message, false);
|
||||||
final JingleFile outputFile = this.mXmppConnectionService
|
final DownloadableFile outputFile = this.mXmppConnectionService
|
||||||
.getFileBackend().getJingleFile(message, true);
|
.getFileBackend().getConversationsFile(message, true);
|
||||||
outputFile.createNewFile();
|
outputFile.createNewFile();
|
||||||
InputStream is = new FileInputStream(inputFile);
|
InputStream is = new FileInputStream(inputFile);
|
||||||
OutputStream os = new FileOutputStream(outputFile);
|
OutputStream os = new FileOutputStream(outputFile);
|
||||||
|
@ -197,10 +197,10 @@ public class PgpEngine {
|
||||||
});
|
});
|
||||||
} else if (message.getType() == Message.TYPE_IMAGE) {
|
} else if (message.getType() == Message.TYPE_IMAGE) {
|
||||||
try {
|
try {
|
||||||
JingleFile inputFile = this.mXmppConnectionService
|
DownloadableFile inputFile = this.mXmppConnectionService
|
||||||
.getFileBackend().getJingleFile(message, true);
|
.getFileBackend().getConversationsFile(message, true);
|
||||||
JingleFile outputFile = this.mXmppConnectionService
|
DownloadableFile outputFile = this.mXmppConnectionService
|
||||||
.getFileBackend().getJingleFile(message, false);
|
.getFileBackend().getConversationsFile(message, false);
|
||||||
outputFile.createNewFile();
|
outputFile.createNewFile();
|
||||||
InputStream is = new FileInputStream(inputFile);
|
InputStream is = new FileInputStream(inputFile);
|
||||||
OutputStream os = new FileOutputStream(outputFile);
|
OutputStream os = new FileOutputStream(outputFile);
|
||||||
|
|
|
@ -374,4 +374,8 @@ public class Contact implements ListItem {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean trusted() {
|
||||||
|
return getOption(Options.FROM) && getOption(Options.TO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package eu.siacs.conversations.entities;
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
public interface Downloadable {
|
public interface Downloadable {
|
||||||
|
|
||||||
|
public final String[] VALID_EXTENSIONS = { "webp", "jpeg", "jpg", "png" };
|
||||||
|
public final String[] VALID_CRYPTO_EXTENSIONS = { "pgp", "gpg", "otr" };
|
||||||
|
|
||||||
public void start();
|
public void start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package eu.siacs.conversations.entities;
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.text.InputFilter.LengthFilter;
|
||||||
|
|
||||||
public class Message extends AbstractEntity {
|
public class Message extends AbstractEntity {
|
||||||
|
|
||||||
|
@ -131,14 +136,8 @@ public class Message extends AbstractEntity {
|
||||||
if (this.trueCounterpart == null) {
|
if (this.trueCounterpart == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
Account account = this.conversation.getAccount();
|
return this.conversation.getAccount().getRoster().getContactFromRoster(
|
||||||
Contact contact = account.getRoster().getContact(
|
|
||||||
this.trueCounterpart);
|
this.trueCounterpart);
|
||||||
if (contact.showInRoster()) {
|
|
||||||
return contact;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,4 +368,32 @@ public class Message extends AbstractEntity {
|
||||||
return prev.mergable(this);
|
return prev.mergable(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean bodyContainsDownloadable() {
|
||||||
|
Contact contact = this.getContact();
|
||||||
|
if (contact == null || !contact.trusted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
URL url = new URL(this.getBody());
|
||||||
|
if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (url.getPath()==null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String[] pathParts = url.getPath().split("/");
|
||||||
|
String filename = pathParts[pathParts.length - 1];
|
||||||
|
String[] extensionParts = filename.split("\\.");
|
||||||
|
if (extensionParts.length == 2 && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(extensionParts[extensionParts.length -1])) {
|
||||||
|
return true;
|
||||||
|
} else if (extensionParts.length == 3 && Arrays.asList(Downloadable.VALID_CRYPTO_EXTENSIONS).contains(extensionParts.length -1) && Arrays.asList(Downloadable.VALID_EXTENSIONS).contains(extensionParts[extensionParts.length -2])) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class Roster {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Contact getContactAsShownInRoster(String jid) {
|
public Contact getContactFromRoster(String jid) {
|
||||||
String cleanJid = jid.split("/", 2)[0];
|
String cleanJid = jid.split("/", 2)[0];
|
||||||
Contact contact = contacts.get(cleanJid);
|
Contact contact = contacts.get(cleanJid);
|
||||||
if (contact != null && contact.showInRoster()) {
|
if (contact != null && contact.showInRoster()) {
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
package eu.siacs.conversations.http;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
|
import eu.siacs.conversations.entities.Downloadable;
|
||||||
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
|
||||||
|
public class HttpConnection implements Downloadable {
|
||||||
|
|
||||||
|
private HttpConnectionManager mHttpConnectionManager;
|
||||||
|
private XmppConnectionService mXmppConnectionService;
|
||||||
|
|
||||||
|
private URL mUrl;
|
||||||
|
private Message message;
|
||||||
|
private DownloadableFile file;
|
||||||
|
|
||||||
|
public HttpConnection(HttpConnectionManager manager) {
|
||||||
|
this.mHttpConnectionManager = manager;
|
||||||
|
this.mXmppConnectionService = manager.getXmppConnectionService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
new Thread(new FileDownloader()).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Message message) {
|
||||||
|
this.message = message;
|
||||||
|
this.message.setDownloadable(this);
|
||||||
|
try {
|
||||||
|
mUrl = new URL(message.getBody());
|
||||||
|
this.file = mXmppConnectionService.getFileBackend().getConversationsFile(message,false);
|
||||||
|
message.setType(Message.TYPE_IMAGE);
|
||||||
|
mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED_OFFER);
|
||||||
|
checkFileSize();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkFileSize() {
|
||||||
|
new Thread(new FileSizeChecker()).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
mXmppConnectionService.markMessage(message, Message.STATUS_RECEPTION_FAILED);
|
||||||
|
Log.d(Config.LOGTAG,"canceled download");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FileSizeChecker implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
long size = retrieveFileSize();
|
||||||
|
file.setExpectedSize(size);
|
||||||
|
if (size <= mHttpConnectionManager.getAutoAcceptFileSize()) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
Log.d(Config.LOGTAG,"file size: "+size);
|
||||||
|
} catch (IOException e) {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long retrieveFileSize() throws IOException {
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
|
||||||
|
connection.setRequestMethod("HEAD");
|
||||||
|
if (connection instanceof HttpsURLConnection) {
|
||||||
|
|
||||||
|
}
|
||||||
|
String contentLength = connection.getHeaderField("Content-Length");
|
||||||
|
if (contentLength == null) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Long.parseLong(contentLength, 10);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FileDownloader implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVING);
|
||||||
|
download();
|
||||||
|
mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED);
|
||||||
|
} catch (IOException e) {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void download() throws IOException {
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
|
||||||
|
if (connection instanceof HttpsURLConnection) {
|
||||||
|
|
||||||
|
}
|
||||||
|
BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
|
||||||
|
OutputStream os = file.createOutputStream();
|
||||||
|
int count = -1;
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while ((count = is.read(buffer)) != -1) {
|
||||||
|
os.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
is.close();
|
||||||
|
Log.d(Config.LOGTAG,"finished downloading "+file.getAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package eu.siacs.conversations.http;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.AbstractConnectionManager;
|
||||||
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
|
||||||
|
public class HttpConnectionManager extends AbstractConnectionManager {
|
||||||
|
|
||||||
|
public HttpConnectionManager(XmppConnectionService service) {
|
||||||
|
super(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmppConnectionService mXmppConnectionService;
|
||||||
|
|
||||||
|
private List<HttpConnection> connections = new CopyOnWriteArrayList<HttpConnection>();
|
||||||
|
|
||||||
|
|
||||||
|
public HttpConnection createNewConnection(Message message) {
|
||||||
|
HttpConnection connection = new HttpConnection(this);
|
||||||
|
connection.init(message);
|
||||||
|
this.connections.add(connection);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -478,6 +478,9 @@ public class MessageParser extends AbstractParser implements
|
||||||
mXmppConnectionService.databaseBackend.createMessage(message);
|
mXmppConnectionService.databaseBackend.createMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (message.getStatus() == Message.STATUS_RECEIVED && message.bodyContainsDownloadable()) {
|
||||||
|
this.mXmppConnectionService.getHttpConnectionManager().createNewConnection(message);
|
||||||
|
}
|
||||||
notify = notify && !conversation.isMuted();
|
notify = notify && !conversation.isMuted();
|
||||||
if (notify) {
|
if (notify) {
|
||||||
mXmppConnectionService.getNotificationService().push(message);
|
mXmppConnectionService.getNotificationService().push(message);
|
||||||
|
|
|
@ -30,13 +30,13 @@ import android.util.Base64OutputStream;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.LruCache;
|
import android.util.LruCache;
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.services.ImageProvider;
|
import eu.siacs.conversations.services.ImageProvider;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.utils.UIHelper;
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
import eu.siacs.conversations.xmpp.jingle.JingleFile;
|
|
||||||
import eu.siacs.conversations.xmpp.pep.Avatar;
|
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||||
|
|
||||||
public class FileBackend {
|
public class FileBackend {
|
||||||
|
@ -66,11 +66,11 @@ public class FileBackend {
|
||||||
return thumbnailCache;
|
return thumbnailCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFile getJingleFileLegacy(Message message) {
|
public DownloadableFile getJingleFileLegacy(Message message) {
|
||||||
return getJingleFileLegacy(message, true);
|
return getJingleFileLegacy(message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFile getJingleFileLegacy(Message message, boolean decrypted) {
|
public DownloadableFile getJingleFileLegacy(Message message, boolean decrypted) {
|
||||||
Conversation conversation = message.getConversation();
|
Conversation conversation = message.getConversation();
|
||||||
String prefix = context.getFilesDir().getAbsolutePath();
|
String prefix = context.getFilesDir().getAbsolutePath();
|
||||||
String path = prefix + "/" + conversation.getAccount().getJid() + "/"
|
String path = prefix + "/" + conversation.getAccount().getJid() + "/"
|
||||||
|
@ -85,14 +85,14 @@ public class FileBackend {
|
||||||
filename = message.getUuid() + ".webp.pgp";
|
filename = message.getUuid() + ".webp.pgp";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new JingleFile(path + "/" + filename);
|
return new DownloadableFile(path + "/" + filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFile getJingleFile(Message message) {
|
public DownloadableFile getJingleFile(Message message) {
|
||||||
return getJingleFile(message, true);
|
return getConversationsFile(message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFile getJingleFile(Message message, boolean decrypted) {
|
public DownloadableFile getConversationsFile(Message message, boolean decrypted) {
|
||||||
StringBuilder filename = new StringBuilder();
|
StringBuilder filename = new StringBuilder();
|
||||||
filename.append(Environment.getExternalStoragePublicDirectory(
|
filename.append(Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_PICTURES).getAbsolutePath());
|
Environment.DIRECTORY_PICTURES).getAbsolutePath());
|
||||||
|
@ -107,7 +107,7 @@ public class FileBackend {
|
||||||
filename.append(".webp.pgp");
|
filename.append(".webp.pgp");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new JingleFile(filename.toString());
|
return new DownloadableFile(filename.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap resize(Bitmap originalBitmap, int size) {
|
public Bitmap resize(Bitmap originalBitmap, int size) {
|
||||||
|
@ -139,17 +139,17 @@ public class FileBackend {
|
||||||
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
|
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFile copyImageToPrivateStorage(Message message, Uri image)
|
public DownloadableFile copyImageToPrivateStorage(Message message, Uri image)
|
||||||
throws ImageCopyException {
|
throws ImageCopyException {
|
||||||
return this.copyImageToPrivateStorage(message, image, 0);
|
return this.copyImageToPrivateStorage(message, image, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JingleFile copyImageToPrivateStorage(Message message, Uri image,
|
private DownloadableFile copyImageToPrivateStorage(Message message, Uri image,
|
||||||
int sampleSize) throws ImageCopyException {
|
int sampleSize) throws ImageCopyException {
|
||||||
try {
|
try {
|
||||||
InputStream is = context.getContentResolver()
|
InputStream is = context.getContentResolver()
|
||||||
.openInputStream(image);
|
.openInputStream(image);
|
||||||
JingleFile file = getJingleFile(message);
|
DownloadableFile file = getJingleFile(message);
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
Bitmap originalBitmap;
|
Bitmap originalBitmap;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import eu.siacs.conversations.entities.Presences;
|
||||||
import eu.siacs.conversations.generator.IqGenerator;
|
import eu.siacs.conversations.generator.IqGenerator;
|
||||||
import eu.siacs.conversations.generator.MessageGenerator;
|
import eu.siacs.conversations.generator.MessageGenerator;
|
||||||
import eu.siacs.conversations.generator.PresenceGenerator;
|
import eu.siacs.conversations.generator.PresenceGenerator;
|
||||||
|
import eu.siacs.conversations.http.HttpConnectionManager;
|
||||||
import eu.siacs.conversations.parser.IqParser;
|
import eu.siacs.conversations.parser.IqParser;
|
||||||
import eu.siacs.conversations.parser.MessageParser;
|
import eu.siacs.conversations.parser.MessageParser;
|
||||||
import eu.siacs.conversations.parser.PresenceParser;
|
import eu.siacs.conversations.parser.PresenceParser;
|
||||||
|
@ -106,6 +107,7 @@ public class XmppConnectionService extends Service {
|
||||||
private CopyOnWriteArrayList<Conversation> conversations = null;
|
private CopyOnWriteArrayList<Conversation> conversations = null;
|
||||||
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
|
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
|
||||||
this);
|
this);
|
||||||
|
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(this);
|
||||||
|
|
||||||
private OnConversationUpdate mOnConversationUpdate = null;
|
private OnConversationUpdate mOnConversationUpdate = null;
|
||||||
private int convChangedListenerCount = 0;
|
private int convChangedListenerCount = 0;
|
||||||
|
@ -1780,7 +1782,7 @@ public class XmppConnectionService extends Service {
|
||||||
for (Account account : getAccounts()) {
|
for (Account account : getAccounts()) {
|
||||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||||
Contact contact = account.getRoster()
|
Contact contact = account.getRoster()
|
||||||
.getContactAsShownInRoster(jid);
|
.getContactFromRoster(jid);
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
contacts.add(contact);
|
contacts.add(contact);
|
||||||
}
|
}
|
||||||
|
@ -1792,4 +1794,8 @@ public class XmppConnectionService extends Service {
|
||||||
public NotificationService getNotificationService() {
|
public NotificationService getNotificationService() {
|
||||||
return this.mNotificationService;
|
return this.mNotificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpConnectionManager getHttpConnectionManager() {
|
||||||
|
return this.mHttpConnectionManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Downloadable;
|
import eu.siacs.conversations.entities.Downloadable;
|
||||||
|
@ -54,7 +55,7 @@ public class JingleConnection implements Downloadable {
|
||||||
|
|
||||||
private String transportId;
|
private String transportId;
|
||||||
private Element fileOffer;
|
private Element fileOffer;
|
||||||
private JingleFile file = null;
|
private DownloadableFile file = null;
|
||||||
|
|
||||||
private String contentName;
|
private String contentName;
|
||||||
private String contentCreator;
|
private String contentCreator;
|
||||||
|
@ -83,7 +84,7 @@ public class JingleConnection implements Downloadable {
|
||||||
final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
|
final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileTransmitted(JingleFile file) {
|
public void onFileTransmitted(DownloadableFile file) {
|
||||||
if (responder.equals(account.getFullJid())) {
|
if (responder.equals(account.getFullJid())) {
|
||||||
sendSuccess();
|
sendSuccess();
|
||||||
if (acceptedAutomatically) {
|
if (acceptedAutomatically) {
|
||||||
|
@ -323,7 +324,7 @@ public class JingleConnection implements Downloadable {
|
||||||
.push(message);
|
.push(message);
|
||||||
}
|
}
|
||||||
this.file = this.mXmppConnectionService.getFileBackend()
|
this.file = this.mXmppConnectionService.getFileBackend()
|
||||||
.getJingleFile(message, false);
|
.getConversationsFile(message, false);
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
||||||
byte[] key = conversation.getSymmetricKey();
|
byte[] key = conversation.getSymmetricKey();
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
|
@ -355,7 +356,7 @@ public class JingleConnection implements Downloadable {
|
||||||
if (message.getType() == Message.TYPE_IMAGE) {
|
if (message.getType() == Message.TYPE_IMAGE) {
|
||||||
content.setTransportId(this.transportId);
|
content.setTransportId(this.transportId);
|
||||||
this.file = this.mXmppConnectionService.getFileBackend()
|
this.file = this.mXmppConnectionService.getFileBackend()
|
||||||
.getJingleFile(message, false);
|
.getConversationsFile(message, false);
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
||||||
Conversation conversation = this.message.getConversation();
|
Conversation conversation = this.message.getConversation();
|
||||||
this.mXmppConnectionService.renewSymmetricKey(conversation);
|
this.mXmppConnectionService.renewSymmetricKey(conversation);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import eu.siacs.conversations.AbstractConnectionManager;
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
@ -16,10 +17,7 @@ import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||||
|
|
||||||
public class JingleConnectionManager {
|
public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
|
|
||||||
private XmppConnectionService xmppConnectionService;
|
|
||||||
|
|
||||||
private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>();
|
private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>();
|
||||||
|
|
||||||
private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>();
|
private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>();
|
||||||
|
@ -28,7 +26,7 @@ public class JingleConnectionManager {
|
||||||
private SecureRandom random = new SecureRandom();
|
private SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
public JingleConnectionManager(XmppConnectionService service) {
|
public JingleConnectionManager(XmppConnectionService service) {
|
||||||
this.xmppConnectionService = service;
|
super(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deliverPacket(Account account, JinglePacket packet) {
|
public void deliverPacket(Account account, JinglePacket packet) {
|
||||||
|
@ -68,10 +66,6 @@ public class JingleConnectionManager {
|
||||||
this.connections.remove(connection);
|
this.connections.remove(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmppConnectionService getXmppConnectionService() {
|
|
||||||
return this.xmppConnectionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getPrimaryCandidate(Account account,
|
public void getPrimaryCandidate(Account account,
|
||||||
final OnPrimaryCandidateFound listener) {
|
final OnPrimaryCandidateFound listener) {
|
||||||
if (!this.primaryCandidates.containsKey(account.getJid())) {
|
if (!this.primaryCandidates.containsKey(account.getJid())) {
|
||||||
|
@ -128,16 +122,6 @@ public class JingleConnectionManager {
|
||||||
return new BigInteger(50, random).toString(32);
|
return new BigInteger(50, random).toString(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getAutoAcceptFileSize() {
|
|
||||||
String config = this.xmppConnectionService.getPreferences().getString(
|
|
||||||
"auto_accept_file_size", "524288");
|
|
||||||
try {
|
|
||||||
return Long.parseLong(config);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return 524288;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deliverIbbPacket(Account account, IqPacket packet) {
|
public void deliverIbbPacket(Account account, IqPacket packet) {
|
||||||
String sid = null;
|
String sid = null;
|
||||||
Element payload = null;
|
Element payload = null;
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.security.Key;
|
|
||||||
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class JingleFile extends File {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 2247012619505115863L;
|
|
||||||
|
|
||||||
private long expectedSize = 0;
|
|
||||||
private String sha1sum;
|
|
||||||
private Key aeskey;
|
|
||||||
|
|
||||||
public JingleFile(String path) {
|
|
||||||
super(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return super.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getExpectedSize() {
|
|
||||||
if (this.aeskey != null) {
|
|
||||||
return (this.expectedSize / 16 + 1) * 16;
|
|
||||||
} else {
|
|
||||||
return this.expectedSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpectedSize(long size) {
|
|
||||||
this.expectedSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSha1Sum() {
|
|
||||||
return this.sha1sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSha1Sum(String sum) {
|
|
||||||
this.sha1sum = sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKey(byte[] key) {
|
|
||||||
if (key.length >= 32) {
|
|
||||||
byte[] secretKey = new byte[32];
|
|
||||||
System.arraycopy(key, 0, secretKey, 0, 32);
|
|
||||||
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
|
||||||
} else if (key.length >= 16) {
|
|
||||||
byte[] secretKey = new byte[16];
|
|
||||||
System.arraycopy(key, 0, secretKey, 0, 16);
|
|
||||||
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
|
||||||
} else {
|
|
||||||
Log.d(Config.LOGTAG, "weird key");
|
|
||||||
}
|
|
||||||
Log.d(Config.LOGTAG,
|
|
||||||
"using aes key "
|
|
||||||
+ CryptoHelper.bytesToHex(this.aeskey.getEncoded()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Key getKey() {
|
|
||||||
return this.aeskey;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle;
|
package eu.siacs.conversations.xmpp.jingle;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -9,6 +8,7 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
|
@ -26,7 +26,7 @@ public class JingleInbandTransport extends JingleTransport {
|
||||||
|
|
||||||
private boolean established = false;
|
private boolean established = false;
|
||||||
|
|
||||||
private JingleFile file;
|
private DownloadableFile file;
|
||||||
|
|
||||||
private InputStream fileInputStream = null;
|
private InputStream fileInputStream = null;
|
||||||
private OutputStream fileOutputStream;
|
private OutputStream fileOutputStream;
|
||||||
|
@ -77,7 +77,7 @@ public class JingleInbandTransport extends JingleTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receive(JingleFile file,
|
public void receive(DownloadableFile file,
|
||||||
OnFileTransmissionStatusChanged callback) {
|
OnFileTransmissionStatusChanged callback) {
|
||||||
this.onFileTransmissionStatusChanged = callback;
|
this.onFileTransmissionStatusChanged = callback;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
@ -86,7 +86,7 @@ public class JingleInbandTransport extends JingleTransport {
|
||||||
digest.reset();
|
digest.reset();
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
this.fileOutputStream = getOutputStream(file);
|
this.fileOutputStream = file.createOutputStream();
|
||||||
if (this.fileOutputStream == null) {
|
if (this.fileOutputStream == null) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
|
@ -100,20 +100,18 @@ public class JingleInbandTransport extends JingleTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(JingleFile file, OnFileTransmissionStatusChanged callback) {
|
public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) {
|
||||||
this.onFileTransmissionStatusChanged = callback;
|
this.onFileTransmissionStatusChanged = callback;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
try {
|
try {
|
||||||
this.digest = MessageDigest.getInstance("SHA-1");
|
this.digest = MessageDigest.getInstance("SHA-1");
|
||||||
this.digest.reset();
|
this.digest.reset();
|
||||||
fileInputStream = this.getInputStream(file);
|
fileInputStream = this.file.createInputStream();
|
||||||
if (fileInputStream == null) {
|
if (fileInputStream == null) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.sendNextBlock();
|
this.sendNextBlock();
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
callback.onFileTransferAborted();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
|
||||||
public class JingleSocks5Transport extends JingleTransport {
|
public class JingleSocks5Transport extends JingleTransport {
|
||||||
|
@ -86,7 +87,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send(final JingleFile file,
|
public void send(final DownloadableFile file,
|
||||||
final OnFileTransmissionStatusChanged callback) {
|
final OnFileTransmissionStatusChanged callback) {
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
|
@ -96,7 +97,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
try {
|
try {
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
digest.reset();
|
digest.reset();
|
||||||
fileInputStream = getInputStream(file);
|
fileInputStream = file.createInputStream();
|
||||||
if (fileInputStream == null) {
|
if (fileInputStream == null) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
|
@ -132,7 +133,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void receive(final JingleFile file,
|
public void receive(final DownloadableFile file,
|
||||||
final OnFileTransmissionStatusChanged callback) {
|
final OnFileTransmissionStatusChanged callback) {
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
socket.setSoTimeout(30000);
|
socket.setSoTimeout(30000);
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
OutputStream fileOutputStream = getOutputStream(file);
|
OutputStream fileOutputStream = file.createOutputStream();
|
||||||
if (fileOutputStream == null) {
|
if (fileOutputStream == null) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,88 +1,13 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle;
|
package eu.siacs.conversations.xmpp.jingle;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.CipherOutputStream;
|
|
||||||
import javax.crypto.CipherInputStream;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public abstract class JingleTransport {
|
public abstract class JingleTransport {
|
||||||
public abstract void connect(final OnTransportConnected callback);
|
public abstract void connect(final OnTransportConnected callback);
|
||||||
|
|
||||||
public abstract void receive(final JingleFile file,
|
public abstract void receive(final DownloadableFile file,
|
||||||
final OnFileTransmissionStatusChanged callback);
|
final OnFileTransmissionStatusChanged callback);
|
||||||
|
|
||||||
public abstract void send(final JingleFile file,
|
public abstract void send(final DownloadableFile file,
|
||||||
final OnFileTransmissionStatusChanged callback);
|
final OnFileTransmissionStatusChanged callback);
|
||||||
|
|
||||||
private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
||||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
|
|
||||||
|
|
||||||
protected InputStream getInputStream(JingleFile file)
|
|
||||||
throws FileNotFoundException {
|
|
||||||
if (file.getKey() == null) {
|
|
||||||
return new FileInputStream(file);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
IvParameterSpec ips = new IvParameterSpec(iv);
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, file.getKey(), ips);
|
|
||||||
Log.d(Config.LOGTAG, "opening encrypted input stream");
|
|
||||||
return new CipherInputStream(new FileInputStream(file), cipher);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (InvalidAlgorithmParameterException e) {
|
|
||||||
Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected OutputStream getOutputStream(JingleFile file)
|
|
||||||
throws FileNotFoundException {
|
|
||||||
if (file.getKey() == null) {
|
|
||||||
return new FileOutputStream(file);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
IvParameterSpec ips = new IvParameterSpec(iv);
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, file.getKey(), ips);
|
|
||||||
Log.d(Config.LOGTAG, "opening encrypted output stream");
|
|
||||||
return new CipherOutputStream(new FileOutputStream(file),
|
|
||||||
cipher);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (InvalidAlgorithmParameterException e) {
|
|
||||||
Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle;
|
package eu.siacs.conversations.xmpp.jingle;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
|
|
||||||
public interface OnFileTransmissionStatusChanged {
|
public interface OnFileTransmissionStatusChanged {
|
||||||
public void onFileTransmitted(JingleFile file);
|
public void onFileTransmitted(DownloadableFile file);
|
||||||
|
|
||||||
public void onFileTransferAborted();
|
public void onFileTransferAborted();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.DownloadableFile;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xmpp.jingle.JingleFile;
|
|
||||||
|
|
||||||
public class Content extends Element {
|
public class Content extends Element {
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public class Content extends Element {
|
||||||
this.transportId = sid;
|
this.transportId = sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFileOffer(JingleFile actualFile, boolean otr) {
|
public void setFileOffer(DownloadableFile actualFile, boolean otr) {
|
||||||
Element description = this.addChild("description",
|
Element description = this.addChild("description",
|
||||||
"urn:xmpp:jingle:apps:file-transfer:3");
|
"urn:xmpp:jingle:apps:file-transfer:3");
|
||||||
Element offer = description.addChild("offer");
|
Element offer = description.addChild("offer");
|
||||||
|
|
Loading…
Reference in New Issue