aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorChristian S <christian@pix-art.de>2015-08-12 12:41:13 +0200
committerChristian S <christian@pix-art.de>2015-08-12 12:41:13 +0200
commita0145e779d3caf58b0bfdb6a76587c61b345bf55 (patch)
tree619423e3139836b9ad78dc472e7a6592b0d015ba /src/main
parent6ef1f090840eec062f23c3433eaeef77df75f874 (diff)
parent81b7e27a468edb37994225fa7ad3581e26f73c9d (diff)
version 1.6.0 beta 3
Diffstat (limited to 'src/main')
-rw-r--r--src/main/AndroidManifest.xml2
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java2
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java99
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java13
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/FileBackend.java37
-rw-r--r--src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java24
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java21
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java4
-rw-r--r--src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java6
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java8
-rw-r--r--src/main/java/eu/siacs/conversations/utils/FileUtils.java144
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java10
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java67
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java37
-rw-r--r--src/main/res/layout/activity_edit_account.xml10
-rw-r--r--src/main/res/layout/contact_key.xml4
-rw-r--r--src/main/res/menu/encryption_choices.xml6
-rw-r--r--src/main/res/values/strings.xml27
20 files changed, 357 insertions, 168 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index b0611f848..15d5bc255 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -132,7 +132,7 @@
</activity>
<activity
android:name=".ui.TrustKeysActivity"
- android:label="@string/trust_keys"
+ android:label="@string/trust_omemo_fingerprints"
android:windowSoftInputMode="stateAlwaysHidden"/>
<activity
android:name="de.duenndns.ssl.MemorizingActivity"
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index f0eb96832..a92b1024a 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -43,6 +43,8 @@ public final class Config {
public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false;
+ public static final boolean REPORT_WRONG_FILESIZE_IN_OTR_JINGLE = true;
+
public static final boolean SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON = false;
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
index 79e4654be..0d202bb92 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
@@ -2,10 +2,12 @@ package eu.siacs.conversations.http;
import android.content.Intent;
import android.net.Uri;
+import android.os.PowerManager;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
@@ -125,6 +127,17 @@ public class HttpDownloadConnection implements Transferable {
mXmppConnectionService.updateConversationUi();
}
+ private void showToastForException(Exception e) {
+ e.printStackTrace();
+ if (e instanceof java.net.UnknownHostException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found);
+ } else if (e instanceof java.net.ConnectException) {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect);
+ } else {
+ mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found);
+ }
+ }
+
private class FileSizeChecker implements Runnable {
private boolean interactive = false;
@@ -146,7 +159,7 @@ public class HttpDownloadConnection implements Transferable {
} catch (IOException e) {
Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage());
if (interactive) {
- mXmppConnectionService.showErrorToastInUi(R.string.file_not_found_on_remote_host);
+ showToastForException(e);
}
cancel();
return;
@@ -163,20 +176,23 @@ public class HttpDownloadConnection implements Transferable {
}
private long retrieveFileSize() throws IOException {
- Log.d(Config.LOGTAG,"retrieve file size. interactive:"+String.valueOf(interactive));
- changeStatus(STATUS_CHECKING);
- HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
- connection.setRequestMethod("HEAD");
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.connect();
- String contentLength = connection.getHeaderField("Content-Length");
- if (contentLength == null) {
- throw new IOException();
- }
try {
+ Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive));
+ changeStatus(STATUS_CHECKING);
+ HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
+ connection.setRequestMethod("HEAD");
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.connect();
+ String contentLength = connection.getHeaderField("Content-Length");
+ connection.disconnect();
+ if (contentLength == null) {
+ throw new IOException();
+ }
return Long.parseLong(contentLength, 10);
+ } catch (IOException e) {
+ throw e;
} catch (NumberFormatException e) {
throw new IOException();
}
@@ -202,37 +218,46 @@ public class HttpDownloadConnection implements Transferable {
updateImageBounds();
finish();
} catch (SSLHandshakeException e) {
- FileBackend.close(os);
changeStatus(STATUS_OFFER);
} catch (IOException e) {
- FileBackend.close(os);
- mXmppConnectionService.showErrorToastInUi(R.string.file_not_found_on_remote_host);
+ if (interactive) {
+ showToastForException(e);
+ }
cancel();
}
}
- private void download() throws IOException {
- HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
- if (connection instanceof HttpsURLConnection) {
- mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
- }
- connection.connect();
- BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
- file.getParentFile().mkdirs();
- file.createNewFile();
- os = AbstractConnectionManager.createOutputStream(file,true);
- long transmitted = 0;
- long expected = file.getExpectedSize();
- int count = -1;
- byte[] buffer = new byte[1024];
- while ((count = is.read(buffer)) != -1) {
- transmitted += count;
- os.write(buffer, 0, count);
- updateProgress((int) ((((double) transmitted) / expected) * 100));
+ private void download() throws IOException {
+ InputStream is = null;
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
+ try {
+ wakeLock.acquire();
+ HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
+ }
+ connection.connect();
+ is = new BufferedInputStream(connection.getInputStream());
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ os = AbstractConnectionManager.createOutputStream(file, true);
+ long transmitted = 0;
+ long expected = file.getExpectedSize();
+ int count = -1;
+ byte[] buffer = new byte[1024];
+ while ((count = is.read(buffer)) != -1) {
+ transmitted += count;
+ os.write(buffer, 0, count);
+ updateProgress((int) ((((double) transmitted) / expected) * 100));
+ }
+ os.flush();
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ FileBackend.close(os);
+ FileBackend.close(is);
+ wakeLock.release();
}
- os.flush();
- os.close();
- is.close();
}
private void updateImageBounds() {
diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
index 768915a94..2e5458423 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
@@ -3,9 +3,11 @@ package eu.siacs.conversations.http;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
+import android.os.PowerManager;
import android.util.Log;
import android.util.Pair;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -103,7 +105,13 @@ public class HttpUploadConnection implements Transferable {
mXmppConnectionService.getRNG().nextBytes(this.key);
this.file.setKeyAndIv(this.key);
}
- Pair<InputStream,Integer> pair = AbstractConnectionManager.createInputStream(file,true);
+ Pair<InputStream,Integer> pair;
+ try {
+ pair = AbstractConnectionManager.createInputStream(file, true);
+ } catch (FileNotFoundException e) {
+ fail();
+ return;
+ }
this.file.setExpectedSize(pair.second);
this.mFileInputStream = pair.first;
Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
@@ -143,7 +151,9 @@ public class HttpUploadConnection implements Transferable {
private void upload() {
OutputStream os = null;
HttpURLConnection connection = null;
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid());
try {
+ wakeLock.acquire();
Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
connection = (HttpURLConnection) mPutUrl.openConnection();
if (connection instanceof HttpsURLConnection) {
@@ -211,6 +221,7 @@ public class HttpUploadConnection implements Transferable {
if (connection != null) {
connection.disconnect();
}
+ wakeLock.release();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 3a8248f16..7fd5d88b8 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -39,6 +39,7 @@ import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExifHelper;
+import eu.siacs.conversations.utils.FileUtils;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class FileBackend {
@@ -126,25 +127,25 @@ public class FileBackend {
return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}
- public String getOriginalPath(Uri uri) {
- String path = null;
- if (uri.getScheme().equals("file")) {
- return uri.getPath();
- } else if (uri.toString().startsWith("content://media/")) {
- String[] projection = {MediaStore.MediaColumns.DATA};
- Cursor metaCursor = mXmppConnectionService.getContentResolver().query(uri,
- projection, null, null, null);
- if (metaCursor != null) {
- try {
- if (metaCursor.moveToFirst()) {
- path = metaCursor.getString(0);
- }
- } finally {
- metaCursor.close();
- }
- }
+ public boolean useImageAsIs(Uri uri) {
+ String path = getOriginalPath(uri);
+ if (path == null) {
+ return false;
}
- return path;
+ Log.d(Config.LOGTAG,"using image as is. path: "+path);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ try {
+ BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri), null, options);
+ return (options.outWidth <= Config.IMAGE_SIZE && options.outHeight <= Config.IMAGE_SIZE && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase()));
+ } catch (FileNotFoundException e) {
+ return false;
+ }
+ }
+
+ public String getOriginalPath(Uri uri) {
+ Log.d(Config.LOGTAG,"get original path for uri: "+uri.toString());
+ return FileUtils.getPath(mXmppConnectionService,uri);
}
public DownloadableFile copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException {
diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
index b7e7c8d3a..5def05dd4 100644
--- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
@@ -1,5 +1,7 @@
package eu.siacs.conversations.services;
+import android.content.Context;
+import android.os.PowerManager;
import android.util.Log;
import android.util.Pair;
@@ -49,17 +51,13 @@ public class AbstractConnectionManager {
}
}
- public static Pair<InputStream,Integer> createInputStream(DownloadableFile file, boolean gcm) {
+ public static Pair<InputStream,Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
FileInputStream is;
int size;
- try {
- is = new FileInputStream(file);
- size = (int) file.getSize();
- if (file.getKey() == null) {
- return new Pair<InputStream,Integer>(is,size);
- }
- } catch (FileNotFoundException e) {
- return null;
+ is = new FileInputStream(file);
+ size = (int) file.getSize();
+ if (file.getKey() == null) {
+ return new Pair<InputStream,Integer>(is,size);
}
try {
if (gcm) {
@@ -72,7 +70,8 @@ public class AbstractConnectionManager {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
Log.d(Config.LOGTAG, "opening encrypted input stream");
- return new Pair<InputStream,Integer>(new CipherInputStream(is, cipher),(size / 16 + 1) * 16);
+ final int s = Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE ? size : (size / 16 + 1) * 16;
+ return new Pair<InputStream,Integer>(new CipherInputStream(is, cipher),s);
}
} catch (InvalidKeyException e) {
return null;
@@ -117,4 +116,9 @@ public class AbstractConnectionManager {
return null;
}
}
+
+ public PowerManager.WakeLock createWakeLock(String name) {
+ PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
+ return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,name);
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index ffe587d69..ed3bb8792 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -404,8 +404,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- public void attachImageToConversation(final Conversation conversation,
- final Uri uri, final UiCallback<Message> callback) {
+ public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ if (getFileBackend().useImageAsIs(uri)) {
+ Log.d(Config.LOGTAG,"using image as is");
+ attachFileToConversation(conversation, uri, callback);
+ return;
+ }
final Message message;
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
@@ -855,7 +859,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
}
- iqPacket.query(Xmlns.ROSTER).setAttribute("ver",account.getRosterVersion());
+ iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
sendIqPacket(account,iqPacket,mIqParser);
}
@@ -1000,6 +1004,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onMessageFound(Message message) {
if (!getFileBackend().isFileAvailable(message)) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
+ final int s = message.getStatus();
+ if(s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
+ markMessage(message,Message.STATUS_SEND_FAILED);
+ }
}
}
});
@@ -1011,7 +1019,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (message != null) {
if (!getFileBackend().isFileAvailable(message)) {
message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED));
- updateConversationUi();
+ final int s = message.getStatus();
+ if(s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) {
+ markMessage(message,Message.STATUS_SEND_FAILED);
+ } else {
+ updateConversationUi();
+ }
}
return;
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 79cb006d1..49878463b 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -333,7 +333,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
mEditMessage.setHint(getString(R.string.send_otr_message));
break;
case Message.ENCRYPTION_AXOLOTL:
- mEditMessage.setHint(getString(R.string.send_axolotl_message));
+ mEditMessage.setHint(getString(R.string.send_omemo_message));
break;
case Message.ENCRYPTION_PGP:
mEditMessage.setHint(getString(R.string.send_pgp_message));
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index 7536ea753..f861e73df 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -577,10 +577,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override
public void onClick(final View v) {
- if (copyTextToClipboard(axolotlFingerprint, R.string.axolotl_fingerprint)) {
+ if (copyTextToClipboard(axolotlFingerprint, R.string.omemo_fingerprint)) {
Toast.makeText(
EditAccountActivity.this,
- R.string.toast_message_axolotl_fingerprint,
+ R.string.toast_message_omemo_fingerprint,
Toast.LENGTH_SHORT).show();
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
index c55518579..cf22416fb 100644
--- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
@@ -111,7 +111,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
}
private void populateView() {
- setTitle(getString(R.string.trust_keys));
+ setTitle(getString(R.string.trust_omemo_fingerprints));
ownKeys.removeAllViews();
foreignKeys.removeAllViews();
boolean hasOwnKeys = false;
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 3a163ba45..7734dc116 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -696,15 +696,15 @@ public abstract class XmppActivity extends Activity {
}
if (showTag) {
- keyType.setText(getString(R.string.axolotl_fingerprint));
+ keyType.setText(getString(R.string.omemo_fingerprint));
} else {
keyType.setVisibility(View.GONE);
}
if (highlight) {
keyType.setTextColor(getResources().getColor(R.color.accent));
- keyType.setText(getString(R.string.axolotl_fingerprint_selected_message));
+ keyType.setText(getString(R.string.omemo_fingerprint_selected_message));
} else {
- keyType.setText(getString(R.string.axolotl_fingerprint));
+ keyType.setText(getString(R.string.omemo_fingerprint));
}
key.setText(CryptoHelper.prettifyFingerprint(identityKey.getFingerprint()));
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 7b4abe48c..323c67d6b 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -303,8 +303,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
final Spannable span = new SpannableString(privateMarker + " "
+ formattedBody);
- span.setSpan(new ForegroundColorSpan(activity
- .getSecondaryTextColor()), 0, privateMarker
+ span.setSpan(new ForegroundColorSpan(getMessageTextColor(type,false)), 0, privateMarker
.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new StyleSpan(Typeface.BOLD), 0,
privateMarker.length(),
@@ -396,10 +395,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(activity.xmppConnectionService
- .getFileBackend().getJingleFileUri(message), "image/*");
- getContext().startActivity(intent);
+ openDownloadable(message);
}
});
viewHolder.image.setOnLongClickListener(openContextMenu);
diff --git a/src/main/java/eu/siacs/conversations/utils/FileUtils.java b/src/main/java/eu/siacs/conversations/utils/FileUtils.java
new file mode 100644
index 000000000..a13300d43
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/utils/FileUtils.java
@@ -0,0 +1,144 @@
+package eu.siacs.conversations.utils;
+
+import android.annotation.SuppressLint;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+public class FileUtils {
+
+ /**
+ * Get a file path from a Uri. This will get the the path for Storage Access
+ * Framework Documents, as well as the _data field for the MediaStore and
+ * other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @author paulburke
+ */
+ @SuppressLint("NewApi")
+ public static String getPath(final Context context, final Uri uri) {
+
+ final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+ // DocumentProvider
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ }
+ // DownloadsProvider
+ else if (isDownloadsDocument(uri)) {
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ }
+ // MediaProvider
+ else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[]{
+ split[1]
+ };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ }
+ // MediaStore (and general)
+ else if ("content".equalsIgnoreCase(uri.getScheme())) {
+ return getDataColumn(context, uri, null, null);
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index bf89ba816..5ac089766 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -81,7 +81,6 @@ public class XmppConnection implements Runnable {
private static final int PACKET_IQ = 0;
private static final int PACKET_MESSAGE = 1;
private static final int PACKET_PRESENCE = 2;
- private final Context applicationContext;
protected Account account;
private final WakeLock wakeLock;
private Socket socket;
@@ -123,7 +122,6 @@ public class XmppConnection implements Runnable {
PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
tagWriter = new TagWriter();
mXmppConnectionService = service;
- applicationContext = service.getApplicationContext();
}
protected void changeStatus(final Account.State nextStatus) {
@@ -531,14 +529,6 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(startTLS);
}
- private SharedPreferences getPreferences() {
- return PreferenceManager.getDefaultSharedPreferences(applicationContext);
- }
-
- private boolean enableLegacySSL() {
- return getPreferences().getBoolean("enable_legacy_ssl", false);
- }
-
private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
tagReader.readTag();
try {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index e9ca66b7c..7b140842a 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -5,6 +5,7 @@ import android.net.Uri;
import android.util.Log;
import android.util.Pair;
+import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
@@ -28,7 +29,6 @@ import eu.siacs.conversations.entities.TransferablePlaceholder;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -97,15 +97,13 @@ public class JingleConnection implements Transferable {
public void onFileTransmitted(DownloadableFile file) {
if (responder.equals(account.getJid())) {
sendSuccess();
+ mXmppConnectionService.getFileBackend().updateFileParams(message);
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ mXmppConnectionService.markMessage(message,Message.STATUS_RECEIVED);
if (acceptedAutomatically) {
message.markUnread();
- JingleConnection.this.mXmppConnectionService
- .getNotificationService().push(message);
+ JingleConnection.this.mXmppConnectionService.getNotificationService().push(message);
}
- mXmppConnectionService.getFileBackend().updateFileParams(message);
- mXmppConnectionService.databaseBackend.createMessage(message);
- mXmppConnectionService.markMessage(message,
- Message.STATUS_RECEIVED);
} else {
if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
file.delete();
@@ -274,7 +272,7 @@ public class JingleConnection implements Transferable {
});
mergeCandidate(candidate);
} else {
- Log.d(Config.LOGTAG,"no primary candidate of our own was found");
+ Log.d(Config.LOGTAG, "no primary candidate of our own was found");
sendInitRequest();
}
}
@@ -393,7 +391,11 @@ public class JingleConnection implements Transferable {
}
}
this.mFileOutputStream = AbstractConnectionManager.createOutputStream(this.file,message.getEncryption() == Message.ENCRYPTION_AXOLOTL);
- this.file.setExpectedSize(size);
+ if (message.getEncryption() == Message.ENCRYPTION_OTR && Config.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE) {
+ this.file.setExpectedSize((size / 16 + 1) * 16);
+ } else {
+ this.file.setExpectedSize(size);
+ }
Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
} else {
this.sendCancel();
@@ -412,26 +414,31 @@ public class JingleConnection implements Transferable {
content.setTransportId(this.transportId);
this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
Pair<InputStream,Integer> pair;
- if (message.getEncryption() == Message.ENCRYPTION_OTR) {
- Conversation conversation = this.message.getConversation();
- if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not set symmetric key");
- cancel();
+ try {
+ if (message.getEncryption() == Message.ENCRYPTION_OTR) {
+ Conversation conversation = this.message.getConversation();
+ if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key");
+ cancel();
+ }
+ this.file.setKeyAndIv(conversation.getSymmetricKey());
+ pair = AbstractConnectionManager.createInputStream(this.file, false);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, true);
+ } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
+ this.file.setKey(mXmppAxolotlMessage.getInnerKey());
+ this.file.setIv(mXmppAxolotlMessage.getIV());
+ pair = AbstractConnectionManager.createInputStream(this.file, true);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false).addChild(mXmppAxolotlMessage.toElement());
+ } else {
+ pair = AbstractConnectionManager.createInputStream(this.file, false);
+ this.file.setExpectedSize(pair.second);
+ content.setFileOffer(this.file, false);
}
- this.file.setKeyAndIv(conversation.getSymmetricKey());
- pair = AbstractConnectionManager.createInputStream(this.file,false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, true);
- } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
- this.file.setKey(mXmppAxolotlMessage.getInnerKey());
- this.file.setIv(mXmppAxolotlMessage.getIV());
- pair = AbstractConnectionManager.createInputStream(this.file,true);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false).addChild(mXmppAxolotlMessage.toElement());
- } else {
- pair = AbstractConnectionManager.createInputStream(this.file,false);
- this.file.setExpectedSize(pair.second);
- content.setFileOffer(this.file, false);
+ } catch (FileNotFoundException e) {
+ cancel();
+ return;
}
this.mFileInputStream = pair.first;
this.transportId = this.mJingleConnectionManager.nextRandomId();
@@ -1012,4 +1019,8 @@ public class JingleConnection implements Transferable {
public int getProgress() {
return this.mProgress;
}
+
+ public AbstractConnectionManager getConnectionManager() {
+ return this.mJingleConnectionManager;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 7545dd646..556395aef 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.xmpp.jingle;
+import android.os.PowerManager;
import android.util.Log;
import java.io.FileNotFoundException;
@@ -96,14 +97,15 @@ public class JingleSocks5Transport extends JingleTransport {
}
- public void send(final DownloadableFile file,
- final OnFileTransmissionStatusChanged callback) {
+ public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() {
@Override
public void run() {
InputStream fileInputStream = null;
+ final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_"+connection.getSessionId());
try {
+ wakeLock.acquire();
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
fileInputStream = connection.getFileInputStream();
@@ -138,6 +140,7 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransferAborted();
} finally {
FileBackend.close(fileInputStream);
+ wakeLock.release();
}
}
}).start();
@@ -150,7 +153,9 @@ public class JingleSocks5Transport extends JingleTransport {
@Override
public void run() {
OutputStream fileOutputStream = null;
+ final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_"+connection.getSessionId());
try {
+ wakeLock.acquire();
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
inputStream.skip(45);
@@ -166,7 +171,7 @@ public class JingleSocks5Transport extends JingleTransport {
double size = file.getExpectedSize();
long remainingSize = file.getExpectedSize();
byte[] buffer = new byte[8192];
- int count = buffer.length;
+ int count;
while (remainingSize > 0) {
count = inputStream.read(buffer);
if (count == -1) {
@@ -194,7 +199,9 @@ public class JingleSocks5Transport extends JingleTransport {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
+ wakeLock.release();
FileBackend.close(fileOutputStream);
+ FileBackend.close(inputStream);
}
}
}).start();
@@ -209,27 +216,9 @@ public class JingleSocks5Transport extends JingleTransport {
}
public void disconnect() {
- if (this.outputStream != null) {
- try {
- this.outputStream.close();
- } catch (IOException e) {
-
- }
- }
- if (this.inputStream != null) {
- try {
- this.inputStream.close();
- } catch (IOException e) {
-
- }
- }
- if (this.socket != null) {
- try {
- this.socket.close();
- } catch (IOException e) {
-
- }
- }
+ FileBackend.close(inputStream);
+ FileBackend.close(outputStream);
+ FileBackend.close(socket);
}
public boolean isEstablished() {
diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml
index 581614c76..4599b3a2f 100644
--- a/src/main/res/layout/activity_edit_account.xml
+++ b/src/main/res/layout/activity_edit_account.xml
@@ -380,7 +380,7 @@
android:layout_height="wrap_content"
android:textColor="@color/black54"
android:textSize="?attr/TextSizeInfo"
- android:text="@string/this_device_axolotl_fingerprint"/>
+ android:text="@string/this_device_omemo_fingerprint"/>
</LinearLayout>
<LinearLayout
@@ -399,16 +399,16 @@
android:padding="@dimen/image_button_padding"
android:src="?attr/icon_copy"
android:visibility="visible"
- android:contentDescription="@string/copy_axolotl_clipboard_description"/>
+ android:contentDescription="@string/copy_omemo_clipboard_description"/>
<ImageButton
- android:id="@+id/action_regenerate_axolotl_key"
+ android:id="@+id/action_regenerate_omemo_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:padding="@dimen/image_button_padding"
android:src="?attr/icon_refresh"
android:visibility="gone"
- android:contentDescription="@string/regenerate_axolotl_key"/>
+ android:contentDescription="@string/regenerate_omemo_key"/>
</LinearLayout>
</RelativeLayout>
@@ -484,4 +484,4 @@
android:textColor="@color/black54" />
</LinearLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/src/main/res/layout/contact_key.xml b/src/main/res/layout/contact_key.xml
index 0f61a15f1..a4fd29e97 100644
--- a/src/main/res/layout/contact_key.xml
+++ b/src/main/res/layout/contact_key.xml
@@ -8,7 +8,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
- android:padding="8dp" >
+ android:paddingTop="8dp"
+ android:paddingLeft="8dp"
+ android:paddingBottom="8dp">
<TextView
android:id="@+id/key"
diff --git a/src/main/res/menu/encryption_choices.xml b/src/main/res/menu/encryption_choices.xml
index 1b3fe76ef..9af2cd346 100644
--- a/src/main/res/menu/encryption_choices.xml
+++ b/src/main/res/menu/encryption_choices.xml
@@ -6,14 +6,14 @@
android:id="@+id/encryption_choice_none"
android:title="@string/encryption_choice_none"/>
<item
+ android:id="@+id/encryption_choice_axolotl"
+ android:title="@string/encryption_choice_omemo"/>
+ <item
android:id="@+id/encryption_choice_otr"
android:title="@string/encryption_choice_otr"/>
<item
android:id="@+id/encryption_choice_pgp"
android:title="@string/encryption_choice_pgp"/>
- <item
- android:id="@+id/encryption_choice_axolotl"
- android:title="@string/encryption_choice_axolotl"/>
</group>
</menu> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 0c7557e4f..b01c62177 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -80,7 +80,7 @@
<string name="choose_presence">Choose presence to contact</string>
<string name="send_plain_text_message">Send plain text message</string>
<string name="send_otr_message">Send OTR encrypted message</string>
- <string name="send_axolotl_message">Send Multi-End encrypted message</string>
+ <string name="send_omemo_message">Send OMEMO encrypted message</string>
<string name="send_pgp_message">Send OpenPGP encrypted message</string>
<string name="your_nick_has_been_changed">Your nickname has been changed</string>
<string name="send_unencrypted">Send unencrypted</string>
@@ -155,7 +155,7 @@
<string name="encryption_choice_none">Plain text</string>
<string name="encryption_choice_otr">OTR</string>
<string name="encryption_choice_pgp">OpenPGP</string>
- <string name="encryption_choice_axolotl">Multi-End</string>
+ <string name="encryption_choice_omemo">OMEMO</string>
<string name="mgmt_account_edit">Edit account</string>
<string name="mgmt_account_delete">Delete account</string>
<string name="mgmt_account_disable">Temporarily disable</string>
@@ -208,14 +208,13 @@
<string name="reception_failed">Reception failed</string>
<string name="your_fingerprint">Your fingerprint</string>
<string name="otr_fingerprint">OTR fingerprint</string>
- <string name="axolotl_fingerprint">Multi-End fingerprint</string>
- <string name="axolotl_fingerprint_selected_message">Multi-End fingerprint of message</string>
- <string name="this_device_axolotl_fingerprint">Own Multi-End fingerprint</string>
+ <string name="omemo_fingerprint">OMEMO fingerprint</string>
+ <string name="omemo_fingerprint_selected_message">OMEMO fingerprint of message</string>
+ <string name="this_device_omemo_fingerprint">Own OMEMO fingerprint</string>
<string name="other_devices">Other devices</string>
- <string name="trust_keys">Trust Multi-End Keys</string>
+ <string name="trust_omemo_fingerprints">Trust OMEMO Fingerprints</string>
<string name="fetching_keys">Fetching keys...</string>
<string name="done">Done</string>
- <string name="axolotl_devicelist">Other own Multi-End Devices</string>
<string name="verify">Verify</string>
<string name="decrypt">Decrypt</string>
<string name="conferences">Conferences</string>
@@ -322,7 +321,7 @@
<string name="pref_conference_name">Conference name</string>
<string name="pref_conference_name_summary">Use room’s subject instead of JID to identify conferences</string>
<string name="toast_message_otr_fingerprint">OTR fingerprint copied to clipboard!</string>
- <string name="toast_message_axolotl_fingerprint">Multi-End fingerprint copied to clipboard!</string>
+ <string name="toast_message_omemo_fingerprint">OMEMO fingerprint copied to clipboard!</string>
<string name="conference_banned">You are banned from this conference</string>
<string name="conference_members_only">This conference is members only</string>
<string name="conference_kicked">You have been kicked from this conference</string>
@@ -389,11 +388,11 @@
<string name="reset">Reset</string>
<string name="account_image_description">Account avatar</string>
<string name="copy_otr_clipboard_description">Copy OTR fingerprint to clipboard</string>
- <string name="copy_axolotl_clipboard_description">Copy Multi-End fingerprint to clipboard</string>
- <string name="regenerate_axolotl_key">Copy Axolotl fingerprint to clipboard</string>
- <string name="wipe_axolotl_pep">Wipe other devices from PEP</string>
+ <string name="copy_omemo_clipboard_description">Copy OMEMO fingerprint to clipboard</string>
+ <string name="regenerate_omemo_key">Regenerate OMEMO key</string>
+ <string name="wipe_omemo_pep">Wipe other devices from PEP</string>
<string name="clear_other_devices">Clear devices</string>
- <string name="clear_other_devices_desc">Are you sure you want to clear all other devices from the Multi-End announcement? The next time your devices connect, they will reannounce themselves, but they might not receive messages sent in the meantime.</string>
+ <string name="clear_other_devices_desc">Are you sure you want to clear all other devices from the OMEMO announcement? The next time your devices connect, they will reannounce themselves, but they might not receive messages sent in the meantime.</string>
<string name="purge_key">Purge key</string>
<string name="purge_key_desc_part1">Are you sure you want to purge this key?</string>
<string name="purge_key_desc_part2">It will irreversibly be considered compromised, and you can never build a session with it again.</string>
@@ -495,7 +494,6 @@
<string name="none">None</string>
<string name="recently_used">Most recently used</string>
<string name="choose_quick_action">Choose quick action</string>
- <string name="file_not_found_on_remote_host">File not found on remote server</string>
<string name="search_for_contacts_or_groups">Search for contacts or groups</string>
<string name="send_private_message">Send private message</string>
<string name="user_has_left_conference">%s has left the conference!</string>
@@ -503,4 +501,7 @@
<string name="username">Username</string>
<string name="username_hint">Username</string>
<string name="invalid_username">This is not a valid username</string>
+ <string name="download_failed_server_not_found">Download failed: Server not found</string>
+ <string name="download_failed_file_not_found">Download failed: File not found</string>
+ <string name="download_failed_could_not_connect">Download failed: Could not connect to host</string>
</resources>