Android Open Source - bitfynd-wallet-android Input Parser






From Project

Back to project page bitfynd-wallet-android.

License

The source code is released under:

GNU General Public License

If you think the Android project bitfynd-wallet-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2013-2014 the original author or authors.
 */*from   w  w w .j a va2 s . c o m*/
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.schildbach.wallet.ui;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Date;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.bitcoin.protocols.payments.Protos;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Base58;
import org.bitcoinj.core.DumpedPrivateKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.ProtocolException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.core.VersionedChecksummedBytes;
import org.bitcoinj.crypto.BIP38PrivateKey;
import org.bitcoinj.crypto.TrustStoreLoader;
import org.bitcoinj.protocols.payments.PaymentProtocol;
import org.bitcoinj.protocols.payments.PaymentProtocol.PkiVerificationData;
import org.bitcoinj.protocols.payments.PaymentProtocolException;
import org.bitcoinj.protocols.payments.PaymentSession;
import org.bitcoinj.uri.BitcoinURI;
import org.bitcoinj.uri.BitcoinURIParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.content.Context;
import android.content.DialogInterface.OnClickListener;

import com.google.common.hash.Hashing;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.UninitializedMessageException;

import de.schildbach.wallet.Constants;
import de.schildbach.wallet.data.PaymentIntent;
import de.schildbach.wallet.util.Io;
import de.schildbach.wallet.util.Qr;
import de.schildbach.wallet.R;

/**
 * @author Andreas Schildbach
 */
public abstract class InputParser
{
  private static final Logger log = LoggerFactory.getLogger(InputParser.class);

  public abstract static class StringInputParser extends InputParser
  {
    private final String input;

    public StringInputParser(@Nonnull final String input)
    {
      this.input = input;
    }

    @Override
    public void parse()
    {
      if (input.startsWith("BITCOIN:-"))
      {
        try
        {
          final byte[] serializedPaymentRequest = Qr.decodeBinary(input.substring(9));

          parseAndHandlePaymentRequest(serializedPaymentRequest);
        }
        catch (final IOException x)
        {
          log.info("i/o error while fetching payment request", x);

          error(R.string.input_parser_io_error, x.getMessage());
        }
        catch (final PaymentProtocolException.PkiVerificationException x)
        {
          log.info("got unverifyable payment request", x);

          error(R.string.input_parser_unverifyable_paymentrequest, x.getMessage());
        }
        catch (final PaymentProtocolException x)
        {
          log.info("got invalid payment request", x);

          error(R.string.input_parser_invalid_paymentrequest, x.getMessage());
        }
      }
      else if (input.startsWith("bitcoin:"))
      {
        try
        {
          final BitcoinURI bitcoinUri = new BitcoinURI(null, input);
          final Address address = bitcoinUri.getAddress();
          if (address != null && !Constants.NETWORK_PARAMETERS.equals(address.getParameters()))
            throw new BitcoinURIParseException("mismatched network");

          handlePaymentIntent(PaymentIntent.fromBitcoinUri(bitcoinUri));
        }
        catch (final BitcoinURIParseException x)
        {
          log.info("got invalid bitcoin uri: '" + input + "'", x);

          error(R.string.input_parser_invalid_bitcoin_uri, input);
        }
      }
      else if (PATTERN_BITCOIN_ADDRESS.matcher(input).matches())
      {
        try
        {
          final Address address = new Address(Constants.NETWORK_PARAMETERS, input);

          handlePaymentIntent(PaymentIntent.fromAddress(address, null));
        }
        catch (final AddressFormatException x)
        {
          log.info("got invalid address", x);

          error(R.string.input_parser_invalid_address);
        }
      }
            else if (PATTERN_DUMPED_PRIVATE_KEY_UNCOMPRESSED.matcher(input).matches()
                    || PATTERN_DUMPED_PRIVATE_KEY_COMPRESSED.matcher(input).matches())
      {
        try
        {
                    final VersionedChecksummedBytes key = new DumpedPrivateKey(Constants.NETWORK_PARAMETERS, input);

                    handlePrivateKey(key);
                }
                catch (final AddressFormatException x)
                {
                    log.info("got invalid address", x);

                    error(R.string.input_parser_invalid_address);
                }
            }
            else if (PATTERN_BIP38_PRIVATE_KEY.matcher(input).matches())
            {
                try
                {
                    final VersionedChecksummedBytes key = new BIP38PrivateKey(Constants.NETWORK_PARAMETERS, input);

          handlePrivateKey(key);
        }
        catch (final AddressFormatException x)
        {
          log.info("got invalid address", x);

          error(R.string.input_parser_invalid_address);
        }
      }
      else if (PATTERN_TRANSACTION.matcher(input).matches())
      {
        try
        {
          final Transaction tx = new Transaction(Constants.NETWORK_PARAMETERS, Qr.decodeDecompressBinary(input));

          handleDirectTransaction(tx);
        }
        catch (final IOException x)
        {
          log.info("i/o error while fetching transaction", x);

          error(R.string.input_parser_invalid_transaction, x.getMessage());
        }
        catch (final ProtocolException x)
        {
          log.info("got invalid transaction", x);

          error(R.string.input_parser_invalid_transaction, x.getMessage());
        }
      }
      else
      {
        cannotClassify(input);
      }
    }
  }

