package com.dgex.offspring.ui; import java.io.UnsupportedEncodingException; import nxt.Account; import nxt.Constants; import nxt.NxtException.ValidationException; import nxt.Transaction; import nxt.util.Convert; import org.apache.log4j.Logger; import org.eclipse.jface.window.Window; 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.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Text; import com.dgex.offspring.config.Config; import com.dgex.offspring.nxtCore.service.IAccount; import com.dgex.offspring.nxtCore.service.INxtService; import com.dgex.offspring.nxtCore.service.TransactionException; import com.dgex.offspring.swt.wizard.GenericTransactionWizard; import com.dgex.offspring.swt.wizard.IGenericTransaction; import com.dgex.offspring.swt.wizard.IGenericTransactionField; import com.dgex.offspring.swt.wizard.IMultiLineTextTransactionField; import com.dgex.offspring.ui.messaging.MessageCrypto; import com.dgex.offspring.user.service.IUserService; public class SendMessageWizard extends GenericTransactionWizard { static Logger logger = Logger.getLogger(SendMessageWizard.class); final IGenericTransactionField fieldRecipient = new IGenericTransactionField() { private Text textRecipient; private Text textRecipientReadonly; @Override public String getLabel() { return "Recipient"; } @Override public Object getValue() { String recipientValue = textRecipient.getText().trim(); try { return Convert.parseUnsignedLong(recipientValue); } catch (RuntimeException e) { logger.error("Parse Recipient ID", e); } return null; } @Override public Control createControl(Composite parent) { textRecipient = new Text(parent, SWT.BORDER); textRecipient.setMessage("account number"); if (accountId != null) textRecipient.setText(Convert.toUnsignedLong(accountId)); else textRecipient.setText(""); textRecipient.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { requestVerification(); } }); return textRecipient; } @Override public Control createReadonlyControl(Composite parent) { textRecipientReadonly = new Text(parent, SWT.BORDER); textRecipientReadonly.setText(""); textRecipientReadonly.setEditable(false); return textRecipientReadonly; } @Override public boolean verify(String[] message) { String recipientValue = textRecipient.getText().trim(); if ("0".equals(recipientValue)) { message[0] = "Missing recipient"; return false; } try { Convert.parseUnsignedLong(recipientValue); } catch (RuntimeException e) { message[0] = "Incorrect recipient"; return false; } textRecipientReadonly.setText(recipientValue); return true; } }; final IGenericTransactionField fieldReferenced = new IGenericTransactionField() { private Text textReferenced; private Text textReferencedReadonly; @Override public String getLabel() { return "In-reply to"; } @Override public Object getValue() { String recipientValue = textReferenced.getText().trim(); if (recipientValue.isEmpty()) { return null; } try { return Convert.parseUnsignedLong(recipientValue); } catch (RuntimeException e) { logger.error("Parse Recipient ID", e); } return null; } @Override public Control createControl(Composite parent) { textReferenced = new Text(parent, SWT.BORDER); textReferenced.setMessage("transaction id or empty"); if (referencedTransactionId != null) textReferenced.setText(Convert.toUnsignedLong(referencedTransactionId)); else textReferenced.setText(""); textReferenced.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { requestVerification(); } }); return textReferenced; } @Override public Control createReadonlyControl(Composite parent) { textReferencedReadonly = new Text(parent, SWT.BORDER); textReferencedReadonly.setText(""); textReferencedReadonly.setEditable(false); return textReferencedReadonly; } @Override public boolean verify(String[] message) { String recipientValue = textReferenced.getText().trim(); if (!recipientValue.isEmpty()) { try { /* Long id = */Convert.parseUnsignedLong(recipientValue); // Transaction t = Nxt.getBlockchain().getTransaction(id); // if (t == null) { // message[0] = "Referenced transaction does not exist"; // return false; // } } catch (RuntimeException e) { message[0] = "Incorrect referenced transaction"; return false; } } textReferencedReadonly.setText(recipientValue); return true; } }; final IGenericTransactionField fieldEncrypt = new IGenericTransactionField() { private Button buttonEncrypt; private Button buttonEncryptReadonly; @Override public String getLabel() { return "Encrypt"; } @Override public Object getValue() { return Boolean.valueOf(buttonEncrypt.getSelection()); } @Override public Control createControl(Composite parent) { buttonEncrypt = new Button(parent, SWT.CHECK); buttonEncrypt .setText("Encrypt message (only you and recipient can read this message)"); buttonEncrypt.setSelection(useEncryption); buttonEncrypt.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { requestVerification(); }; }); return buttonEncrypt; } @Override public Control createReadonlyControl(Composite parent) { buttonEncryptReadonly = new Button(parent, SWT.CHECK); buttonEncryptReadonly .setText("Private message (Private/Public key encryption)"); buttonEncryptReadonly.setSelection(true); return buttonEncryptReadonly; } @Override public boolean verify(String[] message) { buttonEncryptReadonly.setSelection((boolean) getValue()); return true; } }; final IMultiLineTextTransactionField fieldMessage = new IMultiLineTextTransactionField() { private Text textMessage; private Text textMessageReadonly; @Override public String getLabel() { return "Message"; } @Override public Object getValue() { return textMessage.getText().trim(); } @Override public Control createControl(Composite parent) { textMessage = new Text(parent, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL); textMessage.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { requestVerification(); } }); return textMessage; } @Override public Control createReadonlyControl(Composite parent) { textMessageReadonly = new Text(parent, SWT.BORDER); textMessageReadonly.setText(""); textMessageReadonly.setEditable(false); return textMessageReadonly; } @Override public boolean verify(String[] message) { String text = textMessage.getText().trim(); /* Message cannot excede max length */ try { int length = text.getBytes("UTF-8").length; if ((Boolean) fieldEncrypt.getValue()) { length += Config.MAGIC_ENCRYPTED_MESSAGE_NUMBER.length + 32; } else { length += Config.MAGIC_UNENCRYPTED_MESSAGE_NUMBER.length; } if (length > Constants.MAX_ARBITRARY_MESSAGE_LENGTH) { message[0] = "Message is to long (max 1000 bytes, yours is " + length + "bytes)"; return false; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); message[0] = "UTF-8 character set not supported on your machine"; return false; } textMessageReadonly.setText(text); return true; } }; private Long accountId = null; private Long referencedTransactionId = null; private boolean useEncryption = true; public SendMessageWizard(final IUserService userService, final INxtService nxt, Long accountId, Long referencedTransactionId, boolean useEncryption) { super(userService); this.accountId = accountId; this.referencedTransactionId = referencedTransactionId; this.useEncryption = useEncryption; setWindowTitle("Send Message"); setTransaction(new IGenericTransaction() { @Override public String sendTransaction(String[] message) { IAccount sender = user.getAccount(); Long recipient = (Long) fieldRecipient.getValue(); Long referencedId = (Long) fieldReferenced.getValue(); boolean encrypt = (Boolean) fieldEncrypt.getValue(); String messageValue = (String) fieldMessage.getValue(); PromptFeeDeadline dialog = new PromptFeeDeadline(getShell()); if (dialog.open() != Window.OK) { message[0] = "Invalid fee and deadline"; return null; } int fee = dialog.getFee(); short deadline = dialog.getDeadline(); try { byte[] data; if (encrypt) { byte[] theirPublicKey = Account.getAccount(recipient) .getPublicKey(); data = MessageCrypto.encrypt(messageValue, sender.getPrivateKey(), theirPublicKey); } else { byte[] magic = Config.MAGIC_UNENCRYPTED_MESSAGE_NUMBER; byte[] bytes = messageValue.getBytes("UTF-8"); data = new byte[magic.length + bytes.length]; System.arraycopy(magic, 0, data, 0, magic.length); System.arraycopy(bytes, 0, data, magic.length, bytes.length); } Transaction t = nxt.createSendMessageTransaction(sender, recipient, data, deadline, fee, referencedId); return t.getStringId(); } catch (TransactionException e) { message[0] = e.getMessage(); } catch (ValidationException e) { message[0] = e.getMessage(); } catch (UnsupportedEncodingException e) { message[0] = e.getMessage(); } return null; } @Override public IGenericTransactionField[] getFields() { return new IGenericTransactionField[] { fieldSender, fieldRecipient, fieldReferenced, fieldEncrypt, fieldMessage }; } @Override public boolean verifySender(String message[]) { if (user == null) { message[0] = "Invalid sender"; return false; } if (user.getAccount().isReadOnly()) { message[0] = "This is a readonly account"; return false; } if (user.getAccount().getBalance() < 1) { message[0] = "Insufficient balance"; return false; } return true; } }); } }