package com.dgex.offspring.application.dialogs;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Random;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.log4j.Logger;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.LayoutConstants;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import com.dgex.offspring.application.utils.DiceWords;
import com.dgex.offspring.messages.Messages;
import com.dgex.offspring.nxtCore.service.INxtService;
import com.dgex.offspring.wallet.IWallet;
import com.dgex.offspring.wallet.IWallet.DuplicateAccountException;
import com.dgex.offspring.wallet.IWallet.WalletBackupException;
import com.dgex.offspring.wallet.IWallet.WalletNotInitializedException;
import com.dgex.offspring.wallet.NXTAccount;
public class AddAccountDialog extends TitleAreaDialog {
static Logger logger = Logger.getLogger(AddAccountDialog.class);
private final long balance = 0l;
private Text textAccountLabel;
private Text textPrivateKey;
private Text textAccountNumber;
private Text textBalance;
private ControlDecoration decoAccountLabel;
private ControlDecoration decoPrivateKey;
private Button buttonGenerate;
private final IWallet wallet;
private final INxtService nxt;
private static Image errorImage = FieldDecorationRegistry.getDefault()
.getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage();
private final ModifyListener modifyPrivateKeyListener = new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
textAccountNumber.setText(""); //$NON-NLS-1$
textBalance.setText(""); //$NON-NLS-1$
String account = nxt.getAccountForPrivateKey(textPrivateKey.getText());
textAccountNumber.setText(account);
Long balance = nxt.getBalanceForAccount(account);
if (balance == null)
textBalance.setText("");
else
textBalance.setText(Long.toString(balance));
verifyInput();
}
};
private final ModifyListener modifyAccountNumberListener = new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
Long balance = nxt.getBalanceForAccount(textAccountNumber.getText()
.trim());
if (balance == null)
textBalance.setText("");
else
textBalance.setText(Long.toString(balance));
verifyInput();
}
};
private final ModifyListener modifyLabelListener = new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
verifyInput();
}
};
private final SelectionListener selectGenerateListener = new SelectionAdapter() {
@Override
public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
// textPrivateKey.setText(generatePassphrase());
try {
textPrivateKey.setText(generatePassphrase2());
}
catch (NoSuchAlgorithmException e1) {
logger.error("Error in key generator", e1);
}
catch (NoSuchProviderException e1) {
logger.error("Error in key generator", e1);
}
};
};
private Button readonlyButton;
private ControlDecoration decoAccountNumber;
public AddAccountDialog(Shell shell, IWallet wallet, INxtService nxt) {
super(shell);
this.wallet = wallet;
this.nxt = nxt;
}
@Override
public void create() {
super.create();
setTitle(Messages.AddAccountDialog_title);
setMessage(Messages.AddAccountDialog_message_default);
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true);
createButton(parent, IDialogConstants.CANCEL_ID,
IDialogConstants.CANCEL_LABEL, false);
getButton(IDialogConstants.OK_ID).setEnabled(false);
}
@Override
protected void buttonPressed(int buttonId) {
if (buttonId == IDialogConstants.OK_ID) {
boolean readonly = readonlyButton.getSelection();
String label = textAccountLabel.getText();
String account = textAccountNumber.getText();
String privateKey = readonly ? null : textPrivateKey.getText();
try {
wallet.addAccount(NXTAccount
.create(label, account, privateKey, balance));
super.buttonPressed(buttonId);
}
catch (WalletNotInitializedException e) {
setMessage(Messages.AddAccountDialog_message_wallet_not_initialized,
IMessageProvider.ERROR);
// e.printStackTrace(System.err);
}
catch (DuplicateAccountException e) {
setMessage(Messages.AddAccountDialog_message_duplicate_account,
IMessageProvider.ERROR);
// e.printStackTrace(System.err);
}
catch (WalletBackupException e) {
setMessage(Messages.AddAccountDialog_message_backup_error,
IMessageProvider.ERROR);
// e.printStackTrace(System.err);
}
}
else {
super.buttonPressed(buttonId);
}
}
@Override
protected Control createDialogArea(Composite parent) {
initializeDialogUnits(parent);
Composite parentComposite = (Composite) super.createDialogArea(parent);
GridData gd = new GridData(GridData.FILL, GridData.FILL, false, true);
gd.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
Composite contents = new Composite(parentComposite, SWT.NONE);
contents.setLayoutData(gd);
Label label = new Label(contents, SWT.WRAP);
label.setText(Messages.AddAccountDialog_label_descr_1);
GridDataFactory.generate(label, 3, 1);
new Label(contents, SWT.NONE)
.setText(Messages.AddAccountDialog_label_account_label);
textAccountLabel = new Text(contents, SWT.SINGLE | SWT.BORDER);
GridDataFactory.generate(textAccountLabel, 2, 1);
textAccountLabel.addModifyListener(modifyLabelListener);
decoAccountLabel = new ControlDecoration(textAccountLabel, SWT.TOP
| SWT.RIGHT);
decoAccountLabel.setImage(errorImage);
decoAccountLabel.hide();
label = new Label(contents, SWT.WRAP);
label.setText(Messages.AddAccountDialog_label_descr_2);
GridDataFactory.generate(label, 3, 1);
label = new Label(contents, SWT.NONE);
label.setText(Messages.AddAccountDialog_label_private_key);
GridDataFactory.defaultsFor(label).align(SWT.BEGINNING, SWT.BEGINNING)
.indent(0, 4).applyTo(label);
textPrivateKey = new Text(contents, SWT.MULTI | SWT.BORDER | SWT.WRAP
| SWT.V_SCROLL);
textPrivateKey.setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT));
textPrivateKey.addModifyListener(modifyPrivateKeyListener);
GC gc = new GC(textPrivateKey);
FontMetrics fm = gc.getFontMetrics();
int height = fm.getHeight();
gc.dispose();
GridDataFactory.defaultsFor(textPrivateKey)
.hint(SWT.DEFAULT, (height * 3) + 6).applyTo(textPrivateKey);
buttonGenerate = new Button(contents, SWT.PUSH);
buttonGenerate.setText(Messages.AddAccountDialog_label_generate);
GridDataFactory.defaultsFor(buttonGenerate)
.align(SWT.BEGINNING, SWT.BEGINNING).indent(0, 4)
.applyTo(buttonGenerate);
buttonGenerate.addSelectionListener(selectGenerateListener);
decoPrivateKey = new ControlDecoration(textPrivateKey, SWT.TOP | SWT.RIGHT);
decoPrivateKey.setImage(errorImage);
decoPrivateKey.hide();
new Label(contents, SWT.NONE);
readonlyButton = new Button(contents, SWT.CHECK);
readonlyButton.setText("Readonly account (without private key)");
readonlyButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (readonlyButton.getSelection()) {
buttonGenerate.setEnabled(false);
textPrivateKey.setEnabled(false);
textAccountNumber.setEditable(true);
decoPrivateKey.hide();
textAccountNumber.addModifyListener(modifyAccountNumberListener);
}
else {
textAccountNumber.removeModifyListener(modifyAccountNumberListener);
buttonGenerate.setEnabled(true);
textPrivateKey.setEnabled(true);
textAccountNumber.setEditable(false);
decoAccountNumber.hide();
}
verifyInput();
}
});
new Label(contents, SWT.NONE);
new Label(contents, SWT.NONE)
.setText(Messages.AddAccountDialog_label_account_number);
textAccountNumber = new Text(contents, SWT.SINGLE | SWT.BORDER);
textAccountNumber.setEditable(false);
new Label(contents, SWT.NONE);
decoAccountNumber = new ControlDecoration(textAccountNumber, SWT.TOP
| SWT.RIGHT);
decoAccountNumber.setImage(errorImage);
decoAccountNumber.hide();
new Label(contents, SWT.NONE)
.setText(Messages.AddAccountDialog_label_account_balance);
textBalance = new Text(contents, SWT.SINGLE | SWT.BORDER);
textBalance.setEditable(false);
new Label(contents, SWT.NONE);
Point defaultMargins = LayoutConstants.getMargins();
Point defaultSpacing = LayoutConstants.getSpacing();
GridLayoutFactory.fillDefaults().numColumns(3)
.spacing(defaultSpacing.x, defaultSpacing.y)
.margins(defaultMargins.x, defaultMargins.y).generateLayout(contents);
return contents;
}
private boolean verifyInput() {
boolean verified = true;
if (textAccountLabel.getText().trim().isEmpty()) {
decoAccountLabel
.setDescriptionText(Messages.AddAccountDialog_label_account_label_empty);
decoAccountLabel.show();
verified = false;
}
else {
decoAccountLabel.hide();
}
if (readonlyButton.getSelection()) {
if (textAccountNumber.getText().trim().isEmpty()) {
decoAccountNumber.setDescriptionText("Account number can not be empty");
decoAccountNumber.show();
verified = false;
}
else {
decoAccountNumber.hide();
}
}
else {
if (textPrivateKey.getText().isEmpty()) {
decoPrivateKey
.setDescriptionText(Messages.AddAccountDialog_label_private_key_empty);
decoPrivateKey.show();
verified = false;
}
else {
decoPrivateKey.hide();
}
}
getButton(IDialogConstants.OK_ID).setEnabled(verified);
return verified;
}
// Fisher–Yates shuffle
private static void shuffleSeedText(char[] ar) {
Random rnd = new SecureRandom();
for (int i = ar.length - 1; i > 0; i--) {
int index = rnd.nextInt(i + 1);
char a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
private String generatePassphrase() {
// No space, backslash, newline, tab
String symbols = "!\"$%^&*()-_=+[{]};:'@#~|,<.>/?"; //$NON-NLS-1$
String alphaNum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890"; //$NON-NLS-1$
char[] chars = new String(symbols + alphaNum).toCharArray();
shuffleSeedText(chars);
int low = 70;
int high = 90;
Random random = new Random();
int count = random.nextInt(high - low) + low;
return RandomStringUtils.random(count, 0, 0, false, false, chars,
new SecureRandom());
}
private String generatePassphrase2() throws NoSuchAlgorithmException,
NoSuchProviderException {
DiceWords words = new DiceWords();
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] bytes = new byte[8];
rand.nextBytes(bytes); // initialize seed
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 12; i++) {
String word = words.getDiceWd(rand.nextInt(words.getCount()));
sb.append(word).append(' ');
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}