aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack/conversationsplus/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/utils')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/CryptoHelper.java128
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/DNSHelper.java139
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHandler.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHelper.java76
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ExifHelper.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java149
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java22
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/MimeUtils.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/OnPhoneContactsLoadedListener.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/PhoneHelper.java102
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/SSLSocketHelper.java73
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/SerialSingleThreadExecutor.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/SocksSocketFactory.java52
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java15
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java49
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java8
19 files changed, 662 insertions, 177 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
index 660100a3..206ee668 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java
@@ -16,8 +16,8 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import de.thedevstack.android.logcat.Logging;
-import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.xmpp.pep.Avatar;
/**
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/CryptoHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/CryptoHelper.java
index 9599aa37..7e4a4fc5 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/CryptoHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/CryptoHelper.java
@@ -1,20 +1,35 @@
package de.thedevstack.conversationsplus.utils;
-import java.security.SecureRandom;
+import android.os.Bundle;
+import android.util.Pair;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x500.style.IETFUtils;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
+import java.security.MessageDigest;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
import java.text.Normalizer;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Locale;
import de.thedevstack.conversationsplus.Config;
+import de.thedevstack.conversationsplus.R;
+import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
+import de.thedevstack.conversationsplus.xmpp.jid.Jid;
public final class CryptoHelper {
public static final String FILETRANSFER = "?FILETRANSFERv1:";
private final static char[] hexArray = "0123456789abcdef".toCharArray();
- private final static char[] vowels = "aeiou".toCharArray();
- private final static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray();
final public static byte[] ONE = new byte[] { 0, 0, 0, 1 };
public static String bytesToHex(byte[] bytes) {
@@ -48,22 +63,6 @@ public final class CryptoHelper {
return result;
}
- public static String randomMucName(SecureRandom random) {
- return randomWord(3, random) + "." + randomWord(7, random);
- }
-
- private static String randomWord(int lenght, SecureRandom random) {
- StringBuilder builder = new StringBuilder(lenght);
- for (int i = 0; i < lenght; ++i) {
- if (i % 2 == 0) {
- builder.append(consonants[random.nextInt(consonants.length)]);
- } else {
- builder.append(vowels[random.nextInt(vowels.length)]);
- }
- }
- return builder.toString();
- }
-
/**
* Escapes usernames or passwords for SASL.
*/
@@ -96,11 +95,18 @@ public final class CryptoHelper {
} else if (fingerprint.length() < 40) {
return fingerprint;
}
+ StringBuilder builder = new StringBuilder(fingerprint.toLowerCase(Locale.US).replaceAll("\\s", ""));
+ for(int i=8;i<builder.length();i+=9) {
+ builder.insert(i, ' ');
+ }
+ return builder.toString();
+ }
+
+ public static String prettifyFingerprintCert(String fingerprint) {
StringBuilder builder = new StringBuilder(fingerprint);
- builder.insert(8, " ");
- builder.insert(17, " ");
- builder.insert(26, " ");
- builder.insert(35, " ");
+ for(int i=2;i < builder.length(); i+=3) {
+ builder.insert(i,':');
+ }
return builder.toString();
}
@@ -126,4 +132,80 @@ public final class CryptoHelper {
}
}
}
+
+ public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
+ Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
+ List<String> emails = new ArrayList<>();
+ if (alternativeNames != null) {
+ for(List<?> san : alternativeNames) {
+ Integer type = (Integer) san.get(0);
+ if (type == 1) {
+ emails.add((String) san.get(1));
+ }
+ }
+ }
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ if (emails.size() == 0) {
+ emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
+ }
+ String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
+ if (emails.size() >= 1) {
+ return new Pair<>(Jid.fromString(emails.get(0)), name);
+ } else {
+ return null;
+ }
+ }
+
+ public static Bundle extractCertificateInformation(X509Certificate certificate) {
+ Bundle information = new Bundle();
+ try {
+ JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
+ X500Name subject = holder.getSubject();
+ try {
+ information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("subject_o",subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+
+ X500Name issuer = holder.getIssuer();
+ try {
+ information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+ } catch (Exception e) {
+ //ignored
+ }
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] fingerprint = md.digest(certificate.getEncoded());
+ information.putString("sha1", prettifyFingerprintCert(bytesToHex(fingerprint)));
+ } catch (Exception e) {
+
+ }
+ return information;
+ } catch (CertificateEncodingException e) {
+ return information;
+ }
+ }
+
+ public static int encryptionTypeToText(int encryption) {
+ switch (encryption) {
+ case Message.ENCRYPTION_OTR:
+ return R.string.encryption_choice_otr;
+ case Message.ENCRYPTION_AXOLOTL:
+ return R.string.encryption_choice_omemo;
+ case Message.ENCRYPTION_NONE:
+ return R.string.encryption_choice_unencrypted;
+ default:
+ return R.string.encryption_choice_pgp;
+ }
+ }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/DNSHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/DNSHelper.java
index 9481572d..90618292 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/DNSHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/DNSHelper.java
@@ -1,33 +1,50 @@
package de.thedevstack.conversationsplus.utils;
-import de.measite.minidns.Client;
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.Record;
-import de.measite.minidns.Record.TYPE;
-import de.measite.minidns.Record.CLASS;
-import de.measite.minidns.record.SRV;
-import de.measite.minidns.record.Data;
-import de.measite.minidns.util.NameUtil;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.RouteInfo;
+import android.os.Build;
import java.io.IOException;
import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.TreeSet;
import java.util.regex.Pattern;
+import de.measite.minidns.Client;
+import de.measite.minidns.DNSMessage;
+import de.measite.minidns.Record;
+import de.measite.minidns.Record.CLASS;
+import de.measite.minidns.Record.TYPE;
+import de.measite.minidns.record.Data;
+import de.measite.minidns.record.SRV;
+import de.measite.minidns.util.NameUtil;
import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.dto.SrvRecord;
+import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
public class DNSHelper {
private static final String CLIENT_SRV_PREFIX = "_xmpp-client._tcp.";
- private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
- private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
- private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
+ private static final String SECURE_CLIENT_SRV_PREFIX = "_xmpps-client._tcp.";
+ private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
+ private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z");
+ private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
protected static Client client = new Client();
+ static {
+ client.setTimeout(Config.PING_TIMEOUT * 1000);
+ }
+
/**
* Queries the SRV record for the server JID.
* This method uses all available Domain Name Servers.
@@ -36,12 +53,13 @@ public class DNSHelper {
*/
public static final TreeSet<SrvRecord> querySrvRecord(Jid jid) {
String host = jid.getDomainpart();
- String dns[] = client.findDNS();
TreeSet<SrvRecord> result = new TreeSet<>();
- if (dns != null) {
- for (String dnsserver : dns) {
- result = querySrvRecord(host, dnsserver);
+ final List<InetAddress> dnsServers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers() : getDnsServersPreLollipop();
+
+ if (dnsServers != null) {
+ for (InetAddress dnsServer : dnsServers) {
+ result = querySrvRecord(host, dnsServer);
if (!result.isEmpty()) {
break;
}
@@ -51,51 +69,92 @@ public class DNSHelper {
return result;
}
+ @TargetApi(21)
+ private static List<InetAddress> getDnsServers() {
+ List<InetAddress> servers = new ArrayList<>();
+ ConnectivityManager connectivityManager = (ConnectivityManager) ConversationsPlusApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
+ Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
+ if (networks == null) {
+ return getDnsServersPreLollipop();
+ }
+ for(int i = 0; i < networks.length; ++i) {
+ LinkProperties linkProperties = connectivityManager.getLinkProperties(networks[i]);
+ if (linkProperties != null) {
+ if (hasDefaultRoute(linkProperties)) {
+ servers.addAll(0, linkProperties.getDnsServers());
+ } else {
+ servers.addAll(linkProperties.getDnsServers());
+ }
+ }
+ }
+ if (servers.size() > 0) {
+ Logging.d("dns", "used lollipop variant to discover dns servers in " + networks.length + " networks");
+ }
+ return servers.size() > 0 ? servers : getDnsServersPreLollipop();
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private static boolean hasDefaultRoute(LinkProperties linkProperties) {
+ for(RouteInfo route: linkProperties.getRoutes()) {
+ if (route.isDefaultRoute()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static List<InetAddress> getDnsServersPreLollipop() {
+ List<InetAddress> servers = new ArrayList<>();
+ String[] dns = client.findDNS();
+ for(int i = 0; i < dns.length; ++i) {
+ try {
+ servers.add(InetAddress.getByName(dns[i]));
+ } catch (UnknownHostException e) {
+ //ignore
+ }
+ }
+ return servers;
+ }
+
/**
* Queries the SRV record for an host from the given Domain Name Server.
* @param host the host to query for
- * @param dnsserver the DNS to query on
+ * @param dnsServerAddress the DNS to query on
* @return TreeSet with SrvRecords.
*/
- private static final TreeSet<SrvRecord> querySrvRecord(String host, String dnsserver) {
+ private static final TreeSet<SrvRecord> querySrvRecord(String host, InetAddress dnsServerAddress) {
TreeSet<SrvRecord> result = new TreeSet<>();
+ querySrvRecord(host, dnsServerAddress, false, result);
+ querySrvRecord(host, dnsServerAddress, true, result);
+ return result;
+ }
+
+ private static final void querySrvRecord(String host, InetAddress dnsServerAddress, boolean tlsSrvRecord, TreeSet<SrvRecord> result) {
+ String qname = (tlsSrvRecord ? SECURE_CLIENT_SRV_PREFIX : CLIENT_SRV_PREFIX) + host;
+ String dnsServerHostAddress = dnsServerAddress.getHostAddress();
+ Logging.d("dns", "using dns server: " + dnsServerHostAddress + " to look up " + qname);
try {
- InetAddress dnsServerAddress = InetAddress.getByName(dnsserver);
- String qname = CLIENT_SRV_PREFIX + host;
- DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServerAddress.getHostAddress());
+ DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServerHostAddress);
Record[] rrset = message.getAnswers();
for (Record rr : rrset) {
Data d = rr.getPayload();
if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
SRV srv = (SRV) d;
- SrvRecord srvRecord = new SrvRecord(srv.getPriority(), srv.getName(), srv.getPort());
+ SrvRecord srvRecord = new SrvRecord(srv.getPriority(), srv.getName(), srv.getPort(), tlsSrvRecord);
result.add(srvRecord);
}
}
} catch (IOException e) {
- Logging.d("dns", "Error while retrieving SRV record for '" + host + "' from DNS '" + dnsserver + "': " + e.getMessage());
+ Logging.d("dns", "Error while retrieving SRV record '" + qname + "' for '" + host + "' from DNS '" + dnsServerHostAddress + "': " + e.getMessage());
}
- return result;
}
- /**
- * Checks whether the given server is an IP address or not.
- * The following patterns are treated as valid IP addresses:
- * <ul>
- * <li>{@link #PATTERN_IPV4}</li>
- * <li>{@link #PATTERN_IPV6}</li>
- * <li>{@link #PATTERN_IPV6_6HEX4DEC}</li>
- * <li>{@link #PATTERN_IPV6_HEX4DECCOMPRESSED}</li>
- * <li>{@link #PATTERN_IPV6_HEXCOMPRESSED}</li>
- * </ul>
- * @param server the string to check
- * @return <code>true</code> if one of the patterns is matched <code>false</code> otherwise
- */
public static boolean isIp(final String server) {
- return PATTERN_IPV4.matcher(server).matches()
+ return server != null && (
+ PATTERN_IPV4.matcher(server).matches()
|| PATTERN_IPV6.matcher(server).matches()
|| PATTERN_IPV6_6HEX4DEC.matcher(server).matches()
|| PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches()
- || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches();
+ || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches());
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHandler.java b/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHandler.java
index 256287f1..a810b089 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHandler.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHandler.java
@@ -1,5 +1,7 @@
package de.thedevstack.conversationsplus.utils;
+import android.content.Context;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
@@ -8,8 +10,6 @@ import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
-import android.content.Context;
-
public class ExceptionHandler implements UncaughtExceptionHandler {
private UncaughtExceptionHandler defaultHandler;
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHelper.java
index 32f45127..913f2ab5 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ExceptionHelper.java
@@ -1,5 +1,14 @@
package de.thedevstack.conversationsplus.utils;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.text.format.DateUtils;
+
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
@@ -7,27 +16,17 @@ import java.io.InputStreamReader;
import java.util.List;
import de.thedevstack.android.logcat.Logging;
-import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
+import de.thedevstack.conversationsplus.ui.ConversationActivity;
import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.content.DialogInterface.OnClickListener;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.preference.PreferenceManager;
-import android.text.format.DateUtils;
-
public class ExceptionHelper {
public static void init(Context context) {
if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) {
@@ -36,12 +35,11 @@ public class ExceptionHelper {
}
}
- public static void checkForCrash(final Context context,
- final XmppConnectionService service) {
+ public static boolean checkForCrash(final ConversationActivity activity, final XmppConnectionService service) {
try {
boolean neverSend = ConversationsPlusPreferences.neverSend();
if (neverSend) {
- return;
+ return false;
}
List<Account> accounts = service.getAccounts();
Account account = null;
@@ -52,24 +50,25 @@ public class ExceptionHelper {
}
}
if (account == null) {
- return;
+ return false;
}
final Account finalAccount = account;
- FileInputStream file = context.openFileInput("stacktrace.txt");
+ FileInputStream file = activity.openFileInput("stacktrace.txt");
InputStreamReader inputStreamReader = new InputStreamReader(file);
BufferedReader stacktrace = new BufferedReader(inputStreamReader);
final StringBuilder report = new StringBuilder();
- PackageManager pm = context.getPackageManager();
+ PackageManager pm = activity.getPackageManager();
PackageInfo packageInfo = null;
try {
- packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
+ packageInfo = pm.getPackageInfo(activity.getPackageName(), 0);
report.append("Version: " + packageInfo.versionName + '\n');
report.append("Last Update: "
- + DateUtils.formatDateTime(context,
- packageInfo.lastUpdateTime,
- DateUtils.FORMAT_SHOW_TIME
- | DateUtils.FORMAT_SHOW_DATE) + '\n');
+ + DateUtils.formatDateTime(activity,
+ packageInfo.lastUpdateTime,
+ DateUtils.FORMAT_SHOW_TIME
+ | DateUtils.FORMAT_SHOW_DATE) + '\n');
} catch (NameNotFoundException e) {
+ return false;
}
String line;
while ((line = stacktrace.readLine()) != null) {
@@ -77,11 +76,11 @@ public class ExceptionHelper {
report.append('\n');
}
file.close();
- context.deleteFile("stacktrace.txt");
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(context.getString(R.string.crash_report_title));
- builder.setMessage(context.getText(R.string.crash_report_message));
- builder.setPositiveButton(context.getText(R.string.send_now),
+ activity.deleteFile("stacktrace.txt");
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(activity.getString(R.string.crash_report_title));
+ builder.setMessage(activity.getText(R.string.crash_report_message));
+ builder.setPositiveButton(activity.getText(R.string.send_now),
new OnClickListener() {
@Override
@@ -90,18 +89,18 @@ public class ExceptionHelper {
Logging.d(Config.LOGTAG, "using account="
+ finalAccount.getJid().toBareJid()
+ " to send in stack trace");
- Conversation conversation = null;
- try {
- conversation = service.findOrCreateConversation(finalAccount,
- Jid.fromString(context.getString(R.string.cplus_bugreport_jabberid)), false);
- } catch (final InvalidJidException ignored) {
- }
- Message message = new Message(conversation, report
+ Conversation conversation = null;
+ try {
+ conversation = service.findOrCreateConversation(finalAccount,
+ Jid.fromString(activity.getString(R.string.cplus_bugreport_jabberid)), false);
+ } catch (final InvalidJidException ignored) {
+ }
+ Message message = new Message(conversation, report
.toString(), Message.ENCRYPTION_NONE);
service.sendMessage(message);
}
});
- builder.setNegativeButton(context.getText(R.string.send_never),
+ builder.setNegativeButton(activity.getText(R.string.send_never),
new OnClickListener() {
@Override
@@ -110,8 +109,9 @@ public class ExceptionHelper {
}
});
builder.create().show();
+ return true;
} catch (final IOException ignored) {
- }
-
+ return false;
+ }
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ExifHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/ExifHelper.java
index 576698ce..031bdfa5 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/ExifHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ExifHelper.java
@@ -16,11 +16,11 @@
package de.thedevstack.conversationsplus.utils;
-import de.thedevstack.android.logcat.Logging;
-
import java.io.IOException;
import java.io.InputStream;
+import de.thedevstack.android.logcat.Logging;
+
public class ExifHelper {
private static final String TAG = "CameraExif";
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
new file mode 100644
index 00000000..2cf5679b
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java
@@ -0,0 +1,149 @@
+package de.thedevstack.conversationsplus.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) {
+ if (uri == null) {
+ return null;
+ }
+
+ 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);
+ }
+ } catch(Exception e) {
+ return null;
+ } 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/de/thedevstack/conversationsplus/utils/ImageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
index 9250c432..bf79d7e0 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java
@@ -11,16 +11,14 @@ import android.util.LruCache;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.exceptions.ImageResizeException;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Message;
-import de.thedevstack.conversationsplus.exceptions.FileCopyException;
-import de.thedevstack.conversationsplus.exceptions.ImageResizeException;
import de.thedevstack.conversationsplus.persistance.FileBackend;
/**
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
index c04ebdb8..7afc3fa4 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java
@@ -3,6 +3,8 @@ package de.thedevstack.conversationsplus.utils;
import android.graphics.BitmapFactory;
import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import de.thedevstack.conversationsplus.entities.DownloadableFile;
import de.thedevstack.conversationsplus.entities.Message;
@@ -12,6 +14,26 @@ import de.thedevstack.conversationsplus.persistance.FileBackend;
* Created by tzur on 15.12.2015.
*/
public final class MessageUtil {
+ public static boolean wasHighlightedOrPrivate(final Message message) {
+ final String nick = message.getConversation().getMucOptions().getActualNick();
+ final Pattern highlight = generateNickHighlightPattern(nick);
+ if (message.getBody() == null || nick == null) {
+ return false;
+ }
+ final Matcher m = highlight.matcher(message.getBody());
+ return (m.find() || message.getType() == Message.TYPE_PRIVATE);
+ }
+
+ private static Pattern generateNickHighlightPattern(final String nick) {
+ // We expect a word boundary, i.e. space or start of string, followed by
+ // the
+ // nick (matched in case-insensitive manner), followed by optional
+ // punctuation (for example "bob: i disagree" or "how are you alice?"),
+ // followed by another word boundary.
+ return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b",
+ Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
+ }
+
public static void updateMessageWithImageDetails(Message message, String filePath, long size, int imageWidth, int imageHeight) {
message.setRelativeFilePath(filePath);
MessageUtil.updateMessageBodyWithImageParams(message, size, imageWidth, imageHeight);
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MimeUtils.java b/src/main/java/de/thedevstack/conversationsplus/utils/MimeUtils.java
index c1c40f92..fdf55d2d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/MimeUtils.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/MimeUtils.java
@@ -458,7 +458,7 @@ public final class MimeUtils {
if (extension == null || extension.isEmpty()) {
return null;
}
- return extensionToMimeTypeMap.get(extension);
+ return extensionToMimeTypeMap.get(extension.toLowerCase());
}
/**
* Returns true if the given extension has a registered MIME type.
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/OnPhoneContactsLoadedListener.java b/src/main/java/de/thedevstack/conversationsplus/utils/OnPhoneContactsLoadedListener.java
index e701d62b..eecee159 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/OnPhoneContactsLoadedListener.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/OnPhoneContactsLoadedListener.java
@@ -1,9 +1,9 @@
package de.thedevstack.conversationsplus.utils;
-import java.util.List;
-
import android.os.Bundle;
+import java.util.List;
+
public interface OnPhoneContactsLoadedListener {
public void onPhoneContactsLoaded(List<Bundle> phoneContacts);
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/PhoneHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/PhoneHelper.java
index d1f598c0..86a67661 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/PhoneHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/PhoneHelper.java
@@ -1,8 +1,6 @@
package de.thedevstack.conversationsplus.utils;
-import java.util.List;
-import java.util.concurrent.RejectedExecutionException;
-
+import android.Manifest;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
@@ -10,20 +8,27 @@ import android.content.Loader.OnLoadCompleteListener;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Profile;
-import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
public class PhoneHelper {
- public static void loadPhoneContacts(Context context,final List<Bundle> phoneContacts, final OnPhoneContactsLoadedListener listener) {
- final String[] PROJECTION = new String[] { ContactsContract.Data._ID,
+ public static void loadPhoneContacts(Context context, final List<Bundle> phoneContacts, final OnPhoneContactsLoadedListener listener) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ listener.onPhoneContactsLoaded(phoneContacts);
+ return;
+ }
+ final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.PHOTO_URI,
ContactsContract.Data.LOOKUP_KEY,
- ContactsContract.CommonDataKinds.Im.DATA };
+ ContactsContract.CommonDataKinds.Im.DATA};
final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
+ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
@@ -31,39 +36,39 @@ public class PhoneHelper {
+ "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
+ "\")";
- CursorLoader mCursorLoader = new CursorLoader(context,
+ CursorLoader mCursorLoader = new NotThrowCursorLoader(context,
ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null,
null);
mCursorLoader.registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) {
- if (cursor == null) {
- return;
- }
- while (cursor.moveToNext()) {
- Bundle contact = new Bundle();
- contact.putInt("phoneid", cursor.getInt(cursor
- .getColumnIndex(ContactsContract.Data._ID)));
- contact.putString(
- "displayname",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
- contact.putString("photouri", cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
- contact.putString("lookup", cursor.getString(cursor
- .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
-
- contact.putString(
- "jid",
- cursor.getString(cursor
- .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
- phoneContacts.add(contact);
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Bundle contact = new Bundle();
+ contact.putInt("phoneid", cursor.getInt(cursor
+ .getColumnIndex(ContactsContract.Data._ID)));
+ contact.putString(
+ "displayname",
+ cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.DISPLAY_NAME)));
+ contact.putString("photouri", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.PHOTO_URI)));
+ contact.putString("lookup", cursor.getString(cursor
+ .getColumnIndex(ContactsContract.Data.LOOKUP_KEY)));
+
+ contact.putString(
+ "jid",
+ cursor.getString(cursor
+ .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
+ phoneContacts.add(contact);
+ }
+ cursor.close();
}
+
if (listener != null) {
listener.onPhoneContactsLoaded(phoneContacts);
}
- cursor.close();
}
});
try {
@@ -75,8 +80,30 @@ public class PhoneHelper {
}
}
+ private static class NotThrowCursorLoader extends CursorLoader {
+
+ public NotThrowCursorLoader(Context c, Uri u, String[] p, String s, String[] sa, String so) {
+ super(c, u, p, s, sa, so);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+
+ try {
+ return (super.loadInBackground());
+ } catch (SecurityException e) {
+ return(null);
+ }
+ }
+
+ }
+
public static Uri getSefliUri(Context context) {
- String[] mProjection = new String[] { Profile._ID, Profile.PHOTO_URI };
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ String[] mProjection = new String[]{Profile._ID, Profile.PHOTO_URI};
Cursor mProfileCursor = context.getContentResolver().query(
Profile.CONTENT_URI, mProjection, null, null, null);
@@ -93,4 +120,17 @@ public class PhoneHelper {
}
}
}
+
+ public static String getVersionName(Context context) {
+ final String packageName = context == null ? null : context.getPackageName();
+ if (packageName != null) {
+ try {
+ return context.getPackageManager().getPackageInfo(packageName, 0).versionName;
+ } catch (final PackageManager.NameNotFoundException | RuntimeException e) {
+ return "unknown";
+ }
+ } else {
+ return "unknown";
+ }
+ }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/SSLSocketHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/SSLSocketHelper.java
new file mode 100644
index 00000000..1842e897
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/SSLSocketHelper.java
@@ -0,0 +1,73 @@
+package de.thedevstack.conversationsplus.utils;
+
+import android.os.Build;
+
+import java.lang.reflect.Method;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+public class SSLSocketHelper {
+
+ public static void setSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
+ final String[] supportProtocols;
+ final Collection<String> supportedProtocols = new LinkedList<>(
+ Arrays.asList(sslSocket.getSupportedProtocols()));
+ supportedProtocols.remove("SSLv3");
+ supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
+
+ sslSocket.setEnabledProtocols(supportProtocols);
+
+ final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
+ sslSocket.getSupportedCipherSuites());
+ if (cipherSuites.length > 0) {
+ sslSocket.setEnabledCipherSuites(cipherSuites);
+ }
+ }
+
+ public static void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
+ if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ ((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
+ } else {
+ try {
+ socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
+ } catch (Throwable e) {
+ // ignore any error, we just can't set the hostname...
+ }
+ }
+ }
+
+ public static void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
+ try {
+ if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ // can't call directly because of @hide?
+ //((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
+ android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
+ } else {
+ final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
+ // the concatenation of 8-bit, length prefixed protocol names, just one in our case...
+ // http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
+ final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
+ final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
+ lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
+ System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
+ method.invoke(socket, new Object[]{lengthPrefixedProtocols});
+ }
+ } catch (Throwable e) {
+ // ignore any error, we just can't set the alpn protocol...
+ }
+ }
+
+ public static SSLContext getSSLContext() throws NoSuchAlgorithmException {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return SSLContext.getInstance("TLSv1.2");
+ } else {
+ return SSLContext.getInstance("TLS");
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/SerialSingleThreadExecutor.java b/src/main/java/de/thedevstack/conversationsplus/utils/SerialSingleThreadExecutor.java
index 264645c6..4871af43 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/SerialSingleThreadExecutor.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/SerialSingleThreadExecutor.java
@@ -31,4 +31,4 @@ public class SerialSingleThreadExecutor implements Executor {
executor.execute(active);
}
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/SocksSocketFactory.java b/src/main/java/de/thedevstack/conversationsplus/utils/SocksSocketFactory.java
new file mode 100644
index 00000000..940399c3
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/SocksSocketFactory.java
@@ -0,0 +1,52 @@
+package de.thedevstack.conversationsplus.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+
+import de.thedevstack.conversationsplus.Config;
+
+public class SocksSocketFactory {
+
+ public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
+ InputStream proxyIs = socket.getInputStream();
+ OutputStream proxyOs = socket.getOutputStream();
+ proxyOs.write(new byte[]{0x05, 0x01, 0x00});
+ byte[] response = new byte[2];
+ proxyIs.read(response);
+ byte[] dest = destination.getBytes();
+ ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
+ request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
+ request.put((byte) dest.length);
+ request.put(dest);
+ request.putShort((short) port);
+ proxyOs.write(request.array());
+ response = new byte[7 + dest.length];
+ proxyIs.read(response);
+ if (response[1] != 0x00) {
+ throw new SocksConnectionException();
+ }
+ }
+
+ public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
+ Socket socket = new Socket();
+ try {
+ socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new SocksProxyNotFoundException();
+ }
+ createSocksConnection(socket, destination, port);
+ return socket;
+ }
+
+ static class SocksConnectionException extends IOException {
+
+ }
+
+ public static class SocksProxyNotFoundException extends IOException {
+
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java
index 64f46314..729bdf11 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java
@@ -6,6 +6,7 @@ import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.net.Socket;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
@@ -40,6 +41,20 @@ public final class StreamUtil {
}
/**
+ * Closes a socket.
+ * IOException is silently ignored.
+ * @param socket the socket to close
+ */
+ public static void close(Socket socket) {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
* Avoid instantiation of util class.
*/
private StreamUtil() {
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java
index 532ff1cb..e1915e99 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java
@@ -1,44 +1,27 @@
package de.thedevstack.conversationsplus.utils;
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.util.Pair;
+
import java.math.BigDecimal;
-import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.net.URLConnection;
import java.util.Calendar;
import java.util.Date;
-import java.util.HashMap;
import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Contact;
import de.thedevstack.conversationsplus.entities.Conversation;
-import de.thedevstack.conversationsplus.entities.Presences;
-import de.thedevstack.conversationsplus.entities.Transferable;
import de.thedevstack.conversationsplus.entities.Message;
+import de.thedevstack.conversationsplus.entities.Presence;
+import de.thedevstack.conversationsplus.entities.Transferable;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
-import android.content.Context;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.Spannable.Factory;
-import android.text.style.ImageSpan;
-import android.text.Spannable;
-import android.util.Pair;
-
public class UIHelper {
- private static String BLACK_HEART_SUIT = "\u2665";
- private static String HEAVY_BLACK_HEART_SUIT = "\u2764";
- private static String WHITE_HEART_SUIT = "\u2661";
-
- public static final ArrayList<String> HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT,HEAVY_BLACK_HEART_SUIT,WHITE_HEART_SUIT));
-
private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList(
"where are you", //en
"where are you now", //en
@@ -144,7 +127,7 @@ public class UIHelper {
}
public static int getColorForName(String name) {
- if (name.isEmpty()) {
+ if (name == null || name.isEmpty()) {
return 0xFF202020;
}
int colors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5,
@@ -184,7 +167,9 @@ public class UIHelper {
return new Pair<>("",false);
}
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- return new Pair<>(context.getString(R.string.encrypted_message_received),true);
+ return new Pair<>(context.getString(R.string.pgp_message),true);
+ } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
+ return new Pair<>(context.getString(R.string.decryption_failed), true);
} else if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
return new Pair<>(context.getString(R.string.received_x_file,
@@ -254,15 +239,15 @@ public class UIHelper {
}
}
- public static String getStatusColor(int status) {
+ public static String getStatusColor(Presence.Status status) {
switch (status) {
- case Presences.ONLINE:
- case Presences.CHAT:
+ case ONLINE:
+ case CHAT:
return "#259B23";
- case Presences.AWAY:
- case Presences.XA:
+ case AWAY:
+ case XA:
return "#FF9800";
- case Presences.DND:
+ case DND:
return "#E51C23";
}
return "#CCCCCC";
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java b/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java
index 85f99c30..f937e35f 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/Xmlns.java
@@ -1,9 +1,11 @@
package de.thedevstack.conversationsplus.utils;
+import de.thedevstack.conversationsplus.Config;
+
public final class Xmlns {
public static final String BLOCKING = "urn:xmpp:blocking";
public static final String ROSTER = "jabber:iq:roster";
public static final String REGISTER = "jabber:iq:register";
public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams";
- public static final String HTTP_UPLOAD = "urn:xmpp:http:upload";
+ public static final String HTTP_UPLOAD = Config.LEGACY_NAMESPACE_HTTP_UPLOAD ? "eu:siacs:conversations:http:upload" : "urn:xmpp:http:upload";
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
index eb3c5da4..11d05832 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
@@ -4,6 +4,7 @@ import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
import de.thedevstack.conversationsplus.xmpp.XmppConnection;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.MessagePacket;
import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket;
/**
@@ -23,4 +24,11 @@ public class XmppSendUtil {
connection.sendPresencePacket(packet);
}
}
+
+ public static void sendMessagePacket(Account account, MessagePacket packet) {
+ XmppConnection connection = account.getXmppConnection();
+ if (connection != null) {
+ connection.sendMessagePacket(packet);
+ }
+ }
}