1 package ro
.ieval
.fonbot
;
3 import static ro
.ieval
.fonbot
.R
.string
.*;
4 import static ro
.ieval
.fonbot
.Utils
.toNonNull
;
6 import java
.io
.InputStream
;
7 import java
.io
.OutputStream
;
9 import java
.security
.cert
.CertificateException
;
10 import java
.security
.cert
.X509Certificate
;
11 import java
.util
.Arrays
;
12 import java
.util
.Collection
;
14 import javax
.net
.ssl
.HttpsURLConnection
;
15 import javax
.net
.ssl
.SSLContext
;
16 import javax
.net
.ssl
.SSLSocketFactory
;
17 import javax
.net
.ssl
.X509TrustManager
;
19 import org
.eclipse
.jdt
.annotation
.Nullable
;
21 import android
.content
.Context
;
22 import android
.preference
.PreferenceManager
;
23 import android
.util
.Base64
;
24 import android
.util
.Log
;
26 import com
.google
.android
.gcm
.GCMRegistrar
;
29 * Copyright © 2013 Marius Gavrilescu
31 * This file is part of FonBot.
33 * FonBot is free software: you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published by
35 * the Free Software Foundation, either version 3 of the License, or
36 * (at your option) any later version.
38 * FonBot is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 * GNU General Public License for more details.
43 * You should have received a copy of the GNU General Public License
44 * along with FonBot. If not, see <http://www.gnu.org/licenses/>.
48 * ExecutableRunnable that makes a HTTPS call to the server and hands the response to a callback
50 * @author Marius Gavrilescu <marius@ieval.ro>
52 public final class HttpCallExecutableRunnable
extends ExecutableRunnable
{
54 * X509TrustManager that trusts any certificate
56 * @author Marius Gavrilescu
58 private static final class TotallyInsecureTrustManager
implements X509TrustManager
{
60 public @Nullable X509Certificate
[] getAcceptedIssuers() {
61 return new X509Certificate
[0];
65 public void checkServerTrusted(final @Nullable X509Certificate
[] chain
, final @Nullable String authType
)
66 throws CertificateException
{
71 public void checkClientTrusted(final @Nullable X509Certificate
[] chain
, final @Nullable String authType
)
72 throws CertificateException
{
78 * X509TrustManager that trusts self-signed certificates with a certain public key.
80 * It is used by default when the server hostname is 'fonbot.ieval.ro'
82 * @author Marius Gavrilescu <marius@ieval.ro>
84 private static final class IEvalTrustManager
implements X509TrustManager
{
85 /** The iEval public key */
86 private static final byte[] pubkey
={-66, -35, -47, -78, 8, 14, -118, -49, 72, 37, -106, 122, 102, 124, -58, -38, 107, -124, 104, -70, -53, -126, -121, 125, -117, -121, -70, -61, -83, 114, -93, -95, -26, 26, 75, 114, -101, 77, -83, -91, -64, -79, 26, -15, 46, -101, -3, 111, -94, -105, 92, -79, -61, 45, -37, -4, -23, -37, -77, 59, 98, -22, -87, 28, -86, 124, 105, -21, 66, 24, -15, -101, 30, 98, -109, -76, 81, 27, -65, -21, 90, -22, 70, 100, -63, 75, -67, -11, -69, 65, 115, 44, -70, -31, 21, 96, -114, -72, 7, -20, 99, 2, -13, 43, 107, 99, -94, -69, 17, -48, 2, 87, -55, 2, -24, 47, 74, 23, -123, -120, -17, 116, 7, -86, -8, -9, 55, 1, 126, -12, 59, -4, 76, 31, -35, 99, 46, 63, -125, 13, 103, 49, -103, -118, 106, -69, -68, 33, 98, 84, 31, 45, 120, -26, 12, -114, -7, 54, -114, 60, -66, -105, -6, 108, -38, -37, 30, 119, -2, -101, -75, 127, 11, -8, 110, -83, 34, -97, -6, 33, -18, 86, 38, -75, 52, -114, -113, 57, 97, 38, -64, -6, -55, 81, -15, 92, -111, 13, 127, 123, 54, 95, -66, -8, 124, 76, 37, -64, 54, -126, -8, -61, -78, 10, -89, -79, 5, 58, -47, -52, 59, -92, -118, 114, 98, 51, 78, 77, -93, 19, -46, -108, 25, -27, -57, -78, -48, -104, -18, -57, -126, 55, 12, 117, -93, -60, -5, -6, 53, -104, 49, 108, -18, 43, 71, 28, -99, 70, -81, -3, 33, -7, -69, -72, -11, 33, 79, -59, -25, 55, -54, -118, -52, -44, 73, 75, -35, 30, 24, -67, 95, -12, -16, -104, -88, 94, 106, -18, -47, -87, -13, -79, 127, -70, 66, 61, -98, -62, 124, -51, 13, 93, -72, -100, -15, -97, 112, -94, -109, 3, -86, -72, 5, -18, -3, 103, 58, -6, 102, -10, -54, 89, -51, 12, -15, -42, -112, -14, -49, -102, -55, 42, -78, -28, -46, 15, -14, -90, -27, 56, 126, 26, -53, 83, 98, 87, 97, 86, -73, -64, 56, -86, 92, 2, 72, -69, 43, -6, -72, -22, -38, -100, -115, -64, -11, 115, 102, -66, 33, 120, -106, -41, -7, -107, 12, -13, -113, 116, -21, 61, -36, 40, 2, -3, -56, -93, -24, 32, 73, -47, 9, -110, 56, -120, -45, -104, 8, 1, 46, -93, -64, -76, -39, -19, 27, 81, -90, -110, -22, 80, 67, -45, 12, 63, -55, -117, -28, -41, -76, 31, 46, -19, 15, 58, 81, -113, 26, -76, -16, -93, -5, 59, -12, 85, -4, 100, -12, -93, 70, 73, 96, -12, -76, 120, 29, -113, 4, 56, 48, 67, -26, -45, 83, 9, -57, -111, 82, 75, 88, 58, 71, 72, -114, -53, 42, -5, 97, 115, 36, -17, 55, -7, -109, -117, -20, 45, -51, -96, -106, -3, 95, 28, 39, 91, -19, 100, -24, -104, 81, -28, 9, -16, -38, 104, -96, -53, 107, -50, -24, -92, 41, 61, 26, -82, 108, 29, 2, -112, 6, -9, -125, 110, 26, -117, 99, 77, 19, -8, 31, 82, 110, 116, -5, -91, 74, -56, 6, 50, -43, 32, -104, 105, 82, 51, 61, -58, -72, 77, 111, -66, 11, 30, -75, 82, -9, 11, 26, 25, -72, 116, -92, 51, 61, -6, 108, -38, -72, -41, 42, 69, -9, 19, 123, 68, -28, 84, -89, -20, -120, -113, 66, 59, 102, -106, 6, -26, 1, 61, -109, -123, -50, -23, 33, 79, -43, -9, 3, 86, -25, -16, 10, 70, 80, -76, -99, -12, -19, 15, 1, 114, -2, 42, -63, -31, 97, -66, -39, 125, 125, 122, 62, -78, 61, 26, -47, -120, -75, -98, 79, -61, -93, 28, -61, -30, -19, 44, -41, -32, -96, -34, -23, -76, -21, 3, -91, -66, -69, 109, -52, -75, 20, 85, 51, -85, -76, -98, 77, 56, 40, -114, 52, 101, -91, -116, 101, -119, -117, 52, 127, 31, -32, 21, -114, -66, 52, -114, 15, -47, 78, 75, 10, 37, -9, 50, -49, -55, -41, 32, 81, 17, 78, -3, 85, -92, -41, -53, -80, -23, -64, 86, -91, 123, 108, 29, -89, 58, 77, 81, 77, 102, -115, 126, -71, 127, -116, 1, 38, -110, -110, 88, 93, -69, 1, 57, 82, 19, 121, -127, 50, 56, 34, -8, -67, 22, -122, -33, -27, -32, 42, -29, -123, -81, -8, -62, -103, 63, -12, 75, -100, 55, 21, 81, -99, -105, 99, 26, -27, -124, -78, -2, 23, -2, 29, 45, -113, 9, 19, -81, -53, 5, -44, -120, 100, 86, 81, -106, 95, -55, -125, -20, 58, -37, -17, -27, -67, 7, 5, -123, 7, -6, 96, 17, -6, -31, -66, -43, 65, -46, -56, 40, -73, 98, 124, -48, 109, -59, -44, -92, 16, -62, -69, -31, -111, -92, -91, -17, -84, -105, 103, 94, 33, 100, 104, 88, 8, -106, 106, 98, 97, -100, 24, -51, -99, 73, -90, -109, 75, 57, -78, -52, 66, 11, 49, 16, -10, -80, 13, 81, -60, 27, -5, 4, -102, 13, 7, 14, 116, -96, -13, 85, -82, 50, 16, -59, 87, -71, 62, -61, 97, -107, -6, -98, 31, 118, -22, 55, -125, -120, 105, -120, 101, 61, -46, -19, -104, 78, 102, -121, 84, -97, 22, 45, 8, 63, -52, 25, 118, 30, 38, 7, 99, 102, -89, -51, 7, 4, -126, 36, 89, -110, -90, 33, 16, 127, -16, 109, 116, 45, -8, -96, -31, 114, -15, -116, 121, 55, -30, 95, -44, -53, -7, -124, -94, 62, -9, 99, 101, -38, 23, -59, 127, 6, -63, 81, 73, -12, 39, 2, -55, 94, -54, 75, -92, 36, 102, -26, -77, 32, -127, -108, -98, -41, -66, 11, 30, 106, -91, 115, -67, 125, -124, -53, -121, 66, -114, 84, 111, 46, -1, -114, -61, -55, 45, 106, 119, 88, 19, -105, 5, 124, 85, 94, -99, -42, 20, 37, -28, -40, -116, 106, 10, 80, -86, -65, 12, -93, 35, 117, 87, -75, -91, -57, -118, -126, 2, 12, 10, -33, 83, -52, -80, -38, 68, -80, -24, 95, 107, 117, 113, -121, -14, 120, 78, 84};
89 public void checkClientTrusted(final @Nullable X509Certificate
[] chain
, final @Nullable String authType
)
90 throws CertificateException
{
95 public void checkServerTrusted(final @Nullable X509Certificate
[] chain
, final @Nullable String authType
)
96 throws CertificateException
{
98 throw new CertificateException("Certificate chain is null");
100 throw new CertificateException("Certificate chain length is greater than 1");
101 if(!Arrays
.equals(pubkey
, chain
[0].getSignature()))
102 throw new CertificateException("Certificate signature is incorrect");
106 public X509Certificate
[] getAcceptedIssuers() {
107 return new X509Certificate
[0];
112 * Callback which is run after a HTTP call.
114 * @author Marius Gavrilescu
116 public static interface ResultCallback
{
118 * Callback invoked if the HTTP call is successful.
120 * @param responseCode HTTP response code
121 * @param responseMessage HTTP response message
122 * @param inputStream HTTP content InputStream
124 public void onResult(final int responseCode
, final String responseMessage
, final InputStream inputStream
);
126 * Callback invoked if the HTTP call is unsuccessful.
128 * @param error localized error message
130 public void onError(final String error
);
133 /** SSLSocketFactory that uses {@link TotallyInsecureTrustManager} */
134 private static final SSLSocketFactory DEFAULT_SOCKET_FACTORY
;
135 /** SSLSocketFactory that uses {@link IEvalTrustManager} */
136 private static final SSLSocketFactory IEVAL_SOCKET_FACTORY
;
140 final SSLContext sslcontext
=SSLContext
.getInstance("TLS");
141 sslcontext
.init(null, new X509TrustManager
[]{new TotallyInsecureTrustManager()}, null);
142 DEFAULT_SOCKET_FACTORY
=sslcontext
.getSocketFactory();
145 final SSLContext sslcontext
=SSLContext
.getInstance("TLS");
146 sslcontext
.init(null, new X509TrustManager
[]{new IEvalTrustManager()}, null);
147 IEVAL_SOCKET_FACTORY
=sslcontext
.getSocketFactory();
149 } catch(final Exception e
){
150 Log
.wtf("HttpCallExecutableRunnable", "Cannot create SSLSocketFactory", e
);
151 throw new AssertionError("Log.wtf did not terminate the process");
156 * List of extra request headers.
158 private final Collection
<Header
> headers
;
160 * Context instance used by this class
162 private final Context context
;
164 * Data to send to the server
166 private final byte[] data
;
170 private final String path
;
172 * Callback to run after the request returns
174 private final ResultCallback callback
;
175 /** If true, the task should be retried if it fails */
176 private final boolean mustRetryTask
;
179 * Constructs a SendHttpMessageAsyncTask which sends a binary message.
181 * @param path URL path
182 * @param headers the extra headers
183 * @param context the context instance
184 * @param resultCallback {@link ResultCallback} instance
185 * @param mustRetryTask true if this task should be retried if it fails
186 * @param data the message to send
188 public HttpCallExecutableRunnable(final String path
, final @Nullable Collection
<Header
> headers
, final Context context
, final @Nullable ResultCallback resultCallback
, final boolean mustRetryTask
, final byte[] data
){//NOPMD array is supposed to be immutable.
190 this.headers
=headers
;
191 this.context
=context
;
192 this.callback
=resultCallback
;
193 this.mustRetryTask
=mustRetryTask
;
198 * Constructs a SendHttpMessageAsyncTask which sends a text message.
200 * @param path URL path
201 * @param headers the extra headers
202 * @param context the context instance
203 * @param resultCallback {@link ResultCallback} instance
204 * @param mustRetryTask true if this task should be retried if it fails
205 * @param message message to send
207 public HttpCallExecutableRunnable(final String path
, final @Nullable Collection
<Header
> headers
, final Context context
, final @Nullable ResultCallback resultCallback
, final boolean mustRetryTask
, final String
... message
){
209 this.headers
=headers
;
210 this.context
=context
;
211 this.callback
=resultCallback
;
212 this.mustRetryTask
=mustRetryTask
;
213 if(message
.length
== 0)
214 this.data
=null;//NOPMD final field
216 this.data
=Utils
.join(" ", message
).getBytes();
222 final URL url
=Utils
.getServerURL(toNonNull(context
),toNonNull(path
));
223 final HttpsURLConnection conn
=(HttpsURLConnection
) url
.openConnection();
224 if(url
.getHost().equals("fonbot.ieval.ro"))
225 conn
.setSSLSocketFactory(IEVAL_SOCKET_FACTORY
);
227 conn
.setSSLSocketFactory(DEFAULT_SOCKET_FACTORY
);
229 conn
.setDoOutput(true);
230 conn
.setFixedLengthStreamingMode(data
.length
);
232 conn
.setRequestProperty("X-ID", GCMRegistrar
.getRegistrationId(context
));
233 final String user
=PreferenceManager
.getDefaultSharedPreferences(context
).getString("username", null);
234 final String password
=PreferenceManager
.getDefaultSharedPreferences(context
).getString("password", null);
235 if(user
== null || password
== null){
237 callback
.onError(toNonNull(context
.getString(user_or_password_not_set
)));
241 conn
.setRequestProperty("Authorization", "Basic "+Base64
.encodeToString(
242 (user
+':'+password
).getBytes(), Base64
.NO_WRAP
));
244 for (final Header header
: headers
)
245 conn
.setRequestProperty(header
.name
, header
.value
);
248 final OutputStream stream
=conn
.getOutputStream();
252 Log
.d(getClass().getName(),"HTTP Response: "+conn
.getResponseCode()+" "+conn
.getResponseMessage());
253 final String message
=conn
.getResponseMessage();
254 if(message
==null && callback
!= null)
255 callback
.onError(toNonNull(context
.getString(no_response_returned_from_server
)));
256 else if(message
!= null && callback
!= null)
257 callback
.onResult(conn
.getResponseCode(), message
,
258 toNonNull(conn
.getInputStream()));
260 } catch (Exception e
) {
263 callback
.onError(toNonNull(context
.getString(connection_error
)));