package com.subgraph.orchid.directory.consensus; import com.subgraph.orchid.ConsensusDocument; import com.subgraph.orchid.TorParsingException; 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 ConsensusDocumentParser implements DocumentParser<ConsensusDocument> { public enum DocumentSection { NO_SECTION, PREAMBLE, AUTHORITY, ROUTER_STATUS, FOOTER }; // dir-spec.txt 3.2 // Unlike other formats described above, a SP in these documents must be a // single space character (hex 20). private final static String ITEM_DELIMITER = " "; private final PreambleSectionParser preambleParser; private final AuthoritySectionParser authorityParser; private final RouterStatusSectionParser routerStatusParser; private final FooterSectionParser footerParser; private final DocumentFieldParser fieldParser; private DocumentSection currentSection = DocumentSection.PREAMBLE; private final ConsensusDocumentImpl document; private DocumentParsingResultHandler<ConsensusDocument> resultHandler; public ConsensusDocumentParser(DocumentFieldParser fieldParser) { this.fieldParser = fieldParser; initializeParser(); document = new ConsensusDocumentImpl(); preambleParser = new PreambleSectionParser(fieldParser, document); authorityParser = new AuthoritySectionParser(fieldParser, document); routerStatusParser = new RouterStatusSectionParser(fieldParser, document); footerParser = new FooterSectionParser(fieldParser, document); } private void initializeParser() { fieldParser.resetRawDocument(); fieldParser.setHandler(createParsingHandler()); fieldParser.setDelimiter(ITEM_DELIMITER); fieldParser.setSignatureIgnoreToken("directory-signature"); fieldParser.startSignedEntity(); } public boolean parse(DocumentParsingResultHandler<ConsensusDocument> resultHandler) { this.resultHandler = resultHandler; try { fieldParser.processDocument(); return true; } catch(TorParsingException e) { resultHandler.parsingError(e.getMessage()); return false; } } public DocumentParsingResult<ConsensusDocument> parse() { final BasicDocumentParsingResult<ConsensusDocument> result = new BasicDocumentParsingResult<ConsensusDocument>(); parse(result); return result; } private DocumentParsingHandler createParsingHandler() { return new DocumentParsingHandler() { public void endOfDocument() { document.setRawDocumentData(fieldParser.getRawDocument()); resultHandler.documentParsed(document); fieldParser.logDebug("Finished parsing status document."); } public void parseKeywordLine() { processKeywordLine(); } }; } private void processKeywordLine() { DocumentSection newSection = null; while(currentSection != DocumentSection.NO_SECTION) { switch(currentSection) { case PREAMBLE: newSection = preambleParser.parseKeywordLine(); break; case AUTHORITY: newSection = authorityParser.parseKeywordLine(); break; case ROUTER_STATUS: newSection = routerStatusParser.parseKeywordLine(); break; case FOOTER: newSection = footerParser.parseKeywordLine(); break; default: break; } if(newSection == currentSection) return; currentSection = newSection; } } }