XmppConnection: refactor registration code. 'Open Website' on PAYMENT_REQUIRED failure

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java |  20 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java    | 118 +
2 files changed, 79 insertions(+), 59 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java 🔗

@@ -39,6 +39,7 @@ import android.widget.Toast;
 
 import org.openintents.openpgp.util.OpenPgpUtils;
 
+import java.net.URL;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
@@ -154,10 +155,13 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
 			}
 
 			XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
-			String url = connection != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getWebRegistrationUrl() : null;
-			if (url != null && registerNewAccount && !wasDisabled) {
+			boolean openRegistrationUrl = registerNewAccount && mAccount != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB;
+			boolean openPaymentUrl = mAccount != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED;
+			final boolean redirectionWorthyStatus = openPaymentUrl || openRegistrationUrl;
+			URL url = connection != null && redirectionWorthyStatus ? connection.getRedirectionUrl() : null;
+			if (url != null && redirectionWorthyStatus && !wasDisabled) {
 				try {
-					startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+					startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url.toString())));
 					return;
 				} catch (ActivityNotFoundException e) {
 					Toast.makeText(EditAccountActivity.this,R.string.application_found_to_open_website,Toast.LENGTH_SHORT);
@@ -449,11 +453,17 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
 						this.mSaveButton.setTextColor(getSecondaryTextColor());
 					}
 				} else {
-					this.mSaveButton.setText(R.string.connect);
+					XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
+					URL url = connection != null &&  mAccount.getStatus() == Account.State.PAYMENT_REQUIRED ? connection.getRedirectionUrl() : null;
+					if (url != null) {
+						this.mSaveButton.setText(R.string.open_website);
+					} else {
+						this.mSaveButton.setText(R.string.connect);
+					}
 				}
 			} else {
 				XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
-				String url = connection != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getWebRegistrationUrl() : null;
+				URL url = connection != null &&  mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getRedirectionUrl() : null;
 				if (url != null && mRegisterNew.isChecked()) {
 					this.mSaveButton.setText(R.string.open_website);
 				} else {

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -20,6 +20,7 @@ import java.net.ConnectException;
 import java.net.IDN;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
 import java.net.Socket;
 import java.net.URL;
 import java.net.UnknownHostException;
@@ -141,7 +142,7 @@ public class XmppConnection implements Runnable {
 	private final XmppConnectionService mXmppConnectionService;
 
 	private SaslMechanism saslMechanism;
-	private String webRegistrationUrl = null;
+	private URL redirectionUrl = null;
 	private String verifiedHostname = null;
 
 	private class MyKeyManager implements X509KeyManager {
@@ -192,8 +193,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);
-				forceCloseSocket();
-				changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
+				throw new StateChangingError(Account.State.REGISTRATION_SUCCESSFUL);
 			} else {
 				final List<String> PASSWORD_TOO_WEAK_MSGS = Arrays.asList(
 						"The password is too weak",
@@ -211,8 +211,7 @@ public class XmppConnection implements Runnable {
 						state = Account.State.REGISTRATION_PASSWORD_TOO_WEAK;
 					}
 				}
-				changeStatus(state);
-				forceCloseSocket();
+				throw new StateChangingError(state);
 			}
 		}
 	};
