diff --git a/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java b/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
index 52c067258..bc9b06d5f 100644
--- a/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
+++ b/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
@@ -2,7 +2,9 @@ package eu.siacs.conversations.ui;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.databinding.DataBindingUtil;
import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
@@ -15,102 +17,124 @@ import java.security.SecureRandom;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.MagicCreateBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper;
import rocks.xmpp.addr.Jid;
public class MagicCreateActivity extends XmppActivity implements TextWatcher {
- private TextView mFullJidDisplay;
- private EditText mUsername;
+ public static final String EXTRA_DOMAIN = "domain";
+ public static final String EXTRA_PRE_AUTH = "pre_auth";
- @Override
- protected void refreshUiReal() {
+ private MagicCreateBinding binding;
+ private String domain;
+ private String preAuth;
- }
+ @Override
+ protected void refreshUiReal() {
- @Override
- void onBackendConnected() {
+ }
- }
+ @Override
+ void onBackendConnected() {
- @Override
- public void onStart() {
- super.onStart();
- final int theme = findTheme();
- if (this.mTheme != theme) {
- recreate();
- }
- }
+ }
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (getResources().getBoolean(R.bool.portrait_only)) {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }
- super.onCreate(savedInstanceState);
- setContentView(R.layout.magic_create);
- setSupportActionBar(findViewById(R.id.toolbar));
- configureActionBar(getSupportActionBar());
- mFullJidDisplay = findViewById(R.id.full_jid);
- mUsername = findViewById(R.id.username);
- Button next = findViewById(R.id.create_account);
- next.setOnClickListener(v -> {
- try {
- String username = mUsername.getText().toString();
- Jid jid = Jid.of(username.toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
- if (!jid.getEscapedLocal().equals(jid.getLocal())|| username.length() < 3) {
- mUsername.setError(getString(R.string.invalid_username));
- mUsername.requestFocus();
- } else {
- mUsername.setError(null);
- Account account = xmppConnectionService.findAccountByJid(jid);
- if (account == null) {
- account = new Account(jid, CryptoHelper.createPassword(new SecureRandom()));
- account.setOption(Account.OPTION_REGISTER, true);
- account.setOption(Account.OPTION_DISABLED, true);
- account.setOption(Account.OPTION_MAGIC_CREATE, true);
- xmppConnectionService.createAccount(account);
- }
- Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().asBareJid().toString());
- intent.putExtra("init", true);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
- StartConversationActivity.addInviteUri(intent, getIntent());
- startActivity(intent);
- }
- } catch (IllegalArgumentException e) {
- mUsername.setError(getString(R.string.invalid_username));
- mUsername.requestFocus();
- }
- });
- mUsername.addTextChangedListener(this);
- }
+ @Override
+ public void onStart() {
+ super.onStart();
+ final int theme = findTheme();
+ if (this.mTheme != theme) {
+ recreate();
+ }
+ }
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ final Intent data = getIntent();
+ this.domain = data == null ? null : data.getStringExtra(EXTRA_DOMAIN);
+ this.preAuth = data == null ? null : data.getStringExtra(EXTRA_PRE_AUTH);
+ if (getResources().getBoolean(R.bool.portrait_only)) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ super.onCreate(savedInstanceState);
+ this.binding = DataBindingUtil.setContentView(this, R.layout.magic_create);
+ setSupportActionBar((Toolbar) this.binding.toolbar);
+ configureActionBar(getSupportActionBar(), this.domain == null);
+ if (domain != null) {
+ binding.instructions.setText(getString(R.string.magic_create_text_on_x, domain));
+ binding.finePrint.setVisibility(View.INVISIBLE);
+ }
+ binding.createAccount.setOnClickListener(v -> {
+ try {
+ final String username = binding.username.getText().toString();
+ final Jid jid;
+ if (this.domain == null) {
+ jid = Jid.ofLocalAndDomain(username, Config.MAGIC_CREATE_DOMAIN);
+ } else {
+ jid = Jid.ofLocalAndDomain(username, this.domain);
+ }
+ if (!jid.getEscapedLocal().equals(jid.getLocal()) || username.length() < 3) {
+ binding.username.setError(getString(R.string.invalid_username));
+ binding.username.requestFocus();
+ } else {
+ binding.username.setError(null);
+ Account account = xmppConnectionService.findAccountByJid(jid);
+ if (account == null) {
+ account = new Account(jid, CryptoHelper.createPassword(new SecureRandom()));
+ account.setOption(Account.OPTION_REGISTER, true);
+ account.setOption(Account.OPTION_DISABLED, true);
+ account.setOption(Account.OPTION_MAGIC_CREATE, true);
+ if (this.preAuth != null) {
+ account.setKey(Account.PRE_AUTH_REGISTRATION_TOKEN, this.preAuth);
+ }
+ xmppConnectionService.createAccount(account);
+ }
+ Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().asBareJid().toString());
+ intent.putExtra("init", true);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
+ StartConversationActivity.addInviteUri(intent, getIntent());
+ startActivity(intent);
+ }
+ } catch (IllegalArgumentException e) {
+ binding.username.setError(getString(R.string.invalid_username));
+ binding.username.requestFocus();
+ }
+ });
+ binding.username.addTextChangedListener(this);
+ }
- }
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
- }
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
- @Override
- public void afterTextChanged(Editable s) {
- if (s.toString().trim().length() > 0) {
- try {
- mFullJidDisplay.setVisibility(View.VISIBLE);
- Jid jid = Jid.of(s.toString().toLowerCase(), Config.MAGIC_CREATE_DOMAIN, null);
- mFullJidDisplay.setText(getString(R.string.your_full_jid_will_be, jid.toEscapedString()));
- } catch (IllegalArgumentException e) {
- mFullJidDisplay.setVisibility(View.INVISIBLE);
- }
+ }
- } else {
- mFullJidDisplay.setVisibility(View.INVISIBLE);
- }
- }
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.toString().trim().length() > 0) {
+ try {
+ binding.fullJid.setVisibility(View.VISIBLE);
+ final Jid jid;
+ if (this.domain == null) {
+ jid = Jid.ofLocalAndDomain(s.toString(), Config.MAGIC_CREATE_DOMAIN);
+ } else {
+ jid = Jid.ofLocalAndDomain(s.toString(), this.domain);
+ }
+ binding.fullJid.setText(getString(R.string.your_full_jid_will_be, jid.toEscapedString()));
+ } catch (IllegalArgumentException e) {
+ binding.fullJid.setVisibility(View.INVISIBLE);
+ }
+
+ } else {
+ binding.fullJid.setVisibility(View.INVISIBLE);
+ }
+ }
}
diff --git a/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java b/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
index fc5d874d3..f03a9a7bc 100644
--- a/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
+++ b/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
@@ -8,6 +8,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.EditAccountActivity;
+import eu.siacs.conversations.ui.MagicCreateActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.ui.PickServerActivity;
import eu.siacs.conversations.ui.StartConversationActivity;
@@ -15,6 +16,17 @@ import eu.siacs.conversations.ui.WelcomeActivity;
public class SignupUtils {
+ public static boolean isSupportTokenRegistry() {
+ return true;
+ }
+
+ public static Intent getTokenRegistrationIntent(final Activity activity, String domain, String preauth) {
+ final Intent intent = new Intent(activity, MagicCreateActivity.class);
+ intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, domain);
+ intent.putExtra(MagicCreateActivity.EXTRA_PRE_AUTH, preauth);
+ return intent;
+ }
+
public static Intent getSignUpIntent(final Activity activity) {
return getSignUpIntent(activity, false);
}
diff --git a/src/conversations/res/layout/magic_create.xml b/src/conversations/res/layout/magic_create.xml
index e08dd84c8..51ef104ea 100644
--- a/src/conversations/res/layout/magic_create.xml
+++ b/src/conversations/res/layout/magic_create.xml
@@ -1,97 +1,112 @@
-
+
-
+
-
+
+
+
+
+ android:fillViewport="true">
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:maxLines="1"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:text="@string/free_for_six_month"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="@dimen/fineprint_size" />
-
-
-
-
+
+
+
diff --git a/src/conversations/res/values/strings.xml b/src/conversations/res/values/strings.xml
index b61e61259..d112c305b 100644
--- a/src/conversations/res/values/strings.xml
+++ b/src/conversations/res/values/strings.xml
@@ -5,5 +5,5 @@
Create new account
Do you already have an XMPP account? This might be the case if you are already using a different XMPP client or have used Conversations before. If not you can create a new XMPP account right now.\nHint: Some email providers also provide XMPP accounts.
XMPP is a provider independent instant messaging network. You can use this client with what ever XMPP server you choose.\nHowever for your convenience we made it easy to create an account on conversations.imĀ¹; a provider specially suited for the use with Conversations.
-
+ You have been invited to %1$s. We will guide you through the process of creating an account.\nWhen picking %1$s as a provider you will be able to communicate with users of other providers by giving them your full XMPP address.
\ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index ef180ded9..82a8126a7 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -50,6 +50,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public static final String RESOURCE = "resource";
public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
+ public static final String PRE_AUTH_REGISTRATION_TOKEN = "pre_auth_registration";
public static final int OPTION_USETLS = 0;
public static final int OPTION_DISABLED = 1;
@@ -619,6 +620,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
REGISTRATION_CONFLICT(true, false),
REGISTRATION_NOT_SUPPORTED(true, false),
REGISTRATION_PLEASE_WAIT(true, false),
+ REGISTRATION_INVALID_TOKEN(true,false),
REGISTRATION_PASSWORD_TOO_WEAK(true, false),
TLS_ERROR,
INCOMPATIBLE_SERVER,
@@ -683,6 +685,8 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
return R.string.account_status_regis_success;
case REGISTRATION_NOT_SUPPORTED:
return R.string.account_status_regis_not_sup;
+ case REGISTRATION_INVALID_TOKEN:
+ return R.string.account_status_regis_invalid_token;
case TLS_ERROR:
return R.string.account_status_tls_error;
case INCOMPATIBLE_SERVER:
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index 640ed2412..7a102bc98 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -403,6 +403,16 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
xmppConnectionService.deleteAccount(mAccount);
}
+ final boolean magicCreate = mAccount != null && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY);
+ final Jid jid = mAccount == null ? null : mAccount.getJid();
+
+ if (SignupUtils.isSupportTokenRegistry() && jid != null && magicCreate && !jid.getDomain().equals(Config.MAGIC_CREATE_DOMAIN)) {
+ final Intent intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), mAccount.getKey(Account.PRE_AUTH_REGISTRATION_TOKEN));
+ startActivity(intent);
+ return;
+ }
+
+
if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) {
Intent intent = SignupUtils.getSignUpIntent(this, mForceRegister != null && mForceRegister);
startActivity(intent);
@@ -816,6 +826,9 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
return false;
}
switch (item.getItemId()) {
+ case android.R.id.home:
+ deleteAccountAndReturnIfNecessary();
+ break;
case R.id.action_show_block_list:
final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
index d1c0fa7d4..b2e39dd51 100644
--- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
@@ -9,12 +9,14 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
import android.widget.Toast;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.utils.SignupUtils;
@@ -87,6 +89,18 @@ public class UriHandlerActivity extends AppCompatActivity {
final XmppUri xmppUri = new XmppUri(uri);
final List accounts = DatabaseBackend.getInstance(this).getAccountJids(true);
+ if (SignupUtils.isSupportTokenRegistry() && xmppUri.isJidValid() && xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
+ final String preauth = xmppUri.getParamater("preauth");
+ final Jid jid = xmppUri.getJid();
+ if (jid.isDomainJid()) {
+ intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preauth);
+ startActivity(intent);
+ return;
+ }
+ Log.d(Config.LOGTAG,"attempting to register on "+jid+" with preauth="+preauth);
+ return;
+ }
+
if (accounts.size() == 0) {
if (xmppUri.isJidValid()) {
intent = SignupUtils.getSignUpIntent(this);
diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
index 8feae0b15..33224e980 100644
--- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java
+++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
@@ -28,6 +28,7 @@ public class XmppUri {
public static final String ACTION_JOIN = "join";
public static final String ACTION_MESSAGE = "message";
+ public static final String ACTION_REGISTER = "register";
public XmppUri(String uri) {
try {
@@ -194,6 +195,10 @@ public class XmppUri {
return parameters.get("name");
}
+ public String getParamater(String key) {
+ return this.parameters.get(key);
+ }
+
public List getFingerprints() {
return this.fingerprints;
}
diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java
index 189492fbf..921a2f580 100644
--- a/src/main/java/eu/siacs/conversations/xml/Namespace.java
+++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java
@@ -37,4 +37,6 @@ public final class Namespace {
public static final String MUC_USER = "http://jabber.org/protocol/muc#user";
public static final String BOOKMARKS2 = "urn:xmpp:bookmarks:0";
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2+"#compat";
+ public static final String INVITE = "urn:xmpp:invite";
+ public static final String PARS = "urn:xmpp:pars:0";
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index bc75f4aba..da0eb9656 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -112,6 +112,7 @@ public class XmppConnection implements Runnable {
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
account.setOption(Account.OPTION_REGISTER, false);
+ Log.d(Config.LOGTAG, account.getJid().asBareJid()+": successfully registered new account on server");
throw new StateChangingError(Account.State.REGISTRATION_SUCCESSFUL);
} else {
final List PASSWORD_TOO_WEAK_MSGS = Arrays.asList(
@@ -838,7 +839,7 @@ public class XmppConnection implements Runnable {
sendStartTLS();
} else if (this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
if (isSecure) {
- sendRegistryRequest();
+ register();
} else {
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": unable to find STARTTLS for registration process "+ XmlHelper.printElementNames(this.streamFeatures));
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
@@ -912,6 +913,26 @@ public class XmppConnection implements Runnable {
return mechanisms;
}
+
+ private void register() {
+ final String preAuth = account.getKey(Account.PRE_AUTH_REGISTRATION_TOKEN);
+ if (preAuth != null && features.invite()) {
+ final IqPacket preAuthRequest = new IqPacket(IqPacket.TYPE.SET);
+ preAuthRequest.addChild("preauth", Namespace.PARS).setAttribute("token", preAuth);
+ sendUnmodifiedIqPacket(preAuthRequest, (account, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ sendRegistryRequest();
+ } else {
+ final Element error = response.getError();
+ Log.d(Config.LOGTAG,account.getJid().asBareJid()+": failed to pre auth. "+error);
+ throw new StateChangingError(Account.State.REGISTRATION_INVALID_TOKEN);
+ }
+ }, true);
+ } else {
+ sendRegistryRequest();
+ }
+ }
+
private void sendRegistryRequest() {
final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
register.query(Namespace.REGISTER);
@@ -1776,6 +1797,10 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(Jid.of(account.getServer()), Namespace.REGISTER);
}
+ public boolean invite() {
+ return connection.streamFeatures != null && connection.streamFeatures.hasChild("register", Namespace.INVITE);
+ }
+
public boolean sm() {
return streamId != null
|| (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm"));
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index fbac96230..82ed17fdd 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -155,6 +155,7 @@
Username already in use
Registration completed
Server does not support registration
+ Invalid registration token
TLS negotiation failed
Policy violation
Incompatible server