From b23cb5a9e43d804551dc18f399ffbec991479ee6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 9 Oct 2015 13:37:08 +0200 Subject: initial UI work to allow setting up accounts from certifcates --- .../eu/siacs/conversations/entities/Account.java | 16 ++-- .../services/XmppConnectionService.java | 85 ++++++++++++++++++---- .../conversations/ui/EditAccountActivity.java | 46 ++++++------ .../conversations/ui/ManageAccountActivity.java | 38 +++++++++- .../eu/siacs/conversations/ui/XmppActivity.java | 5 ++ src/main/res/menu/manageaccounts.xml | 6 ++ src/main/res/values/strings.xml | 3 + 7 files changed, 155 insertions(+), 44 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 002c276e2..0eb38c9fd 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -194,18 +194,14 @@ public class Account extends AbstractEntity { return jid.getLocalpart(); } - public void setUsername(final String username) throws InvalidJidException { - jid = Jid.fromParts(username, jid.getDomainpart(), jid.getResourcepart()); + public void setJid(final Jid jid) { + this.jid = jid; } public Jid getServer() { return jid.toDomainJid(); } - public void setServer(final String server) throws InvalidJidException { - jid = Jid.fromParts(jid.getLocalpart(), server, jid.getResourcepart()); - } - public String getPassword() { return password; } @@ -272,6 +268,14 @@ public class Account extends AbstractEntity { } } + public boolean setPrivateKeyAlias(String alias) { + return setKey("private_key_alias", alias); + } + + public String getPrivateKeyAlias() { + return getKey("private_key_alias"); + } + @Override public ContentValues getContentValues() { final ContentValues values = new ContentValues(); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ddc40d34e..4d0228e94 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -25,6 +25,8 @@ import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.ContactsContract; +import android.security.KeyChain; +import android.security.KeyChainException; import android.util.Log; import android.util.LruCache; @@ -34,11 +36,22 @@ import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; +import org.bouncycastle.asn1.x500.RDN; +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 org.bouncycastle.jce.PrincipalUtil; +import org.bouncycastle.jce.X509Principal; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.math.BigInteger; +import java.security.PrivateKey; import java.security.SecureRandom; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1285,6 +1298,43 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa updateAccountUi(); } + public void createAccountFromKey(final String alias, final OnAccountCreated callback) { + new Thread(new Runnable() { + @Override + public void run() { + try { + X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); + PrivateKey key = KeyChain.getPrivateKey(XmppConnectionService.this, alias); + X500Name x500name = new JcaX509CertificateHolder(chain[0]).getSubject(); + String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()); + String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()); + Jid jid = Jid.fromString(email); + if (findAccountByJid(jid) == null) { + Account account = new Account(jid, ""); + account.setPrivateKeyAlias(alias); + account.setOption(Account.OPTION_DISABLED, true); + createAccount(account); + callback.onAccountCreated(account); + } else { + callback.informUser(R.string.account_already_exists); + } + } catch (KeyChainException e) { + callback.informUser(R.string.unable_to_parse_certificate); + } catch (InterruptedException e) { + callback.informUser(R.string.unable_to_parse_certificate); + e.printStackTrace(); + } catch (CertificateEncodingException e) { + callback.informUser(R.string.unable_to_parse_certificate); + e.printStackTrace(); + } catch (InvalidJidException e) { + callback.informUser(R.string.unable_to_parse_certificate); + e.printStackTrace(); + } + } + }).start(); + + } + public void updateAccount(final Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); @@ -2699,54 +2749,59 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public interface OnAccountCreated { + void onAccountCreated(Account account); + void informUser(int r); + } + public interface OnMoreMessagesLoaded { - public void onMoreMessagesLoaded(int count, Conversation conversation); + void onMoreMessagesLoaded(int count, Conversation conversation); - public void informUser(int r); + void informUser(int r); } public interface OnAccountPasswordChanged { - public void onPasswordChangeSucceeded(); + void onPasswordChangeSucceeded(); - public void onPasswordChangeFailed(); + void onPasswordChangeFailed(); } public interface OnAffiliationChanged { - public void onAffiliationChangedSuccessful(Jid jid); + void onAffiliationChangedSuccessful(Jid jid); - public void onAffiliationChangeFailed(Jid jid, int resId); + void onAffiliationChangeFailed(Jid jid, int resId); } public interface OnRoleChanged { - public void onRoleChangedSuccessful(String nick); + void onRoleChangedSuccessful(String nick); - public void onRoleChangeFailed(String nick, int resid); + void onRoleChangeFailed(String nick, int resid); } public interface OnConversationUpdate { - public void onConversationUpdate(); + void onConversationUpdate(); } public interface OnAccountUpdate { - public void onAccountUpdate(); + void onAccountUpdate(); } public interface OnRosterUpdate { - public void onRosterUpdate(); + void onRosterUpdate(); } public interface OnMucRosterUpdate { - public void onMucRosterUpdate(); + void onMucRosterUpdate(); } public interface OnConferenceConfigurationFetched { - public void onConferenceConfigurationFetched(Conversation conversation); + void onConferenceConfigurationFetched(Conversation conversation); } public interface OnConferenceOptionsPushed { - public void onPushSucceeded(); + void onPushSucceeded(); - public void onPushFailed(); + void onPushFailed(); } public interface OnShowErrorToast { diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 00e3a0f91..dbd5f117b 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -74,6 +74,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate private LinearLayout keysCard; private Jid jidToEdit; + private boolean mInitMode = false; private Account mAccount; private String messageFingerprint; @@ -83,6 +84,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate @Override public void onClick(final View v) { + if (mInitMode && mAccount != null) { + mAccount.setOption(Account.OPTION_DISABLED, false); + } if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) { mAccount.setOption(Account.OPTION_DISABLED, false); xmppConnectionService.updateAccount(mAccount); @@ -129,12 +133,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } } if (mAccount != null) { - try { - mAccount.setUsername(jid.hasLocalpart() ? jid.getLocalpart() : ""); - mAccount.setServer(jid.getDomainpart()); - } catch (final InvalidJidException ignored) { - return; - } + mAccount.setJid(jid); mAccountJid.setError(null); mPasswordConfirm.setError(null); mAccount.setPassword(password); @@ -152,9 +151,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); xmppConnectionService.createAccount(mAccount); } - if (jidToEdit != null - && !mAccount.isOptionSet(Account.OPTION_DISABLED) - && !registerNewAccount) { + if (!mAccount.isOptionSet(Account.OPTION_DISABLED) + && !registerNewAccount + && !mInitMode) { finish(); } else { updateSaveButton(); @@ -179,12 +178,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate startActivity(new Intent(getApplicationContext(), ManageAccountActivity.class)); finish(); - } else if (jidToEdit == null && mAccount != null - && mAccount.getStatus() == Account.State.ONLINE) { + } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) { if (!mFetchingAvatar) { mFetchingAvatar = true; - xmppConnectionService.checkForAvatar(mAccount, - mAvatarFetchCallback); + xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback); } } else { updateSaveButton(); @@ -236,8 +233,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate @Override public void onClick(final View view) { if (mAccount != null) { - final Intent intent = new Intent(getApplicationContext(), - PublishProfilePictureActivity.class); + final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); intent.putExtra("account", mAccount.getJid().toBareJid().toString()); startActivity(intent); } @@ -269,7 +265,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } protected void updateSaveButton() { - if (accountInfoEdited() && jidToEdit != null) { + if (accountInfoEdited() && !mInitMode) { this.mSaveButton.setText(R.string.save); this.mSaveButton.setEnabled(true); this.mSaveButton.setTextColor(getPrimaryTextColor()); @@ -277,14 +273,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mSaveButton.setEnabled(false); this.mSaveButton.setTextColor(getSecondaryTextColor()); this.mSaveButton.setText(R.string.account_status_connecting); - } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED) { + } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) { this.mSaveButton.setEnabled(true); this.mSaveButton.setTextColor(getPrimaryTextColor()); this.mSaveButton.setText(R.string.enable); } else { this.mSaveButton.setEnabled(true); this.mSaveButton.setTextColor(getPrimaryTextColor()); - if (jidToEdit != null) { + if (!mInitMode) { if (mAccount != null && mAccount.isOnlineAndConnected()) { this.mSaveButton.setText(R.string.save); if (!accountInfoEdited()) { @@ -421,8 +417,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } catch (final InvalidJidException | NullPointerException ignored) { this.jidToEdit = null; } + this.mInitMode = getIntent().getBooleanExtra("init", false) || this.jidToEdit == null; this.messageFingerprint = getIntent().getStringExtra("fingerprint"); - if (this.jidToEdit != null) { + if (!mInitMode) { this.mRegisterNew.setVisibility(View.GONE); if (getActionBar() != null) { getActionBar().setTitle(getString(R.string.account_details)); @@ -440,7 +437,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate protected void onBackendConnected() { if (this.jidToEdit != null) { this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); - updateAccountInformation(true); + if (this.mAccount != null) { + if (this.mAccount.getPrivateKeyAlias() != null) { + this.mPassword.setHint(R.string.authenticate_with_certificate); + if (this.mInitMode) { + this.mPassword.requestFocus(); + } + } updateAccountInformation(true); + } } else if (this.xmppConnectionService.getAccounts().size() == 0) { if (getActionBar() != null) { getActionBar().setDisplayHomeAsUpEnabled(false); @@ -492,7 +496,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } this.mPassword.setText(this.mAccount.getPassword()); } - if (this.jidToEdit != null) { + if (!mInitMode) { this.mAvatar.setVisibility(View.VISIBLE); this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72))); } diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java index 5b9b73550..80e775069 100644 --- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -5,6 +5,9 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.os.Bundle; +import android.security.KeyChain; +import android.security.KeyChainAliasCallback; +import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; @@ -14,6 +17,7 @@ import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; +import android.widget.Toast; import java.util.ArrayList; import java.util.List; @@ -21,10 +25,11 @@ import java.util.List; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.ui.adapter.AccountAdapter; -public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate { +public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated { protected Account selectedAccount = null; @@ -61,7 +66,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda @Override public void onItemClick(AdapterView arg0, View view, - int position, long arg3) { + int position, long arg3) { switchToAccount(accountList.get(position)); } }); @@ -144,6 +149,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda case R.id.action_enable_all: enableAllAccounts(); break; + case R.id.action_add_account_from_key: + addAccountFromKey(); + break; default: break; } @@ -179,6 +187,10 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } } + private void addAccountFromKey() { + KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); + } + private void publishAvatar(Account account) { Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); @@ -281,4 +293,26 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } } } + + @Override + public void alias(String alias) { + if (alias != null) { + xmppConnectionService.createAccountFromKey(alias, this); + } + } + + @Override + public void onAccountCreated(Account account) { + switchToAccount(account, true); + } + + @Override + public void informUser(final int r) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(ManageAccountActivity.this,r,Toast.LENGTH_LONG).show(); + } + }); + } } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 967efec92..e3848fe28 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -435,8 +435,13 @@ public abstract class XmppActivity extends Activity { } public void switchToAccount(Account account) { + switchToAccount(account,false); + } + + public void switchToAccount(Account account, boolean init) { Intent intent = new Intent(this, EditAccountActivity.class); intent.putExtra("jid", account.getJid().toBareJid().toString()); + intent.putExtra("init", init); startActivity(intent); } diff --git a/src/main/res/menu/manageaccounts.xml b/src/main/res/menu/manageaccounts.xml index f8a30ff7b..6dd57ae54 100644 --- a/src/main/res/menu/manageaccounts.xml +++ b/src/main/res/menu/manageaccounts.xml @@ -6,6 +6,12 @@ android:icon="?attr/icon_add_person" android:showAsAction="always" android:title="@string/action_add_account"/> + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 29b531554..37263ed4f 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -524,4 +524,7 @@ Marks your resource as away when the screen is turned off Not available in silent mode Marks your resource as not available when phone is in silent mode + Add account from key + Unable to parse certificate + Leave empty to authenticate w/ certificate -- cgit v1.2.3 From 08174e3b0585c0dddd1cfc1d9bf60d84a8b6d5db Mon Sep 17 00:00:00 2001 From: Safwat Halaby Date: Sat, 10 Oct 2015 12:03:25 +0300 Subject: Fixed a typo in string.xml --- src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 37263ed4f..5be800883 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -76,7 +76,7 @@ Clear Conversation History Do you want to delete all messages within this Conversation?\n\nWarning: This will not influence messages stored on other devices or servers. Delete messages - End this conversations afterwards + End this conversation afterwards Choose presence to contact Send unencrypted message Send OTR encrypted message -- cgit v1.2.3 From 6a6cb43b17df1557033c217d199706bd4e09673e Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Sun, 11 Oct 2015 13:11:50 +0200 Subject: Captcha support. --- .../siacs/conversations/generator/IqGenerator.java | 10 ++ .../services/XmppConnectionService.java | 57 +++++++++++ .../conversations/ui/EditAccountActivity.java | 74 +++++++++++++- .../eu/siacs/conversations/ui/XmppActivity.java | 6 ++ .../siacs/conversations/xmpp/XmppConnection.java | 112 ++++++++++++++++----- src/main/res/values/strings.xml | 3 + 6 files changed, 238 insertions(+), 24 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 42c57b24f..ba852606d 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -266,4 +266,14 @@ public class IqGenerator extends AbstractGenerator { } return packet; } + + public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) { + final IqPacket register = new IqPacket(IqPacket.TYPE.SET); + + register.setTo(account.getServer()); + register.setId(id); + register.query("jabber:iq:register").addChild(data); + + return register; + } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 4d0228e94..9938c18fb 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -29,6 +29,7 @@ import android.security.KeyChain; import android.security.KeyChainException; import android.util.Log; import android.util.LruCache; +import android.util.DisplayMetrics; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; @@ -257,6 +258,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private int showErrorToastListenerCount = 0; private int unreadCount = -1; private OnAccountUpdate mOnAccountUpdate = null; + private OnCaptchaRequested mOnCaptchaRequested = null; private OnStatusChanged statusListener = new OnStatusChanged() { @Override @@ -315,6 +317,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } }; private int accountChangedListenerCount = 0; + private int captchaRequestedListenerCount = 0; private OnRosterUpdate mOnRosterUpdate = null; private OnUpdateBlocklist mOnUpdateBlocklist = null; private int updateBlocklistListenerCount = 0; @@ -1459,6 +1462,31 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) { + synchronized (this) { + if (checkListeners()) { + switchToForeground(); + } + this.mOnCaptchaRequested = listener; + if (this.captchaRequestedListenerCount < 2) { + this.captchaRequestedListenerCount++; + } + } + } + + public void removeOnCaptchaRequestedListener() { + synchronized (this) { + this.captchaRequestedListenerCount--; + if (this.captchaRequestedListenerCount <= 0) { + this.mOnCaptchaRequested = null; + this.captchaRequestedListenerCount = 0; + if (checkListeners()) { + switchToBackground(); + } + } + } + } + public void setOnRosterUpdateListener(final OnRosterUpdate listener) { synchronized (this) { if (checkListeners()) { @@ -1563,6 +1591,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return (this.mOnAccountUpdate == null && this.mOnConversationUpdate == null && this.mOnRosterUpdate == null + && this.mOnCaptchaRequested == null && this.mOnUpdateBlocklist == null && this.mOnShowErrorToast == null && this.mOnKeyStatusUpdated == null); @@ -2464,6 +2493,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) { + boolean rc = false; + if (mOnCaptchaRequested != null) { + DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics(); + Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int)(captcha.getWidth() * metrics.scaledDensity), + (int)(captcha.getHeight() * metrics.scaledDensity), false); + + mOnCaptchaRequested.onCaptchaRequested(account, id, data, scaled); + rc = true; + } + + return rc; + } + public void updateBlocklistUi(final OnUpdateBlocklist.Status status) { if (mOnUpdateBlocklist != null) { mOnUpdateBlocklist.OnUpdateBlocklist(status); @@ -2620,6 +2663,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendCaptchaRegistryRequest(id, data); + } + } + public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) { final XmppConnection connection = account.getXmppConnection(); if (connection != null) { @@ -2786,6 +2836,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa void onAccountUpdate(); } + public interface OnCaptchaRequested { + void onCaptchaRequested(Account account, + String id, + Data data, + Bitmap captcha); + } + public interface OnRosterUpdate { void onRosterUpdate(); } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index dbd5f117b..cba9275fc 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -1,9 +1,11 @@ package eu.siacs.conversations.ui; +import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.Bitmap; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; @@ -31,17 +33,20 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; import eu.siacs.conversations.xmpp.XmppConnection.Features; +import eu.siacs.conversations.xmpp.forms.Data; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; -public class EditAccountActivity extends XmppActivity implements OnAccountUpdate, OnKeyStatusUpdated { +public class EditAccountActivity extends XmppActivity implements OnAccountUpdate, + OnKeyStatusUpdated, OnCaptchaRequested { private AutoCompleteTextView mAccountJid; private EditText mPassword; @@ -72,6 +77,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate private ImageButton mRegenerateAxolotlKeyButton; private LinearLayout keys; private LinearLayout keysCard; + private AlertDialog mCaptchaDialog = null; private Jid jidToEdit; private boolean mInitMode = false; @@ -681,4 +687,70 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate public void onKeyStatusUpdated() { refreshUi(); } + + @Override + public void onCaptchaRequested(final Account account, final String id, final Data data, + final Bitmap captcha) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + final ImageView view = new ImageView(this); + final LinearLayout layout = new LinearLayout(this); + final EditText input = new EditText(this); + + view.setImageBitmap(captcha); + view.setScaleType(ImageView.ScaleType.FIT_CENTER); + + input.setHint(getString(R.string.captcha_hint)); + + layout.setOrientation(LinearLayout.VERTICAL); + layout.addView(view); + layout.addView(input); + + builder.setTitle(getString(R.string.captcha_required)); + builder.setView(layout); + + builder.setPositiveButton(getString(R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String rc = input.getText().toString(); + data.put("username", account.getUsername()); + data.put("password", account.getPassword()); + data.put("ocr", rc); + data.submit(); + + if (xmppConnectionServiceBound) { + xmppConnectionService.sendCreateAccountWithCaptchaPacket( + account, id, data); + } + } + }); + builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (xmppConnectionService != null) { + xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null); + } + } + }); + + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + if (xmppConnectionService != null) { + xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null); + } + } + }); + + runOnUiThread(new Runnable() { + @Override + public void run() { + if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) { + mCaptchaDialog.dismiss(); + } + mCaptchaDialog = builder.create(); + mCaptchaDialog.show(); + } + }); + } } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index e3848fe28..5e6c188da 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -281,6 +281,9 @@ public abstract class XmppActivity extends Activity { if (this instanceof XmppConnectionService.OnAccountUpdate) { this.xmppConnectionService.setOnAccountListChangedListener((XmppConnectionService.OnAccountUpdate) this); } + if (this instanceof XmppConnectionService.OnCaptchaRequested) { + this.xmppConnectionService.setOnCaptchaRequestedListener((XmppConnectionService.OnCaptchaRequested) this); + } if (this instanceof XmppConnectionService.OnRosterUpdate) { this.xmppConnectionService.setOnRosterUpdateListener((XmppConnectionService.OnRosterUpdate) this); } @@ -305,6 +308,9 @@ public abstract class XmppActivity extends Activity { if (this instanceof XmppConnectionService.OnAccountUpdate) { this.xmppConnectionService.removeOnAccountListChangedListener(); } + if (this instanceof XmppConnectionService.OnCaptchaRequested) { + this.xmppConnectionService.removeOnCaptchaRequestedListener(); + } if (this instanceof XmppConnectionService.OnRosterUpdate) { this.xmppConnectionService.removeOnRosterUpdateListener(); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index c41b69748..b62c4e6b1 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1,10 +1,15 @@ package eu.siacs.conversations.xmpp; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.wifi.WifiConfiguration; import android.os.Bundle; import android.os.Parcelable; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; +import android.util.Base64; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -14,6 +19,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,6 +30,8 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; +import java.net.MalformedURLException; +import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -59,6 +67,8 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Tag; import eu.siacs.conversations.xml.TagWriter; import eu.siacs.conversations.xml.XmlReader; +import eu.siacs.conversations.xmpp.forms.Data; +import eu.siacs.conversations.xmpp.forms.Field; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; @@ -116,6 +126,29 @@ public class XmppConnection implements Runnable { private SaslMechanism saslMechanism; + private OnIqPacketReceived createPacketReceiveHandler() { + OnIqPacketReceived receiver = new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + account.setOption(Account.OPTION_REGISTER, + false); + changeStatus(Account.State.REGISTRATION_SUCCESSFUL); + } else if (packet.hasChild("error") + && (packet.findChild("error") + .hasChild("conflict"))) { + changeStatus(Account.State.REGISTRATION_CONFLICT); + } else { + changeStatus(Account.State.REGISTRATION_FAILED); + Log.d(Config.LOGTAG, packet.toString()); + } + disconnect(true); + } + }; + + return receiver; + } + public XmppConnection(final Account account, final XmppConnectionService service) { this.account = account; this.wakeLock = service.getPowerManager().newWakeLock( @@ -643,6 +676,15 @@ public class XmppConnection implements Runnable { return mechanisms; } + public void sendCaptchaRegistryRequest(String id, Data data) { + if (data == null) { + setAccountCreationFailed(""); + } else { + IqPacket request = getIqGenerator().generateCreateAccountWithCaptcha(account, id, data); + sendIqPacket(request, createPacketReceiveHandler()); + } + } + private void sendRegistryRequest() { final IqPacket register = new IqPacket(IqPacket.TYPE.GET); register.query("jabber:iq:register"); @@ -651,6 +693,7 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { + boolean failed = false; if (packet.getType() == IqPacket.TYPE.RESULT && packet.query().hasChild("username") && (packet.query().hasChild("password"))) { @@ -659,37 +702,60 @@ public class XmppConnection implements Runnable { final Element password = new Element("password").setContent(account.getPassword()); register.query("jabber:iq:register").addChild(username); register.query().addChild(password); - sendIqPacket(register, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(final Account account, final IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - account.setOption(Account.OPTION_REGISTER, - false); - changeStatus(Account.State.REGISTRATION_SUCCESSFUL); - } else if (packet.hasChild("error") - && (packet.findChild("error") - .hasChild("conflict"))) { - changeStatus(Account.State.REGISTRATION_CONFLICT); - } else { - changeStatus(Account.State.REGISTRATION_FAILED); - Log.d(Config.LOGTAG, packet.toString()); - } - disconnect(true); + sendIqPacket(register, createPacketReceiveHandler()); + } else if (packet.getType() == IqPacket.TYPE.RESULT + && (packet.query().hasChild("x", "jabber:x:data"))) { + final Data data = Data.parse(packet.query().findChild("x", "jabber:x:data")); + final Element blob = packet.query().findChild("data", "urn:xmpp:bob"); + final String id = packet.getId(); + + Bitmap captcha = null; + if (blob != null) { + try { + final String base64Blob = blob.getContent(); + final byte[] strBlob = Base64.decode(base64Blob, Base64.DEFAULT); + InputStream stream = new ByteArrayInputStream(strBlob); + captcha = BitmapFactory.decodeStream(stream); + } catch (Exception e) { + + } + } else { + try { + Field url = data.getFieldByName("url"); + String urlString = url.findChildContent("value"); + + URL uri = new URL(urlString); + captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream()); + } catch(MalformedURLException e) { + Log.e(Config.LOGTAG, e.toString()); + } catch(IOException e) { + Log.e(Config.LOGTAG, e.toString()); } - }); + } + + if (captcha != null) { + failed = !mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha); + } } else { + failed = true; + } + + if (failed) { final Element instructions = packet.query().findChild("instructions"); - changeStatus(Account.State.REGISTRATION_FAILED); - disconnect(true); - Log.d(Config.LOGTAG, account.getJid().toBareJid() - + ": could not register. instructions are" - + (instructions != null ? instructions.getContent() : "")); + setAccountCreationFailed((instructions != null) ? instructions.getContent() : ""); } } }); } + private void setAccountCreationFailed(String instructions) { + changeStatus(Account.State.REGISTRATION_FAILED); + disconnect(true); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + + ": could not register. instructions are" + + instructions); + } + private void sendBindRequest() { while(!mXmppConnectionService.areMessagesInitialized()) { try { diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 5be800883..3307b973e 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -527,4 +527,7 @@ Add account from key Unable to parse certificate Leave empty to authenticate w/ certificate + Captcha text + Captcha required + enter the text from the image -- cgit v1.2.3 From 9e1393bc1c8903d400e559b3528a0fe5d04bd389 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 11 Oct 2015 14:27:09 +0200 Subject: prevent null pointer when trying to display device fingerprints of not existing sessions --- src/main/java/eu/siacs/conversations/ui/XmppActivity.java | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index e3848fe28..94292f7e7 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -619,6 +619,9 @@ public abstract class XmppActivity extends Activity { protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight) { final XmppAxolotlSession.Trust trust = account.getAxolotlService() .getFingerprintTrust(fingerprint); + if (trust == null) { + return false; + } return addFingerprintRowWithListeners(keys, account, fingerprint, highlight, trust, true, new CompoundButton.OnCheckedChangeListener() { @Override -- cgit v1.2.3 From a7c7a421365dc637e44a956c2be112118a752bc3 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Sun, 11 Oct 2015 16:01:03 +0200 Subject: Improve InvalidJidException handling in Jid class This code should never be triggered anway, so with this 'fix', we should at least get more meaningful stack traces. Plus, it makes the linter happy by preventing NullPointerExceptions. --- src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java index f989c0c25..a15abe14a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -176,7 +176,7 @@ public final class Jid { return resourcepart.isEmpty() ? this : fromParts(localpart, domainpart, ""); } catch (final InvalidJidException e) { // This should never happen. - return null; + throw new AssertionError("Jid " + this.toString() + " invalid"); } } @@ -185,7 +185,7 @@ public final class Jid { return resourcepart.isEmpty() && localpart.isEmpty() ? this : fromString(getDomainpart()); } catch (final InvalidJidException e) { // This should never happen. - return null; + throw new AssertionError("Jid " + this.toString() + " invalid"); } } -- cgit v1.2.3 From fdd88aa530235913714df983957bd7bd6756335f Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Sun, 11 Oct 2015 16:04:31 +0200 Subject: Clean up Fixes some random linter warnings. --- .../conversations/crypto/axolotl/AxolotlService.java | 17 +++++------------ .../crypto/axolotl/SQLiteAxolotlStore.java | 1 - 2 files changed, 5 insertions(+), 13 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 70de2777d..4319efb0a 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -140,9 +140,6 @@ public class AxolotlService { putDevicesForJid(account.getJid().toBareJid().toString(), deviceIds, store); for (Contact contact : account.getRoster().getContacts()) { Jid bareJid = contact.getJid().toBareJid(); - if (bareJid == null) { - continue; // FIXME: handle this? - } String address = bareJid.toString(); deviceIds = store.getSubDeviceSessions(address); putDevicesForJid(address, deviceIds, store); @@ -162,7 +159,7 @@ public class AxolotlService { } } - private static enum FetchStatus { + private enum FetchStatus { PENDING, SUCCESS, ERROR @@ -212,14 +209,12 @@ public class AxolotlService { private Set findOwnSessions() { AxolotlAddress ownAddress = getAddressForJid(account.getJid().toBareJid()); - Set ownDeviceSessions = new HashSet<>(this.sessions.getAll(ownAddress).values()); - return ownDeviceSessions; + return new HashSet<>(this.sessions.getAll(ownAddress).values()); } private Set findSessionsforContact(Contact contact) { AxolotlAddress contactAddress = getAddressForJid(contact.getJid()); - Set sessions = new HashSet<>(this.sessions.getAll(contactAddress).values()); - return sessions; + return new HashSet<>(this.sessions.getAll(contactAddress).values()); } public Set getFingerprintsForOwnSessions() { @@ -509,10 +504,8 @@ public class AxolotlService { } public boolean isContactAxolotlCapable(Contact contact) { - Jid jid = contact.getJid().toBareJid(); - AxolotlAddress address = new AxolotlAddress(jid.toString(), 0); - return sessions.hasAny(address) || + return hasAny(contact) || (deviceIds.containsKey(jid) && !deviceIds.get(jid).isEmpty()); } @@ -796,7 +789,7 @@ public class AxolotlService { } public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) { - XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage = null; + XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage; XmppAxolotlSession session = getReceivingSession(message); keyTransportMessage = message.getParameters(session, getOwnDeviceId()); diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java index 4d7793023..a78317183 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java @@ -88,7 +88,6 @@ public class SQLiteAxolotlStore implements AxolotlStore { // -------------------------------------- private IdentityKeyPair loadIdentityKeyPair() { - String ownName = account.getJid().toBareJid().toString(); IdentityKeyPair ownKey = mXmppConnectionService.databaseBackend.loadOwnIdentityKeyPair(account); if (ownKey != null) { -- cgit v1.2.3 From 09816b61df2fa72e7a80588982f78144fd43f173 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 9 Oct 2015 13:46:54 +0200 Subject: make add account from key visible --- src/main/res/menu/manageaccounts.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/res/menu/manageaccounts.xml b/src/main/res/menu/manageaccounts.xml index 6dd57ae54..2eeb30db0 100644 --- a/src/main/res/menu/manageaccounts.xml +++ b/src/main/res/menu/manageaccounts.xml @@ -11,7 +11,7 @@ android:showAsAction="never" android:icon="?attr/icon_add_person" android:title="@string/action_add_account_from_key" - android:visible="false"/> + android:visible="true"/> -- cgit v1.2.3 From 7be331bbb2ae2abde55eb031f4fdf4616846360e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 11 Oct 2015 15:48:58 +0200 Subject: add menu item in account details to renew certificate --- .../crypto/axolotl/AxolotlService.java | 105 ++++++++++++++++----- .../siacs/conversations/generator/IqGenerator.java | 21 +++++ .../services/XmppConnectionService.java | 52 +++++++--- .../conversations/ui/EditAccountActivity.java | 80 +++++++++++----- .../eu/siacs/conversations/utils/CryptoHelper.java | 19 ++++ src/main/res/menu/editaccount.xml | 7 ++ src/main/res/values/strings.xml | 5 +- 7 files changed, 228 insertions(+), 61 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 4319efb0a..2c50778aa 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.crypto.axolotl; +import android.security.KeyChain; +import android.security.KeyChainException; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; @@ -18,7 +20,12 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import org.whispersystems.libaxolotl.util.KeyHelper; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -46,6 +53,7 @@ public class AxolotlService { public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl"; public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist"; public static final String PEP_BUNDLES = PEP_PREFIX + ".bundles"; + public static final String PEP_VERIFICATION = PEP_PREFIX + ".verification"; public static final String LOGPREFIX = "AxolotlService"; @@ -242,11 +250,11 @@ public class AxolotlService { return this.pepBroken; } - public void regenerateKeys() { + public void regenerateKeys(boolean wipeOther) { axolotlStore.regenerate(); sessions.clear(); fetchStatusMap.clear(); - publishBundlesIfNeeded(true); + publishBundlesIfNeeded(true, wipeOther); } public int getOwnDeviceId() { @@ -380,7 +388,43 @@ public class AxolotlService { } } - public void publishBundlesIfNeeded(final boolean announceAfter) { + public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord, + final Set preKeyRecords, + final boolean announceAfter, + final boolean wipe) { + try { + IdentityKey axolotlPublicKey = axolotlStore.getIdentityKeyPair().getPublicKey(); + PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias()); + X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias()); + Signature verifier = Signature.getInstance("sha256WithRSA"); + verifier.initSign(x509PrivateKey,mXmppConnectionService.getRNG()); + verifier.update(axolotlPublicKey.serialize()); + byte[] signature = verifier.sign(); + IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId()); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId()); + Log.d(Config.LOGTAG,"verification : "+packet.toString()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe); + } + }); + } catch (KeyChainException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + Log.d(Config.LOGTAG,"no such algo "+e.getMessage()); + e.printStackTrace(); + } catch (java.security.InvalidKeyException e) { + e.printStackTrace(); + } catch (SignatureException e) { + e.printStackTrace(); + } + + } + + public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) { if (pepBroken) { Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... "); return; @@ -470,27 +514,16 @@ public class AxolotlService { if (changed) { - IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles( - signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(), - preKeyRecords, getOwnDeviceId()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish); - mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. "); - if (announceAfter) { - Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); - publishOwnDeviceIdIfNeeded(); - } - } else { - Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error")); - } - } - }); + if (account.getPrivateKeyAlias() == null) { + publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); + } else { + publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); + } } else { Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current"); - if (announceAfter) { + if (wipe) { + wipeOtherPepDevices(); + } else if (announce) { Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); publishOwnDeviceIdIfNeeded(); } @@ -503,6 +536,32 @@ public class AxolotlService { }); } + private void publishDeviceBundle(SignedPreKeyRecord signedPreKeyRecord, + Set preKeyRecords, + final boolean announceAfter, + final boolean wipe) { + IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles( + signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(), + preKeyRecords, getOwnDeviceId()); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish); + mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. "); + if (wipe) { + wipeOtherPepDevices(); + } else if (announceAfter) { + Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); + publishOwnDeviceIdIfNeeded(); + } + } else { + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error")); + } + } + }); + } + public boolean isContactAxolotlCapable(Contact contact) { Jid jid = contact.getJid().toBareJid(); return hasAny(contact) || @@ -774,7 +833,7 @@ public class AxolotlService { plaintextMessage = message.decrypt(session, getOwnDeviceId()); Integer preKeyId = session.getPreKeyId(); if (preKeyId != null) { - publishBundlesIfNeeded(false); + publishBundlesIfNeeded(false, false); session.resetPreKeyId(); } } catch (CryptoFailedException e) { diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index ba852606d..fb69860db 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -2,16 +2,20 @@ package eu.siacs.conversations.generator; import android.util.Base64; +import android.util.Log; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Set; +import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; @@ -173,6 +177,23 @@ public class IqGenerator extends AbstractGenerator { return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item); } + public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) { + final Element item = new Element("item"); + final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX); + final Element chain = verification.addChild("chain"); + for(int i = 0; i < certificates.length; ++i) { + try { + Element certificate = chain.addChild("certificate"); + certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT)); + certificate.setAttribute("index",i); + } catch (CertificateEncodingException e) { + Log.d(Config.LOGTAG, "could not encode certificate"); + } + } + verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT)); + return publish(AxolotlService.PEP_VERIFICATION+":"+deviceId, item); + } + public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) { final IqPacket packet = new IqPacket(IqPacket.TYPE.SET); final Element query = packet.query("urn:xmpp:mam:0"); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 9938c18fb..f3bd545ae 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -30,6 +30,7 @@ import android.security.KeyChainException; import android.util.Log; import android.util.LruCache; import android.util.DisplayMetrics; +import android.util.Pair; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; @@ -37,21 +38,17 @@ import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; -import org.bouncycastle.asn1.x500.RDN; 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 org.bouncycastle.jce.PrincipalUtil; -import org.bouncycastle.jce.X509Principal; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.math.BigInteger; -import java.security.PrivateKey; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateParsingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -175,7 +172,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mMessageArchiveService.executePendingQueries(account); mJingleConnectionManager.cancelInTransmission(); syncDirtyContacts(account); - account.getAxolotlService().publishBundlesIfNeeded(true); + account.getAxolotlService().publishBundlesIfNeeded(true, false); } }; private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { @@ -1307,17 +1304,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void run() { try { X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); - PrivateKey key = KeyChain.getPrivateKey(XmppConnectionService.this, alias); - X500Name x500name = new JcaX509CertificateHolder(chain[0]).getSubject(); - String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()); - String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()); - Jid jid = Jid.fromString(email); - if (findAccountByJid(jid) == null) { - Account account = new Account(jid, ""); + Pair info = CryptoHelper.extractJidAndName(chain[0]); + if (findAccountByJid(info.first) == null) { + Account account = new Account(info.first, ""); account.setPrivateKeyAlias(alias); account.setOption(Account.OPTION_DISABLED, true); createAccount(account); callback.onAccountCreated(account); + try { + getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + } catch (CertificateException e) { + callback.informUser(R.string.certificate_chain_is_not_trusted); + } } else { callback.informUser(R.string.account_already_exists); } @@ -1338,6 +1336,34 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } + public void updateKeyInAccount(final Account account, final String alias) { + Log.d(Config.LOGTAG,"update key in account "+alias); + try { + X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); + Pair info = CryptoHelper.extractJidAndName(chain[0]); + if (account.getJid().toBareJid().equals(info.first)) { + account.setPrivateKeyAlias(alias); + databaseBackend.updateAccount(account); + try { + getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + } catch (CertificateException e) { + showErrorToastInUi(R.string.certificate_chain_is_not_trusted); + } + account.getAxolotlService().regenerateKeys(true); + } else { + showErrorToastInUi(R.string.jid_does_not_match_certificate); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (KeyChainException e) { + e.printStackTrace(); + } catch (InvalidJidException e) { + e.printStackTrace(); + } catch (CertificateEncodingException e) { + e.printStackTrace(); + } + } + public void updateAccount(final Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index cba9275fc..faafc24ac 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -7,6 +7,8 @@ import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; +import android.security.KeyChain; +import android.security.KeyChainAliasCallback; import android.text.Editable; import android.text.TextWatcher; import android.view.Menu; @@ -34,6 +36,7 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.utils.CryptoHelper; @@ -46,7 +49,7 @@ import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; public class EditAccountActivity extends XmppActivity implements OnAccountUpdate, - OnKeyStatusUpdated, OnCaptchaRequested { + OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast { private AutoCompleteTextView mAccountJid; private EditText mPassword; @@ -107,7 +110,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate final Jid jid; try { if (Config.DOMAIN_LOCK != null) { - jid = Jid.fromParts(mAccountJid.getText().toString(),Config.DOMAIN_LOCK,null); + jid = Jid.fromParts(mAccountJid.getText().toString(), Config.DOMAIN_LOCK, null); } else { jid = Jid.fromString(mAccountJid.getText().toString()); } @@ -182,7 +185,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate && mAccount.getStatus() != Account.State.ONLINE && mFetchingAvatar) { startActivity(new Intent(getApplicationContext(), - ManageAccountActivity.class)); + ManageAccountActivity.class)); finish(); } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) { if (!mFetchingAvatar) { @@ -201,6 +204,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate public void onAccountUpdate() { refreshUi(); } + private final UiCallback mAvatarFetchCallback = new UiCallback() { @Override @@ -318,7 +322,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate @Override protected String getShareableUri() { - if (mAccount!=null) { + if (mAccount != null) { return mAccount.getShareableUri(); } else { return ""; @@ -369,7 +373,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() { @Override public void onCheckedChanged(final CompoundButton buttonView, - final boolean isChecked) { + final boolean isChecked) { if (isChecked) { mPasswordConfirm.setVisibility(View.VISIBLE); } else { @@ -393,6 +397,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more); final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server); final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices); + final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate); + + renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null); + if (mAccount != null && mAccount.isOnlineAndConnected()) { if (!mAccount.getXmppConnection().getFeatures().blocking()) { showBlocklist.setVisible(false); @@ -445,11 +453,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); if (this.mAccount != null) { if (this.mAccount.getPrivateKeyAlias() != null) { - this.mPassword.setHint(R.string.authenticate_with_certificate); - if (this.mInitMode) { - this.mPassword.requestFocus(); + this.mPassword.setHint(R.string.authenticate_with_certificate); + if (this.mInitMode) { + this.mPassword.requestFocus(); + } } - } updateAccountInformation(true); + updateAccountInformation(true); } } else if (this.xmppConnectionService.getAccounts().size() == 0) { if (getActionBar() != null) { @@ -489,10 +498,24 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate case R.id.action_clear_devices: showWipePepDialog(); break; + case R.id.action_renew_certificate: + renewCertificate(); + break; } return super.onOptionsItemSelected(item); } + private void renewCertificate() { + KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); + } + + @Override + public void alias(String alias) { + if (alias != null) { + xmppConnectionService.updateKeyInAccount(mAccount, alias); + } + } + private void updateAccountInformation(boolean init) { if (init) { if (Config.DOMAIN_LOCK != null) { @@ -517,7 +540,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) { this.mStats.setVisibility(View.VISIBLE); this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection() - .getLastSessionEstablished())); + .getLastSessionEstablished())); Features features = this.mAccount.getXmppConnection().getFeatures(); if (features.rosterVersioning()) { this.mServerInfoRosterVersion.setText(R.string.server_info_available); @@ -528,7 +551,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mServerInfoCarbons.setText(R.string.server_info_available); } else { this.mServerInfoCarbons - .setText(R.string.server_info_unavailable); + .setText(R.string.server_info_unavailable); } if (features.mam()) { this.mServerInfoMam.setText(R.string.server_info_available); @@ -570,21 +593,21 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mOtrFingerprintBox.setVisibility(View.VISIBLE); this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint)); this.mOtrFingerprintToClipboardButton - .setVisibility(View.VISIBLE); + .setVisibility(View.VISIBLE); this.mOtrFingerprintToClipboardButton - .setOnClickListener(new View.OnClickListener() { + .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { + @Override + public void onClick(final View v) { - if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) { - Toast.makeText( - EditAccountActivity.this, - R.string.toast_message_otr_fingerprint, - Toast.LENGTH_SHORT).show(); + if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) { + Toast.makeText( + EditAccountActivity.this, + R.string.toast_message_otr_fingerprint, + Toast.LENGTH_SHORT).show(); + } } - } - }); + }); } else { this.mOtrFingerprintBox.setVisibility(View.GONE); } @@ -627,7 +650,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate boolean hasKeys = false; keys.removeAllViews(); for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) { - if(ownFingerprint.equals(fingerprint)) { + if (ownFingerprint.equals(fingerprint)) { continue; } boolean highlight = fingerprint.equals(messageFingerprint); @@ -661,7 +684,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mAccount.getAxolotlService().regenerateKeys(); + mAccount.getAxolotlService().regenerateKeys(false); } }); builder.create().show(); @@ -753,4 +776,13 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } }); } + + public void onShowErrorToast(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show(); + } + }); + } } diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index c7c9ac423..e9ad71971 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,6 +1,15 @@ package eu.siacs.conversations.utils; +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.SecureRandom; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.text.Normalizer; import java.util.Arrays; import java.util.Collection; @@ -9,6 +18,8 @@ import java.util.LinkedHashSet; import java.util.List; import eu.siacs.conversations.Config; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; public final class CryptoHelper { public static final String FILETRANSFER = "?FILETRANSFERv1:"; @@ -125,4 +136,12 @@ public final class CryptoHelper { } } } + + public static Pair extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException { + X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); + //String xmpp = IETFUtils.valueToString(x500name.getRDNs(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.8.5"))[0].getFirst().getValue()); + String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()); + String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()); + return new Pair<>(Jid.fromString(email),name); + } } diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml index 2076805ef..62981a454 100644 --- a/src/main/res/menu/editaccount.xml +++ b/src/main/res/menu/editaccount.xml @@ -10,6 +10,13 @@ android:title="@string/show_block_list" android:showAsAction="never" /> + + /> + Marks your resource as away when the screen is turned off Not available in silent mode Marks your resource as not available when phone is in silent mode - Add account from key + Add account from certificate Unable to parse certificate Leave empty to authenticate w/ certificate Captcha text Captcha required enter the text from the image + Certificate chain is not trusted + Jabber ID does not match certificate + Renew certificate -- cgit v1.2.3 From b519411d34da07d9b249f921548b079666b2cc18 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 11 Oct 2015 20:45:01 +0200 Subject: enable SASL EXTERNAL (certificate login --- .../siacs/conversations/crypto/sasl/External.java | 29 ++++++++++ .../siacs/conversations/xmpp/XmppConnection.java | 64 +++++++++++++++++++++- 2 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/crypto/sasl/External.java (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java new file mode 100644 index 000000000..df92898c1 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java @@ -0,0 +1,29 @@ +package eu.siacs.conversations.crypto.sasl; + +import android.util.Base64; +import java.security.SecureRandom; + +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xml.TagWriter; + +public class External extends SaslMechanism { + + public External(TagWriter tagWriter, Account account, SecureRandom rng) { + super(tagWriter, account, rng); + } + + @Override + public int getPriority() { + return 25; + } + + @Override + public String getMechanism() { + return "EXTERNAL"; + } + + @Override + public String getClientFirstMessage() { + return Base64.encodeToString(account.getJid().toBareJid().toString().getBytes(),Base64.NO_WRAP); + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index b62c4e6b1..fd2b221db 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -9,6 +9,8 @@ import android.os.Parcelable; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; +import android.security.KeyChain; +import android.security.KeyChainException; import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -33,7 +35,12 @@ import java.net.UnknownHostException; import java.net.MalformedURLException; import java.net.URL; import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -45,14 +52,17 @@ import java.util.List; import java.util.Map.Entry; import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import de.duenndns.ssl.MemorizingTrustManager; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.sasl.DigestMd5; +import eu.siacs.conversations.crypto.sasl.External; import eu.siacs.conversations.crypto.sasl.Plain; import eu.siacs.conversations.crypto.sasl.SaslMechanism; import eu.siacs.conversations.crypto.sasl.ScramSha1; @@ -126,6 +136,46 @@ public class XmppConnection implements Runnable { private SaslMechanism saslMechanism; + private X509KeyManager mKeyManager = new X509KeyManager() { + @Override + public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { + return account.getPrivateKeyAlias(); + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, Socket socket) { + return null; + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + try { + return KeyChain.getCertificateChain(mXmppConnectionService, alias); + } catch (Exception e) { + return new X509Certificate[0]; + } + } + + @Override + public String[] getClientAliases(String s, Principal[] principals) { + return new String[0]; + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return new String[0]; + } + + @Override + public PrivateKey getPrivateKey(String alias) { + try { + return KeyChain.getPrivateKey(mXmppConnectionService, alias); + } catch (Exception e) { + return null; + } + } + }; + private OnIqPacketReceived createPacketReceiveHandler() { OnIqPacketReceived receiver = new OnIqPacketReceived() { @Override @@ -551,7 +601,13 @@ public class XmppConnection implements Runnable { try { final SSLContext sc = SSLContext.getInstance("TLS"); MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager(); - sc.init(null,new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()},mXmppConnectionService.getRNG()); + KeyManager[] keyManager; + if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) { + keyManager = new KeyManager[]{ mKeyManager }; + } else { + keyManager = null; + } + sc.init(keyManager,new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()},mXmppConnectionService.getRNG()); final SSLSocketFactory factory = sc.getSocketFactory(); final HostnameVerifier verifier; if (mInteractive) { @@ -598,7 +654,7 @@ public class XmppConnection implements Runnable { processStream(tagReader.readTag()); sslSocket.close(); } catch (final NoSuchAlgorithmException | KeyManagementException e1) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); throw new SecurityException(); } } @@ -622,7 +678,9 @@ public class XmppConnection implements Runnable { .findChild("mechanisms")); final Element auth = new Element("auth"); auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); - if (mechanisms.contains("SCRAM-SHA-1")) { + if (mechanisms.contains("EXTERNAL")) { + saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG()); + } else if (mechanisms.contains("SCRAM-SHA-1")) { saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG()); } else if (mechanisms.contains("PLAIN")) { saslMechanism = new Plain(tagWriter, account); -- cgit v1.2.3 From 933538a39da9fb80025e07fc173514f45033c261 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 12 Oct 2015 12:36:54 +0200 Subject: code clean up --- .../conversations/crypto/axolotl/AxolotlService.java | 14 +------------- .../conversations/crypto/sasl/SaslMechanism.java | 2 +- .../siacs/conversations/crypto/sasl/ScramSha1.java | 3 +-- .../java/eu/siacs/conversations/ui/UiCallback.java | 6 +++--- .../conversations/ui/adapter/KnownHostsAdapter.java | 7 +++---- .../conversations/ui/adapter/ListItemAdapter.java | 2 +- .../eu/siacs/conversations/xmpp/XmppConnection.java | 20 ++++---------------- .../conversations/xmpp/jingle/JingleConnection.java | 3 +-- .../xmpp/jingle/OnFileTransmissionStatusChanged.java | 4 ++-- .../xmpp/jingle/OnJinglePacketReceived.java | 2 +- .../xmpp/jingle/OnPrimaryCandidateFound.java | 3 +-- 11 files changed, 19 insertions(+), 47 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index 2c50778aa..fe801755e 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -409,19 +409,9 @@ public class AxolotlService { publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe); } }); - } catch (KeyChainException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG,"no such algo "+e.getMessage()); - e.printStackTrace(); - } catch (java.security.InvalidKeyException e) { - e.printStackTrace(); - } catch (SignatureException e) { + } catch (Exception e) { e.printStackTrace(); } - } public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) { @@ -530,7 +520,6 @@ public class AxolotlService { } } catch (InvalidKeyException e) { Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage()); - return; } } }); @@ -639,7 +628,6 @@ public class AxolotlService { fetchStatusMap.put(address, FetchStatus.ERROR); Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error")); finish(); - return; } } }); diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java b/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java index 14d8b944b..5b4b99efb 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java @@ -11,7 +11,7 @@ public abstract class SaslMechanism { final protected Account account; final protected SecureRandom rng; - protected static enum State { + protected enum State { INITIAL, AUTH_TEXT_SENT, RESPONSE_SENT, diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java index f47677f6e..3a05446c1 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java @@ -21,7 +21,6 @@ public class ScramSha1 extends SaslMechanism { // TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage. final private static String GS2_HEADER = "n,,"; private String clientFirstMessageBare; - private byte[] serverFirstMessage; final private String clientNonce; private byte[] serverSignature = null; private static HMac HMAC; @@ -104,7 +103,7 @@ public class ScramSha1 extends SaslMechanism { if (challenge == null) { throw new AuthenticationException("challenge can not be null"); } - serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT); + byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT); final Tokenizer tokenizer = new Tokenizer(serverFirstMessage); String nonce = ""; int iterationCount = -1; diff --git a/src/main/java/eu/siacs/conversations/ui/UiCallback.java b/src/main/java/eu/siacs/conversations/ui/UiCallback.java index c80199e17..d056d6289 100644 --- a/src/main/java/eu/siacs/conversations/ui/UiCallback.java +++ b/src/main/java/eu/siacs/conversations/ui/UiCallback.java @@ -3,9 +3,9 @@ package eu.siacs.conversations.ui; import android.app.PendingIntent; public interface UiCallback { - public void success(T object); + void success(T object); - public void error(int errorCode, T object); + void error(int errorCode, T object); - public void userInputRequried(PendingIntent pi, T object); + void userInputRequried(PendingIntent pi, T object); } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java index 471526afe..39bfc082e 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java @@ -15,7 +15,7 @@ public class KnownHostsAdapter extends ArrayAdapter { @Override protected FilterResults performFiltering(CharSequence constraint) { if (constraint != null) { - ArrayList suggestions = new ArrayList(); + ArrayList suggestions = new ArrayList<>(); final String[] split = constraint.toString().split("@"); if (split.length == 1) { for (String domain : domains) { @@ -58,10 +58,9 @@ public class KnownHostsAdapter extends ArrayAdapter { } }; - public KnownHostsAdapter(Context context, int viewResourceId, - List mKnownHosts) { + public KnownHostsAdapter(Context context, int viewResourceId, List mKnownHosts) { super(context, viewResourceId, new ArrayList()); - domains = new ArrayList(mKnownHosts); + domains = new ArrayList<>(mKnownHosts); } @Override diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java index ad7d76224..4be4931f7 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java @@ -92,7 +92,7 @@ public class ListItemAdapter extends ArrayAdapter { } public interface OnTagClickedListener { - public void onTagClicked(String tag); + void onTagClicked(String tag); } class BitmapWorkerTask extends AsyncTask { diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index fd2b221db..fc9c98122 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1,16 +1,13 @@ package eu.siacs.conversations.xmpp; -import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.net.wifi.WifiConfiguration; import android.os.Bundle; import android.os.Parcelable; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.security.KeyChain; -import android.security.KeyChainException; import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -35,8 +32,6 @@ import java.net.UnknownHostException; import java.net.MalformedURLException; import java.net.URL; import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; @@ -177,7 +172,7 @@ public class XmppConnection implements Runnable { }; private OnIqPacketReceived createPacketReceiveHandler() { - OnIqPacketReceived receiver = new OnIqPacketReceived() { + return new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() == IqPacket.TYPE.RESULT) { @@ -195,8 +190,6 @@ public class XmppConnection implements Runnable { disconnect(true); } }; - - return receiver; } public XmppConnection(final Account account, final XmppConnectionService service) { @@ -775,17 +768,14 @@ public class XmppConnection implements Runnable { InputStream stream = new ByteArrayInputStream(strBlob); captcha = BitmapFactory.decodeStream(stream); } catch (Exception e) { - + //ignored } } else { try { Field url = data.getFieldByName("url"); String urlString = url.findChildContent("value"); - URL uri = new URL(urlString); captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream()); - } catch(MalformedURLException e) { - Log.e(Config.LOGTAG, e.toString()); } catch(IOException e) { Log.e(Config.LOGTAG, e.toString()); } @@ -885,11 +875,9 @@ public class XmppConnection implements Runnable { this.sendUnmodifiedIqPacket(startSession, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { - return; - } else if (packet.getType() == IqPacket.TYPE.RESULT) { + if (packet.getType() == IqPacket.TYPE.RESULT) { sendPostBindInitialization(); - } else { + } else if (packet.getType() != IqPacket.TYPE.TIMEOUT){ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions"); disconnect(true); } 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 4f733b10e..388c5dec2 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -737,8 +737,7 @@ public class JingleConnection implements Transferable { JinglePacket answer = bootstrapPacket("transport-accept"); Content content = new Content("initiator", "a-file-offer"); content.setTransportId(this.transportId); - content.ibbTransport().setAttribute("block-size", - Integer.toString(this.ibbBlockSize)); + content.ibbTransport().setAttribute("block-size",this.ibbBlockSize); answer.setContent(content); this.sendJinglePacket(answer); return true; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java index e45e7441d..91cba39f5 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java @@ -3,7 +3,7 @@ package eu.siacs.conversations.xmpp.jingle; import eu.siacs.conversations.entities.DownloadableFile; public interface OnFileTransmissionStatusChanged { - public void onFileTransmitted(DownloadableFile file); + void onFileTransmitted(DownloadableFile file); - public void onFileTransferAborted(); + void onFileTransferAborted(); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java index 2aaf62a1b..9a60b3924 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/OnJinglePacketReceived.java @@ -5,5 +5,5 @@ import eu.siacs.conversations.xmpp.PacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; public interface OnJinglePacketReceived extends PacketReceived { - public void onJinglePacketReceived(Account account, JinglePacket packet); + void onJinglePacketReceived(Account account, JinglePacket packet); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java index 03a437b2b..76e337177 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.xmpp.jingle; public interface OnPrimaryCandidateFound { - public void onPrimaryCandidateFound(boolean success, - JingleCandidate canditate); + void onPrimaryCandidateFound(boolean success, JingleCandidate canditate); } -- cgit v1.2.3 From 212d1a8c91d5b83d1132d3a02664607ce37f8ecf Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 12 Oct 2015 13:18:20 +0200 Subject: add config variable to enable x509 verification --- src/main/java/eu/siacs/conversations/Config.java | 2 ++ .../crypto/axolotl/AxolotlService.java | 6 ++-- .../services/XmppConnectionService.java | 33 +++++++++------------- .../conversations/ui/ManageAccountActivity.java | 11 ++++++-- src/main/res/menu/manageaccounts.xml | 4 +-- src/main/res/values/strings.xml | 2 +- 6 files changed, 31 insertions(+), 27 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index b4dcf209d..bd9ad1a82 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -48,6 +48,8 @@ public final class Config { public static final boolean SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON = false; + public static final boolean X509_VERIFICATION = false; //use x509 certificates to verify OMEMO keys + public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2; public static final int MAM_MAX_MESSAGES = 500; diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index fe801755e..58e5a0957 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -504,10 +504,10 @@ public class AxolotlService { if (changed) { - if (account.getPrivateKeyAlias() == null) { - publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); - } else { + if (account.getPrivateKeyAlias() != null && Config.X509_VERIFICATION) { publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); + } else { + publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); } } else { Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current"); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index f3bd545ae..7a39bd062 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1311,25 +1311,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa account.setOption(Account.OPTION_DISABLED, true); createAccount(account); callback.onAccountCreated(account); - try { - getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); - } catch (CertificateException e) { - callback.informUser(R.string.certificate_chain_is_not_trusted); + if (Config.X509_VERIFICATION) { + try { + getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + } catch (CertificateException e) { + callback.informUser(R.string.certificate_chain_is_not_trusted); + } } } else { callback.informUser(R.string.account_already_exists); } - } catch (KeyChainException e) { - callback.informUser(R.string.unable_to_parse_certificate); - } catch (InterruptedException e) { + } catch (Exception e) { callback.informUser(R.string.unable_to_parse_certificate); - e.printStackTrace(); - } catch (CertificateEncodingException e) { - callback.informUser(R.string.unable_to_parse_certificate); - e.printStackTrace(); - } catch (InvalidJidException e) { - callback.informUser(R.string.unable_to_parse_certificate); - e.printStackTrace(); } } }).start(); @@ -1344,12 +1337,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (account.getJid().toBareJid().equals(info.first)) { account.setPrivateKeyAlias(alias); databaseBackend.updateAccount(account); - try { - getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); - } catch (CertificateException e) { - showErrorToastInUi(R.string.certificate_chain_is_not_trusted); + if (Config.X509_VERIFICATION) { + try { + getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + } catch (CertificateException e) { + showErrorToastInUi(R.string.certificate_chain_is_not_trusted); + } + account.getAxolotlService().regenerateKeys(true); } - account.getAxolotlService().regenerateKeys(true); } else { showErrorToastInUi(R.string.jid_does_not_match_certificate); } diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java index 80e775069..6024177ae 100644 --- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.os.Bundle; import android.security.KeyChain; import android.security.KeyChainAliasCallback; -import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; @@ -103,6 +102,14 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.manageaccounts, menu); MenuItem enableAll = menu.findItem(R.id.action_enable_all); + MenuItem addAccount = menu.findItem(R.id.action_add_account); + MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert); + + if (Config.X509_VERIFICATION) { + addAccount.setVisible(false); + addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + } + if (!accountsLeftToEnable()) { enableAll.setVisible(false); } @@ -149,7 +156,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda case R.id.action_enable_all: enableAllAccounts(); break; - case R.id.action_add_account_from_key: + case R.id.action_add_account_with_cert: addAccountFromKey(); break; default: diff --git a/src/main/res/menu/manageaccounts.xml b/src/main/res/menu/manageaccounts.xml index 2eeb30db0..ffa692a0b 100644 --- a/src/main/res/menu/manageaccounts.xml +++ b/src/main/res/menu/manageaccounts.xml @@ -7,10 +7,10 @@ android:showAsAction="always" android:title="@string/action_add_account"/> Marks your resource as away when the screen is turned off Not available in silent mode Marks your resource as not available when phone is in silent mode - Add account from certificate + Add account with certificate Unable to parse certificate Leave empty to authenticate w/ certificate Captcha text -- cgit v1.2.3 From f24649c819278979523c46f4469af71f8e8ec20f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 13 Oct 2015 16:58:08 +0200 Subject: set cursor to end of jid in edit account --- src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index faafc24ac..d962c57ea 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -518,10 +518,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate private void updateAccountInformation(boolean init) { if (init) { + this.mAccountJid.getEditableText().clear(); if (Config.DOMAIN_LOCK != null) { - this.mAccountJid.setText(this.mAccount.getJid().getLocalpart()); + this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart()); } else { - this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); + this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString()); } this.mPassword.setText(this.mAccount.getPassword()); } -- cgit v1.2.3 From 76828950ee6529a6da98af484507a48f992d9442 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 13 Oct 2015 23:34:09 +0200 Subject: cleaned up some code. log last tag --- .../siacs/conversations/xmpp/XmppConnection.java | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index fc9c98122..999aecc9f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -29,7 +29,6 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; -import java.net.MalformedURLException; import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -286,7 +285,7 @@ public class XmppConnection implements Runnable { Tag nextTag; while ((nextTag = tagReader.readTag()) != null) { if (nextTag.isStart("stream")) { - processStream(nextTag); + processStream(); break; } else { throw new IOException("unknown tag on connect"); @@ -338,11 +337,9 @@ public class XmppConnection implements Runnable { connect(); } - private void processStream(final Tag currentTag) throws XmlPullParserException, - IOException, NoSuchAlgorithmException { + private void processStream() throws XmlPullParserException, IOException, NoSuchAlgorithmException { Tag nextTag = tagReader.readTag(); - - while ((nextTag != null) && (!nextTag.isEnd("stream"))) { + while (nextTag != null && !nextTag.isEnd("stream")) { if (nextTag.isStart("error")) { processStreamError(nextTag); } else if (nextTag.isStart("features")) { @@ -362,7 +359,11 @@ public class XmppConnection implements Runnable { String.valueOf(saslMechanism.getPriority())); tagReader.reset(); sendStartStream(); - processStream(tagReader.readTag()); + if (tagReader.readTag().isStart("stream")) { + processStream(); + } else { + throw new IOException("server didn't restart stream after successful auth"); + } break; } else if (nextTag.isStart("failure")) { throw new UnauthorizedException(); @@ -458,6 +459,7 @@ public class XmppConnection implements Runnable { } nextTag = tagReader.readTag(); } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": last tag was "+nextTag); if (account.getStatus() == Account.State.ONLINE) { account. setStatus(Account.State.OFFLINE); if (statusListener != null) { @@ -644,7 +646,11 @@ public class XmppConnection implements Runnable { sendStartStream(); Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": TLS connection established"); features.encryptionEnabled = true; - processStream(tagReader.readTag()); + if (tagReader.readTag().isStart("stream")) { + processStream(); + } else { + throw new IOException("server didn't restart stream after STARTTLS"); + } sslSocket.close(); } catch (final NoSuchAlgorithmException | KeyManagementException e1) { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); -- cgit v1.2.3 From beca74863479c533675d8b2568cd8dd571f5306b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 13 Oct 2015 23:36:16 +0200 Subject: pulled translations from transifex --- src/main/res/values-ar-rEG/strings.xml | 315 ------------------------- src/main/res/values-ar-rSY/strings.xml | 2 - src/main/res/values-ar/strings.xml | 316 ++++++++++++++++++++++++- src/main/res/values-de/strings.xml | 134 +++++------ src/main/res/values-iw/strings.xml | 407 +++++++++++++++++++++++++++------ src/main/res/values-nb-rNO/strings.xml | 177 ++++++++++++++ src/main/res/values-pl/strings.xml | 15 ++ 7 files changed, 913 insertions(+), 453 deletions(-) delete mode 100644 src/main/res/values-ar-rEG/strings.xml delete mode 100644 src/main/res/values-ar-rSY/strings.xml create mode 100644 src/main/res/values-nb-rNO/strings.xml (limited to 'src/main') diff --git a/src/main/res/values-ar-rEG/strings.xml b/src/main/res/values-ar-rEG/strings.xml deleted file mode 100644 index 55465f36e..000000000 --- a/src/main/res/values-ar-rEG/strings.xml +++ /dev/null @@ -1,315 +0,0 @@ - - - إعدادات - محادثة جديدة - إدارة الحسابات - إنهاء المحادثة - بيانات جهة الإتصال - بيانات الغرفة - تشفير المحادثة - إضافة حساب - تعديل الإسم - إضافة لسجل الهاتف - حذف من الإضافات - حجب جهة إتصال - إنهاء حجب جهة اتصال - حجب دومين - إنهاء حجب دومين - إدارة الحسابات - إعدادات - بيانات الغرفه - بيانات جهة الإتصال - مشاركة مع محادثة - بدأ محادثة - إختيار جهة اتصال - قائمة المحجوبين - الآن - منذ 1 دقيقة - دقائق %d منذ - محادثات غير مقروءة - ارسال - رسالة مشفّرة .. الرجاء الإنتظار - اللقب مستخدم من قبل - مدير - مالك - مشرف - مشترك - زائر - هل ترغب بحذف جهة الإتصال %s من اضافاتك? ستبقى محادثاتك معه محفوظه - هل ترغب في حجب %s من ارسال الرسائل لك? - هل ترغب في انهاء حجب %s والسماح له بمراسلتك? - هل تريد حجب جميع جهات الإتصال من %s? - الغاء حجب جميع جهات الإتصال من %s? - جهة الاتصال محجوبه - هل ترغب في حذف %s من المفضلات? المحادثات المحفوظه ستبقى كما هي - تسجيل حساب جديد في سيرفر - تغيير كلمة المرور في سيرفر - مشاركة مع - بدأ محادثة - دعوة صديق - جهات الإتصال - الغاء - تعيين - اضافة - تعديل - حذف - حجب - الغاء حجب - حفظ - موافق - توقف التطبيق عن العمل - بموافقتك على ارسال تقارير الأخطاء ستساعد مبرمجي التطبيق على تحسين جودة التطبيق\nتنويه: سيرسل تقارير الأخطاء ان وجدت بأحد الحسابات التي أضفتها. - ارسال الآن - لا تسألني ثانية - لا يمكن تسجيل الدخول لحسابك - لا يمكن تسجيل الدخول بحساباتك - المس الشاشه لعرض المحادثات - ارفاق ملف - جهة الاتصال ليست مضافه لديك هل ترغب في إضافتها ؟؟ - اضافة جهة اتصال - فشل التسليم - مرفوض - اعداد صورة للإرسال - حذف سجل المحفوظات - حذف سجل المحفوظات للمحادثة - هل ترغب بحذف جميع الرسائل في تلك المحادثة?\n\nتنويه: هذا لن يؤثر على الرسائل المخزنة على الأجهزة أو أي أماكن أخرى. - حذف الرسائل - انهاء هذه المحادثة بعد الكلمات - اختيار ظهورك لجهات الإتصال - OTRارساله رساله مشفره عبر - OpenPGPارساله رساله مشفره عبر - تم تغيير لقبك بنجاح - إرسال بدون تشفير - فشل فك التشفير. ربما لم يكن لديك المفتاح الخاص الصحيح. - OpenKeychain - Conversations :: يستخدم تطبيق آخر يسمى OpenKeychain لتشفير وفك تشفير الرسائل وإدارة المفاتيح العامة الخاصة بك \n\nOpenKeychain تحت الرخصة GPLv3 و لتحميل التطبيق من جوجل بلاي \n\n (وأعد تشغيل التطبيق مرة أخرى) - اعادة تشغيل - تثبيت - عرض .. - انتظار .. - OpenPGP-لايوجد مفتاح - Conversations::لا يستطيع تشفير الرساله\n\nمن فضلك أخبر صديقك بتنصيب تطبيق OpenPGP. - OpenPGP-لايوجد مفاتيح - Conversations::لا يستطيع تشفير الرساله\n\nمن فضلك أخبر صديقك بتنصيب تطبيق OpenPGP. - تلقيت رساله مشفّرة .. لمسه بأناملك لعرضها. - عام - الريسورس - ضبط استقبال الملفات - اقبل تلقائيا الملفات أقل من - اعدادات الإشعارات - الإشعارات - أخبرني عندما تصل رساله جديده - إعداد الإهتزاز - تفعيل الاهتزاز عندما تصل رساله جديده - التبيه الصوتي - سماع صوت عندما تصل رساله - اعدادات متقدمّة - لا ترسل تقارير أخطاء - الغاء ارسال تقارير الأخطاء يقلل من فرص حل المشكلة سريعا فكن متعاون - تأكيد الرسالة - قبول - حدث خطأ ما - منح تحديثات الظهور - اسأل واقبل السؤال عن تحديثات الظهور - حسابك - مفاتيح - ارسال تحديثات الظهور - اختيار صورة - التقاط صورة - الملف غير موجود - غير معروف - متصل - غير متصل - غير مصرح له - لا يمكن الاتصال بالسرفر - تحقق من اتصالك بالانترنت - فشل تسجيل حساب بالسيرفر - اسم المستخدم مستخدم من قبل - تم تسجيل حسابك بنجاح - تسجيل الحسابات غير متاح على هذا السرفر - OTRرسالة مشفرة عبر - OpenPGPرسالة مشفرة عبر - تعديل الحساب - حذف الحساب - نشر الصورة الرمزية - تفعيل الحساب - هل أنت متأكد ؟ - اذا مسحت حسابك ستفقد جميع الرسائل المحفوظه !! - تسجيل صوت - حساب جابر - كلمة السر - username@example.com - تأكيد كلمة السر - كلمة السر - تأكيد كلمة السر - الكلمتان غير متطابقتان - حساب جابر غير صالح - متصل - متاح للدردشة - بعيد - بعيد جدا - مشغول - غير متصل - الغرف - المشتركين - معلومات السرفر - متاح - غير متاح - آخر ظهور الآن - آخر ظهور منذ 1 دقيقة - آخر ظهور منذ %d دقيقة - آخر ظهور منذ 1 ساعة - آخر ظهور منذ %d ساعة - آخر ظهور منذ 1 يوم - آخر ظهور منذ %d يوم - لم يظهر متصلا حتى الآن - تأكيد - الغرف - بحث - اضافة جهة اتصال - دخول الغرف - حذف جهة اتصال - عرض بيانات جهة الاتصال - حجب جهة اتصال - الغاء حجب جهة اتصال - أضف - جهة الاتصال موجودة لديك مسبقا - دخول - اسم الغرفة كامل - room@conference.example.com - حفظ بالمفضلة - حذف من المفضلة - موجوده بالمفضلة سابقا - انت - تعديل موضوع الغرفة - الغرفة غير متاحه .. تأكد من عنوان الغرفة - غادر - جهة اتصال أضافتك - نشر - نشر ... - الى %s - ارسال رسالة خاصة الى %s - اتصال - الحساب موجود من قبل - التالي - معلومات اضافية - تجاهل - ايقاف التنبيهات - ايقاف التنبيهات لتلك المحادثة - التنبيهات غير فعاله - تفعيل - الغرفة تطلب كلمة مرور - أدخل كلمة المرور - اطلب الآن - تجاهل - أخرى - اسم الغرفة - أنت مفصول في هذه الغرفة - الغرفة للأعضاء فقط - تم طردك من الغرفة - انقطع الإتصال .. حاول مرة أخرى - خيارات الرساله - نسخ النص - نص الرسالة - تفاصيل الحساب - سكان - تأكيد - جاري الاتصال - فشل الاتصال - حاول مرة أخرى - انهاء - تأكيد! - Conversations - احتفظ بالتطبيق يعمل في المقدمة - منع نظام التشغيل من انهاء اتصالك - اختيار ملف - اكتمل الإستلام %1$s (%2$d%% بنسبة) - تنزيل %s - ملف - فتح %s - إكتمل الإرسال (%1$d%% بنسبة) - إعداد ارسال الملفات - %s عرض وتنزيل - الغاء الارسال - تعذر ارسال الملف - تم مسح الملف - لا يوجد تطبيق متاح لعرض الملف - لا يمكن التحقق من البصمة - تأكيد يدوي - هل ترغب في تأكيد بصمات OTR لجهات اتصالك ؟ - عرض العلامات التلقائية - عرض العلامات للقراءة فقط أسفل بيانات جهات الإتصال - تفعيل الإشعارات - انشاء غرفة بــ - سيرفر الغرف غير موجود - فشل انشاء الغرفة - تم انشاء الرغرفة بنجاح - الاجابة السريّة مقبولة - إعادة تعيين - الصورة الرمزية للحساب - نسخ OTR بصمات الأصابع إلى الحافظة - جلب المحفوظات من السرفر - لا مزيد من المحفوظات بالسرفر - جاري التحديث.. - تم تغيير كلمة السر! - لايمكن تغيير كلمة السر - ارسل رساله لبدأ محادثة مشفّرة - أسال سؤال - سترغب جهة الإتصال بتأكيد بصمتك عبر السر المشترك بينكما لذلك أخبره تلميحا أو إسأله سؤالا يذكره بالسر ليكتبه برده - التلميح الذي يراه صديقك لا يمكن ان يكون فارغ - السر المشترك بينكما لا يمكن ان يترك فارغا !! - بعناية قارن بين بصمتك المعروضه أدناه وبصمة جهات اتصالك\n يمكنك استخدام أي شكل موثوق به للاتصال مثل بريد إلكتروني مشفر أو مكالمة هاتفية لتبادلها. - تغيير كلمة المرور - كلمة المرور الحالية - كلمة مرور جديدة - كلمة المرور لا يمكن ان تبقى فارغة - تفعيل كل الحسابات - تعطيل كل الحسابات - تنفيذ الإجراء مع - زائر - لا دور - مفصول - عضو - الوضع المتقدم - منح عضوية - إلغاء عضوية - منح امتيازات الإداره - إلغاء امتيازات الإدارة - إزالة من الغرفة - لا يمكن تغيير انتساب %s - حظر من الغرفة - تحاول انهاء تواجد %s الغرفه. سيتم فصله - حظر الآن - لا يمكن تغيير دول %s - الوصول العام للغرفة - الخاص , اعضاء الغرفة فقط - اعدادت المؤتمر - لا تسمح للمجهولين بالدخول - تم تعديل اعدادات المؤتمر! - لا يمكن تعديل اعدادات المؤتمر - أبداً - 30 دقيقة - ساعة - ساعتين - 8 ساعات - حتى إشعار آخر - خيارات الادخال - أدخل للإرسال - استخدام مفتاح الدخول لإرسال رسالة - عرض مفتاح الادخال - تغيير مفتاح الرموز إلى مفتاح الدخول - صوت - فيديو - صورة - مستند PDF - تطبيق اندرويد - تواصل - تم التلقي %s - ايقاف عرض تنويهات الخدمات على رئيسية الهاتف - ألمس لفتح المحادثات - تم نشر الصورة! - ارسال %s - عرض %s - اخفاء غير المتصلين - ايقاف الحساب - diff --git a/src/main/res/values-ar-rSY/strings.xml b/src/main/res/values-ar-rSY/strings.xml deleted file mode 100644 index c757504ac..000000000 --- a/src/main/res/values-ar-rSY/strings.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index c757504ac..7bffdf248 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -1,2 +1,316 @@ - + + إعدادات + محادثة جديدة + إدارة الحسابات + إنهاء المحادثة + بيانات جهة الإتصال + بيانات الغرفة + تشفير المحادثة + إضافة حساب + تعديل الإسم + إضافة لسجل الهاتف + حذف من الإضافات + حجب جهة إتصال + إنهاء حجب جهة اتصال + حجب دومين + إنهاء حجب دومين + إدارة الحسابات + إعدادات + بيانات الغرفه + بيانات جهة الإتصال + مشاركة مع محادثة + بدأ محادثة + إختيار جهة اتصال + قائمة المحجوبين + الآن + منذ 1 دقيقة + دقائق %d منذ + محادثات غير مقروءة + ارسال + رسالة مشفّرة .. الرجاء الإنتظار + اللقب مستخدم من قبل + مدير + مالك + مشرف + مشترك + زائر + هل ترغب بحذف جهة الإتصال %s من اضافاتك? ستبقى محادثاتك معه محفوظه + هل ترغب في حجب %s من ارسال الرسائل لك? + هل ترغب في انهاء حجب %s والسماح له بمراسلتك? + هل تريد حجب جميع جهات الإتصال من %s? + الغاء حجب جميع جهات الإتصال من %s? + جهة الاتصال محجوبه + هل ترغب في حذف %s من المفضلات? المحادثات المحفوظه ستبقى كما هي + تسجيل حساب جديد في سيرفر + تغيير كلمة المرور في سيرفر + مشاركة مع + بدأ محادثة + دعوة صديق + جهات الإتصال + الغاء + تعيين + اضافة + تعديل + حذف + حجب + الغاء حجب + حفظ + موافق + توقف التطبيق عن العمل + بموافقتك على ارسال تقارير الأخطاء ستساعد مبرمجي التطبيق على تحسين جودة التطبيق\nتنويه: سيرسل تقارير الأخطاء ان وجدت بأحد الحسابات التي أضفتها. + ارسال الآن + لا تسألني ثانية + لا يمكن تسجيل الدخول لحسابك + لا يمكن تسجيل الدخول بحساباتك + المس الشاشه لعرض المحادثات + ارفاق ملف + جهة الاتصال ليست مضافه لديك هل ترغب في إضافتها ؟؟ + اضافة جهة اتصال + فشل التسليم + مرفوض + اعداد صورة للإرسال + حذف سجل المحفوظات + حذف سجل المحفوظات للمحادثة + هل ترغب بحذف جميع الرسائل في تلك المحادثة?\n\nتنويه: هذا لن يؤثر على الرسائل المخزنة على الأجهزة أو أي أماكن أخرى. + حذف الرسائل + انهاء هذه المحادثة بعد الكلمات + اختيار ظهورك لجهات الإتصال + OTRارساله رساله مشفره عبر + OpenPGPارساله رساله مشفره عبر + تم تغيير لقبك بنجاح + إرسال بدون تشفير + فشل فك التشفير. ربما لم يكن لديك المفتاح الخاص الصحيح. + OpenKeychain + Conversations :: يستخدم تطبيق آخر يسمى OpenKeychain لتشفير وفك تشفير الرسائل وإدارة المفاتيح العامة الخاصة بك \n\nOpenKeychain تحت الرخصة GPLv3 و لتحميل التطبيق من جوجل بلاي \n\n (وأعد تشغيل التطبيق مرة أخرى) + اعادة تشغيل + تثبيت + عرض .. + انتظار .. + OpenPGP-لايوجد مفتاح + Conversations::لا يستطيع تشفير الرساله\n\nمن فضلك أخبر صديقك بتنصيب تطبيق OpenPGP. + OpenPGP-لايوجد مفاتيح + Conversations::لا يستطيع تشفير الرساله\n\nمن فضلك أخبر صديقك بتنصيب تطبيق OpenPGP. + تلقيت رساله مشفّرة .. لمسه بأناملك لعرضها. + عام + الريسورس + ضبط استقبال الملفات + اقبل تلقائيا الملفات أقل من + اعدادات الإشعارات + الإشعارات + أخبرني عندما تصل رساله جديده + إعداد الإهتزاز + تفعيل الاهتزاز عندما تصل رساله جديده + التبيه الصوتي + سماع صوت عندما تصل رساله + اعدادات متقدمّة + لا ترسل تقارير أخطاء + الغاء ارسال تقارير الأخطاء يقلل من فرص حل المشكلة سريعا فكن متعاون + تأكيد الرسالة + قبول + حدث خطأ ما + منح تحديثات الظهور + اسأل واقبل السؤال عن تحديثات الظهور + حسابك + مفاتيح + ارسال تحديثات الظهور + اختيار صورة + التقاط صورة + الملف غير موجود + غير معروف + متصل + غير متصل + غير مصرح له + لا يمكن الاتصال بالسرفر + تحقق من اتصالك بالانترنت + فشل تسجيل حساب بالسيرفر + اسم المستخدم مستخدم من قبل + تم تسجيل حسابك بنجاح + تسجيل الحسابات غير متاح على هذا السرفر + OTRرسالة مشفرة عبر + OpenPGPرسالة مشفرة عبر + تعديل الحساب + حذف الحساب + نشر الصورة الرمزية + تفعيل الحساب + هل أنت متأكد ؟ + اذا مسحت حسابك ستفقد جميع الرسائل المحفوظه !! + تسجيل صوت + حساب جابر + كلمة السر + username@example.com + تأكيد كلمة السر + كلمة السر + تأكيد كلمة السر + الكلمتان غير متطابقتان + حساب جابر غير صالح + متصل + متاح للدردشة + بعيد + بعيد جدا + مشغول + غير متصل + الغرف + المشتركين + معلومات السرفر + متاح + غير متاح + آخر ظهور الآن + آخر ظهور منذ 1 دقيقة + آخر ظهور منذ %d دقيقة + آخر ظهور منذ 1 ساعة + آخر ظهور منذ %d ساعة + آخر ظهور منذ 1 يوم + آخر ظهور منذ %d يوم + لم يظهر متصلا حتى الآن + تأكيد + الغرف + بحث + اضافة جهة اتصال + دخول الغرف + حذف جهة اتصال + عرض بيانات جهة الاتصال + حجب جهة اتصال + الغاء حجب جهة اتصال + أضف + جهة الاتصال موجودة لديك مسبقا + دخول + اسم الغرفة كامل + room@conference.example.com + حفظ بالمفضلة + حذف من المفضلة + موجوده بالمفضلة سابقا + انت + تعديل موضوع الغرفة + الغرفة غير متاحه .. تأكد من عنوان الغرفة + غادر + جهة اتصال أضافتك + نشر + نشر ... + الى %s + ارسال رسالة خاصة الى %s + اتصال + الحساب موجود من قبل + التالي + معلومات اضافية + تجاهل + ايقاف التنبيهات + ايقاف التنبيهات لتلك المحادثة + التنبيهات غير فعاله + تفعيل + الغرفة تطلب كلمة مرور + أدخل كلمة المرور + اطلب الآن + تجاهل + أخرى + اسم الغرفة + أنت مفصول في هذه الغرفة + الغرفة للأعضاء فقط + تم طردك من الغرفة + انقطع الإتصال .. حاول مرة أخرى + خيارات الرساله + نسخ النص + نص الرسالة + تفاصيل الحساب + سكان + تأكيد + جاري الاتصال + فشل الاتصال + حاول مرة أخرى + انهاء + تأكيد! + Conversations + احتفظ بالتطبيق يعمل في المقدمة + منع نظام التشغيل من انهاء اتصالك + اختيار ملف + اكتمل الإستلام %1$s (%2$d%% بنسبة) + تنزيل %s + ملف + فتح %s + إكتمل الإرسال (%1$d%% بنسبة) + إعداد ارسال الملفات + %s عرض وتنزيل + الغاء الارسال + تعذر ارسال الملف + تم مسح الملف + لا يوجد تطبيق متاح لعرض الملف + لا يمكن التحقق من البصمة + تأكيد يدوي + هل ترغب في تأكيد بصمات OTR لجهات اتصالك ؟ + عرض العلامات التلقائية + عرض العلامات للقراءة فقط أسفل بيانات جهات الإتصال + تفعيل الإشعارات + انشاء غرفة بــ + سيرفر الغرف غير موجود + فشل انشاء الغرفة + تم انشاء الرغرفة بنجاح + الاجابة السريّة مقبولة + إعادة تعيين + الصورة الرمزية للحساب + نسخ OTR بصمات الأصابع إلى الحافظة + جلب المحفوظات من السرفر + لا مزيد من المحفوظات بالسرفر + جاري التحديث.. + تم تغيير كلمة السر! + لايمكن تغيير كلمة السر + ارسل رساله لبدأ محادثة مشفّرة + أسال سؤال + سترغب جهة الإتصال بتأكيد بصمتك عبر السر المشترك بينكما لذلك أخبره تلميحا أو إسأله سؤالا يذكره بالسر ليكتبه برده + التلميح الذي يراه صديقك لا يمكن ان يكون فارغ + السر المشترك بينكما لا يمكن ان يترك فارغا !! + بعناية قارن بين بصمتك المعروضه أدناه وبصمة جهات اتصالك\n يمكنك استخدام أي شكل موثوق به للاتصال مثل بريد إلكتروني مشفر أو مكالمة هاتفية لتبادلها. + تغيير كلمة المرور + كلمة المرور الحالية + كلمة مرور جديدة + كلمة المرور لا يمكن ان تبقى فارغة + تفعيل كل الحسابات + تعطيل كل الحسابات + تنفيذ الإجراء مع + زائر + لا دور + مفصول + عضو + الوضع المتقدم + منح عضوية + إلغاء عضوية + منح امتيازات الإداره + إلغاء امتيازات الإدارة + إزالة من الغرفة + لا يمكن تغيير انتساب %s + حظر من الغرفة + تحاول انهاء تواجد %s الغرفه. سيتم فصله + حظر الآن + لا يمكن تغيير دول %s + الوصول العام للغرفة + الخاص , اعضاء الغرفة فقط + اعدادت المؤتمر + لا تسمح للمجهولين بالدخول + تم تعديل اعدادات المؤتمر! + لا يمكن تعديل اعدادات المؤتمر + أبداً + 30 دقيقة + ساعة + ساعتين + 8 ساعات + حتى إشعار آخر + خيارات الادخال + أدخل للإرسال + استخدام مفتاح الدخول لإرسال رسالة + عرض مفتاح الادخال + تغيير مفتاح الرموز إلى مفتاح الدخول + صوت + فيديو + صورة + مستند PDF + تطبيق اندرويد + تواصل + تم التلقي %s + ايقاف عرض تنويهات الخدمات على رئيسية الهاتف + ألمس لفتح المحادثات + تم نشر الصورة! + ارسال %s + عرض %s + اخفاء غير المتصلين + ايقاف الحساب + الغاء + diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index d2235f181..74461b720 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -20,7 +20,7 @@ Konferenz-Details Kontakt-Details Mit Unterhaltung teilen - Beginne Unterhaltung + Unterhaltung beginnen Kontakt auswählen Sperrliste gerade @@ -28,7 +28,7 @@ vor %d Minuten ungelesene Unterhaltungen senden… - Entschlüssele Nachricht. Bitte warten… + Nachricht wird entschlüsselt. Bitte warten … Nickname wird bereits verwendet Administrator Eigentümer @@ -38,14 +38,14 @@ Möchtest du %s von deiner Kontaktliste entfernen? Die Unterhaltung mit diesem Kontakt wird dabei nicht entfernt. Möchtest du %s sperren und keine Nachrichten mehr erhalten? Möchtest du %s entsperren und wieder Nachrichten empfangen? - Sperre alle Kontakte von %s? - Entsperre alle Kontakte %s? + Alle Kontakte von %s sperren? + Alle Kontakte von %s entsperren? Kontakt gesperrt Möchtest du %s von deiner Kontaktliste entfernen? Die Unterhaltung mit dieser Konferenz wird dabei nicht entfernt. Neues Konto auf dem Server erstellen Passwort ändern Teile mit… - Beginne Unterhaltung + Unterhaltung beginnen Kontakt einladen Kontakte Abbrechen @@ -63,11 +63,11 @@ Nie mehr nachfragen Es gibt Probleme beim Verbindungsaufbau mit einem Konto Es gibt Probleme beim Verbindungsaufbau mit mehreren Konten - Drücke hier, um das Konto zu verwalten + Hier antippen, um Ihre Konten zu verwalten Datei anfügen Der Kontakt ist nicht in deiner Kontaktliste. Möchtest du ihn hinzufügen? - Zur Kontaktliste hinzufügen - Zustellung nicht erfolgreich + Kontakt hinzufügen + Zustellung fehlgeschlagen abgelehnt Bereite Bild für die Übertragung vor Verlauf löschen @@ -81,7 +81,7 @@ OMEMO-verschlüsselt schreiben… OpenPGP-verschlüsselt schreiben… Dein Nickname wurde geändert - Unverschlüsselt verschicken + Unverschlüsselt senden Entschlüsselung fehlgeschlagen. Vielleicht hast du nicht den richtigen privaten Schlüssel. OpenKeychain Conversations benutzt eine Drittanwendung namens OpenKeychain, um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3-lizenziert und kann über F-Droid oder Google Play bezogen werden.\n\n(Bitte starte Conversations danach neu.) @@ -97,21 +97,21 @@ Allgemeines XMPP-Ressource Der Name, mit dem sich der Client selbst identifiziert - Dateiannahme - Dateien, die kleiner sind als …, automatisch annehmen + Dateien annehmen + Dateien automatisch annehmen, die kleiner sind als … Benachrichtigungen Benachrichtigungen - Benachrichtige mich, wenn eine neue Nachricht ankommt + Benachrichtigen bei Erhalt einer neuen Nachricht Vibrieren - Vibriere, wenn eine neue Nachricht ankommt - Klingelton - Spiele Klingelton, wenn eine neue Nachricht ankommt - öffentliche Konferenz-Benachrichtigungen + Vibrieren bei Erhalt einer neuen Nachricht + Benachrichtigungston + Benachrichtigungston wiedergeben + Benachrichtigungen in öffentlichen Konferenzen Bei jeder Nachricht in öffentlichen Konferenzen benachrichtigen und nicht nur, wenn ich angesprochen werde Gnadenfrist Deaktiviere Benachrichtigungen für eine kurze Zeit nach Erhalt einer Nachricht, die von einem anderen deiner Clients kommt. Erweiterte Optionen - Sende niemals Absturzberichte + Niemals Absturzberichte senden Wenn du Absturzberichte einschickst, hilfst du Conversations stetig zu verbessern Lese- und Empfangsbestätigung senden Informiere deine Kontakte, wenn du eine Nachricht empfangen und gelesen hast @@ -130,7 +130,7 @@ Online-Status anfragen Bild auswählen Bild aufnehmen - Erlaube Statusanfrage vorab + Statusanfragen vorab erlauben Die ausgewählte Datei ist kein Bild Fehler beim Umwandeln des Bildes Datei nicht gefunden @@ -174,14 +174,14 @@ Zu wenig Speicher vorhanden. Das Bild ist zu groß Möchtest du %s zum Telefonbuch hinzufügen? online - Bereit - Abwesend - Abwesend (erweitert) - Nicht stören - Offline + bereit + abwesend + länger abwesend + nicht stören + offline Konferenz Andere Mitglieder - Server Info + Server-Info XEP-0313: MAM XEP-0280: Message Carbons XEP-0352: CSI @@ -204,7 +204,7 @@ Verschlüsselte Nachricht. Bitte installiere OpenKeychain zur Entschlüsselung. Unbekannter OTR-Fingerabdruck Verschlüsselte OpenPGP-Nachricht gefunden - Empfang ist fehlgeschlagen + Empfang fehlgeschlagen Dein Fingerabdruck OTR-Fingerabdruck OMEMO-Fingerabdruck @@ -214,12 +214,12 @@ OMEMO-Fingerabdruck vertrauen Schlüssel abrufen... Erledigt - Verifizieren + Überprüfen Entschlüsseln Konferenzen - Suche + Suchen Kontakt erstellen - Konferenz betreten + Konferenz beitreten Kontakt löschen Kontakt-Details anzeigen Kontakt sperren @@ -233,19 +233,19 @@ Von Kontaktliste entfernen Die Konferenz befindet sich bereits auf deiner Kontaktliste Du - Konferenz-Thema anpassen + Konferenz-Thema bearbeiten Konferenz nicht gefunden Verlassen Der Kontakt hat dich zur Kontaktliste hinzugefügt Auch hinzufügen %s hat bis zu diesem Punkt gelesen Veröffentlichen - Hier klicken, um einen Avatar auszuwählen + Avatar anklicken, um ein Bild aus der Galerie auszuwählen Achtung: Jeder, der deinen Status sehen darf, sieht auch deinen Avatar. Veröffentliche… Der Server hat die Veröffentlichung des Avatars abgelehnt. Bei der Konvertierung des Avatars lief etwas schief. - Kann Avatar nicht speichern. + Avatar kann nicht gespeichert werden (Oder klicke lange, um Standard wiederherzustellen) Dein Server unterstützt die Veröffentlichung von Avataren nicht. private Nachricht: @@ -263,13 +263,13 @@ Aktivieren Konferenz ist passwortgeschützt Passwort eingeben - Fehlender Online-Status vom Kontakt + Fehlender Online-Status des Kontakts Bitte erst Online-Status vom Kontakt anfragen.\n\nDies wird verwendet, um festzustellen, welche Client(s) der Kontakt benutzt. Jetzt anfordern Fingerabdruck löschen - Soll dieser Fingerabdruck gelöscht werden? + Soll dieser Fingerabdruck wirklich gelöscht werden? Ignorieren - Achtung: Ohne gegenseitig den Online-Status zu kennen, kann es zu unerwarteten Problemen kommen.\n\nBitte die Einstellungen in den Kontakt-Details prüfen. + Achtung: Ohne gegenseitige Kenntnis des Online-Status kann es zu unerwarteten Problemen kommen.\n\nBitte die Einstellungen in den Kontakt-Details prüfen. Verschlüsselungs-Einstellungen Ende-zu-Ende-Verschlüsselung erzwingen Nachrichten immer verschlüsseln (außer für Konferenzen) @@ -282,14 +282,14 @@ Ruhige Stunden Beginn Ende - Aktiviere ruhige Stunden + Ruhige Stunden aktivieren Benachrichtigungen sind während der ruhigen Stunden stumm. Schrift vergrößern Größere Schrift verwenden - Absende-Knopf zeigt Online-Status an - Anfrage für Nachrichtenempfang + \"Senden\"-Schaltfläche zeigt Online-Status an + Empfangsbestätigungen anfragen Empfangene Nachrichten werden mit einem grünen Häkchen markiert. Bitte beachte, dass dies nicht in allen Fällen funktioniert. - Absende-Knopf einfärben, um den Online-Status des Kontakts zu signalisieren + \"Senden\"-Schaltfläche einfärben, um den Online-Status des Kontakts anzuzeigen Sonstiges Konferenz-Name Konferenz-Thema statt Raum-JID als Namen verwenden @@ -298,8 +298,8 @@ Du wurdest von der Konferenz ausgeschlossen Die Konferenz ist nur für Mitglieder Du wurdest aus der Konferenz geworfen - Verwende Konto %s - %s wird auf HTTP-Host geprüft + Verwendetes Konto: %s + %s auf HTTP-Host wird überprüft Nicht verbunden, bitte später versuchen %s-Größe prüfen Nachrichtenoptionen @@ -311,18 +311,18 @@ URL in Zwischenablage kopiert Nachricht in Zwischenablage kopiert Bild-Übertragung fehlgeschlagen - Scanne QR-Code - Zeige QR-Code - Zeige Sperrliste + QR-Code scannen + QR-Code anzeigen + Sperrliste anzeigen Konto-Details - Prüfe OTR + OTR prüfen Fingerabdruck der Gegenseite - Scanne + Scannen (oder Touch-Handys) Socialist Millionaire Protocol Hinweis oder Frage Gemeinsamer Schlüssel - Bestätige + Bestätigen In Bearbeitung Antworten Fehlgeschlagen @@ -331,31 +331,31 @@ Fertig Überprüft! Kontakt fordert eine Überprüfung an - Keine gültige OTR Sitzung gefunden! + Keine gültige OTR-Sitzung gefunden! Conversations Den Dienst im Vordergrund ausführen. Verhindert, dass Android Conversations beendet und die Verbindung unterbricht Datei auswählen Empfange %1$s (%2$d%% abgeschlossen) - Lade %s herunter + %s wird heruntergeladen Datei - Öffne %s - Sende (%1$d%% gesendet) - Bereite Datei für die Übertragung vor + %s öffnen + Senden (%1$d%% abgeschlossen) + Datei wird für den Versand vorbereitet %s zum Herunterladen angeboten Datei-Übertragung abbrechen Datei-Übertragung fehlgeschlagen Datei wurde gelöscht Keine Anwendung zum Öffnen der Datei gefunden - Kann Fingerabdruck nicht überprüfen + Fingerabdruck konnte nicht überprüft werden Manuell überprüfen Bist du sicher, dass du den OTR-Fingerabdruck des Kontakts überprüfen willst? Dynamische Tags anzeigen - Zeige schreibgeschützte Tags unterhalb der Kontakte - Aktiviere Benachrichtigungen - Beginne Konferenz mit… + Schreibgeschütze Tags unterhalb der Kontakte anzeigen + Benachrichtigungen aktivieren + Konferenz erstellen mit… Konferenz-Server kann nicht gefunden werden - Beginnen der Konferenz fehlgeschlagen! + Erstellen der Konferenz fehlgeschlagen! Konferenz erstellt! Schlüssel akzeptiert! Zurücksetzen @@ -369,11 +369,11 @@ Schlüssel löschen Soll dieser Schlüssel gelöscht werden? Dieser Vorgang kann nicht rückgängig gemacht werden und es kann nie wieder eine Verbindung mit diesem Schlüssel hergestellt werden. - Es sind keine Schlüssel für diesen Kontakt verfügbar. Falls diese gelöscht wurden, müssen diese neu erstellt werden. + Es sind keine Schlüssel für diesen Kontakt verfügbar. Falls sie gelöscht wurden, müssen diese neu erstellt werden. Fehler Lade Chatverlauf… Keine weiteren Nachrichten vorhanden - Aktualisiere… + Aktualisieren… Passwort geändert. Passwort kann nicht geändert werden. Sende eine Nachricht, um eine verschlüsselte Unterhaltung zu beginnen @@ -394,7 +394,7 @@ Keine Rolle Ausgeschlossen Mitglied - Experten Modus + Erweiterter Modus Mitgliedschaft gewähren Mitgliedschaft entziehen Administratorrechte gewähren @@ -419,12 +419,12 @@ 1 Stunde 2 Stunden 8 Stunden - Bis auf weiters + Bis auf Weiteres Eingabe-Optionen Eingabe-Taste (Enter) sendet Nachricht - Benutze die Eingabe-Taste (Enter) zum Senden einer Nachricht + Eingabe-Taste (Enter) zum Versenden einer Nachricht verwenden Zeige Eingabe-Taste (Enter) - Zeige die Eingabe-Taste (Enter) anstelle der Smiley-Taste + Emoji-Taste durch Eingabe-Taste ersetzen Audio Video Bild @@ -433,11 +433,11 @@ Kontakt %s empfangen Vordergrund-Dienst beenden - Tippen, um Conversations zu öffnen + Antippen, um Conversations zu öffnen Avatar wurde gespeichert - Sende %s - %s angeboten - verstecke offline + %s wird gesendet + %s wird angeboten + Offline verstecken Konto abschalten %s schreibt… %s schreibt nicht mehr @@ -450,7 +450,7 @@ Standort empfangen Unterhaltung beendet Konferenz verlassen - Misstraue Zertifizierungsstellen + Zertifizierungsstellen misstrauen Alle Zertifikate müssen manuell bestätigt werden Zertifikate löschen Als vertrauenswürdig bestätigte Zertifikate löschen diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 68daf846e..a344be2bb 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -1,16 +1,20 @@ הגדרות - דיון חדש + שיחה חדשה נהל חשבונות - סיים את דיון זה + סיים שיחה זו פרטי איש קשר פרטי ועידה דיון מאובטח הוסף חשבון ערוך שם הוסף אל פנקס טלפונים - מחק מתוך רשימה + מחק מרשימת אנשי הקשר + חסום איש קשר + בטל חסימת איש קשר + חסום דומיין + בטל חסימת דומיין נהל חשבונות הגדרות פרטי ועידה @@ -18,86 +22,100 @@ שתף בעזרת Conversations התחל דיון בחר איש קשר - רק כעת - לפני דקה 1 + רשימת חסימה + ממש עכשיו + לפני דקה לפני %d דקות - דיונים שלא נקראו - כעת שולח… - כעת מפענח הודעה. אנא המתן… - שם כינוי כבר מצוי בשימוש + שיחות שלא נקראו + שולח... + מפענח הודעה. אנא המתן… + שם כינוי כבר בשימוש מנהל בעלים - אחראי + אחראי (Moderator) משתתף מבקר - האם ברצונך להסיר את %s מתןך הרשימה שלך? הדיונים אשר משוייכים עם חשבון זה לא יוסרו. - האם ברצונך להסיר את %s בתוור סימנייה? הדיונים אשר משוייכים עם סימנייה זו לא יוסרו. - רשום חשבון חדש על שרת - שתף בעזרת - התחל דיון + האם ברצונך להסיר את %s מתוך רשימת אנשי הקשר? השיחה המשוייכת עם איש קשר זה לא תוסר. + האם ברצונך לחסום קבלת הודעות מ- %s? + האם ברצונך לבטל את החסימה ולאפשר קבלת הודעות מ- %s? + לחסום את כל האנשים מ- %s? + לבטל את חסימת כל האנשים מ- %s? + איש קשר נחסם + האם ברצונך להסיר את %s בתור סימנייה? הדיונים אשר משוייכים עם סימנייה זו לא יוסרו. + צור חשבון חדש בשרת + שינוי סיסמה בשרת + שתף באמצעות... + התחל שיחה הזמן איש קשר אנשי קשר ביטול הוסף ערוך מחק + חסום + בטל חסימה שמור אישור Conversations קרסה - על ידי שליחת עקבות מחסנית אתה עוזר להתקדמות הפיתוח של Conversations\nאזהרה: זו תעשה שימוש בחשבון XMPP שלך כדי לשלוח עקבות מחסנית אל המפתח. + על ידי שליחת Stacktraces אתה עוזר להתקדמות הפיתוח של Conversations\nאזהרה: פעולה זו תעשה שימוש בחשבון ה- XMPP שלך כדי לשלוח עקבות מחסנית אל המפתח. שלח עכשיו לעולם אל תשאל שוב - לא מסוגל להתחבר אל חשבון - לא מסוגל להתחבר אל חשבונות מרובים + התחברות לחשבון נכשלה + התחברות למספר חשבונות נכשלה לחץ כאן כדי לנהל את החשבונות שלך צרף קובץ - איש קשר אינו מצוי בתוך הרשימה שלך. האם ברצונך להוסיפו? + איש קשר אינו מצוי בתוך רשימת אנשי הקשר שלך. האם ברצונך להוסיפו? הוסף איש קשר מסירה נכשלה סורב - כעת מכין תצלום לשם תמסורת - טהר היסטוריה - טהר היסטוריית דיונים - האם ברצונך למחוק את כל ההודעות בתוך דיון זה?\n\nאזהרה: זו לא תשפיע על הודעות מאוחסנות על מכשירים או שרתים אחרים. + מכין תמונה לשליחה + נקה היסטוריה + נקה היסטוריית שיחה + האם ברצונך למחוק את כל ההודעות בשיחה זאת?\n\nאזהרה: פעולה זו לא תשפיע על הודעות מאוחסנות על מכשירים או שרתים אחרים. מחק הודעות - סיים את דיון זה לאחר מכן + בנוסף, סיים שיחה זו בחר נוכחות לאיש קשר - שלח הודעה מוצפנת OTR - שלח הודעה מוצפנת OpenPGP - שם כינוי שלך השתנה - שלח לא מוצפנת + שלח הודעה בלתי מוצפנת + שלח הודעה בהצפנת OTR + של הודעה בהצפנת OMEMO + שלח הודעה בהצפנת OpenPGP + שם הכינוי שלך השתנה + שלח ללא הצפנה פענוח נכשל. אולי אין לך את המפתח הפרטי המתאים. OpenKeychain - Conversations מפיקה תועלת מן אפליקציית צד-שלישי הקרויה OpenKeychain כדי להצפין ולפענח הודעות וגם כדי לנהל את המפתחות הפומביים שלך.\n\nOpenKeychain הינה רשויה תחת GPLv3 וזמינה אצל F-Droid וגם Google Play.\n\n(אנא התחל מחדש את Conversations לאחר מכן.) + Conversations מסתמכת על אפליקציית צד-שלישי הקרויה OpenKeychain כדי להצפין ולפענח הודעות וגם כדי לנהל את המפתחות הפומביים שלך.\n\nOpenKeychain הינה רשויה תחת GPLv3 וזמינה ב F-Droid וגם ב Google Play.\n\n(אנא התחל מחדש את Conversations לאחר מכן.) התחל מחדש התקן - כעת מציע… - כעת ממתין… + מציע… + ממתין… לא נמצא מפתח OpenPGP - Conversations אינה מסוגלת להצפין את הודעותיך משום שאיש הקשר שלך אינו מכריז על המפתח הפומבי שלו או שלה.\n\nאנא בקש מאיש הקשר שלך לארגן OpenPGP. + Conversations אינה מסוגלת להצפין את הודעותיך משום שאיש הקשר שלך אינו מכריז על המפתח הפומבי שלו או שלה.\n\nאנא בקש מאיש הקשר שלך להגדיר את OpenPGP. לא נמצאו מפתחות OpenPGP Conversations אינה מסוגלת להצפין את הודעותיך משום שאנשי הקשר שלך אינם מכריזים על המפתח הפומבי שלהם.\n\nאנא בקש מאנשי הקשר שלך לארגן OpenPGP. הודעה מוצפנת התקבלה. לחץ כדי לצפות ולפענח. - משאב XMPP - השם שלקוח זה מזהה את עצמו עם + כללי + XMPP resource + השם שאפליקציה זו שולחת לשרת קבל קבצים - קבל אוטומטית קבצים קטנים יותר מאשר… - הגדרות התראה + קבל אוטומטית קבצים שגודלם קטן מ… + הגדרות התראות התראות תודיע כאשר הודעה חדשה מגיעה הרטט הרטט גם כאשר הודעה חדשה מגיעה צליל - נגן צלצול עם התראה - משך ארכת התראה - נטרל התראות לזמן קצר לאחר שהודעת פחם התקבלה + נגן צלצול עם כל התראה + התראות בועידות ציבוריות + תמיד + משך תקופת ארכה + נטרל התראות לזמן קצר לאחר שהודעת Carbon Copy מתקבלת אפשרויות מתקדמות לעולם אל תשלח דיווחי קריסה על ידי שליחת עקבות מחסנית אתה עוזר להתקדמות הפיתוח של Conversations אשר הודעות אפשר לאיש קשר שלך לדעת מתי קיבלת וקראת הודעה אפשרויות ממשק משתמש - OpenKeychain דיווח שגיאה + אפליקציית OpenKeychain דיווחה על שגיאה שגיאת I/O פענוח קובץ קבל אירעה שגיאה @@ -110,32 +128,37 @@ קבל עדכוני נוכחות בקש עדכוני נוכחות בחר תמונה - קח תמונה + צלם תמונה הענק בקשת הרשמה מראש - הקובץ שבחרת אינו תצלום - שגיאה במהלך המרת קובץ תצלום + הקובץ שבחרת אינו תמונה + שגיאה במהלך המרת תמונה קובץ לא נמצא שגיאת I/O כללית. אולי אזל לך נפח אחסון? - האפליקציה בה השתמשת כדי לבחור את תצלום זה לא סיפקה לנו מספיק הרשאות כדי לקרוא את הקובץ.\n\nהשתמש במנהל קבצים אחר כדי לבחור תצלום + האפליקציה בה השתמשת כדי לבחור תמונה זו לא סיפקה לנו מספיק הרשאות כדי לקרוא את הקובץ.\n\nהשתמש במנהל קבצים אחר כדי לבחור תצלום לא ידוע מנוטרל זמנית מקוון - כעת מתחבר\u2026 + מתחבר\u2026 לא מקוון לא מורשה שרת לא נמצא אין חיבוריות הרשמה נכשלה - שם משתמש כבר מצוי בשימוש + שם משתמש כבר בשימוש הרשמה הושלמה - שרת לא תומך הרשמה + השרת לא תומך בהרשמת משתמשים חדשים + שגיאת אבטחה + שרת לא מתאים + לא מוצפן OTR OpenPGP + OMEMO ערוך חשבון מחק נטרל זמנית - פרסם אווטאר - אפשר + פרסם תמונת פרופיל + פרסם מפתח ציבורי של OpenPGP + הפעל חשבון האם אתה בטוח? אם אתה מוחק את חשבונך כל היסטוריית הדיון שלך תאבד הקלט קול @@ -146,34 +169,50 @@ סיסמה אמת סיסמה סיסמאות לא תואמות - זה אינו מזהה Jabber תקף + מזהה ה Jabber אינו תקין חסר זיכרון. תצלום גדול מדי האם ברצונך להוסיף את %s אל רשימת קשר טלפונית? מקוון חופשי לשיחה נעדר - נעדר לזמן מה - אל תפריעו + נעדר לזמן ממושך + נא לא להפריע לא מקוון ועידה חברים אחרים - הודעות פחם - ניהול זרם + פרטי השרת + XEP-0313: MAM - היסטוריית שרת + XEP-0280: Message Carbons + XEP-0352: Client State Indication + XEP-0191: Blocking Command - חסימת אנשי קשר + XEP-0237: Roster Versioning + XEP-0198: Stream Management + XEP-0163: PEP (Avatars / OMEMO) - תמונת פרופיל והצפנת OMEMO + XEP-0363: HTTP File Upload - שליחת קבצים דרך HTTP + זמין + לא זמין הכרזות מפתח פומבי חסרות נראה לאחרונה ממש עכשיו - נראה לאחרונה לפני דקה 1 + נראה לאחרונה לפני דקה נראה לאחרונה לפני %d דקות - נראה לאחרונה לפני שעה 1 - נראה לאחרונה לפני %d שעות ago - נראה לאחרונה לפני יום 1 + נראה לאחרונה לפני שעה + נראה לאחרונה לפני %d שעות + נראה לאחרונה לפני יום אחד נראה לאחרונה לפני %d ימים לא נראה מעולם - הודעה מוצפנת. אנא התקן OpenKeychain כדי לפענח. + הודעה מוצפנת. אנא התקן את OpenKeychain כדי לפענח. טביעת אצבע OTR לא מוכרת הודעות מוצפנות OpenPGP נמצאו קבלה נכשלה טביעת אצבע שלך - טביעת אצבע OTR + טביעת אצבע של OTR + טביעת אצבע של OMEMO + טביעת אצבע OMEMO של ההודעה + טביעת אצבע OMEMO שלי + מכשירים אחרים + סמוך על טביעות אצבע OMEMO + משיג מפתחות... + בוצע אמת פענח ועידות @@ -182,6 +221,8 @@ הצטרף לועידה מחק איש קשר צפה בפרטי איש קשר + חסום איש קשר + בטל חסימת איש קשר צור איש קשר כבר קיים הצטרף @@ -197,15 +238,245 @@ איש קשר הוסיף אותך אל רשימת קשר הוסף בחזרה %s קרא עד לנקודה זו - לחץ על אווטאר כדי לבחור תמונה מתוך גלריה - לתשומת לבך: כל מי אשר רשום לעדכוני נוכחות שלך יורשה לראות את תמונה זו. - כעת מפרסם… - השרת פסל פרסום - משהו השתבש במהלך המרת תמונה - לא היה מסוגל לשמור אווטאר אל כונן - (או לחיצה ארוכה כדי להחזיר לשגרה) - שרתך לא תומך בפרסום של אווטארים + פרסם + לחץ על תמות הפרופיל כדי לבחור תמונה מתוך הגלריה + לתשומת לבך: כל מי שרשום לעדכוני נוכחות שלך יורשה לראות את תמונה זו. + מעלה… + השרת דחה את ההעלאה + משהו השתבש במהלך המרת התמונה + שגיאה בעת שמירת תמונה לזיכרון + (או לחיצה ארוכה כדי להחזיר לברירת מחדל) + השרת לא תומך בפרסום של אווטארים בפרטי בפרטי אל %s שלח הודעה פרטית אל %s + התחבר + חשבון זה כבר קיים + הבא + מידע נוסף + דלג + השבת התראות + השבת התראות עבור השיחה הנוכחית + ההתראות מושבתות + הפעל + ועידה זאת דורשת סיסמא + הכנס סיסמא + עדכוני נוסחות חסרים מאיש הקשר + נא לבקש עדכוני נוכחות מאיש הקשר קודם לכךפעולה זו תשמש כדי לקבוע את אקפליקציה שאיש הקשר משתמש בה + בקש/י כעת + מחק טביעת אצבע + האם את/ה בטוח שברצונך למחוק טביעת אצבע זו? + התעלם + אזהרה:שליחה ללא הרשאות עדכוני נוכחות הדדיות עלולה לגרום לתוצאות בלתי צפויות.\n\nהשתמש בתפריט \"פרטי משתמש\" ואשר עדכוני נוכחות + הגדרות הצפנה + אלץ הצפנת end-to-end + תמיד שלח הודעות מוצפנות (חוץ מבועידות) + אל תשמור הודעות מוצפנות + אזהרה: פעולה זו עלולה לגרום לאיבוד הודעות + הגדרות מתקדמות + נא להיזהר! + אודות Conversations + אודות גרסה ורישיון + שעות שקטות + זמן התחלה + זמן סיום + הפעל \"שעות שקטות\" + ההתראות יושבתו במהלך שעות שקטות + הגדל גופן + הגדל גופן בכל האפליקציה + לחצן השליחה מעיד על הסטטוס + בקש אימות הגעת הודעות + יופיע סימן \"וי\" ליד כל הודעה שהתקבלה על ידי איש הקשר, במידה והפעולה נתמכת על ידי איש הקשר. + צבעו של לחצן שליחת ההודעות יעיד על סטטוס איש הקשר. + אחר + שם ועידה + השתמש בכותרת הועידה ולא ב- JID כשם לועידה + טביעת אצבע של OTR הועתקה + טביעת אצבע של OMEMO הועתקה + אתה חסום מלהיכנס לועידה זו + ועידה זו אינה ציבורית + גורשת מועידה זו + משתמש בחשבון: %s + אינך מחובר. נסה שוב אחר כך + בדוק גודל %s + הגדרות הודעה + העתק טקסט + העתק קישור + שלח שוב + קישור קובץ + טקסט הודעה + הקישור הועתק + ההודעה הועתקה + שליחת התמונה נכשלה + סרוק ברקוד QR + הראה ברקוד QR + הראה רשימת חסומים + פרטי חשבון + אמת OTR + טביעת אצבע מרוחקת + סרוק + (או קיים מגע בין הניידים) + Socialist Millionaire Protocol + שאלה או רמז + סוד משותף + אמת + הפעולה מתבצעת + השב + נכשל + הסודות אינם תואמים + נסה שוב + סיים + אומת בהצלחה! + איש הקשר ביקש אימות SMP + לא נמצא OTR Session תקין + השאר שירות ב Foreground + מונע ממערכת ההפעלה לנתק את החיבור לשרת + בחר קובץ + מקבל %1$s ( הושלם %2$d%% ) + הורד %s + קובץ + פתח %s + שולח ( %1$d%% הושלם ) + מכין קובץ לשליחה + הקובץ %s הוצע לאיש הקשר + בטל שליחה + השליחה נכשלה + הקובץ נמחק + אין אפליקציה מתאימה לפתיחת הקובץ + אימות טביעת האצבע נכשל + אימות ידני + האם אתה בטוח שברצונך לאמת טביעת אצבע OTR זו? + הראה תגים דומיים + הראה תגי read-only מתחת לאנשי הקשר + אפשר התראות + צור ועידה עם... + לא נמצא שרת ועידות + יצירת הועידה נכשלה! + הועידה נוצרה! + הסוד התקבל! + איפוס + תמונת פרופיל + העתק טביעת אצבע OTR + העתק טביעת אצבע OMEMO + צור מפתח OMEMO חדש + מחק מכשרים אחרים מ- PEP + נקה מכשירים + האם אתה בטוח שברצונך לנקות את כל המכשירים מהכרזת ה OMEMO? בפעם הבאה שהמכשירים יתחברו, הם יכריזו שוב על עצמם, אך הם עלולים לאבד הודעות עד להכרזה זו. + מחק מפתח לצמיתות + האם אתה בטוח שברצונך למחוק מפתח זה לצמיתות? + הוא ייחשב ללא תקין לצמיתות, ולעולם לא תצליח להשתמש בו שוב. + אין מפתחות שימושיים עבור איש קשר זה. אם מחקת אחד מהמפתחות שלהם, יהיה עליהם ליצר מפתח חדש. + שגיאה + הורדת היסטוריה מהשרת + אין עוד היסטוריה בשרת + מעדכן... + הסיסמה שונתה! + שינוי הסיסמה נכשל + שלח הודעה בכדי להתחיל בהצפנה + שאל שאלה + במידה ויש לך ולאיש הקשר שלך סוד שרק שניכם יודעים (כמו למשל בדיחה פנימית, או מה אכלתם בפעם האחרונה שנפגשתם), ניתן להשתמש בסוד הנ\"ל על מנת לאמת את מפתחות ההצפנה של אחד את השני.\n\nאיש קשר יספק שאלה או רמז, ואיש הקשר השני יספק תשובה. המלל הוא case-sensitive. + איש הקשר רוצה לאמת את הזהות שלך על ידי סוד משותף. איש הקשר סיפק את הרמז הבא, עליך להזין סוד משותף. + הרמז לא יכול להיות ריק + הסוד המשותף לא יכול להיות ריק + יש להשוות בזהירות את הטביעת אצבע שמוצגת פה לבין זאת שמוצגת אצל החבר שלך.\nניתן לעשות זאת על ידי שימוש בכל צורת תקשורת שאתה סומך עליה, כמו שיחת טלפון או אימייל מוצפן. + שינוי סיסמה + סיסמה נוכחית + סיסמה חדשה + הסיסמה לא יכולה להיות ריקה + הפעל את כל החשבונות + נטרל את כל החשבונות + בצע פעולה באמצעות + אין שיוך + אין תפקיד + חבר בקבוצה + מצב מתקדם + הענק חברות בקבוצה + בטל חברות בקבוצה + הענק הרשאות מנהל + שלול הרשאות מנהל + סלק מהועידה + לא ניתן לשנות את השיוך של %s + סלק וחסום כניסה לועידה + הינך מנסה למחוק את %s מועידה ציבורית. הדרך היחידה לעשות זאת היא חסימה לצמיתות. + חסום עכשיו + לא ניתן לשנות את התפקיד של %s + ועידה ציבורית + ועידה פרטית, לחברים בלבד + הגדרות ועידה + פרטי, חברים בלבד + לא-אנונימי + Moderated + אינך משתתף + הגדרות הועידה השתנו בצלחה! + שינוי הגדרות הועידה נכשל + לעולם לא + 30 דקות + שעה אחת + 2 שעות + 8 שעות + עד אחרית הימים + הגדרות קלט + לחצן Enter שולח את ההודעה + השתמש בלחצן ה-Enter כלחצן השליחה + הראה את לחצן ה Enter + שנה את לחצן האימוג\'י ללחצן Enter + קול + סרטון + תמונה + מסמך PDF + אפליקציית אנדרויד + איש קשר + התקבל %s + בטל שירות Foreground + לחץ כדי לפתוח את Conversations + תמונת הפרופיל פורסמה! + שולח %s + מציע %s + הסתר בלתי מקוונים + השבת חשבון + %s מקליד... + %s הפסיק/ה להקליד + התראות הקלדה + אפשר לאנשי הקשר שלך לדעת כאשר אתה מקליד הודעה חדשה + שלח מיקום + הראה מיקום + לא נמצאה אפליקציית המראה את המיקום + מיקום + מיקום שהתקבל + השיחה נסגרה + עזבת את הועידה + אל תסמוך על ה- CAs של המערכת + כל החתימות הדיגטליות יצטרכו לעבור אימות ידני + מחק חתימות דיגטליות + מחק חתימות דיגטליות שאומתו באופן ידני + אין חתימת דיגטליות שאושרו ידנית + מחק חתימות דיגטליות + מחק פריטים שנבחרו + ביטול + + %d חתימה נמחקה + %d חתימות נמחקו + + + בחר איש קשר %d + בחר %d אנשי קשר + + החלף לחצן שליחה בפעולה מהירה + פעולה מהירה + כלום + לפי השימוש האחרון + בחר פעולה מהירה + חפש אנשי קשר או קבוצות + שלח הודעה פרטית + %s עזב את הועידה + שם משתמש + שם משתמש + שם משתמש זה אינו חוקי + ההורדה נכשלה: שרת לא נמצא + ההורדה נכשלה: הקובץ לא נמצא + ההורדה נכשלה: נכשל ביצוע חיבור לשרת + השתמש ברקע לבן + הראה הודעות שהתקבלו בטקסט שחור על גבי רקע לבען + Timeout in DNS + לא עובד diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 000000000..b739bbc70 --- /dev/null +++ b/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,177 @@ + + + Innstillinger + Ny samtale + Kontobehandling + Avslutt denne samtalen + Kontaktdetaljer + Sikret samtale + Legg til samtale + Rediger navn + Legg til i telefonbok + Blokker kontakt + Avblokker kontakt + Blokker domene + Avblokker domene + Kontobehandling + Innstillinger + Kontaktdetaljer + Start samtale + Velg kontakt + Blokkeringsliste + akkurat nå + 1 minutt siden + %d minutter siden + uleste samtaler + sender... + Dekrypterer melding. Vent... + Kallenavn allerede i bruk + Admin + Eier + Moderator + Deltager + Besøkende + Vil du forhindre %s fra å sende deg meldinger? + Ønsker du å avblokkere %s og tillate dem å sende deg meldinger? + Blokker alle kontakter fra %s? + Avblokker alle kontakter fra %s? + Kontakt blokkert + Registrer ny konto på tjeneren + Endre passord på tjeneren + Del med... + Start samtale + Inviter kontakt + Kontakter + Avbryt + Legg til + Rediger + Slett + Blokker + Avblokker + Lagre + OK + Send nå + Aldri spør igjen + Kunne ikke koble til konto + Kunne ikkekoble til flerforldige kontoer + Trykk her for behandling av kontaktene dine + Legg til fil + Legg til kontakt + forsendelse feilet + avslått + Forbereder bilde for forsendelse + Tøm historikk + Tøm samtalehistorikk + Send ukryptert melding + Send OTR-kryptert melding + Send OMEMO-kryptert melding + Send OpenPGP-kryptert melding + Kallenavnet ditt har blitt endret + Send ukryptert + Dekryptering feilet. Kanskje du ikke lenger har den rette private nøkkelen. + Omstart + Installer + tilbyr... + venter... + Ingen OpenPGP-nøkkel funnet + Ingen OpenPGP-nøkler funnet + Kryptert melding mottatt. Trykk for å se og dekrypterte. + XMPP-ressurs + Godta filer + Automatisk godkjenning av filer mindre enn... + Merknadsinnstillinger + Merknader + Merknad om nye meldinger + Vibrer + Vibrer også når ny melding ankommer + Lyd + Spill av ringetone med merknad + Avanserte valg + Aldri send feilrettingsrapporter + Bekreft meldinger + Valg for grensesnitt + OpenKeychain rapporterte en feil + Godta + En feil har inntruffet + Ukjent + Midlertidig avskrudd + Pålogget + Kobler til\u2026 + Avlogget + Fant ikke tjener + Registrering feilet + Brukernavn allerede i bruk + Registrering fullført + Ukryptert + OTR + OpenPGP + OMEMO + Rediger konto + Slett konto + Skru av midlertidig + Publiser avatar + Publiser OpenPGP offentlig nøkkel + Skru på konto + Bekreft. + Hvis du sletter din konto vil hele din konversasjonshistorikk gå tapt + Ta opp stemme + Jabber-ID + Passord + Bekreft passord + Passord + Bekreft passord + Passordene samsvarer ikke + Dette er ikke en gyldig Jabber-ID + Slapp opp for minne, bildet er for stort + Ønsker du å legge til %s til din telefons kontaktliste? + pålogget + tilgjengelig for sludring + fraværende + borte + ikke forstyrr + avlogget + Konferanse + Andre medlemmer + Tjenerinfo + tilgjengelig + utilgjengelig + i syne + sist sett for ett minutt siden + sist sett for %d minutter siden + sist sett én time siden + sist sett %d timer siden + sist sett i går + sist sett for %d dager siden + aldri sett + Kryptert melding. Installer OpenKeychain for å dekryptere. + Ukjent OTR-fingeravtrykk + OTR-fingeravtrykk + Henter nøkler... + Ferdig + Dekrypter + Konferanser + Søk + Opprett kontakt + Ta del i konferanse + Slett kontakt + Vis kontaktdetaljer + Blokker kontakt + Avblokker kontakt + Kontakten finnes allerede + Konferanse-adresse + rom@konferanse.eksempel.no + Lagre som bokmerke + Slett bokmerke + Dette bokmerket finnes allerede + Deg + Rediger temaet for konferansen + Fant ikke konferansen + lyd + film + stillbilde + PDF-dokument + Android-app + Sender %s + Tilbyr %s + Avbryt + diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index c8bbe6271..6ba357d47 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -78,6 +78,7 @@ Wybierz widoczność dla kontaktu Wyślij wiadomość bez szyfrowania Wyślij zaszyfrowaną wiadomość (OTR) + Wyślij wiadomość zaszyfrowaną OMEMO Wyślij zaszyfrowaną wiadomość (OpenPGP) Twoja nazwa została zmieniona Wyślij bez szyfrowania @@ -106,6 +107,7 @@ Dźwięk Odtwórz dźwięk z powiadomieniem Powiadomienia konfrencji + Zawsze powiadamiaj przy odebraniu wiadomości w publicznej konferencji, a nie tylko przy wywołaniu nicka. Opóźnienie powiadomień Wyłącz powiadomienia przez krótki czas po otrzymaniu kopii wiadomości Opcje zaawansowane @@ -205,7 +207,11 @@ Odbiór nieudany Twój odcisk klucza Odcisk klucza OTR + Odcisk OMEMO + Odcisk OMEMO wiadomości + Własny odcisk OMEMO Pozostałe urządzenia + Zaufane odciski OMEMO Pobieranie kluczy... Ukończono Weryfikuj @@ -357,9 +363,13 @@ Skopiuj odcisk klucza OTR do schowka Skopiuj odcisk klucza OMEMO do schowka Wygeneruj ponownie klucz OMEMO + Usuń inne urządzenia z PEP Wyczyść urządzenia + Czy na pewno chcesz usunąć wszystkie inne urządzenia z ogłoszenia OMEMO? Następnym razem gdy połączą się Twoje urdzącenia, ogłoszą się one ponownie, ale mogą nie otrzymać wiadomości wysłanych w międzyczasie. Skasuj klucz Czy na pewno chcesz skasować usunąć odcisk klucza? + Zostanie bez odwołania uznane za zdradzone, i nigdy więcej nie będzie można stworzyć z nim sesji. + Nie ma dostępnych żadnych użytecznych kluczy dla tego kontaktu. Jeśli usunąłeś jakieś jego klucze, kontakt będzie musiał wygenerować nowe. Błąd Pobieranie historii z serwera Koniec historii na serwerze @@ -398,7 +408,10 @@ Konferencja publiczna Konferencja prywatna, dla zaakceptowanych uczestników Opcje konferencji + Prywatne, tylko dla członków. Nieanonimowa + Moderowany + Nie bierzesz udziału Opcje konferencji zostały zmienione! Nie udało się zmienić opcji konferencji Nigdy @@ -471,4 +484,6 @@ Pobieranie nieudane: Nie można połączyć z hostem Białe tło Pokazuj otrzymane wiadomości jako czarny tekst na białym tle + DNS timeout + Zepsute -- cgit v1.2.3 From 0587ba2ad274907ecc35f7fb38cc9e919af93d2a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 14 Oct 2015 11:15:18 +0200 Subject: work with muc services that change the message id --- src/main/java/eu/siacs/conversations/Config.java | 2 ++ src/main/java/eu/siacs/conversations/parser/MessageParser.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index bd9ad1a82..14605cf09 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -50,6 +50,8 @@ public final class Config { public static final boolean X509_VERIFICATION = false; //use x509 certificates to verify OMEMO keys + public static final boolean IGNORE_ID_REWRITE_IN_MUC = true; + public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2; public static final int MAM_MAX_MESSAGES = 500; diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 4d0577566..c53e50ac4 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -315,7 +315,7 @@ public class MessageParser extends AbstractParser implements status = Message.STATUS_SEND_RECEIVED; if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) { return; - } else if (remoteMsgId == null) { + } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { Message message = conversation.findSentMessageWithBody(packet.getBody()); if (message != null) { mXmppConnectionService.markMessage(message, status); -- cgit v1.2.3 From 5f9476448f54113e27f04f38fd64959b13bcd97b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 14 Oct 2015 21:18:34 +0200 Subject: make unread status and notifications presistent across restarts --- .../siacs/conversations/entities/Conversation.java | 28 +++++++++++---- .../eu/siacs/conversations/entities/Message.java | 16 +++++---- .../conversations/persistance/DatabaseBackend.java | 6 +++- .../services/NotificationService.java | 42 ++++++++++++++-------- .../services/XmppConnectionService.java | 13 +++++-- .../conversations/ui/ConversationActivity.java | 3 +- 6 files changed, 74 insertions(+), 34 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index f4d116fe0..f2c4fed42 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -111,6 +111,16 @@ public class Conversation extends AbstractEntity implements Blockable { } } + public void findUnreadMessages(OnMessageFound onMessageFound) { + synchronized (this.messages) { + for(Message message : this.messages) { + if (!message.isRead()) { + onMessageFound.onMessageFound(message); + } + } + } + } + public void findMessagesWithFiles(final OnMessageFound onMessageFound) { synchronized (this.messages) { for (final Message message : this.messages) { @@ -266,9 +276,8 @@ public class Conversation extends AbstractEntity implements Blockable { } } - public interface OnMessageFound { - public void onMessageFound(final Message message); + void onMessageFound(final Message message); } public Conversation(final String name, final Account account, final Jid contactJid, @@ -301,13 +310,18 @@ public class Conversation extends AbstractEntity implements Blockable { return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead(); } - public void markRead() { - for (int i = this.messages.size() - 1; i >= 0; --i) { - if (messages.get(i).isRead()) { - break; + public List markRead() { + final List unread = new ArrayList<>(); + synchronized (this.messages) { + for (int i = this.messages.size() - 1; i >= 0; --i) { + if (this.messages.get(i).isRead()) { + break; + } + this.messages.get(i).markRead(); + unread.add(this.messages.get(i)); } - this.messages.get(i).markRead(); } + return unread; } public Message getLatestMarkableMessage() { diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index bfb26446d..36cc0842f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -56,6 +56,7 @@ public class Message extends AbstractEntity { public static final String SERVER_MSG_ID = "serverMsgId"; public static final String RELATIVE_FILE_PATH = "relativeFilePath"; public static final String FINGERPRINT = "axolotl_fingerprint"; + public static final String READ = "read"; public static final String ME_COMMAND = "/me "; @@ -87,11 +88,8 @@ public class Message extends AbstractEntity { public Message(Conversation conversation, String body, int encryption) { this(conversation, body, encryption, STATUS_UNSEND); } - public Message(Conversation conversation, String body, int encryption, int status) { - this(conversation, body, encryption, status, false); - } - public Message(Conversation conversation, String body, int encryption, int status, boolean carbon) { + public Message(Conversation conversation, String body, int encryption, int status) { this(java.util.UUID.randomUUID().toString(), conversation.getUuid(), conversation.getJid() == null ? null : conversation.getJid().toBareJid(), @@ -105,7 +103,8 @@ public class Message extends AbstractEntity { null, null, null, - null); + null, + true); this.conversation = conversation; } @@ -113,7 +112,7 @@ public class Message extends AbstractEntity { final Jid trueCounterpart, final String body, final long timeSent, final int encryption, final int status, final int type, final boolean carbon, final String remoteMsgId, final String relativeFilePath, - final String serverMsgId, final String fingerprint) { + final String serverMsgId, final String fingerprint, final boolean read) { this.uuid = uuid; this.conversationUuid = conversationUUid; this.counterpart = counterpart; @@ -128,6 +127,7 @@ public class Message extends AbstractEntity { this.relativeFilePath = relativeFilePath; this.serverMsgId = serverMsgId; this.axolotlFingerprint = fingerprint; + this.read = read; } public static Message fromCursor(Cursor cursor) { @@ -166,7 +166,8 @@ public class Message extends AbstractEntity { cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)), cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)), cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)), - cursor.getString(cursor.getColumnIndex(FINGERPRINT))); + cursor.getString(cursor.getColumnIndex(FINGERPRINT)), + cursor.getInt(cursor.getColumnIndex(READ)) > 0); } public static Message createStatusMessage(Conversation conversation, String body) { @@ -202,6 +203,7 @@ public class Message extends AbstractEntity { values.put(RELATIVE_FILE_PATH, relativeFilePath); values.put(SERVER_MSG_ID, serverMsgId); values.put(FINGERPRINT, axolotlFingerprint); + values.put(READ,read); return values; } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index be827929c..070713236 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -43,7 +43,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 17; + private static final int DATABASE_VERSION = 18; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -143,6 +143,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Message.SERVER_MSG_ID + " TEXT, " + Message.FINGERPRINT + " TEXT, " + Message.CARBON + " INTEGER, " + + Message.READ + " NUMBER DEFAULT 1, " + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY(" + Message.CONVERSATION + ") REFERENCES " + Conversation.TABLENAME + "(" + Conversation.UUID @@ -320,6 +321,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { } } } + if (oldVersion < 18 && newVersion >= 18) { + db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "+ Message.READ+ " NUMBER DEFAULT 1"); + } } public static synchronized DatabaseBackend getInstance(Context context) { diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 3ed155a82..e9095020d 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.services; -import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -10,7 +9,6 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; -import android.os.PowerManager; import android.os.SystemClock; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.BigPictureStyle; @@ -115,31 +113,45 @@ public class NotificationService { return mXmppConnectionService.getPreferences().getBoolean("always_notify_in_conference", false); } + public void pushFromBacklog(final Message message) { + if (notify(message)) { + pushToStack(message); + } + } + + public void finishBacklog() { + synchronized (notifications) { + mXmppConnectionService.updateUnreadCountBadge(); + updateNotification(false); + } + } + + private void pushToStack(final Message message) { + final String conversationUuid = message.getConversationUuid(); + if (notifications.containsKey(conversationUuid)) { + notifications.get(conversationUuid).add(message); + } else { + final ArrayList mList = new ArrayList<>(); + mList.add(message); + notifications.put(conversationUuid, mList); + } + } + public void push(final Message message) { mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { return; } - final boolean isScreenOn = mXmppConnectionService.isInteractive(); - if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) { return; } - synchronized (notifications) { - final String conversationUuid = message.getConversationUuid(); - if (notifications.containsKey(conversationUuid)) { - notifications.get(conversationUuid).add(message); - } else { - final ArrayList mList = new ArrayList<>(); - mList.add(message); - notifications.put(conversationUuid, mList); - } + pushToStack(message); final Account account = message.getConversation().getAccount(); final boolean doNotify = (!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn) - && !account.inGracePeriod() - && !this.inMiniGracePeriod(account); + && !account.inGracePeriod() + && !this.inMiniGracePeriod(account); updateNotification(doNotify); if (doNotify) { notifyPebble(message); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 7a39bd062..cfd3ba6e0 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1072,7 +1072,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa for (Conversation conversation : conversations) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); checkDeletedFiles(conversation); + conversation.findUnreadMessages(new Conversation.OnMessageFound() { + @Override + public void onMessageFound(Message message) { + mNotificationService.pushFromBacklog(message); + } + }); } + mNotificationService.finishBacklog(); mRestoredFromDatabase = true; Log.d(Config.LOGTAG,"restored all messages"); updateConversationUi(); @@ -1330,7 +1337,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void updateKeyInAccount(final Account account, final String alias) { - Log.d(Config.LOGTAG,"update key in account "+alias); + Log.d(Config.LOGTAG, "update key in account " + alias); try { X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); Pair info = CryptoHelper.extractJidAndName(chain[0]); @@ -2566,7 +2573,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void markRead(final Conversation conversation) { mNotificationService.clear(conversation); - conversation.markRead(); + for(Message message : conversation.markRead()) { + databaseBackend.updateMessage(message); + } updateUnreadCountBadge(); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 48045a64c..5831df56a 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -216,8 +216,7 @@ public class ConversationActivity extends XmppActivity return null; } listAdapter.remove(swipedConversation); - swipedConversation.markRead(); - xmppConnectionService.getNotificationService().clear(swipedConversation); + xmppConnectionService.markRead(swipedConversation); final boolean formerlySelected = (getSelectedConversation() == swipedConversation); if (position == 0 && listAdapter.getCount() == 0) { -- cgit v1.2.3 From 30dbf97a1c8a66918cf22081bce627bf798c4e4b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 14 Oct 2015 22:55:59 +0200 Subject: clear bitmap cache before running out of memory --- .../eu/siacs/conversations/services/XmppConnectionService.java | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index cfd3ba6e0..f9f9b71e2 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -673,6 +673,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa toggleScreenEventReceiver(); } + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + if (level >= TRIM_MEMORY_COMPLETE) { + Log.d(Config.LOGTAG,"clear cache due to low memory"); + getBitmapCache().evictAll(); + } + } + @Override public void onDestroy() { try { -- cgit v1.2.3 From 4b93351f8f4585548dbc9ce162f68e2bfc003f43 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 14 Oct 2015 23:03:01 +0200 Subject: changed some source strings --- src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 162c277bd..3f3e29d14 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -95,7 +95,7 @@ Conversations is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\nPlease ask your contact to setup OpenPGP. No OpenPGP Keys found Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\nPlease ask your contacts to setup OpenPGP. - Encrypted message received. Touch to view and decrypt. + Encrypted message received. Touch to decrypt. General XMPP resource The name this client identifies itself with @@ -174,7 +174,7 @@ Passwords do not match This is not a valid Jabber ID Out of memory. Image is too large - Do you want to add %s to your phones contact list? + Do you want to add %s to your devices’s contact list? online free to chat away -- cgit v1.2.3 From 1738673c53b8ac0abbb2e5939dff16300a9c67b8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 14 Oct 2015 23:05:00 +0200 Subject: pulled translations from transifex --- src/main/res/values-ar/strings.xml | 1 - src/main/res/values-bg/strings.xml | 21 +++++++++++++++++++-- src/main/res/values-ca/strings.xml | 1 - src/main/res/values-cs/strings.xml | 1 - src/main/res/values-de/strings.xml | 17 +++++++++++++++++ src/main/res/values-el/strings.xml | 1 - src/main/res/values-es/strings.xml | 20 +++++++++++++++++++- src/main/res/values-eu/strings.xml | 1 - src/main/res/values-fr/strings.xml | 1 - src/main/res/values-gl/strings.xml | 1 - src/main/res/values-id/strings.xml | 1 - src/main/res/values-it/strings.xml | 1 - src/main/res/values-iw/strings.xml | 1 - src/main/res/values-ja/strings.xml | 1 - src/main/res/values-ko/strings.xml | 1 - src/main/res/values-nb-rNO/strings.xml | 7 ++++++- src/main/res/values-nl/strings.xml | 1 - src/main/res/values-pl/strings.xml | 1 - src/main/res/values-pt/strings.xml | 1 - src/main/res/values-ro-rRO/strings.xml | 1 - src/main/res/values-ru/strings.xml | 1 - src/main/res/values-sk/strings.xml | 1 - src/main/res/values-sr/strings.xml | 1 - src/main/res/values-sv/strings.xml | 1 - src/main/res/values-zh-rCN/strings.xml | 1 - src/main/res/values-zh-rTW/strings.xml | 1 - 26 files changed, 61 insertions(+), 26 deletions(-) (limited to 'src/main') diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index 7bffdf248..586a58cf6 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -74,7 +74,6 @@ حذف سجل المحفوظات للمحادثة هل ترغب بحذف جميع الرسائل في تلك المحادثة?\n\nتنويه: هذا لن يؤثر على الرسائل المخزنة على الأجهزة أو أي أماكن أخرى. حذف الرسائل - انهاء هذه المحادثة بعد الكلمات اختيار ظهورك لجهات الإتصال OTRارساله رساله مشفره عبر OpenPGPارساله رساله مشفره عبر diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 31928a462..030432ad6 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -74,7 +74,7 @@ Изчистване на историята на разговорите Искате ли да изтриете всички съобщения от този разговор?\n\nВнимание: Това няма да изтрие съобщенията, съхранявани на други устройства или на сървърите. Изтриване на съобщенията - Този разговор да приключи след това + Приключване на този разговор след това Изберете присъствие за контакта Изпр. на нешифр. съобщение Изпр. на съобщение, шифр. чрез OTP @@ -335,6 +335,9 @@ Conversations Услугата да е на преден план Предотвратява прекъсването на връзката Ви от операционната система + Изнасяне на журналите + Записване на журналите в картата с памет + Записване на журналите в картата с памет Изберете файл Получаване на %1$s (%2$d%% завършено) Сваляне на %s @@ -483,5 +486,19 @@ Използване на бял фон Показване на получените съобщения с черен текст на бял фон Време на изчакване на DNS - Недостъпен + Повредено + Настройки за присъствието + Отсъстващ, когато екранът е изключен + Преминава в състояние „отсъстващ“ когато екранът бъде изключен + Недостъпен, в тих режим + Преминава в състояние „недостъпен“ когато телефонът е в тих режим + Добавяне на профил със сертификат + Неуспешно прочитане на сертификата + Оставете празно за удостоверяване със сертификат + Текст за проверка + Проверката е задължителна + въведете текста от изображението + Сертификатът не е потвърден + Jabber идентификатора не съответства на сертификата + Подновяване на сертификата diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index c4d75c0ab..806ed7a03 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -74,7 +74,6 @@ Netejar historial de conversa Vols esborrar tots els missatges d\'aquesta conversa?\n\nAvís: Això no afectarà els missatges desats en altres dispositius o servidors. Esborrar missatges - Finalitzar aquesta conversa més tard Selecciona recurs del contacte Enviar missatge xifrat amb OTR Enviar missatge xifrat amb OpenPGP diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index de7945599..0c5dca552 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -74,7 +74,6 @@ Smaže historii konverzací Chcete smazat všechny zprávy v této konverzaci?\n\nVarování: Toto neovlivní zprávy uložené na jiných přístrojích nebo serverech. Smazat zprávy - Poté ukončit i tuto konverzaci Vybrat aktualizaci stavu pro kontakt Odeslat nešifrovanou zprávu Poslat OTR šifrovanou zprávu diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 74461b720..b7cc4440f 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -335,6 +335,9 @@ Conversations Den Dienst im Vordergrund ausführen. Verhindert, dass Android Conversations beendet und die Verbindung unterbricht + Chats exportieren + Chats auf SD-Karte schreiben + Chats auf SD-Karte schreiben Datei auswählen Empfange %1$s (%2$d%% abgeschlossen) %s wird heruntergeladen @@ -484,4 +487,18 @@ Empfangene Nachrichten als schwarzen Text auf weißem Hintergrund anzeigen Zeitüberschreitung bei der Namensauflösung Fehlerhaft + Status Einstellungen + Abwesend + Setze deinen Status auf abwesend, wenn dein Bildschirm abgeschaltet ist + Nicht verfügbar + Setze deinen Status auf nicht verfügbar, wenn dein Gerät stumm geschaltet ist + Kontakt mit Zertifikat hinzufügen + Zertifikat kann nicht gelesen werden + Leer lassen, um mit Zertifikat anzumelden + Captcha Text + Captcha erforderlich + Text aus Captcha eintragen + Zertifikat wird nicht vertraut + Jabber-ID stimmt nicht dem Zertifikat überein + Zertifikat erneuern diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 535109be0..ed38d0c06 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -74,7 +74,6 @@ Καθαρισμός ιστορικού Συζήτησης Θέλετε να σβήσετε όλα τα μηνύματα αυτής της Συζήτησης;\n\nΠροειδοποίηση: Αυτό δεν θα επηρεάσει τα μηνύματα που είναι αποθηκευμένα σε άλλες συσκευές ή άλλους διακομιστές. Διαγραφή μηνυμάτων - Τερματισμός αυτής της συζήτησης αμέσως μετά Επιλέξτε παρουσία για επικοινωνία Αποστολή κρυπτογραφημένου μηνύματος OTR Αποστολή κρυπτογραφημένου μηνύματος OpenPGP diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index c76c1eea5..cdddfb164 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -74,7 +74,7 @@ Limpiar historial de conversación ¿Quieres borrar todos los mensajes de esta conversación?\n\nAviso: Esto no afectará a los mensajes guardados en otros dispositivos o servidores. Borrar mensajes - Además, terminar esta conversación + Terminar esta conversación después Selecciona recurso del contacto Enviar mensaje sin cifrar Enviar mensaje cifrado con OTR @@ -335,6 +335,9 @@ Conversations Servicio en primer plano Mantener el servicio en primer plano previene que el sistema cierre la conexión + Exportar logs + Escribir logs en la tarjeta SD + Escribiendo logs en la tarjeta SD Seleccionar archivo Recibiendo %1$s (%2$d%% completado) Descargar %s @@ -483,4 +486,19 @@ Usar fondo blanco Mostrar mensajes recibidos en texto negro con fondo blanco Timeout en DNS + Error + Opciones de presencia + Ausente con pantalla apagada + Cambia tu estado a ausente cuando la pantalla está apagada + No disponible en modo silencio + Cambia tu estado a no disponible cuando el teléfono está en modo silencio + Añadir cuenta con certificado + No se ha podido leer el certificado + Dejar vacío para autenticar certificado w/ + Texto captcha + Captcha requerido + Introduce el texto de la imagen + La cadena de certificados no es de confianza + El identificador Jabber no coincide con el del certificado + Renovar certificado diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 70fa22e58..86f39b0f6 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -74,7 +74,6 @@ Elkarrizketa historia garbitu Elkarrizketa honetako mezu guztiak ezabatu nahi al dituzu?\n\nAbisua: Honek ez du beste gailu edo zerbitzarietan gordetako mezuetan eraginik izango. Mezuak ezabatu - Elkarrizketa hau jarraian amaitu Hautatu agerpena kontaktuarentzat Enkriptatu gabeko mezua bidali OTRz enkriptatutako mezua bidali diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 7a3a3256f..b6c5f0daf 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -74,7 +74,6 @@ Vider l\'historique de la conversation Voulez-vous supprimer tous les messages de cette conversation ?\n\nAttention : Les messages seront supprimés uniquement sur cet appareil. Supprimer les messages - Terminer cette conversation après Choisir le status de présence Envoyer un message non chiffré Envoyer un message chiffré avec OTR diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 972a7bfea..019d4677f 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -42,7 +42,6 @@ Limpar historial de conversa ¿Queres borrar todas as mensaxes desta conversa?\n\nOllo: Isto non afectará ás mensaxes gardadas noutros dispositivos ou servidores. Borrar mensaxes - Terminar esta conversa máis tarde Selecciona recurso del contacto Enviar mensaxe cifrado con OTR Enviar mensaxe cifrado con OpenPGP diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index 45f4b33c7..894c18a71 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -74,7 +74,6 @@ Hapus Riwayat Percakapan Apakah Anda ingin menghapus semua pesan dalam Percakapan ini\n\nPeringatan:ini tidak akan mempengaruhi pesan yang disimpan pada perangkat atau server lain. Hapus pesan - Akhiri percakapan setelahnya Pilih kehadiran untuk kontak Kirim pesan terenskripsi OTR Kirim pesan terenskripsi OpenPGP diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 549e40032..97311427c 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -74,7 +74,6 @@ Pulisci la cronologia della Conversazione Vuoi cancellare tutti i messaggi di questa Conversazione?\n\nAttenzione: Questo non influenzerà i messaggi presenti su altri dispositivi o server. Elimina messaggi - Termina questa conversazione in seguito Choose presence to contact Messaggio OTR Messaggio OpenPGP diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index a344be2bb..d5fda1c23 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -73,7 +73,6 @@ נקה היסטוריית שיחה האם ברצונך למחוק את כל ההודעות בשיחה זאת?\n\nאזהרה: פעולה זו לא תשפיע על הודעות מאוחסנות על מכשירים או שרתים אחרים. מחק הודעות - בנוסף, סיים שיחה זו בחר נוכחות לאיש קשר שלח הודעה בלתי מוצפנת שלח הודעה בהצפנת OTR diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index fbc3beb82..45c67940e 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -74,7 +74,6 @@ 会話履歴をクリア この会話のすべてのメッセージを削除しますか?\n\n警告: これは、他のデバイスやサーバーに保存されているメッセージには影響しません。 メッセージを削除 - その後、この会話を終了 連絡する参加を選択 暗号化されていないメッセージを送信 OTR 暗号化メッセージを送信 diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index cfbdb8d41..6f654dcf7 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -74,7 +74,6 @@ 대화 기록 삭제 이 대화의 모든 메세지를 삭제하시겠습니까? 경고: 이것은 다른 기기나 서버에 있는 메세지에는 영향을 미치지 않습니다. 메세지 삭제 - 나중에 이 대화 끝내기 연락할 프레즌스 선택 OTR 암호화된 메세지 전송 OpenPGP 암호화된 메세지 전송 diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index b739bbc70..dee41b7b4 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -90,9 +90,14 @@ Aldri send feilrettingsrapporter Bekreft meldinger Valg for grensesnitt - OpenKeychain rapporterte en feil + Feilmelding fra OpenKeychain Godta En feil har inntruffet + Abonnement + Din konto + Nøkler + Velg bilde + Ta bilde Ukjent Midlertidig avskrudd Pålogget diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 263cdccaf..61d57b260 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -74,7 +74,6 @@ Gespreksgeschiedenis wissen Wil je alle berichten in dit gesprek verwijderen?\n\nWaarschuwing: Dit zal geen invloed hebben op de berichten opgeslagen op andere apparaten of servers. Berichten verwijderen - Beëindig dit gesprek na afloop Kies aanwezigheid om te tonen aan contact Verstuur onversleuteld bericht Verstuur OTR-versleuteld bericht diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 6ba357d47..408672ca1 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -74,7 +74,6 @@ Wyczyść historię konwersacji Czy na pewno usunąć wszystkie wiadomości powiązane z konwersacją?\n\nUwaga: Działanie nie wpływa na wiadomości przechowywane na innych urządzeniach lub serwerach. Usuń wiadomości - Zakończ konwersację po usunięciu historii Wybierz widoczność dla kontaktu Wyślij wiadomość bez szyfrowania Wyślij zaszyfrowaną wiadomość (OTR) diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index f5e2750a8..57f9d7c6e 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -72,7 +72,6 @@ Limpar o histórico de conversas Você deseja remover todas as mensagens nessa conversa?\n\n<b>Atenção:<b> Isso não irá influenciar mensagens salvas em outros dispositivos ou servidores. Remover mensagens - Finalizar essa conversa ao final Escolha a presença do contato Enviar mensagem criptografada com OTR Enviar mensagem criptografada com OpenPGP diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index c70ce0520..991201e54 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -73,7 +73,6 @@ Sterge istoria conversatiei Doresti sa stergi toate mesajele din Conversatii?\n\nAtentie: Aceasta actiune nu va influenta mesajele aflate pe alte telefoane/tabelete/servere. Sterge mesajele - Termina conversatia aceasta dupa Alege prezenta pentru a contacta Trimite mesaj criptat cu OTR Trimite mesaj criptat cu OpenPGP diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index e8b58dd66..4c5c205fe 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -74,7 +74,6 @@ Очистить историю Вы хотите удалить все сообщения в этой беседе?\n\nПредупреждение: Данная операция не повлияет на сообщения, хранящиеся на других устройствах. Удалить сообщения - Завершить беседу Укажите статус для контакта Отправить незащифрованное сообщение Отправить OTR защифрованное сообщение diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index bf6b43649..b0f982c20 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -74,7 +74,6 @@ Vymazať históriu konverzácií Chcete vymazať všetky správy v tejto konverzácii?\n\nVarovanie: Toto neovplyvní správy uložené v iných zariadeniach alebo serveroch. Vymazať správy - Následne ukončiť aj túto konverzáciu Vybrať aktualizáciu stavu pre kontakt Poslať OTR šifrovanú správu Poslať OpenPGP šifrovanú správu diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 86a4561dd..5cf49475f 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -74,7 +74,6 @@ Брисање историјата преписки Желите ли да обришете све поруке ове преписке?\n\nУпозорење: Ово неће утицати на поруке складиштене на осталим уређајима или серверима. Обриши поруке - Окончај ову преписку након тога Избор присутности за контакта Пошаљи нешифровану поруку Пошаљи ОТР шифровану поруку diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 6a7908848..90a0248ed 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -74,7 +74,6 @@ Rensa konversationshistorik Vill du ta bort alla meddelanden i denna konversation?\n\nVarning: Detta kommer inte påverka meddelanden lagrade på andra enheter eller servrar. Ta bort meddelanden - Avsluta sedan denna konversation Välj tillgänglighet till kontakt Skicka okrypterat meddelande Skicka OTR-krypterat meddelande diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index de37c4683..455d13a8b 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -74,7 +74,6 @@ 清除会话记录 删除该会话中所有信息?\n\n注: 该操作不会影响其他设备或服务器保存的信息。 删除消息 - 之后结束该会话 添加在线用户至联系人 发送 OTR 加密信息 发送 OpenPGP 加密信息 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 7c73b646f..161d49db2 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -58,7 +58,6 @@ 清除對話記錄 你確定要刪除該對話中所有訊息嗎?\n\n警告: 這將不會影響其他設備或伺服器儲存的訊息。 刪除訊息 - 之後結束這對話 選擇狀態訊息 發送 OTR 加密訊息 發送 OpenPGP 加密訊息 -- cgit v1.2.3 From e75c2cd731fd7fa55d5f57a07af42911f271a5cc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 15 Oct 2015 17:08:38 +0200 Subject: use own XmppDomainVerifier instead of deprecated StrictHostnameVerifier. fixes #1189 --- .../conversations/crypto/XmppDomainVerifier.java | 113 +++++++++++++++++++++ .../siacs/conversations/xmpp/XmppConnection.java | 5 +- 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java new file mode 100644 index 000000000..0052998e9 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java @@ -0,0 +1,113 @@ +package eu.siacs.conversations.crypto; + +import android.util.Log; + +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.DLSequence; +import org.bouncycastle.asn1.x500.RDN; +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.io.IOException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +public class XmppDomainVerifier implements HostnameVerifier { + + private final String LOGTAG = "XmppDomainVerifier"; + + @Override + public boolean verify(String domain, SSLSession sslSession) { + try { + X509Certificate[] chain = (X509Certificate[]) sslSession.getPeerCertificates(); + Collection> alternativeNames = chain[0].getSubjectAlternativeNames(); + List xmppAddrs = new ArrayList<>(); + List srvNames = new ArrayList<>(); + List domains = new ArrayList<>(); + if (alternativeNames != null) { + for(List san : alternativeNames) { + Integer type = (Integer) san.get(0); + if (type == 0) { + try { + ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray((byte[]) san.get(1)); + if (asn1Primitive instanceof DERTaggedObject) { + ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject(); + if (inner instanceof DLSequence) { + DLSequence sequence = (DLSequence) inner; + if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) { + String oid = sequence.getObjectAt(0).toString(); + ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject(); + switch (oid) { + case "1.3.6.1.5.5.7.8.5": + if (value instanceof DERUTF8String) { + xmppAddrs.add(((DERUTF8String) value).getString()); + } else if (value instanceof DERIA5String) { + xmppAddrs.add(((DERIA5String) value).getString()); + } + break; + case "1.3.6.1.5.5.7.8.7": + if (value instanceof DERUTF8String) { + srvNames.add(((DERUTF8String) value).getString()); + } else if (value instanceof DERIA5String) { + srvNames.add(((DERIA5String) value).getString()); + } + break; + default: + Log.d(LOGTAG,"value was of type:"+value.getClass().getName()+ " oid was:"+oid); + } + } + } + } + } catch (IOException e) { + //ignored + } + } else if (type == 2) { + Object value = san.get(1); + if (value instanceof String) { + domains.add((String) value); + } + } + } + } + if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) { + X500Name x500name = new JcaX509CertificateHolder(chain[0]).getSubject(); + RDN[] rdns = x500name.getRDNs(BCStyle.CN); + for(int i = 0; i < rdns.length; ++i) { + domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue())); + } + } + Log.d(LOGTAG, "searching for " + domain + " in srvNames: " + srvNames + " xmppAddrs: " + xmppAddrs + " domains:" + domains); + return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client."+domain) || matchDomain(domain, domains); + } catch (Exception e) { + return false; + } + } + + private boolean matchDomain(String needle, List haystack) { + for(String entry : haystack) { + if (entry.startsWith("*.")) { + int i = needle.indexOf('.'); + if (i != -1 && needle.substring(i).equals(entry.substring(2))) { + Log.d(LOGTAG,"domain "+needle+" matched "+entry); + return true; + } + } else { + if (entry.equals(needle)) { + Log.d(LOGTAG,"domain "+needle+" matched "+entry); + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 999aecc9f..d21682a79 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -55,6 +55,7 @@ import javax.net.ssl.X509TrustManager; import de.duenndns.ssl.MemorizingTrustManager; import eu.siacs.conversations.Config; +import eu.siacs.conversations.crypto.XmppDomainVerifier; import eu.siacs.conversations.crypto.sasl.DigestMd5; import eu.siacs.conversations.crypto.sasl.External; import eu.siacs.conversations.crypto.sasl.Plain; @@ -606,9 +607,9 @@ public class XmppConnection implements Runnable { final SSLSocketFactory factory = sc.getSocketFactory(); final HostnameVerifier verifier; if (mInteractive) { - verifier = trustManager.wrapHostnameVerifier(new StrictHostnameVerifier()); + verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier()); } else { - verifier = trustManager.wrapHostnameVerifierNonInteractive(new StrictHostnameVerifier()); + verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier()); } final InetAddress address = socket == null ? null : socket.getInetAddress(); -- cgit v1.2.3 From 5b271e1ed8b4b51f4bddc1cf088ebb80d51e5566 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 15 Oct 2015 18:06:26 +0200 Subject: more checks for xmppdomainverifier and better wildcard handling --- .../eu/siacs/conversations/crypto/XmppDomainVerifier.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java index 0052998e9..f882e04e9 100644 --- a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java +++ b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java @@ -14,6 +14,7 @@ import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import java.io.IOException; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; @@ -29,8 +30,12 @@ public class XmppDomainVerifier implements HostnameVerifier { @Override public boolean verify(String domain, SSLSession sslSession) { try { - X509Certificate[] chain = (X509Certificate[]) sslSession.getPeerCertificates(); - Collection> alternativeNames = chain[0].getSubjectAlternativeNames(); + Certificate[] chain = sslSession.getPeerCertificates(); + if (chain.length == 0 || !(chain[0] instanceof X509Certificate)) { + return false; + } + X509Certificate certificate = (X509Certificate) chain[0]; + Collection> alternativeNames = certificate.getSubjectAlternativeNames(); List xmppAddrs = new ArrayList<>(); List srvNames = new ArrayList<>(); List domains = new ArrayList<>(); @@ -80,7 +85,7 @@ public class XmppDomainVerifier implements HostnameVerifier { } } if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) { - X500Name x500name = new JcaX509CertificateHolder(chain[0]).getSubject(); + X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); RDN[] rdns = x500name.getRDNs(BCStyle.CN); for(int i = 0; i < rdns.length; ++i) { domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue())); @@ -97,7 +102,8 @@ public class XmppDomainVerifier implements HostnameVerifier { for(String entry : haystack) { if (entry.startsWith("*.")) { int i = needle.indexOf('.'); - if (i != -1 && needle.substring(i).equals(entry.substring(2))) { + Log.d(LOGTAG,"comparing "+needle.substring(i)+ " and "+entry.substring(1)); + if (i != -1 && needle.substring(i).equals(entry.substring(1))) { Log.d(LOGTAG,"domain "+needle+" matched "+entry); return true; } -- cgit v1.2.3 From fc96dcaa4d1b053b547ba35215d492b32442e929 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 15 Oct 2015 19:14:41 +0200 Subject: use constants for oids in xmppdomainverifier --- .../java/eu/siacs/conversations/crypto/XmppDomainVerifier.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java index f882e04e9..47d4b459d 100644 --- a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java +++ b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java @@ -27,6 +27,9 @@ public class XmppDomainVerifier implements HostnameVerifier { private final String LOGTAG = "XmppDomainVerifier"; + private final String SRVName = "1.3.6.1.5.5.7.8.7"; + private final String xmppAddr = "1.3.6.1.5.5.7.8.5"; + @Override public boolean verify(String domain, SSLSession sslSession) { try { @@ -53,14 +56,14 @@ public class XmppDomainVerifier implements HostnameVerifier { String oid = sequence.getObjectAt(0).toString(); ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject(); switch (oid) { - case "1.3.6.1.5.5.7.8.5": + case xmppAddr: if (value instanceof DERUTF8String) { xmppAddrs.add(((DERUTF8String) value).getString()); } else if (value instanceof DERIA5String) { xmppAddrs.add(((DERIA5String) value).getString()); } break; - case "1.3.6.1.5.5.7.8.7": + case SRVName: if (value instanceof DERUTF8String) { srvNames.add(((DERUTF8String) value).getString()); } else if (value instanceof DERIA5String) { -- cgit v1.2.3 From c1716a35e359cf9b2e8d1b75cc4f0bac413bee5b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 15 Oct 2015 20:05:55 +0200 Subject: moved other name parsing into seperate method --- .../conversations/crypto/XmppDomainVerifier.java | 85 ++++++++++++---------- 1 file changed, 45 insertions(+), 40 deletions(-) (limited to 'src/main') diff --git a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java index 47d4b459d..1fca865ed 100644 --- a/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java +++ b/src/main/java/eu/siacs/conversations/crypto/XmppDomainVerifier.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.crypto; import android.util.Log; +import android.util.Pair; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.DERIA5String; @@ -25,7 +26,7 @@ import javax.net.ssl.SSLSession; public class XmppDomainVerifier implements HostnameVerifier { - private final String LOGTAG = "XmppDomainVerifier"; + private static final String LOGTAG = "XmppDomainVerifier"; private final String SRVName = "1.3.6.1.5.5.7.8.7"; private final String xmppAddr = "1.3.6.1.5.5.7.8.5"; @@ -43,41 +44,21 @@ public class XmppDomainVerifier implements HostnameVerifier { List srvNames = new ArrayList<>(); List domains = new ArrayList<>(); if (alternativeNames != null) { - for(List san : alternativeNames) { + for (List san : alternativeNames) { Integer type = (Integer) san.get(0); if (type == 0) { - try { - ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray((byte[]) san.get(1)); - if (asn1Primitive instanceof DERTaggedObject) { - ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject(); - if (inner instanceof DLSequence) { - DLSequence sequence = (DLSequence) inner; - if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) { - String oid = sequence.getObjectAt(0).toString(); - ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject(); - switch (oid) { - case xmppAddr: - if (value instanceof DERUTF8String) { - xmppAddrs.add(((DERUTF8String) value).getString()); - } else if (value instanceof DERIA5String) { - xmppAddrs.add(((DERIA5String) value).getString()); - } - break; - case SRVName: - if (value instanceof DERUTF8String) { - srvNames.add(((DERUTF8String) value).getString()); - } else if (value instanceof DERIA5String) { - srvNames.add(((DERIA5String) value).getString()); - } - break; - default: - Log.d(LOGTAG,"value was of type:"+value.getClass().getName()+ " oid was:"+oid); - } - } - } + Pair otherName = parseOtherName((byte[]) san.get(1)); + if (otherName != null) { + switch (otherName.first) { + case SRVName: + srvNames.add(otherName.second); + break; + case xmppAddr: + xmppAddrs.add(otherName.second); + break; + default: + Log.d(LOGTAG, "oid: " + otherName.first + " value: " + otherName.second); } - } catch (IOException e) { - //ignored } } else if (type == 2) { Object value = san.get(1); @@ -90,29 +71,53 @@ public class XmppDomainVerifier implements HostnameVerifier { if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) { X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); RDN[] rdns = x500name.getRDNs(BCStyle.CN); - for(int i = 0; i < rdns.length; ++i) { + for (int i = 0; i < rdns.length; ++i) { domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue())); } } Log.d(LOGTAG, "searching for " + domain + " in srvNames: " + srvNames + " xmppAddrs: " + xmppAddrs + " domains:" + domains); - return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client."+domain) || matchDomain(domain, domains); + return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client." + domain) || matchDomain(domain, domains); } catch (Exception e) { return false; } } - private boolean matchDomain(String needle, List haystack) { - for(String entry : haystack) { + private static Pair parseOtherName(byte[] otherName) { + try { + ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName); + if (asn1Primitive instanceof DERTaggedObject) { + ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject(); + if (inner instanceof DLSequence) { + DLSequence sequence = (DLSequence) inner; + if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) { + String oid = sequence.getObjectAt(0).toString(); + ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject(); + if (value instanceof DERUTF8String) { + return new Pair<>(oid, ((DERUTF8String) value).getString()); + } else if (value instanceof DERIA5String) { + return new Pair<>(oid, ((DERIA5String) value).getString()); + } + } + } + } + return null; + } catch (IOException e) { + return null; + } + } + + private static boolean matchDomain(String needle, List haystack) { + for (String entry : haystack) { if (entry.startsWith("*.")) { int i = needle.indexOf('.'); - Log.d(LOGTAG,"comparing "+needle.substring(i)+ " and "+entry.substring(1)); + Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1)); if (i != -1 && needle.substring(i).equals(entry.substring(1))) { - Log.d(LOGTAG,"domain "+needle+" matched "+entry); + Log.d(LOGTAG, "domain " + needle + " matched " + entry); return true; } } else { if (entry.equals(needle)) { - Log.d(LOGTAG,"domain "+needle+" matched "+entry); + Log.d(LOGTAG, "domain " + needle + " matched " + entry); return true; } } -- cgit v1.2.3