Package er.woinstaller.archiver

Source Code of er.woinstaller.archiver.XarFile$XarInputStream

package er.woinstaller.archiver;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.tools.bzip2.CBZip2InputStream;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import er.woinstaller.io.BoundedInputStream;

public class XarFile {
  private static final long XAR_HEADER_MAGIC = 0x78617221;
  private static final int XAR_HEADER_SIZE = 28;
  private static final String[] XAR_CKSUM = new String[] { "NONE", "SHA1", "MD5" };
  private static final int BYTE_MASK = 0xff;
 
  private final byte[] byte2 = new byte[2];
  private final byte[] byte4 = new byte[4];
  private final byte[] byte8 = new byte[8];
  private final Map<String, XarEntry> entries = new HashMap<String, XarEntry>();

  private File file;

  private XarHeader header;
  private XarToc toc;
  private InputStream inputStream;
  private InputStream lastInputStream;
  private long currentOffset = 0;
 
  private class XarHeader {
    private static final int SHORT_MASK = 0xffff;

    public long magic;
    public int size;
    public int version;
    public BigInteger tocLengthCompressed;
    public BigInteger tocLengthUncompressed;
    public long checksumAlgorithm;
   
    protected XarHeader() throws IOException {
      magic = readUint32();
      size = readUint16();
      version = readUint16();
      tocLengthCompressed = readUint64();
      tocLengthUncompressed = readUint64();
      checksumAlgorithm = readUint32();
    }
   
    @SuppressWarnings("unused")
    public void dumpHeader() {
      System.out.println("\nmagic:\t\t\t 0x"+ Long.toHexString((magic >> Short.SIZE & SHORT_MASK))
          Long.toHexString(magic & SHORT_MASK)
          + " " + ((magic == XAR_HEADER_MAGIC)?"(OK)":"(BAD)"));
      System.out.println("size:\t\t\t "+size);
      System.out.println("version:\t\t "+version);
      System.out.println("Compressed TOC length:\t "+tocLengthCompressed);
      System.out.println("Uncompressed TOC length: "+tocLengthUncompressed);
      System.out.println("Checksum algorithm:\t "+checksumAlgorithm + " ("+getCksumName()+")");
    }
  }
 
  private class XarToc {
    private static final int BUFFER_SIZE = 255;
    private final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private DocumentBuilder builder;
   
    private Document doc;
    private String data;

