package com.subgraph.orchid.directory.router;
import com.subgraph.orchid.RouterMicrodescriptor;
import com.subgraph.orchid.TorParsingException;
import com.subgraph.orchid.crypto.TorMessageDigest;
import com.subgraph.orchid.directory.parsing.BasicDocumentParsingResult;
import com.subgraph.orchid.directory.parsing.DocumentFieldParser;
import com.subgraph.orchid.directory.parsing.DocumentParser;
import com.subgraph.orchid.directory.parsing.DocumentParsingHandler;
import com.subgraph.orchid.directory.parsing.DocumentParsingResult;
import com.subgraph.orchid.directory.parsing.DocumentParsingResultHandler;
public class RouterMicrodescriptorParser implements DocumentParser<RouterMicrodescriptor>{
private final DocumentFieldParser fieldParser;
private RouterMicrodescriptorImpl currentDescriptor;
private DocumentParsingResultHandler<RouterMicrodescriptor> resultHandler;
public RouterMicrodescriptorParser(DocumentFieldParser fieldParser) {
this.fieldParser = fieldParser;
this.fieldParser.setHandler(createParsingHandler());
}
private DocumentParsingHandler createParsingHandler() {
return new DocumentParsingHandler() {
public void parseKeywordLine() {
processKeywordLine();
}
public void endOfDocument() {
if(currentDescriptor != null) {
finalizeDescriptor(currentDescriptor);
}
}
};
}
public boolean parse(DocumentParsingResultHandler<RouterMicrodescriptor> resultHandler) {
this.resultHandler = resultHandler;
try {
fieldParser.processDocument();
return true;
} catch(TorParsingException e) {
resultHandler.parsingError(e.getMessage());
return false;
}
}
public DocumentParsingResult<RouterMicrodescriptor> parse() {
final BasicDocumentParsingResult<RouterMicrodescriptor> result = new BasicDocumentParsingResult<RouterMicrodescriptor>();
parse(result);
return result;
}
private void processKeywordLine() {
final RouterMicrodescriptorKeyword keyword = RouterMicrodescriptorKeyword.findKeyword(fieldParser.getCurrentKeyword());
if(!keyword.equals(RouterMicrodescriptorKeyword.UNKNOWN_KEYWORD)) {
processKeyword(keyword);
}
if(currentDescriptor != null) {
currentDescriptor.setRawDocumentData(fieldParser.getRawDocument());
}
}
private void processKeyword(RouterMicrodescriptorKeyword keyword) {
fieldParser.verifyExpectedArgumentCount(keyword.getKeyword(), keyword.getArgumentCount());
switch(keyword) {
case ONION_KEY:
processOnionKeyLine();
break;
case NTOR_ONION_KEY:
if(currentDescriptor != null) {
currentDescriptor.setNtorOnionKey(fieldParser.parseNtorPublicKey());
}
break;
case FAMILY:
while(fieldParser.argumentsRemaining() > 0 && currentDescriptor != null) {
currentDescriptor.addFamilyMember(fieldParser.parseString());
}
break;
case P:
processP();
break;
case A:
default:
break;
}
}
private void processOnionKeyLine() {
if(currentDescriptor != null) {
finalizeDescriptor(currentDescriptor);
}
currentDescriptor = new RouterMicrodescriptorImpl();
fieldParser.resetRawDocument(RouterMicrodescriptorKeyword.ONION_KEY.getKeyword() + "\n");
currentDescriptor.setOnionKey(fieldParser.parsePublicKey());
}
private void finalizeDescriptor(RouterMicrodescriptorImpl descriptor) {
final TorMessageDigest digest = new TorMessageDigest(true);
digest.update(descriptor.getRawDocumentData());
descriptor.setDescriptorDigest(digest.getHexDigest());
if(!descriptor.isValidDocument()) {
resultHandler.documentInvalid(descriptor, "Microdescriptor data invalid");
} else {
resultHandler.documentParsed(descriptor);
}
}
private void processP() {
if(currentDescriptor == null) {
return;
}
final String ruleType = fieldParser.parseString();
if("accept".equals(ruleType)) {
currentDescriptor.addAcceptPorts(fieldParser.parseString());
} else if("reject".equals(ruleType)) {
currentDescriptor.addRejectPorts(fieldParser.parseString());
} else {
fieldParser.logWarn("Unexpected P field in microdescriptor: "+ ruleType);
}
}
}