Package com.subgraph.orchid.directory

Source Code of com.subgraph.orchid.directory.DocumentFieldParserImpl

package com.subgraph.orchid.directory;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Logger;

import com.subgraph.orchid.TorException;
import com.subgraph.orchid.TorParsingException;
import com.subgraph.orchid.crypto.TorMessageDigest;
import com.subgraph.orchid.crypto.TorNTorKeyAgreement;
import com.subgraph.orchid.crypto.TorPublicKey;
import com.subgraph.orchid.crypto.TorSignature;
import com.subgraph.orchid.data.HexDigest;
import com.subgraph.orchid.data.IPv4Address;
import com.subgraph.orchid.data.Timestamp;
import com.subgraph.orchid.directory.parsing.DocumentFieldParser;
import com.subgraph.orchid.directory.parsing.DocumentObject;
import com.subgraph.orchid.directory.parsing.DocumentParsingHandler;
import com.subgraph.orchid.directory.parsing.NameIntegerParameter;
import com.subgraph.orchid.encoders.Base64;

public class DocumentFieldParserImpl implements DocumentFieldParser {
  private final static Logger logger = Logger.getLogger(DocumentFieldParserImpl.class.getName());
  private final static String BEGIN_TAG = "-----BEGIN";
  private final static String END_TAG = "-----END";
  private final static String TAG_DELIMITER = "-----";
  private final static String DEFAULT_DELIMITER = " ";
  private final ByteBuffer inputBuffer;
  private final SimpleDateFormat dateFormat;
  private String delimiter = DEFAULT_DELIMITER;
  private String currentKeyword;
  private List<String> currentItems;
  private int currentItemsPosition;
  private boolean recognizeOpt;
  /* If a line begins with this string do not include it in the current signature. */
   private String signatureIgnoreToken;
  private boolean isProcessingSignedEntity = false;
  private TorMessageDigest signatureDigest;
  private TorMessageDigest signatureDigest256;
  private StringBuilder rawDocumentBuffer;

  private DocumentParsingHandler callbackHandler;

  public DocumentFieldParserImpl(ByteBuffer buffer) {
    buffer.rewind();
    this.inputBuffer = buffer;
    rawDocumentBuffer = new StringBuilder();
    dateFormat = createDateFormat();
  }

  private static SimpleDateFormat createDateFormat() {
    final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    format.setTimeZone(TimeZone.getTimeZone("GMT"));
    format.setLenient(false);
    return format;
  }

  public String parseNickname() {
    // XXX verify valid nickname
    return getItem();
  }
  public String parseString() {
    return getItem();
  }

  public void setRecognizeOpt() {
    recognizeOpt = true;
  }

  public void setHandler(DocumentParsingHandler handler) {
    callbackHandler = handler;
  }

  public void setDelimiter(String delimiter) {
    this.delimiter = delimiter;
  }

  public int argumentsRemaining() {
    return currentItems.size() - currentItemsPosition;
  }

  private String getItem() {
    if(currentItemsPosition >= currentItems.size())
      throw new TorParsingException("Overrun while reading arguments");
    return currentItems.get(currentItemsPosition++);
  }
  /*
   * Return a string containing all remaining arguments concatenated together
   */
  public String parseConcatenatedString() {
    StringBuilder result = new StringBuilder();
    while(argumentsRemaining() > 0) {
      if(result.length() > 0)
        result.append(" ");
      result.append(getItem());
    }
    return result.toString();
  }

  public boolean parseBoolean() {
    final int i = parseInteger();
    if(i == 1)
      return true;
    else if(i == 0)
      return false;
    else
      throw new TorParsingException("Illegal boolean value: "+ i);
  }

  public int parseInteger() {
    return parseInteger(getItem());
  }

  public int parseInteger(String item) {
    try {
      return Integer.parseInt(item);
    } catch(NumberFormatException e) {
      throw new TorParsingException("Failed to parse expected integer value: " + item);
    }
  }

  public int[] parseIntegerList() {
    final String item = getItem();
    final String[] ns = item.split(",");
    final int[] result = new int[ns.length];
    for(int i = 0; i < result.length; i++) {
      result[i] = parseInteger(ns[i]);
    }
    return result;
  }

  public int parsePort() {
    return parsePort(getItem());
  }

