basic image over http downloading

This commit is contained in:
iNPUTmice 2014-10-14 01:06:45 +02:00
parent 7e373bc89f
commit bbb0693f4a
20 changed files with 429 additions and 213 deletions

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -15,6 +15,7 @@ import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.DownloadableFile;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
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.services.XmppConnectionService;
import eu.siacs.conversations.ui.UiCallback;
import eu.siacs.conversations.xmpp.jingle.JingleFile;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.BitmapFactory;
@ -86,10 +86,10 @@ public class PgpEngine {
});
} else if (message.getType() == Message.TYPE_IMAGE) {
try {
final JingleFile inputFile = this.mXmppConnectionService
.getFileBackend().getJingleFile(message, false);
final JingleFile outputFile = this.mXmppConnectionService
.getFileBackend().getJingleFile(message, true);
final DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getConversationsFile(message, false);
final DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getConversationsFile(message, true);
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);
@ -197,10 +197,10 @@ public class PgpEngine {
});
} else if (message.getType() == Message.TYPE_IMAGE) {
try {
JingleFile inputFile = this.mXmppConnectionService
.getFileBackend().getJingleFile(message, true);
JingleFile outputFile = this.mXmppConnectionService
.getFileBackend().getJingleFile(message, false);
DownloadableFile inputFile = this.mXmppConnectionService
.getFileBackend().getConversationsFile(message, true);
DownloadableFile outputFile = this.mXmppConnectionService
.getFileBackend().getConversationsFile(message, false);
outputFile.createNewFile();
InputStream is = new FileInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile);

View File

@ -374,4 +374,8 @@ public class Contact implements ListItem {
return false;
}
}
public boolean trusted() {
return getOption(Options.FROM) && getOption(Options.TO);
}
}

View File

@ -1,5 +1,9 @@
package eu.siacs.conversations.entities;
public interface Downloadable {
public final String[] VALID_EXTENSIONS = { "webp", "jpeg", "jpg", "png" };
public final String[] VALID_CRYPTO_EXTENSIONS = { "pgp", "gpg", "otr" };
public void start();
}

View File

@ -1,10 +1,15 @@
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.R;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.text.InputFilter.LengthFilter;
public class Message extends AbstractEntity {
@ -131,14 +136,8 @@ public class Message extends AbstractEntity {
if (this.trueCounterpart == null) {
return null;
} else {
Account account = this.conversation.getAccount();
Contact contact = account.getRoster().getContact(
return this.conversation.getAccount().getRoster().getContactFromRoster(
this.trueCounterpart);
if (contact.showInRoster()) {
return contact;
} else {
return null;
}
}
}
}
@ -369,4 +368,32 @@ public class Message extends AbstractEntity {
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;
}
}
}

View File