    protected void handlePrivateKey(@Nonnull final VersionedChecksummedBytes key)
    {
        final Address address = new Address(Constants.NETWORK_PARAMETERS, ((DumpedPrivateKey) key).getKey().getPubKeyHash());

        handlePaymentIntent(PaymentIntent.fromAddress(address, null));
    }

  public abstract static class BinaryInputParser extends InputParser
  {
    private final String inputType;
    private final byte[] input;

    public BinaryInputParser(@Nonnull final String inputType, @Nonnull final byte[] input)
    {
      this.inputType = inputType;
      this.input = input;
    }

    @Override
    public void parse()
    {
      if (Constants.MIMETYPE_TRANSACTION.equals(inputType))
      {
        try
        {
          final Transaction tx = new Transaction(Constants.NETWORK_PARAMETERS, input);

          handleDirectTransaction(tx);
        }
        catch (final VerificationException x)
        {
          log.info("got invalid transaction", x);

          error(R.string.input_parser_invalid_transaction, x.getMessage());
        }
      }
      else if (PaymentProtocol.MIMETYPE_PAYMENTREQUEST.equals(inputType))
      {
        try
        {
          parseAndHandlePaymentRequest(input);
        }
        catch (final PaymentProtocolException.PkiVerificationException x)
        {
          log.info("got unverifyable payment request", x);

          error(R.string.input_parser_unverifyable_paymentrequest, x.getMessage());
        }
        catch (final PaymentProtocolException x)
        {
          log.info("got invalid payment request", x);

          error(R.string.input_parser_invalid_paymentrequest, x.getMessage());
        }
      }
      else
      {
        cannotClassify(inputType);
      }
    }

    @Override
    protected final void handleDirectTransaction(@Nonnull final Transaction transaction) throws VerificationException
    {
      throw new UnsupportedOperationException();
    }
  }

  public abstract static class StreamInputParser extends InputParser
  {
    private final String inputType;
    private final InputStream is;

    public StreamInputParser(@Nonnull final String inputType, @Nonnull final InputStream is)
    {
      this.inputType = inputType;
      this.is = is;
    }

    @Override
    public void parse()
    {
      if (PaymentProtocol.MIMETYPE_PAYMENTREQUEST.equals(inputType))
      {
        ByteArrayOutputStream baos = null;

        try
        {
          baos = new ByteArrayOutputStream();
          Io.copy(is, baos);
          parseAndHandlePaymentRequest(baos.toByteArray());
        }
        catch (final IOException x)
        {
          log.info("i/o error while fetching payment request", x);

          error(R.string.input_parser_io_error, x.getMessage());
        }
        catch (final PaymentProtocolException.PkiVerificationException x)
        {
          log.info("got unverifyable payment request", x);

          error(R.string.input_parser_unverifyable_paymentrequest, x.getMessage());
        }
        catch (final PaymentProtocolException x)
        {
          log.info("got invalid payment request", x);

          error(R.string.input_parser_invalid_paymentrequest, x.getMessage());
        }
        finally
        {
          try
          {
            if (baos != null)
              baos.close();
          }
          catch (IOException x)
          {
            x.printStackTrace();
          }

          try
          {
            is.close();
          }
          catch (IOException x)
          {
            x.printStackTrace();
          }
        }
      }
      else
      {
        cannotClassify(inputType);
      }
    }