    protected XarToc() throws IOException {
      try {
        data = readToc();       
        builder = factory.newDocumentBuilder();
        doc = builder.parse(new InputSource(new StringReader(data)));
      } catch (ParserConfigurationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (SAXException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
 
    private String readToc() throws IOException {
      InputStream inflate = new InflaterInputStream(new BoundedInputStream(inputStream, 0,
          header.tocLengthCompressed.longValue()));
      try {
        byte[] buffer = new byte[BUFFER_SIZE];
        BigInteger length = new BigInteger(header.tocLengthUncompressed.toByteArray());
        int read;
        StringBuffer tocFile = new StringBuffer();
        while ((read = inflate.read(buffer, 0, length.intValue() > BUFFER_SIZE?BUFFER_SIZE:length.intValue())) > 0) {
          tocFile.append(new String(buffer, 0, read));
          length = length.subtract(BigInteger.valueOf(read));
        }
        return tocFile.toString();

      } finally {
        inflate.close();
      }
    }
   
    @Override
    public String toString() {
      return data;
    }

    public Map<String, XarEntry> getEntries() {
      return XarEntry.getEntries(doc);
    }
  }
   
  public class XarInputStream extends InputStream {
    private final InputStream _delegate;
    private final XarEntry _entry;
    private final MessageDigest _digest;
   
    public XarInputStream(XarEntry entry, InputStream input) {
      _entry = entry;
      _delegate = input;
      if (_entry.hasChecksum()) {
        _digest = _entry.getMessageDigest(getCksumName());
        _digest.reset();
      } else {
        _digest = null;
      }
    }
   
    @Override
    public int read() throws IOException {
      int result = _delegate.read();
      if (result == -1) {
        if (!validChecksum()) {
          throw new XarException("invalid checksum");
        }
        return result;
      }
      if (_digest != null) {
        _digest.update((byte)(result & BYTE_MASK));
      }
      return result;
    }
   
    @Override
    public int read(byte[] buffer, int off, int len) throws IOException {
      int result = _delegate.read(buffer, off, len);
      if (result == -1) {
        if (!validChecksum()) {
          throw new XarException("invalid checksum");
        }
        return result;
      }
      if (_digest != null) {
        _digest.update(buffer, off, result);
      }
      return result;       
    }
   
    private boolean validChecksum() {
      if (_digest != null) {
        String checksum = toChecksum(_digest.digest());
        if (_entry.hasChecksum() && !checksum.equals(_entry.getExtractedChecksum())) {
          return false;
        }
      }
      return true;
    }
   
    private String toChecksum(byte[] data) {
      StringBuffer checksum = new StringBuffer();
      for (int j = 0; j < data.length; j++) {
        String hexString = Integer.toHexString(data[j] & BYTE_MASK);
        if (hexString.length() == 1) {
          checksum.append("0");
        }
        checksum.append(hexString);
      }
      return checksum.toString();
    }
  }
 
//  public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
//  }
 
  public XarFile(String name) throws IOException {
    this(new File(name));
  }
 
  public XarFile(File file) throws IOException {
    if (!file.exists() || file.length() < XAR_HEADER_SIZE) {
      throw new IOException("error reading header");
    }
    this.file = file;
    setInputStream(new BufferedInputStream(new FileInputStream(file)));
  }
 
  public XarFile(InputStream stream) throws IOException {
    setInputStream(stream);
  }
 
  private void setInputStream(InputStream stream) throws IOException {
    inputStream = stream;
    header = new XarHeader();
    if (header.magic != XAR_HEADER_MAGIC) {
      throw new XarException("invalid magic header");
    }
    getToc();
    try {
      run();
    } catch (Exception e) {
      // TODO: handle exception
    }
    entries.putAll(getToc().getEntries());
  }
 
  private XarToc getToc() {
    if (toc == null) {
      try {
        toc = new XarToc();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return toc;
  }
 
  public void run() throws IOException, NoSuchAlgorithmException {
//    System.out.println(toc);
//    XarEntry entry = getEntry("PackageInfo");
//    InputStream in = getInputStream(entry);
//    Writer writer = new StringWriter();
//    int i;
//    while ((i = in.read()) >= 0) {
//      writer.write(i);
//    }
//    System.out.print(writer.toString());
 
 
  public XarEntry getEntry(String name) {
    if (name == null) {
      throw new IllegalArgumentException("name");
    }
    return entries.get(name);
  }
 
  public Map<String, XarEntry> getEntries() {
    return getToc().getEntries();
  }
 
  public InputStream getInputStream(String name) throws IOException {
    return getInputStream(getEntry(name));
  }
 
  public InputStream getInputStream(XarEntry entry) throws IOException {
    if (entry == null) {
      throw new IllegalArgumentException("entry");
    }
    synchronized (this) {
      try {
        String compression = entry.getCompression();
        long newOffset = 0;
        if (lastInputStream != null) {
          while(lastInputStream.read() != -1) { /* read to end of stream */ };
        }
        if (entry.getOffset()  <= currentOffset) {
          if (file == null) {
            throw new XarException("Cannot seek backwards through stream");
          }
          lastInputStream.close();
          inputStream = new BufferedInputStream(new FileInputStream(file));
          long toSkip = header.size + header.tocLengthCompressed.longValue();
          skipFully(inputStream, toSkip);
          currentOffset = 0;
        }
        newOffset = entry.getOffset() - currentOffset;
        currentOffset = entry.getOffset() + entry.getLength();
        InputStream input = new BoundedInputStream(inputStream, newOffset, entry.getLength());
        if (compression == null) {
          // Do nothing
        } else
        if ("bzip2".equals(compression)) {
          skipFully(input, 2);
          input = new CBZip2InputStream(input);
        } else
        if ("gzip".equals(compression)) {
          input = new GZIPInputStream(input);
        }
        lastInputStream = new XarInputStream(entry, input);
        return lastInputStream;
      } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      throw new XarException("something unexpected happened");
    }
  }
 
  private static void skipFully(InputStream inputStream, long skip) throws IOException {
    long toSkip = skip;
    while (toSkip > 0) {
      toSkip -= inputStream.skip(toSkip);
    }
  }
 
  private static void readFully(InputStream inputStream, byte[] buffer) throws IOException {
    int read = 0;
    while (read < buffer.length) {
      read += inputStream.read(buffer, read, buffer.length - read);
    }
  }
 
  private String getCksumName() {
    if (header.checksumAlgorithm < 0 || header.checksumAlgorithm > XAR_CKSUM.length - 1) {
      return "unknown";
    }
    return XAR_CKSUM[(int)header.checksumAlgorithm];
  }
 
  private int readUint16() throws IOException {
    readFully(inputStream, byte2);
    return ((byte2[0] & BYTE_MASK) << Byte.SIZE) | byte2[1] & BYTE_MASK;
  }

  private long readUint32() throws IOException {
    readFully(inputStream, byte4);
    long result = 0;
    for (int i = 0; i < byte4.length; i++) {
      result |= (byte4[i] & BYTE_MASK) << (byte4.length - (i+1)) * Byte.SIZE;
    }
    return result;
  }

  private byte[] readByte8() throws IOException {
    readFully(inputStream, byte8);
    return byte8.clone();
  }

  private BigInteger readUint64() throws IOException {
    return new BigInteger(readByte8());
  }
}
TOP

Related Classes of er.woinstaller.archiver.XarFile$XarInputStream

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.