@ -14,7 +14,7 @@ public class Roster {
this.account = account;
}
public Contact getContactAsShownInRoster(String jid) {
public Contact getContactFromRoster(String jid) {
String cleanJid = jid.split("/", 2)[0];
Contact contact = contacts.get(cleanJid);
if (contact != null && contact.showInRoster()) {

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -478,6 +478,9 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.databaseBackend.createMessage(message);
}
}
if (message.getStatus() == Message.STATUS_RECEIVED && message.bodyContainsDownloadable()) {
this.mXmppConnectionService.getHttpConnectionManager().createNewConnection(message);
}
notify = notify && !conversation.isMuted();
if (notify) {
mXmppConnectionService.getNotificationService().push(message);

View File

@ -30,13 +30,13 @@ import android.util.Base64OutputStream;
import android.util.Log;
import android.util.LruCache;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.DownloadableFile;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.ImageProvider;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jingle.JingleFile;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
@ -66,11 +66,11 @@ public class FileBackend {
return thumbnailCache;
}
public JingleFile getJingleFileLegacy(Message message) {
public DownloadableFile getJingleFileLegacy(Message message) {
return getJingleFileLegacy(message, true);
}
public JingleFile getJingleFileLegacy(Message message, boolean decrypted) {
public DownloadableFile getJingleFileLegacy(Message message, boolean decrypted) {
Conversation conversation = message.getConversation();
String prefix = context.getFilesDir().getAbsolutePath();
String path = prefix + "/" + conversation.getAccount().getJid() + "/"
@ -85,14 +85,14 @@ public class FileBackend {
filename = message.getUuid() + ".webp.pgp";
}
}
return new JingleFile(path + "/" + filename);
return new DownloadableFile(path + "/" + filename);
}
public JingleFile getJingleFile(Message message) {
return getJingleFile(message, true);
public DownloadableFile getJingleFile(Message message) {
return getConversationsFile(message, true);
}
public JingleFile getJingleFile(Message message, boolean decrypted) {
public DownloadableFile getConversationsFile(Message message, boolean decrypted) {
StringBuilder filename = new StringBuilder();
filename.append(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath());
@ -107,7 +107,7 @@ public class FileBackend {
filename.append(".webp.pgp");
}
}
return new JingleFile(filename.toString());
return new DownloadableFile(filename.toString());
}
public Bitmap resize(Bitmap originalBitmap, int size) {
@ -139,17 +139,17 @@ public class FileBackend {
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 {
return this.copyImageToPrivateStorage(message, image, 0);
}
private JingleFile copyImageToPrivateStorage(Message message, Uri image,
private DownloadableFile copyImageToPrivateStorage(Message message, Uri image,
int sampleSize) throws ImageCopyException {
try {
InputStream is = context.getContentResolver()
.openInputStream(image);
JingleFile file = getJingleFile(message);
DownloadableFile file = getJingleFile(message);
file.getParentFile().mkdirs();
file.createNewFile();
Bitmap originalBitmap;

View File

@ -34,6 +34,7 @@ import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.generator.MessageGenerator;
import eu.siacs.conversations.generator.PresenceGenerator;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.parser.MessageParser;
import eu.siacs.conversations.parser.PresenceParser;
@ -106,6 +107,7 @@ public class XmppConnectionService extends Service {
private CopyOnWriteArrayList<Conversation> conversations = null;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this);
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(this);
private OnConversationUpdate mOnConversationUpdate = null;
private int convChangedListenerCount = 0;
@ -1780,7 +1782,7 @@ public class XmppConnectionService extends Service {
for (Account account : getAccounts()) {
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
Contact contact = account.getRoster()
.getContactAsShownInRoster(jid);
.getContactFromRoster(jid);
if (contact != null) {
contacts.add(contact);
}
@ -1792,4 +1794,8 @@ public class XmppConnectionService extends Service {
public NotificationService getNotificationService() {
return this.mNotificationService;
}
public HttpConnectionManager getHttpConnectionManager() {
return this.mHttpConnectionManager;
}
}

View File

@ -13,6 +13,7 @@ import android.graphics.BitmapFactory;
import android.net.Uri;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.DownloadableFile;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable;
@ -54,7 +55,7 @@ public class JingleConnection implements Downloadable {
private String transportId;
private Element fileOffer;
private JingleFile file = null;
private DownloadableFile file = null;
private String contentName;
private String contentCreator;
@ -83,7 +84,7 @@ public class JingleConnection implements Downloadable {
final OnFileTransmissionStatusChanged onFileTransmissionSatusChanged = new OnFileTransmissionStatusChanged() {
@Override
public void onFileTransmitted(JingleFile file) {
public void onFileTransmitted(DownloadableFile file) {
if (responder.equals(account.getFullJid())) {
sendSuccess();
if (acceptedAutomatically) {
@ -323,7 +324,7 @@ public class JingleConnection implements Downloadable {
.push(message);
}
this.file = this.mXmppConnectionService.getFileBackend()
.getJingleFile(message, false);
.getConversationsFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
byte[] key = conversation.getSymmetricKey();
if (key == null) {
@ -355,7 +356,7 @@ public class JingleConnection implements Downloadable {
if (message.getType() == Message.TYPE_IMAGE) {
content.setTransportId(this.transportId);
this.file = this.mXmppConnectionService.getFileBackend()
.getJingleFile(message, false);
.getConversationsFile(message, false);
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
Conversation conversation = this.message.getConversation();
this.mXmppConnectionService.renewSymmetricKey(conversation);

View File

@ -7,6 +7,7 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import android.annotation.SuppressLint;
import android.util.Log;
import eu.siacs.conversations.AbstractConnectionManager;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
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.stanzas.IqPacket;
public class JingleConnectionManager {
private XmppConnectionService xmppConnectionService;
public class JingleConnectionManager extends AbstractConnectionManager {
private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>();
private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>();
@ -28,7 +26,7 @@ public class JingleConnectionManager {
private SecureRandom random = new SecureRandom();
public JingleConnectionManager(XmppConnectionService service) {
this.xmppConnectionService = service;
super(service);
}
public void deliverPacket(Account account, JinglePacket packet) {
@ -68,10 +66,6 @@ public class JingleConnectionManager {
this.connections.remove(connection);
}
public XmppConnectionService getXmppConnectionService() {
return this.xmppConnectionService;
}
public void getPrimaryCandidate(Account account,
final OnPrimaryCandidateFound listener) {
if (!this.primaryCandidates.containsKey(account.getJid())) {
@ -128,16 +122,6 @@ public class JingleConnectionManager {
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) {
String sid = null;
Element payload = null;

View File

@ -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;
}
}

View File

@ -1,6 +1,5 @@
package eu.siacs.conversations.xmpp.jingle;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -9,6 +8,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import android.util.Base64;
import eu.siacs.conversations.DownloadableFile;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
@ -26,7 +26,7 @@ public class JingleInbandTransport extends JingleTransport {
private boolean established = false;
private JingleFile file;
private DownloadableFile file;
private InputStream fileInputStream = null;
private OutputStream fileOutputStream;
@ -77,7 +77,7 @@ public class JingleInbandTransport extends JingleTransport {
}
@Override
public void receive(JingleFile file,
public void receive(DownloadableFile file,
OnFileTransmissionStatusChanged callback) {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
@ -86,7 +86,7 @@ public class JingleInbandTransport extends JingleTransport {
digest.reset();
file.getParentFile().mkdirs();
file.createNewFile();
this.fileOutputStream = getOutputStream(file);
this.fileOutputStream = file.createOutputStream();
if (this.fileOutputStream == null) {
callback.onFileTransferAborted();
return;
@ -100,20 +100,18 @@ public class JingleInbandTransport extends JingleTransport {
}
@Override
public void send(JingleFile file, OnFileTransmissionStatusChanged callback) {
public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
try {
this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset();
fileInputStream = this.getInputStream(file);
fileInputStream = this.file.createInputStream();
if (fileInputStream == null) {
callback.onFileTransferAborted();
return;
}
this.sendNextBlock();
} catch (FileNotFoundException e) {
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted();
}

View File

@ -10,6 +10,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import eu.siacs.conversations.DownloadableFile;
import eu.siacs.conversations.utils.CryptoHelper;
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) {
new Thread(new Runnable() {
@ -96,7 +97,7 @@ public class JingleSocks5Transport extends JingleTransport {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
fileInputStream = getInputStream(file);
fileInputStream = file.createInputStream();
if (fileInputStream == null) {
callback.onFileTransferAborted();
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) {
new Thread(new Runnable() {
@ -145,7 +146,7 @@ public class JingleSocks5Transport extends JingleTransport {
socket.setSoTimeout(30000);
file.getParentFile().mkdirs();
file.createNewFile();
OutputStream fileOutputStream = getOutputStream(file);
OutputStream fileOutputStream = file.createOutputStream();
if (fileOutputStream == null) {
callback.onFileTransferAborted();
return;

View File

@ -1,88 +1,13 @@
package eu.siacs.conversations.xmpp.jingle;
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.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;
import eu.siacs.conversations.DownloadableFile;
public abstract class JingleTransport {
public abstract void connect(final OnTransportConnected callback);
public abstract void receive(final JingleFile file,
public abstract void receive(final DownloadableFile file,
final OnFileTransmissionStatusChanged callback);
public abstract void send(final JingleFile file,
public abstract void send(final DownloadableFile file,
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;
}
}
}
}

View File

@ -1,7 +1,9 @@
package eu.siacs.conversations.xmpp.jingle;
import eu.siacs.conversations.DownloadableFile;
public interface OnFileTransmissionStatusChanged {
public void onFileTransmitted(JingleFile file);
public void onFileTransmitted(DownloadableFile file);
public void onFileTransferAborted();
}

View File

@ -1,7 +1,7 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import eu.siacs.conversations.DownloadableFile;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jingle.JingleFile;
public class Content extends Element {
@ -25,7 +25,7 @@ public class Content extends Element {
this.transportId = sid;
}
public void setFileOffer(JingleFile actualFile, boolean otr) {
public void setFileOffer(DownloadableFile actualFile, boolean otr) {
Element description = this.addChild("description",
"urn:xmpp:jingle:apps:file-transfer:3");
Element offer = description.addChild("offer");