    @Override
    protected final void handleDirectTransaction(@Nonnull final Transaction transaction) throws VerificationException
    {
      throw new UnsupportedOperationException();
    }
  }

  public abstract void parse();

  protected final void parseAndHandlePaymentRequest(@Nonnull final byte[] serializedPaymentRequest) throws PaymentProtocolException
  {
    final PaymentIntent paymentIntent = parsePaymentRequest(serializedPaymentRequest);

    handlePaymentIntent(paymentIntent);
  }

  public static PaymentIntent parsePaymentRequest(@Nonnull final byte[] serializedPaymentRequest) throws PaymentProtocolException
  {
    try
    {
      if (serializedPaymentRequest.length > 50000)
        throw new PaymentProtocolException("payment request too big: " + serializedPaymentRequest.length);

      final Protos.PaymentRequest paymentRequest = Protos.PaymentRequest.parseFrom(serializedPaymentRequest);

      final String pkiName;
      final String pkiCaName;
      if (!"none".equals(paymentRequest.getPkiType()))
      {
        final KeyStore keystore = new TrustStoreLoader.DefaultTrustStoreLoader().getKeyStore();
        final PkiVerificationData verificationData = PaymentProtocol.verifyPaymentRequestPki(paymentRequest, keystore);
        pkiName = verificationData.displayName;
        pkiCaName = verificationData.rootAuthorityName;
      }
      else
      {
        pkiName = null;
        pkiCaName = null;
      }

      final PaymentSession paymentSession = PaymentProtocol.parsePaymentRequest(paymentRequest);

      if (paymentSession.isExpired())
        throw new PaymentProtocolException.Expired("payment details expired: current time " + new Date() + " after expiry time "
            + paymentSession.getExpires());

      if (!paymentSession.getNetworkParameters().equals(Constants.NETWORK_PARAMETERS))
        throw new PaymentProtocolException.InvalidNetwork("cannot handle payment request network: " + paymentSession.getNetworkParameters());

      final ArrayList<PaymentIntent.Output> outputs = new ArrayList<PaymentIntent.Output>(1);
      for (final PaymentProtocol.Output output : paymentSession.getOutputs())
        outputs.add(PaymentIntent.Output.valueOf(output));

      final String memo = paymentSession.getMemo();

      final String paymentUrl = paymentSession.getPaymentUrl();

      final byte[] merchantData = paymentSession.getMerchantData();

      final byte[] paymentRequestHash = Hashing.sha256().hashBytes(serializedPaymentRequest).asBytes();

      final PaymentIntent paymentIntent = new PaymentIntent(PaymentIntent.Standard.BIP70, pkiName, pkiCaName,
          outputs.toArray(new PaymentIntent.Output[0]), memo, paymentUrl, merchantData, null, paymentRequestHash);

      if (paymentIntent.hasPaymentUrl() && !paymentIntent.isSupportedPaymentUrl())
        throw new PaymentProtocolException.InvalidPaymentURL("cannot handle payment url: " + paymentIntent.paymentUrl);

      return paymentIntent;
    }
    catch (final InvalidProtocolBufferException x)
    {
      throw new PaymentProtocolException(x);
    }
    catch (final UninitializedMessageException x)
    {
      throw new PaymentProtocolException(x);
    }
    catch (final FileNotFoundException x)
    {
      throw new RuntimeException(x);
    }
    catch (final KeyStoreException x)
    {
      throw new RuntimeException(x);
    }
  }

  protected abstract void handlePaymentIntent(@Nonnull PaymentIntent paymentIntent);

