package com.duosecurity.x_ray;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class XrayUpdater {
private Activity activity = null;
private Context context = null;
private final static String TAG = XrayUpdater.class.getSimpleName();
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static final int REQUEST_EXTERNAL_STORAGE = 1;
public static final String CHECKSUM_ALGORITHM = "SHA-256";
public static final String ECDSA_ALGORITHM = "SHA256withECDSA";
public static final String ECDSA_PROVIDER = "SC"; // spongycastle
public static final int CONNECTION_TIMEOUT = 15000;
public static final int READ_TIMEOUT = 15000;
public static final String BASE_URL = "https://labs.duo.com/xray";
public final static String DOWNLOAD_URL = BASE_URL + "/dl";
public final static String VERSION_URL = BASE_URL + "/version";
public final static String DOWNLOAD_DIR = "/Download/";
public final static String FILE_TYPE = "application/vnd.android.package-archive";
public static final String SERVER_PUB_KEY =
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEy5bOzkZ36VV+kjSYso0HTCZwHWMT\n" +
"29lQWpJYAiudtZ65mdcBCgmsB/jAwLIJl8BricbLhGU9FA/Wxha5b3ee7A==";
public static final String[] CERT_PINS = {
"de52af8cdb1f9ab9fe5a67c386faf689587fc91b" // C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA
};
public enum CheckResult {
UP_TO_DATE, OUT_OF_DATE, SSL_ERROR
}
public enum UpdateResult {
DOWNLOAD_SUCCESS, DOWNLOAD_ERROR, SSL_ERROR
}
private static final String SHARED_PREFERENCES = "X-Ray";
private static SharedPreferences sharedPreferences = null;
public XrayUpdater(Activity act) {
activity = act;
context = activity.getApplicationContext();
sharedPreferences = activity.getSharedPreferences(SHARED_PREFERENCES, context.MODE_PRIVATE);
}
public static void setSharedPreference(String preference, String value) {
sharedPreferences.edit().putString(preference, value).commit();
}
public static String getSharedPreference(String preference) {
return sharedPreferences.getString(preference, "");
}
private void displayUpdateErrorPrompt() {
new AlertDialog.Builder(activity)
.setTitle("Unable to update")
.setCancelable(false)
.setMessage(
"Something is interfering with your secure connection to the update server.\n\n" +
"Try downloading a new version of X-Ray from https://xray.io.\n\n" +
"If the problem persists, try connecting to a different network."
)
.setPositiveButton("Download now", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Uri downloadUri = Uri.parse(DOWNLOAD_URL);
Intent browserIntent = new Intent(Intent.ACTION_VIEW, downloadUri);
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(browserIntent);
}
})
.setNegativeButton("Later", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.show();
}
private void startUpdateTask() {
XrayUpdateTask updateTask = new XrayUpdateTask(context, new XrayUpdateTask.TaskListener() {
@Override
public void onFinished(UpdateResult updateResult) {
if (updateResult != UpdateResult.DOWNLOAD_SUCCESS) {
displayUpdateErrorPrompt();
}
}
});
updateTask.execute();
}
public void startCheckTask() {
Log.d(TAG, "Checking for updates...");
XrayCheckTask checkTask = new XrayCheckTask(context, new XrayCheckTask.TaskListener() {
@Override
public void onFinished(CheckResult checkResult) {
if (checkResult == CheckResult.SSL_ERROR) {
displayUpdateErrorPrompt();
}
else if (checkResult == CheckResult.OUT_OF_DATE) {
Log.d(TAG, "Update available");
new AlertDialog.Builder(activity)
.setTitle("Update Available")
.setMessage(
"A new version of X-Ray is available. " +
"Would you like to download it now?\n" +
"(Requires permissions to write to storage)"
)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
int permission = ContextCompat.checkSelfPermission(
context, Manifest.permission.WRITE_EXTERNAL_STORAGE
);
// don't have permissions, so need to request
if (permission != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Requesting permissions...");
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
// already have permissions, start update immediately
else {
Log.d(TAG, "Already have permissions, skipping request procedure");
startUpdateTask();
}
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.d(TAG, "Update request denied by user");
}
})
.show();
}
}
});
checkTask.execute();
}
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// user gave us permission to write to external storage, so kick off update task
startUpdateTask();
} else {
Log.d(TAG, "Update request denied by user");
}
}
public static boolean writeToOutputStream (InputStream inputStream, OutputStream outputStream) {
byte[] buffer = new byte[4096];
int nRead;
try {
while ((nRead = inputStream.read(buffer, 0, buffer.length)) > 0) {
outputStream.write(buffer, 0, nRead);
}
outputStream.flush();
} catch (IOException e) {
return false;
}
return true;
}
}