diff options
author | iNPUTmice <daniel@gultsch.de> | 2014-10-14 01:06:45 +0200 |
---|---|---|
committer | iNPUTmice <daniel@gultsch.de> | 2014-10-14 01:06:45 +0200 |
commit | bbb0693f4a9a62a872b61b12a5118b01c7ca9450 (patch) | |
tree | 7db16c0083e42461921936344fba1f9af7c58f97 /src | |
parent | 7e373bc89fd11cf214fb435f25063d77cc2c48ae (diff) |
basic image over http downloading
Diffstat (limited to '')
20 files changed, 429 insertions, 213 deletions
diff --git a/src/eu/siacs/conversations/AbstractConnectionManager.java b/src/eu/siacs/conversations/AbstractConnectionManager.java new file mode 100644 index 000000000..f2c937ab4 --- /dev/null +++ b/src/eu/siacs/conversations/AbstractConnectionManager.java @@ -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; + } + } +} diff --git a/src/eu/siacs/conversations/DownloadableFile.java b/src/eu/siacs/conversations/DownloadableFile.java new file mode 100644 index 000000000..c8097df83 --- /dev/null +++ b/src/eu/siacs/conversations/DownloadableFile.java @@ -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; + } + } + } +} diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java index e7058a683..d83a210d3 100644 --- a/src/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/eu/siacs/conversations/crypto/PgpEngine.java @@ -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); diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java index b1ebe6623..40eee73d9 100644 --- a/src/eu/siacs/conversations/entities/Contact.java +++ b/src/eu/siacs/conversations/entities/Contact.java @@ -374,4 +374,8 @@ public class Contact implements ListItem { return false; } } + + public boolean trusted() { + return getOption(Options.FROM) && getOption(Options.TO); + } } diff --git a/src/eu/siacs/conversations/entities/Downloadable.java b/src/eu/siacs/conversations/entities/Downloadable.java index 8fb4977ef..c8ee357db 100644 --- a/src/eu/siacs/conversations/entities/Downloadable.java +++ b/src/eu/siacs/conversations/entities/Downloadable.java @@ -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(); } diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java index 49482bbca..b459510c9 100644 --- a/src/eu/siacs/conversations/entities/Message.java +++ b/src/eu/siacs/conversations/entities/Message.java @@ -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; + } + } } diff --git a/src/eu/siacs/conversations/entities/Roster.java b/src/eu/siacs/conversations/entities/Roster.java index b69087934..ef47c5778 100644 --- a/src/eu/siacs/conversations/entities/Roster.java +++ b/src/eu/siacs/conversations/entities/Roster.java @@ -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()) { diff --git a/src/eu/siacs/conversations/http/HttpConnection.java b/src/eu/siacs/conversations/http/HttpConnection.java new file mode 100644 index 000000000..d3b1700bc --- /dev/null +++ b/src/eu/siacs/conversations/http/HttpConnection.java @@ -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()); + } + + } +}
\ No newline at end of file diff --git a/src/eu/siacs/conversations/http/HttpConnectionManager.java b/src/eu/siacs/conversations/http/HttpConnectionManager.java new file mode 100644 index 000000000..ee50ef7e4 --- /dev/null +++ b/src/eu/siacs/conversations/http/HttpConnectionManager.java @@ -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; + } +} diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java index f83290373..71346c7a5 100644 --- a/src/eu/siacs/conversations/parser/MessageParser.java +++ b/src/eu/siacs/conversations/parser/MessageParser.java @@ -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); diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index d86c0ee13..8d5046ab8 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -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; diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index e6297f4ff..3549ce0a6 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -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; + } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 92fdbe0b6..4dac54f6c 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -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); diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 79090af63..93b03ff85 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -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; diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java b/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java deleted file mode 100644 index 9253814b6..000000000 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java +++ /dev/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; - } -} diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index c5498075f..ed64c24a4 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -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(); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 63f5a5078..ec6b2c24a 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -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; diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java index 07dc8ecc3..185018e67 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java @@ -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; - } - } - } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java b/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java index 19fd4d973..a6df50a52 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java +++ b/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java @@ -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(); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java index d19e6dfd1..e74d89658 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java +++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java @@ -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"); |