/* Copyright (C) 2009 Mobile Sorcery AB This program is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License v1.0. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for more details. You should have received a copy of the Eclipse Public License v1.0 along with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html */ package com.mobilesorcery.sdk.builder.linux.deb; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; /** * A singleton class which provides helper methods for building * linux packages. * * @author Ali Mosavian */ public class BuilderUtil { private byte[] m_copyBuffer; /** * Private constructor * */ private BuilderUtil ( ) { m_copyBuffer= new byte[4*1024]; } /** * Calculates the combined size of a directory tree/structure * * @param i The tree to calculate for * * @throws IOException If there's an I/O error */ public long calcCombinedSize ( File i ) { // Is it a file? if ( i.isFile( ) == true ) return i.length( ); // It's a directory long size = 0; for ( File f : i.listFiles( ) ) size += calcCombinedSize( f ); return size; } /** * Adds a directory structure to a gzipped tar. * * @param o The output file that the tar+gz will be written to * @param i The directory to add * * @throws IOException If there's an I/O error */ public void tarGZipDirectory ( File o, File i ) throws IOException { FileOutputStream fos = new FileOutputStream( o ); GzipCompressorOutputStream gzos = new GzipCompressorOutputStream( fos ); TarArchiveOutputStream tos = new TarArchiveOutputStream( gzos ); // Recursivly add to tar doAddFileToTar( tos, "./", i ); // Close tos.close( ); gzos.close( ); fos.close( ); } /** * Recursivly add a directory structure to a tar output stream * * @param t Tar output stream * @param r Relative path up to this point * @param c Current file */ private void doAddFileToTar ( TarArchiveOutputStream t, String r, File c ) throws IOException { // Update relative path r += (r.isEmpty( ) ? "" : (r.endsWith( "/") ? "" : "/")) + c.getName( ); // Is it a file? if ( c.isFile( ) == true ) { ArchiveEntry e = t.createArchiveEntry( c, r ); t.putArchiveEntry( e ); copyFileToOutputStream( t, c ); t.closeArchiveEntry( ); return; } // It's a directory for ( File f : c.listFiles( ) ) doAddFileToTar( t, r, f ); } /** * Simple method for copying file bytes to output stream * * @param t * @param f */ public void copyFileToOutputStream ( OutputStream t, File f ) throws FileNotFoundException, IOException { int read; int size = ( int )f.length( ); FileInputStream fis = new FileInputStream( f ); while ( size > 0 ) { read = fis.read( m_copyBuffer, 0, Math.min( size, m_copyBuffer.length ) ); t.write( m_copyBuffer, 0, read ); size -= read; } } /** * Simple method for copying String bytes to output stream * * @param t * @param s */ public void copyStringToOutputStream ( OutputStream t, String s ) throws IOException { t.write( s.getBytes( ) ); } /** * Simple method for copying file bytes to output stream * * @param f File to output * @param i Inputstream * @param b Bytes to copy */ public void copyInputStreamToFile ( File f, InputStream i, long b ) throws FileNotFoundException, IOException { int written; int size = (int)b; FileOutputStream fos = new FileOutputStream( f ); while ( size > 0 ) { written = i.read( m_copyBuffer, 0, Math.min( size, m_copyBuffer.length ) ); fos.write( m_copyBuffer, 0, written ); size -= written; } fos.close( ); } /** * Simple method for copying file bytes to output stream * * @param t * @param f */ public void copyFile ( File o, File i ) throws FileNotFoundException, IOException { int read; int size = ( int )i.length( ); FileInputStream fis = new FileInputStream( i ); FileOutputStream fos = new FileOutputStream( o ); while ( size > 0 ) { read = fis.read( m_copyBuffer, 0, Math.min( size, m_copyBuffer.length ) ); fos.write( m_copyBuffer, 0, read ); size -= read; } } /** * Adds a directory structure to a gzipped tar. * * @param o The output file that the tar+gz will be written to * @param i The directory to add * * @throws IOException If there's an I/O error */ public void calcMD5Directory ( File o, File i ) throws IOException, NoSuchAlgorithmException { FileOutputStream fos; StringBuilder sb = new StringBuilder( ); // Recursivly calculate md5 sums for directory structure doAddFileToMD5Sums( sb, "", i ); // Write fos = new FileOutputStream( o ); fos.write( sb.toString( ).getBytes( ) ); fos.close( ); } /** * Adds a directory structure to a gzipped tar. * * @param o The output file that the tar+gz will be written to * @param i The directory to add * * @throws IOException If there's an I/O error */ private void doAddFileToMD5Sums ( StringBuilder o, String r, File c ) throws IOException, FileNotFoundException, NoSuchAlgorithmException { // Update relative path r += (r.isEmpty( ) ? "" : (r.endsWith( "/") ? "" : "/")) + c.getName( ); // Is it a file? if ( c.isFile( ) == true ) { String md5 = calcFileMD5Sum( c ); o.append( md5 ); for ( int i = 0; i < 1+(32-md5.length( )); i++ ) o.append( " " ); o.append( r ) .append( "\n" ); return; } // It's a directory for ( File f : c.listFiles( ) ) doAddFileToMD5Sums( o, r, f ); } /** * Calculates the md5 has for a file and returns the * hash as a hex string * * @param f File to hash * @return 128 bit hash in hex * * @throws NoSuchAlgorithmException * @throws FileNotFoundException * @throws IOException */ public String calcFileMD5Sum ( File f ) throws NoSuchAlgorithmException, FileNotFoundException, IOException { MessageDigest m = MessageDigest.getInstance( "MD5" ); FileInputStream fos = new FileInputStream( f ); // Hash file int size = (int)f.length( ); while ( size > 0 ) { int count = fos.read( m_copyBuffer ); m.update( m_copyBuffer, 0, count ); size -= count; } fos.close( ); BigInteger hash = new BigInteger( 1, m.digest( ) ); return hash.toString( 16 ); } /** * Returns singleton instance * Note: Not thread safe * */ private static BuilderUtil m_instance = null; public static BuilderUtil getInstance ( ) { if ( m_instance == null ) m_instance = new BuilderUtil( ); return m_instance; } }