@@ -544,15 +543,21 @@ public class XmppConnection implements Runnable {
 				final Element failure = tagReader.readElement(nextTag);
 				if (Namespace.SASL.equals(failure.getNamespace())) {
 					final String text = failure.findChildContent("text");
-					if (failure.hasChild("account-disabled")
-							&& text != null
-							&& text.contains("renew")
-							&& Config.MAGIC_CREATE_DOMAIN != null
-							&& text.contains(Config.MAGIC_CREATE_DOMAIN)) {
-						throw new StateChangingException(Account.State.PAYMENT_REQUIRED);
-					} else {
-						throw new StateChangingException(Account.State.UNAUTHORIZED);
+					if (failure.hasChild("account-disabled") && text != null) {
+						Matcher matcher = Patterns.AUTOLINK_WEB_URL.matcher(text);
+						if (matcher.find()) {
+							try {
+								URL url = new URL(text.substring(matcher.start(), matcher.end()));
+								if (url.getProtocol().equals("https")) {
+									this.redirectionUrl = url;
+									throw new StateChangingException(Account.State.PAYMENT_REQUIRED);
+								}
+							} catch (MalformedURLException e) {
+								throw new StateChangingException(Account.State.UNAUTHORIZED);
+							}
+						}
 					}
+					throw new StateChangingException(Account.State.UNAUTHORIZED);
 				} else if (Namespace.TLS.equals(failure.getNamespace())) {
 					throw new StateChangingException(Account.State.TLS_ERROR);
 				} else {
@@ -940,10 +945,14 @@ 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"))) {
+				if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
+					return;
+				}
+				if (packet.getType() == IqPacket.TYPE.ERROR) {
+					throw new StateChangingError(Account.State.REGISTRATION_FAILED);
+				}
+				final Element query = packet.query("jabber:iq:register");
+				if (query.hasChild("username") && (query.hasChild("password"))) {
 					final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
 					final Element username = new Element("username").setContent(account.getUsername());
 					final Element password = new Element("password").setContent(account.getPassword());
@@ -951,73 +960,74 @@ public class XmppConnection implements Runnable {
 					register.query().addChild(password);
 					register.setFrom(account.getJid().toBareJid());
 					sendUnmodifiedIqPacket(register, registrationResponseListener);
-				} 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");
+				} else if (query.hasChild("x", "jabber:x:data")) {
+					final Data data = Data.parse(query.findChild("x", "jabber:x:data"));
+					final Element blob = query.findChild("data", "urn:xmpp:bob");
 					final String id = packet.getId();
-
-					Bitmap captcha = null;
+					InputStream is;
 					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);
+							is = new ByteArrayInputStream(strBlob);
 						} catch (Exception e) {
-							//ignored
+							is = null;
 						}
 					} else {
 						try {
-							Field url = data.getFieldByName("url");
-							String urlString = url.findChildContent("value");
-							URL uri = new URL(urlString);
-							captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream());
+							Field field = data.getFieldByName("url");
+							URL url = field != null && field.getValue() != null ? new URL(field.getValue()) : null;
+							is = url != null ? url.openStream() : null;
 						} catch (IOException e) {
-							Log.e(Config.LOGTAG, e.toString());
+							is = null;
 						}
 					}
 
-					if (captcha != null) {
-						failed = !mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha);
+					if (is != null) {
+						Bitmap captcha = BitmapFactory.decodeStream(is);
+						try {
+							if (mXmppConnectionService.displayCaptchaRequest(account, id, data, captcha)) {
+								return;
+							}
+						} catch (Exception e) {
+							throw new StateChangingError(Account.State.REGISTRATION_FAILED);
+						}
 					}
-				} else {
-					failed = true;
-				}
-
-				if (failed) {
-					final Element query = packet.query();
+					throw new StateChangingError(Account.State.REGISTRATION_FAILED);
+				} else if (query.hasChild("instructions") || query.hasChild("x",Namespace.OOB)) {
 					final String instructions = query.findChildContent("instructions");
 					final Element oob = query.findChild("x", Namespace.OOB);
 					final String url = oob == null ? null : oob.findChildContent("url");
-					if (url == null && instructions != null) {
+					if (url != null) {
+						setAccountCreationFailed(url);
+					} else if (instructions != null) {
 						Matcher matcher = Patterns.AUTOLINK_WEB_URL.matcher(instructions);
 						if (matcher.find()) {
 							setAccountCreationFailed(instructions.substring(matcher.start(), matcher.end()));
-						} else {
-							setAccountCreationFailed(null);
 						}
-					} else {
-						setAccountCreationFailed(url);
 					}
+					throw new StateChangingError(Account.State.REGISTRATION_FAILED);
 				}
 			}
 		});
 	}
 
 	private void setAccountCreationFailed(String url) {
-		if (url != null && (url.toLowerCase().startsWith("http://") || url.toLowerCase().startsWith("https://"))) {
-			changeStatus(Account.State.REGISTRATION_WEB);
-			this.webRegistrationUrl = url;
-		} else {
-			changeStatus(Account.State.REGISTRATION_FAILED);
+		if (url != null) {
+			try {
+				this.redirectionUrl = new URL(url);
+				if (this.redirectionUrl.getProtocol().equals("https")) {
+					throw new StateChangingError(Account.State.REGISTRATION_WEB);
+				}
+			} catch (MalformedURLException e) {
+				//fall through
+			}
 		}
-		disconnect(true);
-		Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not register. url=" + url);
+		throw new StateChangingError(Account.State.REGISTRATION_FAILED);
 	}
 
-	public String getWebRegistrationUrl() {
-		return this.webRegistrationUrl;
+	public URL getRedirectionUrl() {
+		return this.redirectionUrl;
 	}
 
 	public void resetEverything() {
@@ -1025,7 +1035,7 @@ public class XmppConnection implements Runnable {
 		resetStreamId();
 		clearIqCallbacks();
 		mStanzaQueue.clear();
-		this.webRegistrationUrl = null;
+		this.redirectionUrl = null;
 		synchronized (this.disco) {
 			disco.clear();
 		}