  protected abstract void handleDirectTransaction(@Nonnull Transaction transaction) throws VerificationException;

  protected abstract void error(int messageResId, Object... messageArgs);

  protected void cannotClassify(@Nonnull final String input)
  {
    error(R.string.input_parser_cannot_classify, input);
  }

  protected void dialog(final Context context, @Nullable final OnClickListener dismissListener, final int titleResId, final int messageResId,
      final Object... messageArgs)
  {
    final DialogBuilder dialog = new DialogBuilder(context);
    if (titleResId != 0)
      dialog.setTitle(titleResId);
    dialog.setMessage(context.getString(messageResId, messageArgs));
    dialog.singleDismissButton(dismissListener);
    dialog.show();
  }

  private static final Pattern PATTERN_BITCOIN_ADDRESS = Pattern.compile("[" + new String(Base58.ALPHABET) + "]{20,40}");
    private static final Pattern PATTERN_DUMPED_PRIVATE_KEY_UNCOMPRESSED = Pattern.compile((Constants.NETWORK_PARAMETERS.getId().equals(
      NetworkParameters.ID_MAINNET) ? "5" : "9")
      + "[" + new String(Base58.ALPHABET) + "]{50}");
    private static final Pattern PATTERN_DUMPED_PRIVATE_KEY_COMPRESSED = Pattern.compile((Constants.NETWORK_PARAMETERS.getId().equals(
      NetworkParameters.ID_MAINNET) ? "[KL]" : "c")
      + "[" + new String(Base58.ALPHABET) + "]{51}");
    private static final Pattern PATTERN_BIP38_PRIVATE_KEY = Pattern.compile("6P" + "[" + new String(Base58.ALPHABET) + "]{56}");
  private static final Pattern PATTERN_TRANSACTION = Pattern.compile("[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$\\*\\+\\-\\.\\/\\:]{100,}");
}




Java Source Code List

