import jp.sfjp.jindolf.data.html.PeriodLoader;
import jp.sfjp.jindolf.data.html.VillageInfoLoader;
import jp.sfjp.jindolf.data.html.VillageListLoader;
+import jp.sfjp.jindolf.data.xml.VillageLoader;
import jp.sfjp.jindolf.dxchg.CsvExporter;
import jp.sfjp.jindolf.dxchg.WebIPCDialog;
import jp.sfjp.jindolf.dxchg.WolfBBS;
if(result != JFileChooser.APPROVE_OPTION) return;
File selected = chooser.getSelectedFile();
+ Village village;
+ try{
+ village = VillageLoader.parseVillage(selected);
+ }catch(IOException e){
+ System.out.println(e);
+ }
//System.out.println(selected);
return;
--- /dev/null
+/*
+ * XML custom error-handler
+ *
+ * License : The MIT License
+ * Copyright(c) 2010 MikuToga Partners
+ */
+
+package jp.sfjp.jindolf.data.xml;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * 自製エラーハンドラ。
+ *
+ * <p>例外を渡されれば即投げ返す。
+ */
+public final class BotherHandler implements ErrorHandler{
+
+ /**
+ * 唯一のシングルトン。
+ */
+ public static final ErrorHandler HANDLER = new BotherHandler();
+
+
+ /**
+ * 隠しコンストラクタ。
+ */
+ private BotherHandler(){
+ super();
+ return;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param exception {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void error(SAXParseException exception) throws SAXException{
+ throw exception;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param exception {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void fatalError(SAXParseException exception) throws SAXException{
+ throw exception;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param exception {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void warning(SAXParseException exception) throws SAXException{
+ throw exception;
+ }
+
+}
--- /dev/null
+/*
+ * No-operation Entity Resolver for XML.
+ *
+ * License : The MIT License
+ * Copyright(c) 2019 olyutorskii
+ */
+
+package jp.sfjp.jindolf.data.xml;
+
+import java.io.Reader;
+import java.io.StringReader;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * No-operation Entity Resolver implementation for preventing XXE.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/XML_external_entity_attack">
+ * XML external entity attack (Wikipedia)
+ * </a>
+ */
+public final class NoopEntityResolver implements EntityResolver{
+
+ /** Singleton resolver. */
+ public static final EntityResolver NOOP_RESOLVER =
+ new NoopEntityResolver();
+
+
+ /**
+ * Constructor.
+ */
+ private NoopEntityResolver(){
+ super();
+ return;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Prevent any external entity reference XXE.
+ *
+ * @param publicId {@inheritDoc}
+ * @param systemId {@inheritDoc}
+ * @return empty input source
+ */
+ @Override
+ public InputSource resolveEntity(String publicId, String systemId){
+ Reader emptyReader = new StringReader("");
+ InputSource source = new InputSource(emptyReader);
+
+ source.setPublicId(publicId);
+ source.setSystemId(systemId);
+
+ return source;
+ }
+
+}
--- /dev/null
+/*
+ * village handler
+ *
+ * License : The MIT License
+ * Copyright(c) 2020 olyutorskii
+ */
+
+package jp.sfjp.jindolf.data.xml;
+
+import jp.sfjp.jindolf.data.Village;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * VillageのXMLパーサ本体。
+ */
+public class VillageHandler implements ContentHandler{
+
+ /**
+ * constructor.
+ */
+ public VillageHandler(){
+ super();
+ return;
+ }
+
+
+ /**
+ * パースした結果のVillageを返す。
+ *
+ * @return 村
+ */
+ public Village getVillage(){
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param locator {@inheritDoc}
+ */
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void startDocument() throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void endDocument() throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param prefix {@inheritDoc}
+ * @param uri {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void startPrefixMapping(String prefix, String uri)
+ throws SAXException {
+ //System.out.println(prefix);
+ //System.out.println(uri);
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param prefix {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void endPrefixMapping(String prefix)
+ throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param uri {@inheritDoc}
+ * @param localName {@inheritDoc}
+ * @param qName {@inheritDoc}
+ * @param atts {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void startElement(String uri,
+ String localName,
+ String qName,
+ Attributes atts)
+ throws SAXException {
+ //System.out.println(uri);
+ //System.out.println(localName);
+ //System.out.println(qName);
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param uri {@inheritDoc}
+ * @param localName {@inheritDoc}
+ * @param qName {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param ch {@inheritDoc}
+ * @param start {@inheritDoc}
+ * @param length {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param ch {@inheritDoc}
+ * @param start {@inheritDoc}
+ * @param length {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void ignorableWhitespace(char[] ch, int start, int length)
+ throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param target {@inheritDoc}
+ * @param data {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void processingInstruction(String target, String data)
+ throws SAXException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param name {@inheritDoc}
+ * @throws SAXException {@inheritDoc}
+ */
+ @Override
+ public void skippedEntity(String name) throws SAXException {
+ return;
+ }
+
+}
package jp.sfjp.jindolf.data.xml;
+import java.io.BufferedInputStream;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.validation.Schema;
import jp.sfjp.jindolf.data.Village;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
/**
* JinArchiverなどでXMLファイルにアーカイブされた人狼BBSの村プレイ記録を
*/
public class VillageLoader {
+ private static final String F_DISALLOW_DOCTYPE_DECL =
+ "http://apache.org/xml/features/disallow-doctype-decl";
+ private static final String F_EXTERNAL_GENERAL_ENTITIES =
+ "http://xml.org/sax/features/external-general-entities";
+ private static final String F_EXTERNAL_PARAMETER_ENTITIES =
+ "http://xml.org/sax/features/external-parameter-entities";
+ private static final String F_LOAD_EXTERNAL_DTD =
+ "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+
/**
* constructor.
*/
}
- public static Village parseVillage(File xmlFile){
- Village result = null;
+ /**
+ * XMLファイルをパースする。
+ *
+ * @param xmlFile XMLファイル
+ * @return 村
+ * @throws IOException I/Oエラー
+ */
+ public static Village parseVillage(File xmlFile) throws IOException{
+ Objects.nonNull(xmlFile);
+
+ boolean isNormal;
+ isNormal = xmlFile.isFile()
+ && xmlFile.exists()
+ && xmlFile.canRead();
+ if(!isNormal){
+ return null;
+ }
+
+ Path path = xmlFile.toPath();
+
+ Village result = parseVillage(path);
+ return result;
+ }
+
+ /**
+ * XMLファイルをパースする。
+ *
+ * @param path XMLファイルのPath
+ * @return 村
+ * @throws IOException I/Oエラー
+ */
+ public static Village parseVillage(Path path) throws IOException{
+ Objects.nonNull(path);
+
+ path = path.normalize();
+
+ boolean isNormal;
+ isNormal = Files.exists(path)
+ && Files.isRegularFile(path)
+ && Files.isReadable(path);
+ if(!isNormal){
+ return null;
+ }
+
+ Village result;
+ try(InputStream is = pathToStream(path)){
+ result = parseVillage(is);
+ }
+
+ return result;
+ }
+
+ /**
+ * Pathから入力ストリームを得る。
+ *
+ * @param path Path
+ * @return バッファリングされた入力ストリーム
+ * @throws IOException I/Oエラー
+ */
+ private static InputStream pathToStream(Path path) throws IOException{
+ InputStream is;
+ is = Files.newInputStream(path);
+ is = new BufferedInputStream(is, 4*1024);
+ return is;
+ }
+
+ /**
+ * XML入力をパースする。
+ *
+ * @param istream XML入力
+ * @return 村
+ * @throws IOException I/Oエラー
+ */
+ public static Village parseVillage(InputStream istream) throws IOException{
+ InputSource isource = new InputSource(istream);
+ Village result = parseVillage(isource);
+ return result;
+ }
+
+ /**
+ * XML入力をパースする。
+ *
+ * @param isource XML入力
+ * @return 村
+ * @throws IOException I/Oエラー
+ */
+ public static Village parseVillage(InputSource isource) throws IOException{
+ XMLReader reader = buildReader();
+ VillageHandler handler = new VillageHandler();
+ reader.setContentHandler(handler);
+
+ try{
+ reader.parse(isource);
+ }catch(SAXException e){
+ System.out.println(e);
+ return null;
+ }
+
+ Village result = handler.getVillage();
return result;
}
+ /**
+ * SAXパーサファクトリを生成する。
+ *
+ * <ul>
+ * <li>XML名前空間機能は有効になる。
+ * <li>DTDによる形式検証は無効となる。
+ * <li>XIncludeによる差し込み機能は無効となる。
+ * </ul>
+ *
+ * @param schema スキーマ
+ * @return ファクトリ
+ */
+ private static SAXParserFactory buildFactory(Schema schema){
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ factory.setXIncludeAware(false);
+
+ try{
+ factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+// factory.setFeature(F_DISALLOW_DOCTYPE_DECL, true);
+ factory.setFeature(F_EXTERNAL_GENERAL_ENTITIES, false);
+ factory.setFeature(F_EXTERNAL_PARAMETER_ENTITIES, false);
+ factory.setFeature(F_LOAD_EXTERNAL_DTD, false);
+ }catch( ParserConfigurationException
+ | SAXNotRecognizedException
+ | SAXNotSupportedException e ){
+ assert false;
+ throw new AssertionError(e);
+ }
+
+ factory.setSchema(schema);
+
+ return factory;
+ }
+
+ /**
+ * SAXパーサを生成する。
+ *
+ * @param schema スキーマ
+ * @return SAXパーサ
+ */
+ private static SAXParser buildParser(Schema schema){
+ SAXParserFactory factory = buildFactory(schema);
+
+ SAXParser parser;
+ try{
+ parser = factory.newSAXParser();
+ }catch(ParserConfigurationException | SAXException e){
+ assert false;
+ throw new AssertionError(e);
+ }
+
+ try{
+ parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ }catch(SAXNotRecognizedException | SAXNotSupportedException e){
+ assert false;
+ throw new AssertionError(e);
+ }
+
+ return parser;
+ }
+
+ /**
+ * XMLリーダを生成する。
+ *
+ * @return XMLリーダ
+ */
+ private static XMLReader buildReader(){
+ SAXParser parser = buildParser((Schema)null);
+
+ XMLReader reader;
+ try{
+ reader = parser.getXMLReader();
+ }catch(SAXException e){
+ assert false;
+ throw new AssertionError(e);
+ }
+
+ reader.setEntityResolver(NoopEntityResolver.NOOP_RESOLVER);
+ reader.setErrorHandler(BotherHandler.HANDLER);
+
+ return reader;
+ }
+
}