d41c7efee78b4988dfe755480e47add1a61791b8
[fonbot.git] / src / ro / ieval / fonbot / HttpCallExecutableRunnable.java
1 package ro.ieval.fonbot;
2
3 import static ro.ieval.fonbot.R.string.*;
4 import static ro.ieval.fonbot.Utils.toNonNull;
5
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.net.URL;
9 import java.security.cert.CertificateException;
10 import java.security.cert.X509Certificate;
11 import java.util.Arrays;
12 import java.util.Collection;
13
14 import javax.net.ssl.HttpsURLConnection;
15 import javax.net.ssl.SSLContext;
16 import javax.net.ssl.SSLSocketFactory;
17 import javax.net.ssl.X509TrustManager;
18
19 import org.eclipse.jdt.annotation.Nullable;
20
21 import android.content.Context;
22 import android.preference.PreferenceManager;
23 import android.util.Base64;
24 import android.util.Log;
25
26 import com.google.android.gcm.GCMRegistrar;
27
28 /*
29 * Copyright © 2013 Marius Gavrilescu
30 *
31 * This file is part of FonBot.
32 *
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.
37 *
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.
42 *
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/>.
45 */
46
47 /**
48 * ExecutableRunnable that makes a HTTPS call to the server and hands the response to a callback
49 *
50 * @author Marius Gavrilescu <marius@ieval.ro>
51 */
52 public final class HttpCallExecutableRunnable extends ExecutableRunnable{
53 /**
54 * X509TrustManager that trusts any certificate
55 *
56 * @author Marius Gavrilescu
57 */
58 private static final class TotallyInsecureTrustManager implements X509TrustManager {
59 @Override
60 public @Nullable X509Certificate[] getAcceptedIssuers() {
61 return new X509Certificate[0];
62 }
63
64 @Override
65 public void checkServerTrusted(final @Nullable X509Certificate[] chain, final @Nullable String authType)
66 throws CertificateException {
67 //do nothing
68 }
69
70 @Override
71 public void checkClientTrusted(final @Nullable X509Certificate[] chain, final @Nullable String authType)
72 throws CertificateException {
73 //do nothing
74 }
75 }
76
77 /**
78 * X509TrustManager that trusts self-signed certificates with a certain public key.
79 *
80 * It is used by default when the server hostname is 'fonbot.ieval.ro'
81 *
82 * @author Marius Gavrilescu <marius@ieval.ro>
83 */
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};
87
88 @Override
89 public void checkClientTrusted(final @Nullable X509Certificate[] chain, final @Nullable String authType)
90 throws CertificateException {
91 //do nothing
92 }
93
94 @Override
95 public void checkServerTrusted(final @Nullable X509Certificate[] chain, final @Nullable String authType)
96 throws CertificateException {
97 if(chain == null)
98 throw new CertificateException("Certificate chain is null");
99 if(chain.length>1)
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");
103 }
104
105 @Override
106 public X509Certificate[] getAcceptedIssuers() {
107 return new X509Certificate[0];
108 }
109
110 }
111 /**
112 * Callback which is run after a HTTP call.
113 *
114 * @author Marius Gavrilescu
115 */
116 public static interface ResultCallback{
117 /**
118 * Callback invoked if the HTTP call is successful.
119 *
120 * @param responseCode HTTP response code
121 * @param responseMessage HTTP response message
122 * @param inputStream HTTP content InputStream
123 */
124 public void onResult(final int responseCode, final String responseMessage, final InputStream inputStream);
125 /**
126 * Callback invoked if the HTTP call is unsuccessful.
127 *
128 * @param error localized error message
129 */
130 public void onError(final String error);
131 }
132
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;
137 static{
138 try{
139 {
140 final SSLContext sslcontext=SSLContext.getInstance("TLS");
141 sslcontext.init(null, new X509TrustManager[]{new TotallyInsecureTrustManager()}, null);
142 DEFAULT_SOCKET_FACTORY=sslcontext.getSocketFactory();
143 }
144 {
145 final SSLContext sslcontext=SSLContext.getInstance("TLS");
146 sslcontext.init(null, new X509TrustManager[]{new IEvalTrustManager()}, null);
147 IEVAL_SOCKET_FACTORY=sslcontext.getSocketFactory();
148 }
149 } catch(final Exception e){
150 Log.wtf("HttpCallExecutableRunnable", "Cannot create SSLSocketFactory", e);
151 throw new AssertionError("Log.wtf did not terminate the process");
152 }
153 }
154
155 /**
156 * List of extra request headers.
157 */
158 private final Collection<Header> headers;
159 /**
160 * Context instance used by this class
161 */
162 private final Context context;
163 /**
164 * Data to send to the server
165 */
166 private final byte[] data;
167 /**
168 * Server URL path
169 */
170 private final String path;
171 /**
172 * Callback to run after the request returns
173 */
174 private final ResultCallback callback;
175
176 /**
177 * Constructs a SendHttpMessageAsyncTask which sends a binary message.
178 *
179 * @param path URL path
180 * @param headers the extra headers
181 * @param context the context instance
182 * @param resultCallback {@link ResultCallback} instance
183 * @param data the message to send
184 */
185 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.
186 this.path=path;
187 this.headers=headers;
188 this.context=context;
189 this.callback=resultCallback;
190 this.data=data;
191 }
192
193 /**
194 * Constructs a SendHttpMessageAsyncTask which sends a text message.
195 *
196 * @param path URL path
197 * @param headers the extra headers
198 * @param context the context instance
199 * @param resultCallback {@link ResultCallback} instance
200 * @param message message to send
201 */
202 public HttpCallExecutableRunnable(final String path, final @Nullable Collection<Header> headers, final Context context, final @Nullable ResultCallback resultCallback, final String... message){
203 this.path=path;
204 this.headers=headers;
205 this.context=context;
206 this.callback=resultCallback;
207 if(message.length == 0)
208 this.data=null;//NOPMD final field
209 else
210 this.data=Utils.join(" ", message).getBytes();
211 }
212
213 @Override
214 public void run() {
215 try {
216 final URL url=Utils.getServerURL(toNonNull(context),toNonNull(path));
217 final HttpsURLConnection conn=(HttpsURLConnection) url.openConnection();
218 if(url.getHost().equals("fonbot.ieval.ro"))
219 conn.setSSLSocketFactory(IEVAL_SOCKET_FACTORY);
220 else
221 conn.setSSLSocketFactory(DEFAULT_SOCKET_FACTORY);
222 if(data!=null){
223 conn.setDoOutput(true);
224 conn.setFixedLengthStreamingMode(data.length);
225 }
226 conn.setRequestProperty("X-ID", GCMRegistrar.getRegistrationId(context));
227 final String user=PreferenceManager.getDefaultSharedPreferences(context).getString("username", null);
228 final String password=PreferenceManager.getDefaultSharedPreferences(context).getString("password", null);
229 if(user == null || password == null){
230 if(callback!=null)
231 callback.onError(toNonNull(context.getString(user_or_password_not_set)));
232 return;
233 }
234
235 conn.setRequestProperty("Authorization", "Basic "+Base64.encodeToString(
236 (user+':'+password).getBytes(), Base64.NO_WRAP));
237 if(headers != null)
238 for (final Header header : headers)
239 conn.setRequestProperty(header.name, header.value);
240 conn.connect();
241 if(data!=null){
242 final OutputStream stream=conn.getOutputStream();
243 stream.write(data);
244 stream.close();
245 }
246 Log.d(getClass().getName(),"HTTP Response: "+conn.getResponseCode()+" "+conn.getResponseMessage());
247 final String message=conn.getResponseMessage();
248 if(message==null && callback != null)
249 callback.onError(toNonNull(context.getString(no_response_returned_from_server)));
250 else if(message != null && callback != null)
251 callback.onResult(conn.getResponseCode(), message,
252 toNonNull(conn.getInputStream()));
253 conn.disconnect();
254 } catch (Exception e) {
255 e.printStackTrace();
256 if(callback != null)
257 callback.onError(toNonNull(context.getString(connection_error)));
258 }
259 }
260 }
This page took 0.027812 seconds and 3 git commands to generate.