package org.awesomeapp.messenger.ui.onboarding;
import org.awesomeapp.messenger.provider.Imps;
import org.awesomeapp.messenger.ui.qr.QrScanActivity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.awesomeapp.messenger.ImApp;
import org.awesomeapp.messenger.ui.legacy.ImPluginHelper;
import org.awesomeapp.messenger.plugin.xmpp.XmppConnection;
import org.awesomeapp.messenger.util.LogCleaner;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.HashMap;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.provider.Telephony;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
public class OnboardingManager {
public final static int REQUEST_SCAN = 1111;
public final static int REQUEST_CHOOSE_AVATAR = REQUEST_SCAN+1;
public final static String BASE_INVITE_URL = "https://zom.im/i/#";
public static void inviteSMSContact (Activity context, String phoneNumber, String message)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) // At least KitKat
{
String defaultSmsPackageName = Telephony.Sms.getDefaultSmsPackage(context); // Need to change the build to API 19
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, message);
if (defaultSmsPackageName != null)// Can be null in case that there is no default, then the user would be able to choose
// any app that support this intent.
{
sendIntent.setPackage(defaultSmsPackageName);
}
context.startActivity(sendIntent);
}
else // For early versions, do what worked for you before.
{
Intent smsIntent = new Intent(android.content.Intent.ACTION_VIEW);
smsIntent.setType("vnd.android-dir/mms-sms");
if (phoneNumber != null)
smsIntent.putExtra("address",phoneNumber);
smsIntent.putExtra("sms_body",message);
context.startActivity(smsIntent);
}
}
public static void inviteShare (Activity context, String message)
{
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, message);
intent.setType("text/plain");
context.startActivity(intent);
}
public static void inviteScan (Activity context, String message)
{
Intent intent = new Intent(context, QrScanActivity.class);
intent.putExtra(Intent.EXTRA_TEXT,message);
intent.setType("text/plain");
context.startActivityForResult(intent, REQUEST_SCAN);
}
public static String generateInviteMessage (Context context, String nickname, String username, String fingerprint)
{
try
{
StringBuffer resp = new StringBuffer();
resp.append(nickname).append(" is inviting you to Zom: ");
resp.append(generateInviteLink(context,username,fingerprint,nickname));
return resp.toString();
} catch (Exception e)
{
Log.d(ImApp.LOG_TAG,"error with link",e);
return null;
}
}
public static String[] decodeInviteLink (String link)
{
Uri inviteLink = Uri.parse(link);
String[] code = inviteLink.toString().split("#");
if (code.length > 1) {
try {
String out = new String(Base64.decode(code[1], Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
// String[] parts = out.split("\\?otr=");
String[] partsTemp = out.split("\\?otr=");
if (partsTemp == null)
{
partsTemp = new String[1];
partsTemp[0] = out;
return partsTemp;
}
else {
if (partsTemp.length > 1 && partsTemp[1].contains("&nickname="))
{
String[] parts = new String[3];
parts[0] = partsTemp[0];
parts[1] = partsTemp[1].substring(0,partsTemp[1].indexOf("&"));
parts[2] = partsTemp[1].substring(partsTemp[1].indexOf("=")+1);
return parts;
}
else
{
return partsTemp;
}
}
}
catch (IllegalArgumentException iae)
{
Log.e(ImApp.LOG_TAG,"bad link decode",iae);
}
}
return null;
}
public static String generateInviteLink (Context context, String username, String fingerprint, String nickname) throws IOException
{
StringBuffer inviteUrl = new StringBuffer();
inviteUrl.append(BASE_INVITE_URL);
StringBuffer code = new StringBuffer();
code.append(username);
code.append("?otr=").append(fingerprint);
if (nickname != null)
code.append("&nickname=").append(nickname);
inviteUrl.append(Base64.encodeToString(code.toString().getBytes(), Base64.URL_SAFE|Base64.NO_WRAP|Base64.NO_PADDING));
return inviteUrl.toString();
}
private final static String PASSWORD_LETTERS = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789+@!#";
private final static int PASSWORD_LENGTH = 12;
public static String generatePassword()
{
// Pick from some letters that won't be easily mistaken for each
// other. So, for example, omit o O and 0, 1 l and L.
SecureRandom random = new SecureRandom();
StringBuffer pw = new StringBuffer();
for (int i=0; i<PASSWORD_LENGTH; i++)
{
int index = (int)(random.nextDouble()*PASSWORD_LETTERS.length());
pw.append(PASSWORD_LETTERS.substring(index, index+1));
}
return pw.toString();
}
public static String[] getServers (Context context)
{
try {
JSONObject obj = new JSONObject(loadServersJSON(context));
JSONArray servers = obj.getJSONArray("servers");
String[] results = new String[servers.length()];
for (int i = 0; i < servers.length(); i++) {
JSONObject server = servers.getJSONObject(i);
results[i] = server.getString("domain");
}
return results;
}
catch (Exception e)
{
return null;
}
}
public static boolean changePassword (Activity context, long providerId, long accountId, String oldPassword, String newPassword)
{
try {
XmppConnection xmppConn = new XmppConnection(context);
xmppConn.initUser(providerId, accountId);
boolean success = xmppConn.changeServerPassword(providerId, accountId, oldPassword, newPassword);
return success;
}
catch (Exception e)
{
return false;
}
}
public static OnboardingAccount registerAccount (Context context, String nickname, String username, String password, String domain, int port) throws JSONException {
if (password == null)
password = generatePassword();
ContentResolver cr = context.getContentResolver();
ImPluginHelper helper = ImPluginHelper.getInstance(context);
long providerId = helper.createAdditionalProvider(helper.getProviderNames().get(0)); //xmpp FIXME
long accountId = ImApp.insertOrUpdateAccount(cr, providerId, -1, nickname, username, password);
Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI, new String[]{Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE}, Imps.ProviderSettings.PROVIDER + "=?", new String[]{Long.toString(providerId)}, null);
Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
pCursor, cr, providerId, false /* don't keep updated */, null /* no handler */);
//should check to see if Orbot is installed and running
JSONObject obj = new JSONObject(loadServersJSON(context));
JSONArray servers = obj.getJSONArray("servers");
settings.setRequireTls(true);
settings.setTlsCertVerify(true);
settings.setAllowPlainAuth(false);
if (domain == null) {
int nameIdx = 0;
for (int i = 0; i < servers.length(); i++) {
JSONObject server = servers.getJSONObject(i);
try {
domain = server.getString("domain");
String host = server.getString("server");
if (host != null) {
settings.setServer(host); //if we have a host, then we should use it
settings.setDoDnsSrv(false);
}
else
{
settings.setServer(null);
settings.setDoDnsSrv(true);
settings.setUseTor(false);
}
settings.setDomain(domain);
settings.setPort(server.getInt("port"));
settings.requery();
HashMap<String, String> aParams = new HashMap<String, String>();
XmppConnection xmppConn = new XmppConnection(context);
xmppConn.initUser(providerId, accountId);
boolean success = xmppConn.registerAccount(settings, username, password, aParams);
if (success) {
OnboardingAccount result = null;
result = new OnboardingAccount();
result.username = username;
result.domain = domain;
result.password = password;
result.providerId = providerId;
result.accountId = accountId;
result.nickname = nickname;
//now keep this account signed-in
ContentValues values = new ContentValues();
values.put(Imps.AccountColumns.KEEP_SIGNED_IN, 1);
cr.update(accountUri, values, null, null);
settings.close();
return result;
}
} catch (Exception e) {
LogCleaner.error(ImApp.LOG_TAG, "error registering new account", e);
}
Toast.makeText(context,"Trying again...",Toast.LENGTH_SHORT).show();
try { Thread.sleep(1000); }
catch (Exception e){}
}
}
else
{
try
{
settings.setDomain(domain);
settings.setPort(port);
settings.requery();
HashMap<String, String> aParams = new HashMap<String, String>();
XmppConnection xmppConn = new XmppConnection(context);
xmppConn.initUser(providerId, accountId);
boolean success = xmppConn.registerAccount(settings, username, password, aParams);
if (success) {
OnboardingAccount result = null;
result = new OnboardingAccount();
result.username = username;
result.domain = domain;
result.password = password;
result.providerId = providerId;
result.accountId = accountId;
result.nickname = nickname;
//now keep this account signed-in
ContentValues values = new ContentValues();
values.put(Imps.AccountColumns.KEEP_SIGNED_IN, 1);
cr.update(accountUri, values, null, null);
settings.close();
return result;
}
} catch (Exception e) {
LogCleaner.error(ImApp.LOG_TAG, "error registering new account", e);
}
}
settings.close();
return null;
}
public static OnboardingAccount addExistingAccount (Activity context, Handler handler, String nickname, String jabberId, String password) {
OnboardingAccount result = null;
String[] jabberParts = jabberId.split("@");
String username = jabberParts[0];
String domain = jabberParts[1];
int port = 5222;
ContentResolver cr = context.getContentResolver();
ImPluginHelper helper = ImPluginHelper.getInstance(context);
long providerId = helper.createAdditionalProvider(helper.getProviderNames().get(0)); //xmpp FIXME
long accountId = ImApp.insertOrUpdateAccount(cr, providerId, -1, nickname, username, password);
Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI, new String[]{Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE}, Imps.ProviderSettings.PROVIDER + "=?", new String[]{Long.toString(providerId)}, null);
Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
pCursor, cr, providerId, false /* don't keep updated */, null /* no handler */);
//should check to see if Orbot is installed and running
boolean useTor = false;
boolean doDnsSrvLookup = true;
settings.setUseTor(useTor);
settings.setRequireTls(true);
settings.setTlsCertVerify(true);
settings.setAllowPlainAuth(false);
settings.setDoDnsSrv(doDnsSrvLookup);
try {
settings.setDomain(domain);
settings.setPort(port);
settings.requery();
result = new OnboardingAccount();
result.username = username;
result.domain = domain;
result.password = password;
result.providerId = providerId;
result.accountId = accountId;
//now keep this account signed-in
ContentValues values = new ContentValues();
values.put(Imps.AccountColumns.KEEP_SIGNED_IN, 1);
cr.update(accountUri, values, null, null);
// settings closed in registerAccount
} catch (Exception e) {
LogCleaner.error(ImApp.LOG_TAG, "error registering new account", e);
}
settings.close();
return result;
}
public static String loadServersJSON(Context context) {
String json = null;
try {
InputStream is = context.getAssets().open("servers.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
json = new String(buffer, "UTF-8");
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return json;
}
}