Convert everything to HTTPS
[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.Collection;
12
13 import javax.net.ssl.HttpsURLConnection;
14 import javax.net.ssl.SSLContext;
15 import javax.net.ssl.SSLSocketFactory;
16 import javax.net.ssl.X509TrustManager;
17
18 import org.eclipse.jdt.annotation.Nullable;
19
20 import android.content.Context;
21 import android.preference.PreferenceManager;
22 import android.util.Base64;
23 import android.util.Log;
24
25 import com.google.android.gcm.GCMRegistrar;
26
27 /*
28 * Copyright © 2013 Marius Gavrilescu
29 *
30 * This file is part of FonBot.
31 *
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.
36 *
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.
41 *
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/>.
44 */
45
46 /**
47 * ExecutableRunnable that makes a HTTPS call to the server and hands the response to a callback
48 *
49 * @author Marius Gavrilescu <marius@ieval.ro>
50 */
51 public final class HttpCallExecutableRunnable extends ExecutableRunnable{
52 /**
53 * X509TrustManager that trusts any certificate
54 *
55 * @author Marius Gavrilescu
56 */
57 private static final class TotallyInsecureTrustManager implements X509TrustManager {
58 @Override
59 public @Nullable X509Certificate[] getAcceptedIssuers() {
60 return new X509Certificate[0];
61 }
62
63 @Override
64 public void checkServerTrusted(final @Nullable X509Certificate[] chain, final @Nullable String authType)
65 throws CertificateException {
66 //do nothing
67 }
68
69 @Override
70 public void checkClientTrusted(final @Nullable X509Certificate[] chain, final @Nullable String authType)
71 throws CertificateException {
72 //do nothing
73 }
74 }
75
76 /**
77 * Callback which is run after a HTTP call.
78 *
79 * @author Marius Gavrilescu
80 */
81 public static interface ResultCallback{
82 /**
83 * Callback invoked if the HTTP call is successful.
84 *
85 * @param responseCode HTTP response code
86 * @param responseMessage HTTP response message
87 * @param inputStream HTTP content InputStream
88 */
89 public void onResult(final int responseCode, final String responseMessage, final InputStream inputStream);
90 /**
91 * Callback invoked if the HTTP call is unsuccessful.
92 *
93 * @param error localized error message
94 */
95 public void onError(final String error);
96 }
97
98 /** SSLSocketFactory that uses {@link TotallyInsecureTrustManager} */
99 private static final SSLSocketFactory DEFAULT_SOCKET_FACTORY;
100 static{
101 try{
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");
108 }
109 }
110
111 /**
112 * List of extra request headers.
113 */
114 private final Collection<Header> headers;
115 /**
116 * Context instance used by this class
117 */
118 private final Context context;
119 /**
120 * Data to send to the server
121 */
122 private final byte[] data;
123 /**
124 * Server URL path
125 */
126 private final String path;
127 /**
128 * Callback to run after the request returns
129 */
130 private final ResultCallback callback;
131
132 /**
133 * Constructs a SendHttpMessageAsyncTask which sends a binary message.
134 *
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
140 */
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.
142 this.path=path;
143 this.headers=headers;
144 this.context=context;
145 this.callback=resultCallback;
146 this.data=data;
147 }
148
149 /**
150 * Constructs a SendHttpMessageAsyncTask which sends a text message.
151 *
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
157 */
158 public HttpCallExecutableRunnable(final String path, final @Nullable Collection<Header> headers, final Context context, final @Nullable ResultCallback resultCallback, final String... message){
159 this.path=path;
160 this.headers=headers;
161 this.context=context;
162 this.callback=resultCallback;
163 if(message.length == 0)
164 this.data=null;//NOPMD final field
165 else
166 this.data=Utils.join(" ", message).getBytes();
167 }
168
169 @Override
170 public void run() {
171 try {
172 final URL url=Utils.getServerURL(toNonNull(context),toNonNull(path));
173 final HttpsURLConnection conn=(HttpsURLConnection) url.openConnection();
174 conn.setSSLSocketFactory(DEFAULT_SOCKET_FACTORY);
175 if(data!=null){
176 conn.setDoOutput(true);
177 conn.setFixedLengthStreamingMode(data.length);
178 }
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){
183 if(callback!=null)
184 callback.onError(toNonNull(context.getString(user_or_password_not_set)));
185 return;
186 }
187
188 conn.setRequestProperty("Authorization", "Basic "+Base64.encodeToString(
189 (user+':'+password).getBytes(), Base64.NO_WRAP));
190 if(headers != null)
191 for (final Header header : headers)
192 conn.setRequestProperty(header.name, header.value);
193 conn.connect();
194 if(data!=null){
195 final OutputStream stream=conn.getOutputStream();
196 stream.write(data);
197 stream.close();
198 }
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(), Utils.parseHttpMessage(message),
205 toNonNull(conn.getInputStream()));
206 conn.disconnect();
207 } catch (Exception e) {
208 e.printStackTrace();
209 if(callback != null)
210 callback.onError(toNonNull(context.getString(connection_error)));
211 }
212 }
213 }
This page took 0.02558 seconds and 4 git commands to generate.