  public int parsePort(String item) {
    final int port = parseInteger(item);
    if(port < 0 || port > 65535)
      throw new TorParsingException("Illegal port value: " + port);
    return port;
  }


  public Timestamp parseTimestamp() {
    String timeAndDate = getItem() + " " + getItem();
    try {
      return new Timestamp(dateFormat.parse(timeAndDate));
    } catch (ParseException e) {
      throw new TorParsingException("Could not parse timestamp value: "+ timeAndDate);
    }
  }

  public HexDigest parseHexDigest() {
    return HexDigest.createFromString(parseString());
  }
 
  public HexDigest parseBase32Digest() {
    return HexDigest.createFromBase32String(parseString());
  }

  public HexDigest parseFingerprint() {
    return HexDigest.createFromString(parseConcatenatedString());
  }

  public void verifyExpectedArgumentCount(String keyword, int argumentCount) {
    verifyExpectedArgumentCount(keyword, argumentCount, argumentCount);
  }

  private  void verifyExpectedArgumentCount(String keyword, int expectedMin, int expectedMax) {
    final int argumentCount = argumentsRemaining();
    if(expectedMin != -1 && argumentCount < expectedMin)
      throw new TorParsingException("Not enough arguments for keyword '"+ keyword +"' expected "+ expectedMin +" and got "+ argumentCount);

    if(expectedMax != -1 && argumentCount > expectedMax)
      // Is this the correct thing to do, or should just be a warning?
      throw new TorParsingException("Too many arguments for keyword '"+ keyword +"' expected "+ expectedMax +" and got "+ argumentCount);
  }

  public byte[] parseBase64Data() {
    final StringBuilder string = new StringBuilder(getItem());
    switch(string.length() % 4) {
    case 2:
      string.append("==");
      break;
    case 3:
      string.append("=");
      break;
    default:
      break;
    }
    try {
      return Base64.decode(string.toString().getBytes("ISO-8859-1"));
    } catch (UnsupportedEncodingException e) {
      throw new TorException(e);
    }

  }

  public IPv4Address parseAddress() {
    return IPv4Address.createFromString(getItem());
  }

  public TorPublicKey parsePublicKey() {
    final DocumentObject documentObject = parseObject();
    return TorPublicKey.createFromPEMBuffer(documentObject.getContent());
  }

 
  public byte[] parseNtorPublicKey() {
    final byte[] key = parseBase64Data();
    if(key.length != TorNTorKeyAgreement.CURVE25519_PUBKEY_LEN) {
      throw new TorParsingException("NTor public key was not expected length after base64 decoding.  Length is "+ key.length);
    }
    return key;
  }

  public TorSignature parseSignature() {
    final DocumentObject documentObject = parseObject();
    TorSignature s = TorSignature.createFromPEMBuffer(documentObject.getContent());
    return s;
  }

  public NameIntegerParameter parseParameter() {
    final String item = getItem();
    final int eq = item.indexOf('=');
    if(eq == -1) {
      throw new TorParsingException("Parameter not in expected form name=value");
    }
    final String name = item.substring(0, eq);
    validateParameterName(name);
    final int value = parseInteger(item.substring(eq + 1));
    return new NameIntegerParameter(name, value);
  }
 
  private void validateParameterName(String name) {
    if(name.isEmpty()) {
      throw new TorParsingException("Parameter name cannot be empty");
    }
    for(char c: name.toCharArray()) {
      if(!(Character.isLetterOrDigit(c) || c == '_')) {
        throw new TorParsingException("Parameter name can only contain letters.  Rejecting: "+ name);
      }
    }
  }

  public DocumentObject parseTypedObject(String type) {
    final DocumentObject object = parseObject();
    if(!type.equals(object.getKeyword()))
      throw new TorParsingException("Unexpected object type.  Expecting: "+ type +", but got: "+ object.getKeyword());
    return object;
  }

  public DocumentObject parseObject() {
    final String line = readLine();
    final String keyword = parseObjectHeader(line);
    final DocumentObject object = new DocumentObject(keyword, line);
    parseObjectBody(object, keyword);
    return object;
  }

