/* 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; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import com.mobilesorcery.sdk.builder.linux.deb.BuilderUtil; /** * This package unpacks and parses a template package, recursivly * resolving variables in file names and files. It also parses the * package meta data which is in JSON, * * @author Ali Mosavian */ public class PackageParser { private Set<String> m_parseSet; private Map<String, String> m_iconMap; private String m_binaryPath; private VariableResolver m_varResolver; private List<String> m_dependsList; private List<String> m_requiresList; private Map<String, String> m_scriptMap; private Map<String, Integer> m_filemodeMap; private String m_programFile; private String m_resourceFile; /** * Constructor */ public PackageParser ( ) { m_parseSet = new HashSet<String>( ); m_iconMap = new HashMap<String, String>( ); m_varResolver = new VariableResolver( ); m_dependsList = new LinkedList<String>( ); m_requiresList= new LinkedList<String>( ); m_scriptMap = new HashMap<String, String>( ); m_filemodeMap = new HashMap<String, Integer>( ); // Sets defaults setAppCategories( "" ); } /** * Sets the application name variable * * @param v Name */ public void setAppName ( String v ) { addVariable( "appname", v ); setSummary( v ); } /** * Set the applications summary. * Note: Keep it short and no newlines * * @param v Summary */ public void setSummary ( String v ) { addVariable( "description", v ); } /** * Sets application category, see gnome/kde/clutter manual * * @param v Category */ public void setAppCategories ( String v ) { addVariable( "categories", v ); } /** * Returns the directory where the program and resource * file needs to be inserted into * * @return Program dir or null */ public String getProgramDir ( ) { return m_binaryPath; } /** * Returns png icon directory * * @param s Icon size, usually 16 ( 16x16 ), 32, 64, 128, 256 * @return Null if no such directory */ public String getPNGIconDir ( int s ) { if ( m_iconMap.containsKey( "png"+s ) == false ) return null; return m_iconMap.get( "png"+s ); } /** * Returns svg icon directory * Note: If SVG is used, png usually isn't needed. * * @return Null if no such directory */ public String getSVGIconDir ( ) { if ( m_iconMap.containsKey( "svg" ) == false ) return null; return m_iconMap.get( "svg" ); } /** * Sets a variable value pair, this is used to resolve * unknown variables during template package processing. * * @param s Variable name * @param v Variable value */ public void addVariable ( String s, String v ) { m_varResolver.addVariable( s.toLowerCase( ), v ); } /** * Returns DEB package dependency list * * @return List of Strings */ public List<String> getDependsList ( ) { return m_dependsList; } /** * Returns RPM package dependency list * * @return List of Strings */ public List<String> getRequiresList ( ) { return m_requiresList; } /** * Returns unix file mode for every path in the template. * * @param n File/path name * @return file mode */ public int getFileMode ( String n ) { if ( n.charAt( 0 ) == '/' ) n = n.substring( 1 ); if ( m_filemodeMap.containsKey( n ) == false ) return 0x1a4; return m_filemodeMap.get( n ); } /** * Returns the path to the program file * */ public String getProgramFilePath ( ) { return m_programFile; } /** * Returns the path to the resource file * */ public String getResourceFilePath ( ) { return m_resourceFile; } /** * Returns a script * * @param s Name of the script, valid values are * - "preinst" * - "postinst" * - "prerm" * - "postrm" * * @return Script or null if it doesn't exist */ public String getScript ( String s ) { s = s.toLowerCase( ); if ( m_scriptMap.containsKey( s ) == false ) return null; return m_scriptMap.get( s ); } /** * Extracts a package template and parses and replaces variables * in filenames and files. * * @param o Output directory * @param i Input file * * @throws Exception If recursion is too deep, a variable isn't defined or * malformed meta data * @throws IOException Error reading inputstream * @throws ParseException Malformed JSON * @throws FileNotFoundException Could not open input file */ public void doProcessTarGZip ( File o, File i ) throws Exception, IOException, ParseException, FileNotFoundException { FileInputStream fis = new FileInputStream( i ); GZIPInputStream gis = new GZIPInputStream( fis ); TarArchiveInputStream tis = new TarArchiveInputStream( gis ); // Remove any old data if any if ( o.exists( ) == true ) o.delete( ); // Find and parse meta data, this should always be the // first file, but it can' be assumed while ( true ) { ArchiveEntry e = tis.getNextEntry( ); if ( e == null ) break; if ( e.getName( ).equals( ".meta/.meta" ) == false ) continue; doParseMeta( tis ); break; } // Reset input tis.close( ); gis.close( ); fis.close( ); fis = new FileInputStream( i ); gis = new GZIPInputStream( fis ); tis = new TarArchiveInputStream( gis ); // Process and extract files while ( true ) { File f; ArchiveEntry e = tis.getNextEntry( ); if ( e == null ) break; // Check if it's a script that we need to load and parse if ( e.getName( ).contains( ".meta" ) == true ) { if ( m_scriptMap.containsKey( e.getName( ) ) == true ) { String name = m_scriptMap.get( e.getName( ) ); String script = m_varResolver.doParseStream( tis ); m_scriptMap.put( name, script ); m_scriptMap.remove( e.getName( ) ); } continue; } // Store its permissions String n = m_varResolver.doResolveString( e.getName( ) ); m_filemodeMap.put( n, ((TarArchiveEntry )e).getMode( ) ); // Directory ? f = new File( o, n ); if ( e.isDirectory( ) == true ) { if ( f.exists( ) == false ) f.mkdirs( ); continue; } // It's a file if ( m_parseSet.contains( e.getName( ) ) == true ) m_varResolver.doParseCopyStream( f, tis ); else BuilderUtil.getInstance( ).copyInputStreamToFile( f, tis, e.getSize( ) ); } } /** * Parses the meta data and (might) set m_iconList, m_binaryDir, * and m_parseSet * * @param i Inputstream which is meta data * * @throws Exception If recursion is too deep, a variable isn't defined or * malformed meta data * @throws IOException Error reading inputstream * @throws ParseException Malformed JSON */ private void doParseMeta ( InputStream i ) throws Exception, IOException, ParseException { Reader r = new BufferedReader( new InputStreamReader( i ) ); JSONParser jParse = new JSONParser( ); JSONObject oMain = (JSONObject)jParse.parse( r ); // Check for program directory if ( oMain.containsKey( "programDir" ) == true ) m_binaryPath = m_varResolver.doResolveString( (String)oMain.get( "programDir" ) ); // Check for parse file list if ( oMain.containsKey( "parseList" ) ) { for ( Object s : ((JSONArray)oMain.get( "parseList" )).toArray( ) ) m_parseSet.add( (String)s ); } // Check for icon list if ( oMain.containsKey( "iconList" ) ) { for ( Object o : ((JSONArray)oMain.get( "iconList" )).toArray( ) ) { String name; JSONObject oIcon = (JSONObject)o; // Get fields if ( oIcon.containsKey( "type" ) == false ) throw new Exception( "Malformed metadata - No 'type' in icon" ); name = (String)oIcon.get( "type" ); if ( name.equals( "png" ) == true ) { if ( oIcon.containsKey( "size" ) == false ) throw new Exception( "Malformed metadata - 'type' is png but no size" ); name += oIcon.get( "size" ); } else if ( name.equals( "svg" ) == true ) { // Don't need to do anything } else throw new Exception( "Malformed metadata - Unrecognised icon type" ); // Associate type with path if ( oIcon.containsKey( "path" ) == false ) throw new Exception( "Malformed metadata - No 'path' in icon" ); m_iconMap.put( name, m_varResolver.doResolveString( (String)oIcon.get( "path" ))); } } // Check for requires list if ( oMain.containsKey( "requires" ) ) { for ( Object s : ((JSONArray)oMain.get( "requires" )).toArray( ) ) m_requiresList.add( (String)s ); } // Check for depends list if ( oMain.containsKey( "depends" ) ) { for ( Object s : ((JSONArray)oMain.get( "depends" )).toArray( ) ) m_dependsList.add( (String)s ); } // Check scripts String[] scriptList = { "preinst", "postinst", "prerm", "postrm" }; for ( String s : scriptList ) if ( oMain.containsKey( s ) ) m_scriptMap.put( (String)oMain.get( s ), s ); // Check for program file (path) if ( oMain.containsKey( "programFile" ) ) m_programFile = m_varResolver.doResolveString( (String)oMain.get( "programFile" ) ); // Check for resource file (path) if ( oMain.containsKey( "resourceFile" ) ) m_resourceFile = m_varResolver.doResolveString( (String)oMain.get( "resourceFile" ) ); } }