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
.Collection
;
13 import javax
.net
.ssl
.HttpsURLConnection
;
14 import javax
.net
.ssl
.SSLContext
;
15 import javax
.net
.ssl
.SSLSocketFactory
;
16 import javax
.net
.ssl
.X509TrustManager
;
18 import org
.eclipse
.jdt
.annotation
.Nullable
;
20 import android
.content
.Context
;
21 import android
.preference
.PreferenceManager
;
22 import android
.util
.Base64
;
23 import android
.util
.Log
;
25 import com
.google
.android
.gcm
.GCMRegistrar
;
28 * Copyright © 2013 Marius Gavrilescu
30 * This file is part of FonBot.
32 * FonBot is free software: you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation, either version 3 of the License, or
35 * (at your option) any later version.
37 * FonBot is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
42 * You should have received a copy of the GNU General Public License
43 * along with FonBot. If not, see <http://www.gnu.org/licenses/>.
47 * ExecutableRunnable that makes a HTTPS call to the server and hands the response to a callback
49 * @author Marius Gavrilescu <marius@ieval.ro>
51 public final class HttpCallExecutableRunnable
extends ExecutableRunnable
{
53 * X509TrustManager that trusts any certificate
55 * @author Marius Gavrilescu
57 private static final class TotallyInsecureTrustManager
implements X509TrustManager
{
59 public @Nullable X509Certificate
[] getAcceptedIssuers() {
60 return new X509Certificate
[0];
64 public void checkServerTrusted(final @Nullable X509Certificate
[] chain
, final @Nullable String authType
)
65 throws CertificateException
{
70 public void checkClientTrusted(final @Nullable X509Certificate
[] chain
, final @Nullable String authType
)
71 throws CertificateException
{
77 * Callback which is run after a HTTP call.
79 * @author Marius Gavrilescu
81 public static interface ResultCallback
{
83 * Callback invoked if the HTTP call is successful.
85 * @param responseCode HTTP response code
86 * @param responseMessage HTTP response message
87 * @param inputStream HTTP content InputStream
89 public void onResult(final int responseCode
, final String responseMessage
, final InputStream inputStream
);
91 * Callback invoked if the HTTP call is unsuccessful.
93 * @param error localized error message
95 public void onError(final String error
);
98 /** SSLSocketFactory that uses {@link TotallyInsecureTrustManager} */
99 private static final SSLSocketFactory DEFAULT_SOCKET_FACTORY
;
102 final SSLContext sslcontext
=SSLContext
.getInstance("TLS");
103 sslcontext
.init(null, new X509TrustManager
[]{new TotallyInsecureTrustManager()}, null);
104 DEFAULT_SOCKET_FACTORY
=sslcontext
.getSocketFactory();
105 } catch(final Exception e
){
106 Log
.wtf("HttpCallExecutableRunnable", "Cannot create SSLSocketFactory", e
);
107 throw new AssertionError("Log.wtf did not terminate the process");
112 * List of extra request headers.
114 private final Collection
<Header
> headers
;
116 * Context instance used by this class
118 private final Context context
;
120 * Data to send to the server
122 private final byte[] data
;
126 private final String path
;
128 * Callback to run after the request returns
130 private final ResultCallback callback
;
133 * Constructs a SendHttpMessageAsyncTask which sends a binary message.
135 * @param path URL path
136 * @param headers the extra headers
137 * @param context the context instance
138 * @param resultCallback {@link ResultCallback} instance
139 * @param data the message to send
141 public HttpCallExecutableRunnable(final String path
, final @Nullable Collection
<Header
> headers
, final Context context
, final @Nullable ResultCallback resultCallback
, final byte[] data
){//NOPMD array is supposed to be immutable.
143 this.headers
=headers
;
144 this.context
=context
;
145 this.callback
=resultCallback
;
150 * Constructs a SendHttpMessageAsyncTask which sends a text message.
152 * @param path URL path
153 * @param headers the extra headers
154 * @param context the context instance
155 * @param resultCallback {@link ResultCallback} instance
156 * @param message message to send
158 public HttpCallExecutableRunnable(final String path
, final @Nullable Collection
<Header
> headers
, final Context context
, final @Nullable ResultCallback resultCallback
, final String
... message
){
160 this.headers
=headers
;
161 this.context
=context
;
162 this.callback
=resultCallback
;
163 if(message
.length
== 0)
164 this.data
=null;//NOPMD final field
166 this.data
=Utils
.join(" ", message
).getBytes();
172 final URL url
=Utils
.getServerURL(toNonNull(context
),toNonNull(path
));
173 final HttpsURLConnection conn
=(HttpsURLConnection
) url
.openConnection();
174 conn
.setSSLSocketFactory(DEFAULT_SOCKET_FACTORY
);
176 conn
.setDoOutput(true);
177 conn
.setFixedLengthStreamingMode(data
.length
);
179 conn
.setRequestProperty("X-ID", GCMRegistrar
.getRegistrationId(context
));
180 final String user
=PreferenceManager
.getDefaultSharedPreferences(context
).getString("username", null);
181 final String password
=PreferenceManager
.getDefaultSharedPreferences(context
).getString("password", null);
182 if(user
== null || password
== null){
184 callback
.onError(toNonNull(context
.getString(user_or_password_not_set
)));
188 conn
.setRequestProperty("Authorization", "Basic "+Base64
.encodeToString(
189 (user
+':'+password
).getBytes(), Base64
.NO_WRAP
));
191 for (final Header header
: headers
)
192 conn
.setRequestProperty(header
.name
, header
.value
);
195 final OutputStream stream
=conn
.getOutputStream();
199 Log
.d(getClass().getName(),"HTTP Response: "+conn
.getResponseCode()+" "+conn
.getResponseMessage());
200 final String message
=conn
.getResponseMessage();
201 if(message
==null && callback
!= null)
202 callback
.onError(toNonNull(context
.getString(no_response_returned_from_server
)));
203 else if(message
!= null && callback
!= null)
204 callback
.onResult(conn
.getResponseCode(), message
,
205 toNonNull(conn
.getInputStream()));
207 } catch (Exception e
) {
210 callback
.onError(toNonNull(context
.getString(connection_error
)));