de.schildbach.wallet.AddressBookProvider.java
de.schildbach.wallet.Configuration.java
de.schildbach.wallet.Constants.java
de.schildbach.wallet.ExchangeRatesProvider.java
de.schildbach.wallet.FileAttachmentProvider.java
de.schildbach.wallet.WalletApplication.java
de.schildbach.wallet.WalletBalanceWidgetProvider.java
de.schildbach.wallet.camera.CameraManager.java
de.schildbach.wallet.data.PaymentIntent.java
de.schildbach.wallet.integration.android.BitcoinIntegration.java
de.schildbach.wallet.offline.AcceptBluetoothService.java
de.schildbach.wallet.offline.AcceptBluetoothThread.java
de.schildbach.wallet.offline.DirectPaymentTask.java
de.schildbach.wallet.service.AutosyncReceiver.java
de.schildbach.wallet.service.BlockchainServiceImpl.java
de.schildbach.wallet.service.BlockchainService.java
de.schildbach.wallet.service.BlockchainStateLoader.java
de.schildbach.wallet.service.BlockchainState.java
de.schildbach.wallet.service.UpgradeWalletService.java
de.schildbach.wallet.ui.AbstractBindServiceActivity.java
de.schildbach.wallet.ui.AbstractWalletActivity.java
de.schildbach.wallet.ui.AddressAndLabel.java
de.schildbach.wallet.ui.AddressBookActivity.java
de.schildbach.wallet.ui.BlockListFragment.java
de.schildbach.wallet.ui.CurrencyAmountView.java
de.schildbach.wallet.ui.CurrencyCalculatorLink.java
de.schildbach.wallet.ui.CurrencySymbolDrawable.java
de.schildbach.wallet.ui.CurrencyTextView.java
de.schildbach.wallet.ui.DialogBuilder.java
de.schildbach.wallet.ui.EditAddressBookEntryFragment.java
de.schildbach.wallet.ui.EncryptKeysDialogFragment.java
de.schildbach.wallet.ui.ExchangeRateLoader.java
de.schildbach.wallet.ui.ExchangeRatesActivity.java
de.schildbach.wallet.ui.ExchangeRatesFragment.java
de.schildbach.wallet.ui.FancyListFragment.java
de.schildbach.wallet.ui.FileAdapter.java
de.schildbach.wallet.ui.HelpDialogFragment.java
de.schildbach.wallet.ui.ImportDialogButtonEnablerListener.java
de.schildbach.wallet.ui.InputParser.java
de.schildbach.wallet.ui.MaybeMaintenanceFragment.java
de.schildbach.wallet.ui.NetworkMonitorActivity.java
de.schildbach.wallet.ui.PeerListFragment.java
de.schildbach.wallet.ui.ProgressDialogFragment.java
de.schildbach.wallet.ui.ReportIssueDialogBuilder.java
de.schildbach.wallet.ui.RequestCoinsActivity.java
de.schildbach.wallet.ui.RequestCoinsFragment.java
de.schildbach.wallet.ui.RestoreWalletActivity.java
de.schildbach.wallet.ui.ScanActivity.java
de.schildbach.wallet.ui.ScannerView.java
de.schildbach.wallet.ui.SendCoinsQrActivity.java
de.schildbach.wallet.ui.SendingAddressesFragment.java
de.schildbach.wallet.ui.ShowPasswordCheckListener.java
de.schildbach.wallet.ui.TransactionsListAdapter.java
de.schildbach.wallet.ui.TransactionsListFragment.java
de.schildbach.wallet.ui.WalletActionsFragment.java
de.schildbach.wallet.ui.WalletActivity.java
de.schildbach.wallet.ui.WalletAddressFragment.java
de.schildbach.wallet.ui.WalletAddressesAdapter.java
de.schildbach.wallet.ui.WalletAddressesFragment.java
de.schildbach.wallet.ui.WalletBalanceFragment.java
de.schildbach.wallet.ui.WalletBalanceLoader.java
de.schildbach.wallet.ui.WalletDisclaimerFragment.java
de.schildbach.wallet.ui.WalletTransactionsFragment.java
de.schildbach.wallet.ui.preference.AboutFragment.java
de.schildbach.wallet.ui.preference.DiagnosticsFragment.java
de.schildbach.wallet.ui.preference.PreferenceActivity.java
de.schildbach.wallet.ui.preference.SettingsFragment.java
de.schildbach.wallet.ui.send.DecodePrivateKeyTask.java
de.schildbach.wallet.ui.send.DeriveKeyTask.java
de.schildbach.wallet.ui.send.MaintenanceDialogFragment.java
de.schildbach.wallet.ui.send.RequestPaymentRequestTask.java
de.schildbach.wallet.ui.send.RequestWalletBalanceTask.java
de.schildbach.wallet.ui.send.SendCoinsActivity.java
de.schildbach.wallet.ui.send.SendCoinsFragment.java
de.schildbach.wallet.ui.send.SendCoinsOfflineTask.java
de.schildbach.wallet.ui.send.SweepWalletActivity.java
de.schildbach.wallet.ui.send.SweepWalletFragment.java
de.schildbach.wallet.util.Base43.java
de.schildbach.wallet.util.BitmapFragment.java
de.schildbach.wallet.util.Bluetooth.java
de.schildbach.wallet.util.CircularProgressView.java
de.schildbach.wallet.util.CrashReporter.java
de.schildbach.wallet.util.Crypto.java
de.schildbach.wallet.util.Formats.java
de.schildbach.wallet.util.GenericUtils.java
de.schildbach.wallet.util.HttpGetThread.java
de.schildbach.wallet.util.Io.java
de.schildbach.wallet.util.Iso8601Format.java
de.schildbach.wallet.util.LinuxSecureRandom.java
de.schildbach.wallet.util.MonetarySpannable.java
de.schildbach.wallet.util.Nfc.java
de.schildbach.wallet.util.Qr.java
de.schildbach.wallet.util.ThrottlingWalletChangeListener.java
de.schildbach.wallet.util.ViewPagerTabs.java
de.schildbach.wallet.util.WalletUtils.java
de.schildbach.wallet.util.WholeStringBuilder.java