diff --git a/pom.xml b/pom.xml index bdcdef0..9a4ee3b 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,16 @@ httpclient 4.5.2 + + com.squareup.retrofit2 + retrofit + 2.2.0 + + + com.squareup.retrofit2 + converter-gson + 2.2.0 + org.apache.commons commons-lang3 diff --git a/src/main/java/org/kontalk/xmppserver/registration/jmp/BaseResult.java b/src/main/java/org/kontalk/xmppserver/registration/jmp/BaseResult.java new file mode 100644 index 0000000..9efcddf --- /dev/null +++ b/src/main/java/org/kontalk/xmppserver/registration/jmp/BaseResult.java @@ -0,0 +1,77 @@ +/* + * Kontalk XMPP Tigase extension + * Copyright (C) 2017 Kontalk Devteam + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kontalk.xmppserver.registration.jmp; + + +/** + * Verification base result class. + * @author Daniele Ricci + */ +public abstract class BaseResult { + + /** + * Verify was successfully submitted to the JMP service + */ + public static final int STATUS_OK = 0; + + /** + * Verify was rejected due to exceeding the maximum throughput allowed for this account.
+ * Verify can be re-requested after a short delay + */ + public static final int STATUS_THROTTLED = 1; + + /** + * Verify was rejected due to a failure within the JMP systems.
+ * Verify can be re-submitted after a short delay + */ + public static final int STATUS_INTERNAL_ERROR = 2; + + /** + * The code inserted does not match the expected value + */ + public static final int STATUS_INVALID_CODE = 16; + + /** + * There are no matching verification requests + */ + public static final int STATUS_NO_RESPONSE = 101; + + /** + * A network error occured + */ + public static final int STATUS_COMMS_FAILURE = -1; + + private final int status; + private final String errorText; + + protected BaseResult(final int status, + final String errorText) { + this.status = status; + this.errorText = errorText; + } + + public int getStatus() { + return this.status; + } + + public String getErrorText() { + return this.errorText; + } + +} diff --git a/src/main/java/org/kontalk/xmppserver/registration/jmp/CheckResult.java b/src/main/java/org/kontalk/xmppserver/registration/jmp/CheckResult.java new file mode 100644 index 0000000..640cf3b --- /dev/null +++ b/src/main/java/org/kontalk/xmppserver/registration/jmp/CheckResult.java @@ -0,0 +1,41 @@ +/* + * Kontalk XMPP Tigase extension + * Copyright (C) 2017 Kontalk Devteam + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kontalk.xmppserver.registration.jmp; + + +/** + * Verification check result. + * @author Daniele Ricci + */ +public class CheckResult extends BaseResult { + + private final String eventId; + + protected CheckResult(final int status, + final String eventId, + final String errorText) { + super(status, errorText); + this.eventId = eventId; + } + + public String getEventId() { + return this.eventId; + } + +} diff --git a/src/main/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyClient.java b/src/main/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyClient.java new file mode 100644 index 0000000..8eda35e --- /dev/null +++ b/src/main/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyClient.java @@ -0,0 +1,156 @@ +/* + * Kontalk XMPP Tigase extension + * Copyright (C) 2017 Kontalk Devteam + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kontalk.xmppserver.registration.jmp; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.util.Locale; + + +/** + * Client for talking to the JMP REST interface + * @author Daniele Ricci + */ +public class JmpVerifyClient { + private static final Log log = LogFactory.getLog(JmpVerifyClient.class); + + /** + * Service url used unless overridden on the constructor + */ + public static final String DEFAULT_BASE_URL = "https://jvr.api.jmp.chat"; + + /** + * Default connection timeout of 5000ms used by this client unless specifically overridden onb the constructor + */ + public static final int DEFAULT_CONNECTION_TIMEOUT = 5000; + + /** + * Default read timeout of 30000ms used by this client unless specifically overridden onb the constructor + */ + public static final int DEFAULT_SO_TIMEOUT = 30000; + + private final String baseUrl; + private final String apiKey; + private final String apiSecret; + + private final int connectionTimeout; + private final int soTimeout; + + private JmpVerifyService service; + + public JmpVerifyClient(final String apiKey, + final String apiSecret) throws ParserConfigurationException { + this(DEFAULT_BASE_URL, + apiKey, + apiSecret, + DEFAULT_CONNECTION_TIMEOUT, + DEFAULT_SO_TIMEOUT); + } + + /** + * Instanciate a new NexmoVerifyClient instance that will communicate using the supplied credentials, and will use the supplied connection and read timeout values.
+ * Additionally, you can specify an alternative service base url. For example submitting to a testing sandbox environment, + * or if requested to submit to an alternative address by Nexmo, for example, in cases where it may be necessary to prioritize your traffic. + * + * @param apiKey Your Nexmo account api key + * @param apiSecret Your Nexmo account api secret + * @param connectionTimeout over-ride the default connection timeout with this value (in milliseconds) + * @param soTimeout over-ride the default read-timeout with this value (in milliseconds) + */ + public JmpVerifyClient(final String baseUrl, + final String apiKey, + final String apiSecret, + final int connectionTimeout, + final int soTimeout) throws ParserConfigurationException { + + // Derive a http and a https version of the supplied base url + if (baseUrl == null) + throw new IllegalArgumentException("base url is null"); + String url = baseUrl.trim(); + String lc = url.toLowerCase(); + if (!lc.startsWith("http://") && !lc.startsWith("https://")) + throw new IllegalArgumentException("base url does not start with http:// or https://"); + + this.baseUrl = url; + this.apiKey = apiKey; + this.apiSecret = apiSecret; + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + this.service = buildService(); + } + + public VerifyResult verify(String number, String brand) throws IOException { + return verify(number, brand, null, -1, null); + } + + public VerifyResult verify(String number, String brand, String from) throws IOException { + return verify(number, brand, from, -1, null); + } + + public VerifyResult verify(String number, String brand, String from, int length, Locale locale) + throws IOException { + if (number == null || brand == null) + throw new IllegalArgumentException("number and brand parameters are mandatory."); + if (length > 0 && length != 4 && length != 6) + throw new IllegalArgumentException("code length must be 4 or 6."); + + Response response = service.verify(apiKey, apiSecret, number, brand).execute(); + if (response.isSuccessful()) { + return response.body(); + } + else { + return new VerifyResult(BaseResult.STATUS_COMMS_FAILURE, null, "Communication error"); + } + } + + public CheckResult check(String requestId, String code) throws IOException { + if (requestId == null || code == null) + throw new IllegalArgumentException("request ID and code parameters are mandatory."); + + Response response = service.check(apiKey, apiSecret, requestId, code).execute(); + if (response.isSuccessful()) { + return response.body(); + } + else { + return new CheckResult(BaseResult.STATUS_COMMS_FAILURE, null, "Communication error"); + } + } + + private JmpVerifyService buildService() { + Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + + return new Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + .create(JmpVerifyService.class); + } + +} diff --git a/src/main/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyService.java b/src/main/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyService.java new file mode 100644 index 0000000..0c18d41 --- /dev/null +++ b/src/main/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyService.java @@ -0,0 +1,36 @@ +/* + * Kontalk XMPP Tigase extension + * Copyright (C) 2017 Kontalk Devteam + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kontalk.xmppserver.registration.jmp; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + + +public interface JmpVerifyService { + + @GET("verify") + Call verify(@Query("api_key") String apiKey, @Query("api_secret") String apiSecret, + @Query("number") String number, @Query("brand") String brand); + + @GET("verify/check") + Call check(@Query("api_key") String apiKey, @Query("api_secret") String apiSecret, + @Query("request_id") String requestId, @Query("code") String code); + +} diff --git a/src/main/java/org/kontalk/xmppserver/registration/jmp/VerifyResult.java b/src/main/java/org/kontalk/xmppserver/registration/jmp/VerifyResult.java new file mode 100644 index 0000000..ce32ed7 --- /dev/null +++ b/src/main/java/org/kontalk/xmppserver/registration/jmp/VerifyResult.java @@ -0,0 +1,41 @@ +/* + * Kontalk XMPP Tigase extension + * Copyright (C) 2017 Kontalk Devteam + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kontalk.xmppserver.registration.jmp; + + +/** + * Verification request result. + * @author Daniele Ricci + */ +public class VerifyResult extends BaseResult { + + private final String requestId; + + protected VerifyResult(final int status, + final String requestId, + final String errorText) { + super(status, errorText); + this.requestId = requestId; + } + + public String getRequestId() { + return this.requestId; + } + +} diff --git a/src/test/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyClientTest.java b/src/test/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyClientTest.java new file mode 100644 index 0000000..9233bb0 --- /dev/null +++ b/src/test/java/org/kontalk/xmppserver/registration/jmp/JmpVerifyClientTest.java @@ -0,0 +1,48 @@ +package org.kontalk.xmppserver.registration.jmp; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +public class JmpVerifyClientTest { + + private static final String API_KEY = null; + private static final String API_SECRET = null; + + // insert values here to execute a verify test + private static final String TEST_NUMBER = null; + private static final String TEST_BRAND = null; + // insert values here to execute a verify check test + private static final String TEST_REQUEST_ID = null; + private static final String TEST_CODE = null; + + private JmpVerifyClient client; + + @Before + public void setUp() throws Exception { + client = new JmpVerifyClient(API_KEY, API_SECRET); + } + + private void assertStatus(BaseResult res) { + assertEquals(BaseResult.STATUS_OK, res.getStatus()); + } + + @Test + public void testRequest() throws Exception { + if (TEST_NUMBER != null && TEST_BRAND != null) { + VerifyResult res = client.verify(TEST_NUMBER, TEST_BRAND); + System.out.println(res.getRequestId()); + assertStatus(res); + } + } + + @Test + public void testCheck() throws Exception { + if (TEST_REQUEST_ID != null && TEST_CODE != null) { + CheckResult res = client.check(TEST_REQUEST_ID, TEST_CODE); + assertStatus(res); + } + } +}