Commit 09c88efe authored by Chok's avatar Chok
Browse files

chok: init commit

parent 02cfb854
Pipeline #18 failed with stages
in 0 seconds
package com.silkimen.cordovahttp;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.TLSConfiguration;
import org.json.JSONObject;
class CordovaHttpOperation extends CordovaHttpBase {
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
int connectTimeout, int readTimeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
CordovaObservableCallbackContext callbackContext) {
super(method, url, serializer, data, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration,
callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject headers, int connectTimeout, int readTimeout, boolean followRedirects,
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
super(method, url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
}
}
package com.silkimen.cordovahttp;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.Future;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.util.Base64;
import javax.net.ssl.TrustManagerFactory;
public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
private static final String TAG = "Cordova-Plugin-HTTP";
private TLSConfiguration tlsConfiguration;
private HashMap<Integer, Future<?>> reqMap;
private final Object reqMapLock = new Object();
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.tlsConfiguration = new TLSConfiguration();
this.reqMap = new HashMap<Integer, Future<?>>();
try {
KeyStore store = KeyStore.getInstance("AndroidCAStore");
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
store.load(null);
tmf.init(store);
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
if (this.preferences.contains("androidblacklistsecuresocketprotocols")) {
this.tlsConfiguration.setBlacklistedProtocols(
this.preferences.getString("androidblacklistsecuresocketprotocols", "").split(",")
);
}
} catch (Exception e) {
Log.e(TAG, "An error occured while loading system's CA certificates", e);
}
}
@Override
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext)
throws JSONException {
if (action == null) {
return false;
}
if ("setServerTrustMode".equals(action)) {
return this.setServerTrustMode(args, callbackContext);
} else if ("setClientAuthMode".equals(action)) {
return this.setClientAuthMode(args, callbackContext);
} else if ("abort".equals(action)) {
return this.abort(args, callbackContext);
}
if (!isNetworkAvailable()) {
CordovaHttpResponse response = new CordovaHttpResponse();
response.setStatus(-6);
response.setErrorMessage("No network connection available");
callbackContext.error(response.toJSON());
return true;
}
if ("get".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("head".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("delete".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("options".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("post".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("put".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("patch".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("uploadFiles".equals(action)) {
return this.uploadFiles(args, callbackContext);
} else if ("downloadFile".equals(action)) {
return this.downloadFile(args, callbackContext);
} else {
return false;
}
}
private boolean executeHttpRequestWithoutData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
int connectTimeout = args.getInt(2) * 1000;
int readTimeout = args.getInt(3) * 1000;
boolean followRedirect = args.getBoolean(4);
String responseType = args.getString(5);
Integer reqId = args.getInt(6);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, connectTimeout, readTimeout,
followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, request);
return true;
}
private boolean executeHttpRequestWithData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
Object data = args.get(1);
String serializer = args.getString(2);
JSONObject headers = args.getJSONObject(3);
int connectTimeout = args.getInt(4) * 1000;
int readTimeout = args.getInt(5) * 1000;
boolean followRedirect = args.getBoolean(6);
String responseType = args.getString(7);
Integer reqId = args.getInt(8);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
connectTimeout, readTimeout, followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, request);
return true;
}
private boolean uploadFiles(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
JSONArray filePaths = args.getJSONArray(2);
JSONArray uploadNames = args.getJSONArray(3);
int connectTimeout = args.getInt(4) * 1000;
int readTimeout = args.getInt(5) * 1000;
boolean followRedirect = args.getBoolean(6);
String responseType = args.getString(7);
Integer reqId = args.getInt(8);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, connectTimeout, readTimeout, followRedirect,
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext);
startRequest(reqId, observableCallbackContext, upload);
return true;
}
private boolean downloadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
int connectTimeout = args.getInt(3) * 1000;
int readTimeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
Integer reqId = args.getInt(6);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, connectTimeout, readTimeout,
followRedirect, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, download);
return true;
}
private void startRequest(Integer reqId, CordovaObservableCallbackContext observableCallbackContext, CordovaHttpBase request) {
synchronized (reqMapLock) {
observableCallbackContext.setObserver(this);
Future<?> task = cordova.getThreadPool().submit(request);
this.addReq(reqId, task, observableCallbackContext);
}
}
private boolean setServerTrustMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(runnable);
return true;
}
private boolean setClientAuthMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
byte[] pkcs = args.isNull(2) ? null : Base64.decode(args.getString(2), Base64.DEFAULT);
CordovaClientAuth runnable = new CordovaClientAuth(args.getString(0), args.isNull(1) ? null : args.getString(1),
pkcs, args.getString(3), this.cordova.getActivity(), this.cordova.getActivity().getApplicationContext(),
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(runnable);
return true;
}
private boolean abort(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
int reqId = args.getInt(0);
boolean result = false;
// NOTE no synchronized (reqMapLock), since even if the req was already removed from reqMap,
// the worst that would happen calling task.cancel(true) is a result of false
// (i.e. same result as locking & not finding the req in reqMap)
Future<?> task = this.reqMap.get(reqId);
if (task != null && !task.isDone()) {
result = task.cancel(true);
}
callbackContext.success(new JSONObject().put("aborted", result));
return true;
}
private void addReq(final Integer reqId, final Future<?> task, final CordovaObservableCallbackContext observableCallbackContext) {
synchronized (reqMapLock) {
if (!task.isDone()){
this.reqMap.put(reqId, task);
}
}
}
private void removeReq(final Integer reqId) {
synchronized (reqMapLock) {
this.reqMap.remove(reqId);
}
}
@Override
public void update(Observable o, Object arg) {
synchronized (reqMapLock) {
CordovaObservableCallbackContext c = (CordovaObservableCallbackContext) arg;
if (c.getCallbackContext().isFinished()) {
removeReq(c.getRequestId());
}
}
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) cordova.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
}
package com.silkimen.cordovahttp;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import android.text.TextUtils;
import android.util.Log;
import android.util.Base64;
class CordovaHttpResponse {
private int status;
private String url;
private Map<String, List<String>> headers;
private String body;
private byte[] rawData;
private JSONObject fileEntry;
private boolean hasFailed;
private boolean isFileOperation;
private boolean isRawResponse;
private String error;
public void setStatus(int status) {
this.status = status;
}
public void setUrl(String url) {
this.url = url;
}
public void setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
}
public void setBody(String body) {
this.body = body;
}
public void setData(byte[] rawData) {
this.isRawResponse = true;
this.rawData = rawData;
}
public void setFileEntry(JSONObject entry) {
this.isFileOperation = true;
this.fileEntry = entry;
}
public void setErrorMessage(String message) {
this.hasFailed = true;
this.error = message;
}
public boolean hasFailed() {
return this.hasFailed;
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
json.put("status", this.status);
json.put("url", this.url);
if (this.headers != null && !this.headers.isEmpty()) {
json.put("headers", new JSONObject(getFilteredHeaders()));
}
if (this.hasFailed) {
json.put("error", this.error);
} else if (this.isFileOperation) {
json.put("file", this.fileEntry);
} else if (this.isRawResponse) {
json.put("data", Base64.encodeToString(this.rawData, Base64.DEFAULT));
} else {
json.put("data", this.body);
}
return json;
}
private Map<String, String> getFilteredHeaders() throws JSONException {
Map<String, String> filteredHeaders = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
String key = entry.getKey();
List<String> value = entry.getValue();
if ((key != null) && (!value.isEmpty())) {
filteredHeaders.put(key.toLowerCase(), TextUtils.join(", ", value));
}
}
return filteredHeaders;
}
}
package com.silkimen.cordovahttp;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.TLSConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
class CordovaHttpUpload extends CordovaHttpBase {
private JSONArray filePaths;
private JSONArray uploadNames;
private Context applicationContext;
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int connectTimeout, int readTimeout,
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
Context applicationContext, CordovaObservableCallbackContext callbackContext) {
super("POST", url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
this.filePaths = filePaths;
this.uploadNames = uploadNames;
this.applicationContext = applicationContext;
}
@Override
protected void sendBody(HttpRequest request) throws JSONException, URISyntaxException, FileNotFoundException {
for (int i = 0; i < this.filePaths.length(); ++i) {
String uploadName = this.uploadNames.getString(i);
String filePath = this.filePaths.getString(i);
Uri fileUri = Uri.parse(filePath);
// File Scheme
if (ContentResolver.SCHEME_FILE.equals(fileUri.getScheme())) {
File file = new File(new URI(filePath));
String fileName = file.getName().trim();
String mimeType = this.getMimeTypeFromFileName(fileName);
request.part(uploadName, fileName, mimeType, file);
}
// Content Scheme
if (ContentResolver.SCHEME_CONTENT.equals(fileUri.getScheme())) {
InputStream inputStream = this.applicationContext.getContentResolver().openInputStream(fileUri);
String fileName = this.getFileNameFromContentScheme(fileUri, this.applicationContext).trim();
String mimeType = this.getMimeTypeFromFileName(fileName);
request.part(uploadName, fileName, mimeType, inputStream);
}
}
}
private String getFileNameFromContentScheme(Uri contentSchemeUri, Context applicationContext) {
Cursor returnCursor = applicationContext.getContentResolver().query(contentSchemeUri, null, null, null, null);
if (returnCursor == null || !returnCursor.moveToFirst()) {
return null;
}
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
String fileName = returnCursor.getString(nameIndex);
returnCursor.close();
return fileName;
}
private String getMimeTypeFromFileName(String fileName) {
if (fileName == null || !fileName.contains(".")) {
return null;
}
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
int extIndex = fileName.lastIndexOf('.') + 1;
String extension = fileName.substring(extIndex).toLowerCase();
return mimeTypeMap.getMimeTypeFromExtension(extension);
}
}
package com.silkimen.cordovahttp;
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
import java.util.Observer;
public class CordovaObservableCallbackContext {
private CallbackContext callbackContext;
private Integer requestId;
private Observer observer;
public CordovaObservableCallbackContext(CallbackContext callbackContext, Integer requestId) {
this.callbackContext = callbackContext;
this.requestId = requestId;
}
public void success(JSONObject message) {
this.callbackContext.success(message);
this.notifyObserver();
}
public void error(JSONObject message) {
this.callbackContext.error(message);
this.notifyObserver();
}
public Integer getRequestId() {
return this.requestId;
}
public CallbackContext getCallbackContext() {
return callbackContext;
}
public Observer getObserver() {
return observer;
}
protected void notifyObserver() {
if(this.observer != null){
this.observer.update(null, this);
}
}
/**
* Set an observer that is notified, when {@link #success(JSONObject)}
* or {@link #error(JSONObject)} are called.
*
* NOTE the observer is notified with
* <pre>observer.update(null, cordovaObservableCallbackContext)</pre>
* @param observer
*/
public void setObserver(Observer observer) {
this.observer = observer;
}
}
package com.silkimen.cordovahttp;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import android.app.Activity;
import android.util.Log;
import android.content.res.AssetManager;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
class CordovaServerTrust implements Runnable {
private static final String TAG = "Cordova-Plugin-HTTP";
private final TrustManager[] noOpTrustManagers;
private String mode;
private Activity activity;
private TLSConfiguration tlsConfiguration;
private CallbackContext callbackContext;
public CordovaServerTrust(final String mode, final Activity activity, final TLSConfiguration configContainer,
final CallbackContext callbackContext) {
this.mode = mode;
this.activity = activity;
this.tlsConfiguration = configContainer;
this.callbackContext = callbackContext;
this.noOpTrustManagers = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// intentionally left blank
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// intentionally left blank
}
} };
}
@Override
public void run() {
try {
if ("legacy".equals(this.mode)) {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(null);
} else if ("nocheck".equals(this.mode)) {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(this.noOpTrustManagers);
} else if ("pinned".equals(this.mode)) {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromBundle("www/certificates")));
} else {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
}
callbackContext.success();
} catch (Exception e) {
Log.e(TAG, "An error occured while configuring SSL cert mode", e);
callbackContext.error("An error occured while configuring SSL cert mode");
}
}
private TrustManager[] getTrustManagers(KeyStore store) throws GeneralSecurityException {
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(store);
return tmf.getTrustManagers();
}
private KeyStore getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
AssetManager assetManager = this.activity.getAssets();
String[] files = assetManager.list(path);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
for (int i = 0; i < files.length; i++) {
int index = files[i].lastIndexOf('.');
if (index == -1 || !files[i].substring(index).equals(".cer")) {
continue;
}
keyStore.setCertificateEntry("CA" + i, cf.generateCertificate(assetManager.open(path + "/" + files[i])));
}
return keyStore;
}
private KeyStore getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
KeyStore store = KeyStore.getInstance(storeType);
store.load(null);
return store;
}
}
package com.silkimen.http;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;
public class HttpBodyDecoder {
private static final String[] ACCEPTED_CHARSETS = new String[] { "UTF-8", "ISO-8859-1" };
public static String decodeBody(byte[] body, String charsetName)
throws CharacterCodingException, MalformedInputException {
return decodeBody(ByteBuffer.wrap(body), charsetName);
}
public static String decodeBody(ByteBuffer body, String charsetName)
throws CharacterCodingException, MalformedInputException {
if (charsetName == null) {
return tryDecodeByteBuffer(body);
} else {
return decodeByteBuffer(body, charsetName);
}
}
private static String tryDecodeByteBuffer(ByteBuffer buffer)
throws CharacterCodingException, MalformedInputException {
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
try {
return decodeByteBuffer(buffer, ACCEPTED_CHARSETS[i]);
} catch (MalformedInputException e) {
continue;
} catch (CharacterCodingException e) {
continue;
}
}
return decodeBody(buffer, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
}
private static String decodeByteBuffer(ByteBuffer buffer, String charsetName)
throws CharacterCodingException, MalformedInputException {
return createCharsetDecoder(charsetName).decode(buffer).toString();
}
private static CharsetDecoder createCharsetDecoder(String charsetName) {
return Charset.forName(charsetName).newDecoder().onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
}
This diff is collapsed.
package com.silkimen.http;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class JsonUtils {
public static HashMap<String, String> getStringMap(JSONObject object) throws JSONException {
HashMap<String, String> map = new HashMap<String, String>();
if (object == null) {
return map;
}
Iterator<?> i = object.keys();
while (i.hasNext()) {
String key = (String) i.next();
map.put(key, object.getString(key));
}
return map;
}
public static HashMap<String, Object> getObjectMap(JSONObject object) throws JSONException {
HashMap<String, Object> map = new HashMap<String, Object>();
if (object == null) {
return map;
}
Iterator<?> i = object.keys();
while (i.hasNext()) {
String key = (String) i.next();
Object value = object.get(key);
if (value instanceof JSONArray) {
map.put(key, getObjectList((JSONArray) value));
} else {
map.put(key, object.get(key));
}
}
return map;
}
public static ArrayList<Object> getObjectList(JSONArray array) throws JSONException {
ArrayList<Object> list = new ArrayList<Object>();
for (int i = 0; i < array.length(); i++) {
list.add(array.get(i));
}
return list;
}
}
package com.silkimen.http;
import android.content.Context;
import android.security.KeyChain;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509ExtendedKeyManager;
public class KeyChainKeyManager extends X509ExtendedKeyManager {
private final String alias;
private final X509Certificate[] chain;
private final PrivateKey key;
public KeyChainKeyManager(String alias, PrivateKey key, X509Certificate[] chain) {
this.alias = alias;
this.key = key;
this.chain = chain;
}
@Override
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
return this.alias;
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return chain;
}
@Override
public PrivateKey getPrivateKey(String alias) {
return key;
}
@Override
public final String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
@Override
public final String[] getClientAliases(String keyType, Principal[] issuers) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
@Override
public final String[] getServerAliases(String keyType, Principal[] issuers) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
}
package com.silkimen.http;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import com.silkimen.http.TLSSocketFactory;
public class TLSConfiguration {
private TrustManager[] trustManagers = null;
private KeyManager[] keyManagers = null;
private HostnameVerifier hostnameVerifier = null;
private String[] blacklistedProtocols = {};
private SSLSocketFactory socketFactory;
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
}
public void setKeyManagers(KeyManager[] keyManagers) {
this.keyManagers = keyManagers;
this.socketFactory = null;
}
public void setTrustManagers(TrustManager[] trustManagers) {
this.trustManagers = trustManagers;
this.socketFactory = null;
}
public void setBlacklistedProtocols(String[] protocols) {
this.blacklistedProtocols = protocols;
this.socketFactory = null;
}
public HostnameVerifier getHostnameVerifier() {
return this.hostnameVerifier;
}
public SSLSocketFactory getTLSSocketFactory() throws IOException {
if (this.socketFactory != null) {
return this.socketFactory;
}
try {
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
this.socketFactory = new TLSSocketFactory(context, this.blacklistedProtocols);
return this.socketFactory;
} catch (GeneralSecurityException e) {
IOException ioException = new IOException("Security exception occured while configuring TLS context");
ioException.initCause(e);
throw ioException;
}
}
}
package com.silkimen.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
private List<String> blacklistedProtocols;
public TLSSocketFactory(SSLContext context, String[] blacklistedProtocols) {
this.delegate = context.getSocketFactory();
this.blacklistedProtocols = new ArrayList();
for (int i = 0; i < blacklistedProtocols.length; ++i) {
this.blacklistedProtocols.add(blacklistedProtocols[i].trim());
}
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(socket, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if (socket == null || !(socket instanceof SSLSocket)) {
return socket;
}
String[] supported = ((SSLSocket) socket).getSupportedProtocols();
List<String> filtered = new ArrayList();
for (int i = 0; i < supported.length; ++i) {
if (!this.blacklistedProtocols.contains(supported[i])) {
filtered.add(supported[i]);
}
}
((SSLSocket) socket).setEnabledProtocols(filtered.toArray(new String[0]));
return socket;
}
}
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var cordovaProxy = require('cordova/exec/proxy');
var jsUtil = require(pluginId + '.js-util');
var reqMap = {};
function serializeJsonData(data) {
try {
return JSON.stringify(data);
} catch (err) {
return null;
}
}
function serializePrimitive(key, value) {
if (value === null || value === undefined) {
return encodeURIComponent(key) + '=';
}
return encodeURIComponent(key) + '=' + encodeURIComponent(value);
}
function serializeArray(key, values) {
return values.map(function (value) {
return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
}).join('&');
}
function serializeParams(params) {
if (params === null) return '';
return Object.keys(params).map(function (key) {
if (jsUtil.getTypeOf(params[key]) === 'Array') {
return serializeArray(key, params[key]);
}
return serializePrimitive(key, params[key]);
}).join('&');
}
function decodeB64(dataString) {
var binaryString = atob(dataString);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; ++i) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
function processMultipartData(data) {
if (!data) return null;
var fd = new FormData();
for (var i = 0; i < data.buffers.length; ++i) {
var buffer = data.buffers[i];
var name = data.names[i];
var fileName = data.fileNames[i];
var type = data.types[i];
if (fileName) {
fd.append(name, new Blob([decodeB64(buffer)], { type: type }), fileName);
} else {
// we assume it's plain text if no filename was given
fd.append(name, atob(buffer));
}
}
return fd;
}
function deserializeResponseHeaders(headers) {
var headerMap = {};
var arr = headers.trim().split(/[\r\n]+/);
arr.forEach(function (line) {
var parts = line.split(': ');
var header = parts.shift().toLowerCase();
var value = parts.join(': ');
headerMap[header] = value;
});
return headerMap;
}
function getResponseData(xhr) {
if (xhr.responseType !== 'text' || jsUtil.getTypeOf(xhr.responseText) !== 'String') {
return xhr.response;
}
return xhr.responseText;
}
function createXhrSuccessObject(xhr) {
return {
url: xhr.responseURL,
status: xhr.status,
data: getResponseData(xhr),
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
};
}
function createXhrFailureObject(xhr) {
var obj = {};
obj.headers = xhr.getAllResponseHeaders();
obj.error = getResponseData(xhr);
obj.error = obj.error || 'advanced-http: please check browser console for error messages';
if (xhr.responseURL) obj.url = xhr.responseURL;
if (xhr.status) obj.status = xhr.status;
return obj;
}
function injectRequestIdHandler(reqId, cb) {
return function (response) {
delete reqMap[reqId];
cb(response);
}
}
function getHeaderValue(headers, headerName) {
let result = null;
Object.keys(headers).forEach(function (key) {
if (key.toLowerCase() === headerName.toLowerCase()) {
result = headers[key];
}
});
return result;
}
function setDefaultContentType(headers, contentType) {
if (getHeaderValue(headers, 'Content-Type') === null) {
headers['Content-Type'] = contentType;
}
}
function setHeaders(xhr, headers) {
Object.keys(headers).forEach(function (key) {
if (key.toLowerCase() === 'cookie') return;
xhr.setRequestHeader(key, headers[key]);
});
}
function sendRequest(method, withData, opts, success, failure) {
var data, serializer, headers, readTimeout, followRedirect, responseType, reqId;
var url = opts[0];
if (withData) {
data = opts[1];
serializer = opts[2];
headers = opts[3];
// connect timeout not applied
// connectTimeout = opts[4];
readTimeout = opts[5];
followRedirect = opts[6];
responseType = opts[7];
reqId = opts[8];
} else {
headers = opts[1];
// connect timeout not applied
// connectTimeout = opts[2];
readTimeout = opts[3];
followRedirect = opts[4];
responseType = opts[5];
reqId = opts[6];
}
var onSuccess = injectRequestIdHandler(reqId, success);
var onFail = injectRequestIdHandler(reqId, failure);
var processedData = null;
var xhr = new XMLHttpRequest();
reqMap[reqId] = xhr;
xhr.open(method, url);
if (headers.Cookie && headers.Cookie.length > 0) {
return onFail('advanced-http: custom cookies not supported on browser platform');
}
if (!followRedirect) {
return onFail('advanced-http: disabling follow redirect not supported on browser platform');
}
switch (serializer) {
case 'json':
setDefaultContentType(headers, 'application/json; charset=utf8');
processedData = serializeJsonData(data);
if (processedData === null) {
return onFail('advanced-http: failed serializing data');
}
break;
case 'utf8':
setDefaultContentType(headers, 'text/plain; charset=utf8');
processedData = data.text;
break;
case 'urlencoded':
setDefaultContentType(headers, 'application/x-www-form-urlencoded');
processedData = serializeParams(data);
break;
case 'multipart':
const contentType = getHeaderValue(headers, 'Content-Type');
// intentionally don't set a default content type
// it's set by the browser together with the content disposition string
if (contentType) {
headers['Content-Type'] = contentType;
}
processedData = processMultipartData(data);
break;
case 'raw':
setDefaultContentType(headers, 'application/octet-stream');
processedData = data;
break;
}
// requesting text instead of JSON because it's parsed in the response handler
xhr.responseType = responseType === 'json' ? 'text' : responseType;
// we can't set connect timeout and read timeout separately on browser platform
xhr.timeout = readTimeout * 1000;
try {
setHeaders(xhr, headers);
} catch(error) {
return onFail({
status: -1,
error: error,
url: url,
headers: headers
});
}
xhr.onerror = function () {
return onFail(createXhrFailureObject(xhr));
};
xhr.onabort = function () {
return onFail({
status: -8,
error: 'Request was aborted',
url: url,
headers: {}
});
};
xhr.ontimeout = function () {
return onFail({
status: -4,
error: 'Request timed out',
url: url,
headers: {}
});
};
xhr.onload = function () {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status < 200 || xhr.status > 299) {
return onFail(createXhrFailureObject(xhr));
}
return onSuccess(createXhrSuccessObject(xhr));
};
xhr.send(processedData);
}
function abort(opts, success, failure) {
var reqId = opts[0];
var result = false;
var xhr = reqMap[reqId];
if(xhr && xhr.readyState !== xhr.DONE){
xhr.abort();
result = true;
}
success({aborted: result});
}
var browserInterface = {
get: function (success, failure, opts) {
return sendRequest('get', false, opts, success, failure);
},
head: function (success, failure, opts) {
return sendRequest('head', false, opts, success, failure);
},
delete: function (success, failure, opts) {
return sendRequest('delete', false, opts, success, failure);
},
post: function (success, failure, opts) {
return sendRequest('post', true, opts, success, failure);
},
put: function (success, failure, opts) {
return sendRequest('put', true, opts, success, failure);
},
patch: function (success, failure, opts) {
return sendRequest('patch', true, opts, success, failure);
},
abort: function (success, failure, opts) {
return abort(opts, success, failure);
},
uploadFile: function (success, failure, opts) {
return failure('advanced-http: function "uploadFile" not supported on browser platform');
},
uploadFiles: function (success, failure, opts) {
return failure('advanced-http: function "uploadFiles" not supported on browser platform');
},
downloadFile: function (success, failure, opts) {
return failure('advanced-http: function "downloadFile" not supported on browser platform');
},
setServerTrustMode: function (success, failure, opts) {
return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
},
setClientAuthMode: function (success, failure, opts) {
return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
}
};
module.exports = browserInterface;
cordovaProxy.add('CordovaHttpPlugin', browserInterface);
#import <Foundation/Foundation.h>
#import "SM_AFURLRequestSerialization.h"
@interface BinaryRequestSerializer : SM_AFHTTPRequestSerializer
+ (instancetype)serializer;
@end
#import "BinaryRequestSerializer.h"
@implementation BinaryRequestSerializer
+ (instancetype)serializer
{
BinaryRequestSerializer *serializer = [[self alloc] init];
return serializer;
}
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody: parameters];
}
return mutableRequest;
}
#pragma mark - NSSecureCoding
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
return self;
}
@end
#import <Foundation/Foundation.h>
#import "SM_AFURLResponseSerialization.h"
@interface BinaryResponseSerializer : SM_AFHTTPResponseSerializer
+ (instancetype)serializer;
@end
#import "BinaryResponseSerializer.h"
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
@implementation BinaryResponseSerializer
+ (instancetype)serializer {
BinaryResponseSerializer *serializer = [[self alloc] init];
return serializer;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = nil;
return self;
}
- (NSString*)decodeResponseData:(NSData*)rawResponseData withEncoding:(CFStringEncoding)cfEncoding {
NSStringEncoding nsEncoding;
NSString* decoded = nil;
if (cfEncoding != kCFStringEncodingInvalidId) {
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
NSStringEncoding supportedEncodings[6] = {
NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
};
for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
}
}
return decoded;
}
- (CFStringEncoding) getEncoding:(NSURLResponse *)response {
CFStringEncoding encoding = kCFStringEncodingInvalidId;
if (response.textEncodingName) {
encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
}
return encoding;
}
#pragma mark -
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey: [response URL],
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
// trying to decode error message in body
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
}
if (error) {
*error = [NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
}
return NO;
}
}
return YES;
}
#pragma mark - SM_AFURLResponseSerialization
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
return [data base64EncodedStringWithOptions:0];
}
@end
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
@interface CordovaHttpPlugin : CDVPlugin
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command;
- (void)post:(CDVInvokedUrlCommand*)command;
- (void)put:(CDVInvokedUrlCommand*)command;
- (void)patch:(CDVInvokedUrlCommand*)command;
- (void)get:(CDVInvokedUrlCommand*)command;
- (void)delete:(CDVInvokedUrlCommand*)command;
- (void)head:(CDVInvokedUrlCommand*)command;
- (void)options:(CDVInvokedUrlCommand*)command;
- (void)uploadFiles:(CDVInvokedUrlCommand*)command;
- (void)downloadFile:(CDVInvokedUrlCommand*)command;
- (void)abort:(CDVInvokedUrlCommand*)command;
@end
This diff is collapsed.
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment