diff options
Diffstat (limited to 'src/main/java/eu/siacs/conversations/utils')
6 files changed, 146 insertions, 112 deletions
diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index 47595c6e..b4a6e65c 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,20 +1,14 @@ package eu.siacs.conversations.utils; -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; - -import eu.siacs.conversations.entities.Account; -import android.util.Base64; +import java.text.Normalizer; public class CryptoHelper { public static final String FILETRANSFER = "?FILETRANSFERv1:"; final protected static char[] hexArray = "0123456789abcdef".toCharArray(); final protected static char[] vowels = "aeiou".toCharArray(); - final protected static char[] consonants = "bcdfghjklmnpqrstvwxyz" - .toCharArray(); + final protected static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray(); + final public static byte[] ONE = new byte[] { 0, 0, 0, 1 }; public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; @@ -36,64 +30,17 @@ public class CryptoHelper { return array; } - public static String saslPlain(String username, String password) { - String sasl = '\u0000' + username + '\u0000' + password; - return Base64.encodeToString(sasl.getBytes(Charset.defaultCharset()), - Base64.NO_WRAP); + public static String hexToString(final String hexString) { + return new String(hexToBytes(hexString)); } - private static byte[] concatenateByteArrays(byte[] a, byte[] b) { + public static byte[] concatenateByteArrays(byte[] a, byte[] b) { byte[] result = new byte[a.length + b.length]; System.arraycopy(a, 0, result, 0, a.length); System.arraycopy(b, 0, result, a.length, b.length); return result; } - public static String saslDigestMd5(Account account, String challenge, - SecureRandom random) { - try { - String[] challengeParts = new String(Base64.decode(challenge, - Base64.DEFAULT)).split(","); - String nonce = ""; - for (int i = 0; i < challengeParts.length; ++i) { - String[] parts = challengeParts[i].split("="); - if (parts[0].equals("nonce")) { - nonce = parts[1].replace("\"", ""); - } else if (parts[0].equals("rspauth")) { - return null; - } - } - String digestUri = "xmpp/" + account.getServer(); - String nonceCount = "00000001"; - String x = account.getUsername() + ":" + account.getServer() + ":" - + account.getPassword(); - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] y = md.digest(x.getBytes(Charset.defaultCharset())); - String cNonce = new BigInteger(100, random).toString(32); - byte[] a1 = concatenateByteArrays(y, - (":" + nonce + ":" + cNonce).getBytes(Charset - .defaultCharset())); - String a2 = "AUTHENTICATE:" + digestUri; - String ha1 = bytesToHex(md.digest(a1)); - String ha2 = bytesToHex(md.digest(a2.getBytes(Charset - .defaultCharset()))); - String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce - + ":auth:" + ha2; - String response = bytesToHex(md.digest(kd.getBytes(Charset - .defaultCharset()))); - String saslString = "username=\"" + account.getUsername() - + "\",realm=\"" + account.getServer() + "\",nonce=\"" - + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount - + ",qop=auth,digest-uri=\"" + digestUri + "\",response=" - + response + ",charset=utf-8"; - return Base64.encodeToString( - saslString.getBytes(Charset.defaultCharset()), - Base64.NO_WRAP); - } catch (NoSuchAlgorithmException e) { - return null; - } - } - public static String randomMucName(SecureRandom random) { return randomWord(3, random) + "." + randomWord(7, random); } @@ -109,4 +56,39 @@ public class CryptoHelper { } return builder.toString(); } + + /** + * Escapes usernames or passwords for SASL. + */ + public static String saslEscape(final String s) { + final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1)); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case ',': + sb.append("=2C"); + break; + case '=': + sb.append("=3D"); + break; + default: + sb.append(c); + break; + } + } + return sb.toString(); + } + + public static String saslPrep(final String s) { + return saslEscape(Normalizer.normalize(s, Normalizer.Form.NFKC)); + } + + public static String prettifyFingerprint(String fingerprint) { + StringBuilder builder = new StringBuilder(fingerprint); + builder.insert(8, " "); + builder.insert(17, " "); + builder.insert(26, " "); + builder.insert(35, " "); + return builder.toString(); + } } diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index f101e883..8c1a8dea 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -11,6 +11,7 @@ import de.measite.minidns.record.AAAA; import de.measite.minidns.record.Data; import de.measite.minidns.util.NameUtil; import eu.siacs.conversations.Config; +import eu.siacs.conversations.xmpp.jid.Jid; import java.io.IOException; import java.net.InetAddress; @@ -26,7 +27,8 @@ import android.util.Log; public class DNSHelper { protected static Client client = new Client(); - public static Bundle getSRVRecord(String host) throws IOException { + public static Bundle getSRVRecord(final Jid jid) throws IOException { + final String host = jid.getDomainpart(); String dns[] = client.findDNS(); if (dns != null) { @@ -62,9 +64,9 @@ public class DNSHelper { // a random order respecting the weight, and dump that priority by // priority - TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<Integer, ArrayList<SRV>>(); - TreeMap<String, ArrayList<String>> ips4 = new TreeMap<String, ArrayList<String>>(); - TreeMap<String, ArrayList<String>> ips6 = new TreeMap<String, ArrayList<String>>(); + TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<>(); + TreeMap<String, ArrayList<String>> ips4 = new TreeMap<>(); + TreeMap<String, ArrayList<String>> ips6 = new TreeMap<>(); for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) { @@ -97,7 +99,7 @@ public class DNSHelper { } Random rnd = new Random(); - ArrayList<SRV> result = new ArrayList<SRV>( + ArrayList<SRV> result = new ArrayList<>( priorities.size() * 2 + 1); for (ArrayList<SRV> s : priorities.values()) { @@ -136,7 +138,7 @@ public class DNSHelper { bundle.putString("error", "nosrv"); return bundle; } - ArrayList<Bundle> values = new ArrayList<Bundle>(); + ArrayList<Bundle> values = new ArrayList<>(); for (SRV srv : result) { Bundle namePort = new Bundle(); namePort.putString("name", srv.getName()); diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java index 88fa18ff..0ad57fe2 100644 --- a/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java +++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHandler.java @@ -31,6 +31,8 @@ public class ExceptionHandler implements UncaughtExceptionHandler { OutputStream os = context.openFileOutput("stacktrace.txt", Context.MODE_PRIVATE); os.write(stacktrace.getBytes()); + os.flush(); + os.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java index b5fc88bd..6f3152ca 100644 --- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java @@ -13,6 +13,9 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -87,12 +90,15 @@ public class ExceptionHelper { public void onClick(DialogInterface dialog, int which) { Log.d(Config.LOGTAG, "using account=" - + finalAccount.getJid() + + finalAccount.getJid().toBareJid() + " to send in stack trace"); - Conversation conversation = service - .findOrCreateConversation(finalAccount, - "bugs@siacs.eu", false); - Message message = new Message(conversation, report + Conversation conversation = null; + try { + conversation = service.findOrCreateConversation(finalAccount, + Jid.fromString("bugs@siacs.eu"), false); + } catch (final InvalidJidException ignored) { + } + Message message = new Message(conversation, report .toString(), Message.ENCRYPTION_NONE); service.sendMessage(message); } @@ -103,15 +109,12 @@ public class ExceptionHelper { @Override public void onClick(DialogInterface dialog, int which) { preferences.edit().putBoolean("never_send", true) - .commit(); + .apply(); } }); builder.create().show(); - } catch (FileNotFoundException e) { - return; - } catch (IOException e) { - return; - } + } catch (final IOException ignored) { + } } } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 5141c83c..c4832d60 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -110,7 +110,7 @@ public class UIHelper { List<Account> accounts) { NotificationManager mNotificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); - List<Account> accountsWproblems = new ArrayList<Account>(); + List<Account> accountsWproblems = new ArrayList<>(); for (Account account : accounts) { if (account.hasErrorStatus()) { accountsWproblems.add(account); @@ -124,7 +124,7 @@ public class UIHelper { } else if (accountsWproblems.size() == 1) { mBuilder.setContentTitle(context .getString(R.string.problem_connecting_to_account)); - mBuilder.setContentText(accountsWproblems.get(0).getJid()); + mBuilder.setContentText(accountsWproblems.get(0).getJid().toBareJid().toString()); } else { mBuilder.setContentTitle(context .getString(R.string.problem_connecting_to_accounts)); @@ -148,40 +148,6 @@ public class UIHelper { mNotificationManager.notify(1111, notification); } - @SuppressLint("InflateParams") - public static AlertDialog getVerifyFingerprintDialog( - final ConversationActivity activity, - final Conversation conversation, final View msg) { - final Contact contact = conversation.getContact(); - final Account account = conversation.getAccount(); - - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("Verify fingerprint"); - LayoutInflater inflater = activity.getLayoutInflater(); - View view = inflater.inflate(R.layout.dialog_verify_otr, null); - TextView jid = (TextView) view.findViewById(R.id.verify_otr_jid); - TextView fingerprint = (TextView) view - .findViewById(R.id.verify_otr_fingerprint); - TextView yourprint = (TextView) view - .findViewById(R.id.verify_otr_yourprint); - - jid.setText(contact.getJid()); - fingerprint.setText(conversation.getOtrFingerprint()); - yourprint.setText(account.getOtrFingerprint()); - builder.setNegativeButton("Cancel", null); - builder.setPositiveButton("Verify", new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - contact.addOtrFingerprint(conversation.getOtrFingerprint()); - msg.setVisibility(View.GONE); - activity.xmppConnectionService.syncRosterToDisk(account); - } - }); - builder.setView(view); - return builder.create(); - } - private final static class EmoticonPattern { Pattern pattern; String replacement; diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java new file mode 100644 index 00000000..09abd049 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java @@ -0,0 +1,79 @@ +package eu.siacs.conversations.utils; + +import android.net.Uri; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +import eu.siacs.conversations.utils.CryptoHelper; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + +public class XmppUri { + + private String jid; + private boolean muc; + private String fingerprint; + + public XmppUri(String uri) { + try { + parse(Uri.parse(uri)); + } catch (IllegalArgumentException e) { + jid = null; + } + } + + public XmppUri(Uri uri) { + parse(uri); + } + + protected void parse(Uri uri) { + String scheme = uri.getScheme(); + if ("xmpp".equals(scheme)) { + // sample: xmpp:jid@foo.com + muc = "join".equalsIgnoreCase(uri.getQuery()); + if (uri.getAuthority() != null) { + jid = uri.getAuthority(); + } else { + jid = uri.getSchemeSpecificPart().split("\\?")[0]; + } + fingerprint = parseFingerprint(uri.getQuery()); + } else if ("imto".equals(scheme)) { + // sample: imto://xmpp/jid@foo.com + try { + jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1]; + } catch (final UnsupportedEncodingException ignored) { + } + } + } + + protected String parseFingerprint(String query) { + if (query == null) { + return null; + } else { + final String NEEDLE = "otr-fingerprint="; + int index = query.indexOf(NEEDLE); + if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) { + return CryptoHelper.prettifyFingerprint(query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40)); + } else { + return null; + } + } + } + + public Jid getJid() { + try { + return Jid.fromString(this.jid); + } catch (InvalidJidException e) { + return null; + } + } + + public String getFingerprint() { + return this.fingerprint; + } + + public boolean isMuc() { + return this.muc; + } +} |