/* * Copyright 2016 Skynav, Inc. All rights reserved. * Portions Copyright 2009 Extensible Formatting Systems, Inc (XFSI). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.xfsi.xav.validation.images.jpeg; import java.io.EOFException; import java.io.IOException; import java.util.LinkedList; import java.util.List; import com.xfsi.xav.validation.images.jpeg.JpegValidator.MsgCode; import com.xfsi.xav.validation.util.AbstractLoggingValidator; /** * base segment parser for any SOF segment (SOFn) */ abstract class SofSegmentParser extends SegmentParser { abstract void loadValidSamplePrecisions(List<Integer> sp); abstract short getMinImageComponentNumber(); abstract short getMaxImageComponentNumber(); abstract byte getMinQuantizationTableDestinationSelector(); abstract byte getMaxQuantizationTableDestinationSelector(); boolean validate(JpegInputStream jis, JpegState js, AbstractLoggingValidator mh) throws EOFException { String symbol; try { performSegmentSpecificChecks(js, mh); symbol = String.format("SOF%1$d", js.getCurrentCode() & 0xf); assertOnlySingleFrameSegmentAllowed(jis, js, mh); int lf = jis.readShort() & 0xffff; int p = jis.readByte() & 0xff; List<Integer> list = new LinkedList<Integer>(); loadValidSamplePrecisions(list); validateSamplePrecision(symbol, p, list, mh); int y = jis.readShort() & 0xffff; validateNumberOfLines(symbol, y, js, mh); js.setResultState("height", Integer.valueOf(y)); int x = jis.readShort() & 0xffff; validateNumberOfSamplesPerLine(symbol, x, mh); js.setResultState("width", Integer.valueOf(x)); int nf = jis.readByte() & 0xff; short minNf = getMinImageComponentNumber(); short maxNf = getMaxImageComponentNumber(); validateImageComponentNumber(symbol, nf, minNf, maxNf, mh); validateFrameHeaderLength(symbol, lf, nf, mh); list.clear(); int value; int minTqi = getMinQuantizationTableDestinationSelector(); int maxTqi = getMaxQuantizationTableDestinationSelector(); for (int i = 1; i <= nf; i++) { value = jis.readByte() & 0xff; validateComponentIdentifier(symbol, i, value, list, mh); value = jis.readByte() & 0xff; validateSamplingFactors(symbol, i, (byte) value, mh); value = jis.readByte() & 0xff; validateQuantizationTableDestinationSelector(symbol, i, value, minTqi, maxTqi, mh); } } catch (EOFException e) { mh.logResult(JpegValidator.MsgCode.JPG01F003, js.getCurrentCode(), js.getSegmentCount(), jis.getTotalBytesRead()); throw e; } catch (IOException e) { assert(false) : mh.msgFormatterNV(MsgCode.JPG01X003.toString(), Thread.currentThread().getStackTrace()[2].getMethodName(), e.getMessage()); } return true; } protected void performSegmentSpecificChecks(JpegState js, AbstractLoggingValidator mh) { // May be overriden for segment specific checks } private void assertOnlySingleFrameSegmentAllowed(JpegInputStream jv, JpegState js, AbstractLoggingValidator mh) { Integer c = js.getCurrentCode(); Integer ifc = js.getInitialFrameCode(); if (ifc != null) mh.logResult(JpegValidator.MsgCode.JPG01E009, ifc, String.format("SOF%1$d", ifc & 0xf), c, String.format("SOF%1$d", c & 0xf), jv.getTotalBytesRead()); else js.setInitialFrameCode(c); } private void validateFrameHeaderLength(String symbol, int actualLf, int actualNf, AbstractLoggingValidator mh) { short expectedLf = (short) (8 + (3 * actualNf)); if (actualLf != expectedLf) mh.logResult(JpegValidator.MsgCode.JPG01E033, symbol, actualLf, actualNf); else mh.logResult(JpegValidator.MsgCode.JPG01I006, symbol, actualLf, actualNf); } private void validateSamplePrecision(String symbol, int actualPrecision, List<Integer> expectedPrecision, AbstractLoggingValidator mh) { if (expectedPrecision.contains(actualPrecision)) mh.logResult(JpegValidator.MsgCode.JPG01I007, symbol, actualPrecision); else mh.logResult(JpegValidator.MsgCode.JPG01E034, symbol, actualPrecision, expectedPrecision); } private void validateNumberOfLines(String symbol, int y, JpegState js, AbstractLoggingValidator mh) { // Nothing to validate, all values allowed: 0-65535 if (y == 0) js.requireDnlSegment(); mh.logResult(JpegValidator.MsgCode.JPG01I008, symbol, y); } private void validateImageComponentNumber(String symbol, int nf, short expectedLow, short expectedHigh, AbstractLoggingValidator mh) { if (nf >= expectedLow && nf <= expectedHigh) { List<Integer> jfifRestrictions = new LinkedList<Integer>(); jfifRestrictions.add(1); jfifRestrictions.add(3); if (!jfifRestrictions.contains(nf)) mh.logResult(JpegValidator.MsgCode.JPG01W008, symbol, nf, jfifRestrictions); else mh.logResult(JpegValidator.MsgCode.JPG01I009, symbol, nf); } else mh.logResult(JpegValidator.MsgCode.JPG01E035, symbol, nf, expectedLow, expectedHigh); } private void validateNumberOfSamplesPerLine(String symbol, int numSamplesPerLine, AbstractLoggingValidator mh) { int expectedLow = 1; int expectedHigh = 65535; if (numSamplesPerLine >= expectedLow && numSamplesPerLine <= expectedHigh) mh.logResult(JpegValidator.MsgCode.JPG01I010, symbol, numSamplesPerLine); else mh.logResult(JpegValidator.MsgCode.JPG01E036, symbol, numSamplesPerLine, expectedLow, expectedHigh); } private void validateComponentIdentifier(String symbol, int index, int id, List<Integer> alreadyFound, AbstractLoggingValidator mh) { if (alreadyFound.contains(id)) mh.logResult(JpegValidator.MsgCode.JPG01E015, symbol, index, id, alreadyFound); else { mh.logResult(JpegValidator.MsgCode.JPG01I011, symbol, index, id); alreadyFound.add(id); } } private void validateSamplingFactors(String symbol, int index, byte factors, AbstractLoggingValidator mh) { int expectedLow = 1; int expectedHigh = 4; int hi = (factors >>> 4) & 0xf; int vi = factors & 0xf; if (hi >= expectedLow && hi <= expectedHigh) mh.logResult(JpegValidator.MsgCode.JPG01I012, symbol, index, hi); else mh.logResult(JpegValidator.MsgCode.JPG01E037, symbol, index, hi, expectedLow, expectedHigh); if (vi >= expectedLow && vi <= expectedHigh) mh.logResult(JpegValidator.MsgCode.JPG01I013, symbol, index, vi); else mh.logResult(JpegValidator.MsgCode.JPG01E038, symbol, index, vi, expectedLow, expectedHigh); } private void validateQuantizationTableDestinationSelector(String symbol, int index, int tq, int expectedLow, int expectedHigh, AbstractLoggingValidator mh) { if (tq >= expectedLow && tq <= expectedHigh) mh.logResult(JpegValidator.MsgCode.JPG01I014, symbol, index, tq); else mh.logResult(JpegValidator.MsgCode.JPG01E039, symbol, index, tq, expectedLow, expectedHigh); } }