  private String parseObjectHeader(String headerLine) {
    if(!(headerLine.startsWith(BEGIN_TAG) && headerLine.endsWith(TAG_DELIMITER)))
      throw new TorParsingException("Did not find expected object start tag.");
    return headerLine.substring(BEGIN_TAG.length() + 1,
        headerLine.length() - TAG_DELIMITER.length());
  }

  private void parseObjectBody(DocumentObject object, String keyword) {
    final String endTag = END_TAG +" "+ keyword +TAG_DELIMITER;
    while(true) {
      final String line = readLine();
      if(line == null) {
        throw new TorParsingException("EOF reached before end of '"+ keyword +"' object.");
      }
      if(line.equals(endTag)) {
        object.addFooterLine(line);
        return;
      }
      parseObjectContent(object, line);
    }
  }

  private void parseObjectContent(DocumentObject object, String content) {
    // XXX verify legal base64 data
    object.addContent(content);
  }

  public String getCurrentKeyword() {
    return currentKeyword;
  }

  public void processDocument() {
    if(callbackHandler == null)
      throw new TorException("DocumentFieldParser#processDocument() called with null callbackHandler");

    while(true) {
      final String line = readLine();
      if(line == null) {
        callbackHandler.endOfDocument();
        return;
      }
      if(processLine(line))
        callbackHandler.parseKeywordLine();
    }
  }

  public void startSignedEntity() {
    isProcessingSignedEntity = true;
    signatureDigest = new TorMessageDigest();
    signatureDigest256 = new TorMessageDigest(true);
  }

  public void endSignedEntity() {
    isProcessingSignedEntity = false;
  }

  public void setSignatureIgnoreToken(String token) {
    signatureIgnoreToken = token;
  }

  public TorMessageDigest getSignatureMessageDigest() {
    return signatureDigest;
  }

  public TorMessageDigest getSignatureMessageDigest256() {
    return signatureDigest256;
  }

  private void updateRawDocument(String line) {
    rawDocumentBuffer.append(line);
    rawDocumentBuffer.append('\n');
  }

  public String getRawDocument() {
    return rawDocumentBuffer.toString();
  }

  public void resetRawDocument() {
    rawDocumentBuffer = new StringBuilder();
  }

  public void resetRawDocument(String initialContent) {
    rawDocumentBuffer = new StringBuilder();
    rawDocumentBuffer.append(initialContent);
  }

  public boolean verifySignedEntity(TorPublicKey publicKey, TorSignature signature) {
    isProcessingSignedEntity = false;
    return publicKey.verifySignature(signature, signatureDigest);
  }

  private String readLine() {
    final String line = nextLineFromInputBuffer();
    if(line != null) {
      updateCurrentSignature(line);
      updateRawDocument(line);
    }
    return line;
  }
 
  private String nextLineFromInputBuffer() {
    if(!inputBuffer.hasRemaining()) {
      return null;
    }
    final StringBuilder sb = new StringBuilder();
    while(inputBuffer.hasRemaining()) {
      char c = (char) (inputBuffer.get() & 0xFF);
      if(c == '\n') {
        return sb.toString();
      } else if(c != '\r') {
        sb.append(c);
      }
    }
    return sb.toString();
  }

  private void updateCurrentSignature(String line) {
    if(!isProcessingSignedEntity)
      return;
    if(signatureIgnoreToken != null && line.startsWith(signatureIgnoreToken))
      return;
    signatureDigest.update(line + "\n");
    signatureDigest256.update(line + "\n");
  }

  private boolean processLine(String line) {
    final List<String> lineItems = Arrays.asList(line.split(delimiter));
    if(lineItems.size() == 0 || lineItems.get(0).length() == 0) {
      // XXX warn
      return false;
    }

    currentKeyword = lineItems.get(0);
    currentItems = lineItems;
    currentItemsPosition = 1;

    if(recognizeOpt && currentKeyword.equals("opt") && lineItems.size() > 1) {
      currentKeyword = lineItems.get(1);
      currentItemsPosition = 2;
    }

    return true;
  }

  public void logDebug(String message) {
    logger.fine(message);
  }

  public void logError(String message) {
    logger.warning(message);
  }

  public void logWarn(String message) {
    logger.info(message);
  }

}
TOP

Related Classes of com.subgraph.orchid.directory.DocumentFieldParserImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.