content: package org.flashtool.system;
  
  import java.io.BufferedInputStream;
  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.io.RandomAccessFile;
  import java.math.BigInteger;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.security.InvalidKeyException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.Collection;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.HashSet;
  import java.util.Set;
  import java.util.TimeZone;
  import java.util.jar.Attributes;
  import java.util.jar.JarFile;
  import java.util.jar.Manifest;
  import java.util.zip.ZipEntry;
  import java.util.zip.ZipInputStream;
  import javax.crypto.NoSuchPaddingException;
  import org.apache.commons.io.FileUtils;
  import org.flashtool.logger.LogProgress;
  import org.flashtool.util.HexDump;
  
  import java.util.zip.CheckedInputStream;
  import java.util.zip.Adler32;
  import lombok.extern.slf4j.Slf4j;
  
  @Slf4j
  public class OS {
  
  	
  	public static String RC4Key = "DoL6FBfnYcNJBjH31Vnz6lKATTaDGe4y";
  	public static String AESKey = "qAp!wmvl!cOS7xSQV!aoR7Qz*neY^5Sx";
  	public static String AESIV  = "5621616F5237517A21634F5337785351";
  
  	public static String getPlatform() {
  		return System.getProperty("sun.arch.data.model");
  	}
  
  	public static void writeToFile(InputStream is, File f) throws IOException {
  		FileUtils.copyInputStreamToFile(is, f);
  	}
  	
  	public static void deleteDirectory(File directory) throws IOException {
  		FileUtils.deleteDirectory(directory);
  	}
  	
  	public static String getName() {
  		  String os = "";
  		  if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {
  		    os = "windows";
  		  } else if (System.getProperty("os.name").toLowerCase().indexOf("linux") > -1) {
  		    os = "linux";
  		  } else if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) {
  		    os = "mac";
  		  }
  		  return os;
  	}
  
  	public static String getTimeStamp() {
      	DateFormat df = new SimpleDateFormat("yyyy-MM-dd");  
      	df.setTimeZone( TimeZone.getTimeZone("PST"));  
      	String date = ( df.format(new Date()));    
      	DateFormat df1 = new SimpleDateFormat("hh-mm-ss") ;    
      	df1.setTimeZone( TimeZone.getDefault()) ;  
      	String time = ( df1.format(new Date()));
      	return date+"_"+time;
      }
  
  	public static void unyaffs(String yaffsfile, String folder) {
  		try {
  			File f = new File(folder);
  			if (!f.exists()) f.mkdirs();
  			else if (f.isFile()) throw new IOException("destination must be a folder");
  			ProcessBuilderWrapper command = new ProcessBuilderWrapper(new String[] {getWorkDir()+File.separator+"x10flasher_lib"+File.separator+"unyaffs",yaffsfile,folder},false);
  		}
  		catch (Exception e) {
  			log.warn("Failed : "+e.getMessage());
  		}
  	}
  	
  	public static String getPathAdb() {
  		if (OS.getName().equals("windows"))
  			return new File(System.getProperty("user.dir")+File.separator+"x10flasher_native"+File.separator+"adb.exe").getAbsolutePath();
  		else
  			return new File(System.getProperty("user.dir")+File.separator+"x10flasher_native"+File.separator+"adb").getAbsolutePath();
  	}
  	
  	public static String getPathBin2Sin() {
  		if (OS.getName().equals("windows"))
  			return new File(System.getProperty("user.dir")+File.separator+"x10flasher_native"+File.separator+"bin2sin.exe").getAbsolutePath();
  		else
  			return new File(System.getProperty("user.dir")+File.separator+"x10flasher_native"+File.separator+"bin2sin").getAbsolutePath();
  	}
  
  	public static String getPathBin2Elf() {
  		String fsep = OS.getFileSeparator();
  		if (OS.getName().equals("windows"))
  			return new File(System.getProperty("user.dir")+fsep+"x10flasher_native"+fsep+"bin2elf.exe").getAbsolutePath();
  		else
  			return new File(System.getProperty("user.dir")+fsep+"x10flasher_native"+fsep+"bin2elf").getAbsolutePath();
  	}
  
  	public static String getPathXperiFirmWrapper() {
  		String fsep = OS.getFileSeparator();
  		return new File(System.getProperty("user.dir")+fsep+"x10flasher_native"+fsep+"xperifirm").getAbsolutePath();
  	}
  
  	public static String getPathFastBoot() {
  		String fsep = OS.getFileSeparator();
  	   if (OS.getName().equals("windows"))
  		   return new File(System.getProperty("user.dir")+fsep+"x10flasher_native"+fsep+"fastboot.exe").getAbsolutePath();
  	   else
  		   return new File(System.getProperty("user.dir")+fsep+"x10flasher_native"+fsep+"fastboot").getAbsolutePath();
  	}
  	
  	public static String getPathXperiFirm() {
  		String fsep = OS.getFileSeparator();
  		return new File(OS.getFolderUserFlashtool()+fsep+"XperiFirm.exe").getAbsolutePath();
  	}
  	
  	public static String getWorkDir() {
  		return System.getProperty("user.dir");
  	}
  
  	public static String getSHA256(byte[] array) {
  		try {
  			MessageDigest digest = MessageDigest.getInstance("SHA-256");
  			digest.update(array, 0, array.length);
  			byte[] sha256 = digest.digest();
  			return HexDump.toHex(sha256);
  		}
  		catch(NoSuchAlgorithmException nsa) {
  			throw new RuntimeException("Unable to process file for SHA-256", nsa);
  		}
  	}
  
  	public static String getSHA1(byte[] array) {
  		try {
  			MessageDigest digest = MessageDigest.getInstance("SHA-1");
  			digest.update(array, 0, array.length);
  			byte[] sha1 = digest.digest();
  			return HexDump.toHex(sha1);
  		}
  		catch(NoSuchAlgorithmException nsa) {
  			throw new RuntimeException("Unable to process file for SHA-256", nsa);
  		}
  	}
  
  	public static String getSHA256(File f) {
  		byte[] buffer = new byte[8192];
  		int read = 0;
  		InputStream is=null;
  		try {
  			MessageDigest digest = MessageDigest.getInstance("SHA-256");
  			is = new FileInputStream(f);
  			while( (read = is.read(buffer)) > 0) {
  				digest.update(buffer, 0, read);
  			}		
  			byte[] sha256 = digest.digest();
  			BigInteger bigInt = new BigInteger(1, sha256);
  			String output = bigInt.toString(32);
  			return output.toUpperCase();
  		}
  		catch(IOException e) {
  			throw new RuntimeException("Unable to process file for SHA-256", e);
  		}
  		catch(NoSuchAlgorithmException nsa) {
  			throw new RuntimeException("Unable to process file for SHA-256", nsa);
  		}
  		finally {
  			try {
  				is.close();
  			}
  			catch(IOException e) {
  				throw new RuntimeException("Unable to close input stream for SHA-256 calculation", e);
  			}
  		}
  	}
  	
  	public static void copyfile(String srFile, String dtFile){
  		  try{
  		  File f1 = new File(srFile);
  		  File f2 = new File(dtFile);
  		  if (!f1.getAbsolutePath().equals(f2.getAbsolutePath())) {
  			  InputStream in = new FileInputStream(f1);
  			  
  			  //For Append the file.
  			//  OutputStream out = new FileOutputStream(f2,true);
  	
  			  //For Overwrite the file.
  			  OutputStream out = new FileOutputStream(f2);
  	
  			  byte[] buf = new byte[1024];
  			  int len;
  			  while ((len = in.read(buf)) > 0){
  			  out.write(buf, 0, len);
  			  }
  			  in.close();
  			  out.close();
  		  }
  		  }
  		  catch(FileNotFoundException ex){
  			  log.error(ex.getMessage() + " in the specified directory.");
  		  }
  		  catch(IOException e){
  			  log.error(e.getMessage());  
  		  }
  	}
  	
  	public static long getAlder32(File f) {
  		try {
  			FileInputStream inputStream = new FileInputStream(f);
  			Adler32 adlerChecksum = new Adler32();
  			CheckedInputStream cinStream = new CheckedInputStream(inputStream, adlerChecksum);
  			byte[] b = new byte[128];
  			while (cinStream.read(b) >= 0) {
  			}
  			long checksum = cinStream.getChecksum().getValue();
  			cinStream.close();
  			return checksum;
  		} catch (IOException e) {
  			return 0;
  		}
  	}
  
  	public static String getMD5(File f) {
  		byte[] buffer = new byte[8192];
  		int read = 0;
  		InputStream is=null;
  		try {
  			MessageDigest digest = MessageDigest.getInstance("MD5");
  			is = new FileInputStream(f);
  			while( (read = is.read(buffer)) > 0) {
  				digest.update(buffer, 0, read);
  			}		
  			byte[] md5sum = digest.digest();
  			BigInteger bigInt = new BigInteger(1, md5sum);
  			String output = bigInt.toString(16);
  			return String.format("%32s", output).replace(' ', '0');
  		}
  		catch(IOException e) {
  			throw new RuntimeException("Unable to process file for MD5", e);
  		}
  		catch(NoSuchAlgorithmException nsa) {
  			throw new RuntimeException("Unable to process file for MD5", nsa);
  		}
  		finally {
  			try {
  				is.close();
  			}
  			catch(IOException e) {
  				throw new RuntimeException("Unable to close input stream for MD5 calculation", e);
  			}
  		}
  	}
  	
  	public static String getVersion() {
  		return System.getProperty("os.version");
  	}
  
  	public static String getFileSeparator() {
  		return System.getProperty("file.separator");
  	}
  	
  	public static String getWinDir() {
  		if (System.getenv("WINDIR")==null) return System.getenv("SYSTEMROOT");
  		if (System.getenv("WINDIR").length()==0) return System.getenv("SYSTEMROOT");
  		return System.getenv("WINDIR");
  	}
  	
  	public static String getSystem32Dir() {
  		return getWinDir()+getFileSeparator()+"System32";
  	}
  
  	public static Collection<File> listFileTree(File dir) {
  	    Set<File> fileTree = new HashSet<File>();
  	    for (File entry : dir.listFiles()) {
  	        if (entry.isFile()) fileTree.add(entry);
  	        else {
  	        	fileTree.addAll(listFileTree(entry));
  	        	fileTree.add(entry);
  	        }
  	    }
  	    return fileTree;
  	}
  
  	
  	public static byte[] copyBytes(byte[] arr, int length)
  	{
  		byte[] newArr = null;
  		if (arr.length == length)
  			newArr = arr;
  		else
  		{
  			newArr = new byte[length];
  			for (int i = 0; i < length; i++)
  			{
  				newArr[i] = (byte) arr[i];
  			}
  		}
  		return newArr;
  	}
  
  	public static RandomAccessFile generateEmptyFile(String fname, long size, byte fill) {
  		// To fill the empty file with FF values
  		if ((size/1024/1024)<0)
  			log.info("File size : "+size/1024/1024+" Mb");
  		else
  			log.info("File size : "+size/1024+" Kb");
  		try {
  			byte[] empty = new byte[65*1024];
  			for (int i=0; i<empty.length;i++)
  				empty[i] = fill;		
  			// Creation of empty file
  			File f = new File(fname);
  			f.delete();
  			FileOutputStream fout = new FileOutputStream(f);
  			LogProgress.initProgress(size/empty.length+size%empty.length);
  			for (long i = 0; i<size/empty.length; i++) {
  				fout.write(empty);
  				LogProgress.updateProgress();
  			}
  			for (long i = 0; i<size%empty.length; i++) {
  				fout.write(fill);
  				LogProgress.updateProgress();
  			}
  			LogProgress.initProgress(0);
  			fout.flush();
  			fout.close();
  			RandomAccessFile fo = new RandomAccessFile(f,"rw");
  			return fo;
  		}
  		catch (Exception e) {
  			e.printStackTrace();
  			log.error(e.getMessage());
  			return null;
  		}
  	}
  
  	public static String padRight(String s, int n) {
  	     return String.format("%1$-" + n + "s", s);  
  	}
  
  	public static void ZipExplodeToFolder(String zippath) throws FileNotFoundException, IOException  {
  		byte buffer[] = new byte[10240];
  		File zipfile = new File(zippath);
  		File outfolder = new File(zipfile.getParentFile().getAbsolutePath()+File.separator+zipfile.getName().replace(".zip", "").replace(".ZIP", ""));
  		outfolder.mkdirs();
  		ZipInputStream zis = new ZipInputStream(new FileInputStream(zippath));
  		ZipEntry ze = zis.getNextEntry();
  		while (ze != null) {
  			FileOutputStream fout = new FileOutputStream(outfolder.getAbsolutePath()+File.separator+ze.getName());
  			int len;
  			while ((len=zis.read(buffer))>0) {
  				fout.write(buffer,0,len);
  			}
  			fout.close();
  			ze=zis.getNextEntry();
  		}
  		zis.closeEntry();
  		zis.close();
  	}
  
  	public static void ZipExplodeToHere(String zippath) throws FileNotFoundException, IOException  {
  		byte buffer[] = new byte[10240];
  		File zipfile = new File(zippath);
  		File outfolder = new File(zipfile.getParentFile().getAbsolutePath());
  		outfolder.mkdirs();
  		ZipInputStream zis = new ZipInputStream(new FileInputStream(zippath));
  		ZipEntry ze = zis.getNextEntry();
  		while (ze != null) {
  			FileOutputStream fout = new FileOutputStream(outfolder.getAbsolutePath()+File.separator+ze.getName());
  			int len;
  			while ((len=zis.read(buffer))>0) {
  				fout.write(buffer,0,len);
  			}
  			fout.close();
  			ze=zis.getNextEntry();
  		}
  		zis.closeEntry();
  		zis.close();
  	}
  
  	public static void ZipExplodeTo(InputStream stream, File outfolder) throws FileNotFoundException, IOException  {
  		byte buffer[] = new byte[10240];
  		outfolder.mkdirs();
  		ZipInputStream zis = new ZipInputStream(stream);
  		ZipEntry ze = zis.getNextEntry();
  		while (ze != null) {
  			FileOutputStream fout = new FileOutputStream(outfolder.getAbsolutePath()+File.separator+ze.getName());
  			int len;
  			while ((len=zis.read(buffer))>0) {
  				fout.write(buffer,0,len);
  			}
  			fout.close();
  			ze=zis.getNextEntry();
  		}
  		zis.closeEntry();
  		zis.close();
  		stream.close();
  	}
  
  	public static void viewAllThreads() {
  
  
      	// Walk up all the way to the root thread group
          ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
          ThreadGroup parent;
          while ((parent = rootGroup.getParent()) != null) {
              rootGroup = parent;
          }
  
          listThreads(rootGroup, "");
      }
  
  
      // List all threads and recursively list all subgroup
      public static void listThreads(ThreadGroup group, String indent) {
          System.out.println(indent + "Group[" + group.getName() + 
          		":" + group.getClass()+"]");
          int nt = group.activeCount();
          Thread[] threads = new Thread[nt*2 + 10]; //nt is not accurate
          nt = group.enumerate(threads, false);
  
          // List every thread in the group
          for (int i=0; i<nt; i++) {
              Thread t = threads[i];
              System.out.println(indent + "  Thread[" + t.getName() 
              		+ ":" + t.getClass() + "]");
          }
  
          // Recursively list all subgroups
          int ng = group.activeGroupCount();
          ThreadGroup[] groups = new ThreadGroup[ng*2 + 10];
          ng = group.enumerate(groups, false);
  
          for (int i=0; i<ng; i++) {
              listThreads(groups[i], indent + "  ");
          }
      }
      
      public static Manifest getManifest( Class<?> cl ) {
          InputStream inputStream = null;
          try {
            URLClassLoader classLoader = (URLClassLoader)cl.getClassLoader();
            String classFilePath = cl.getName().replace('.','/')+".class";
            URL classUrl = classLoader.getResource(classFilePath);
            if ( classUrl==null ) return null;
            String classUri = classUrl.toString();
            if ( !classUri.startsWith("jar:") ) return null;
            int separatorIndex = classUri.lastIndexOf('!');
            if ( separatorIndex<=0 ) return null;
            String manifestUri = classUri.substring(0,separatorIndex+2)+"META-INF/MANIFEST.MF";
            URL url = new URL(manifestUri);
            inputStream = url.openStream();
            return new Manifest( inputStream );
          } catch ( Throwable e ) {
            // handle errors
            //...
            return null;
          } finally {
            if ( inputStream!=null ) {
              try {
                inputStream.close();
              } catch ( Throwable e ) {
                // ignore
              }
            }
          }
        }
      
      public static String getChannel() {
  	    Enumeration resEnum;
  	    try {
  	        resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
  	        while (resEnum.hasMoreElements()) {
  	            try {
  	                URL url = (URL)resEnum.nextElement();
  	                InputStream is = url.openStream();
  	                if (is != null) {
  	                    Manifest manifest = new Manifest(is);
  	                    Attributes mainAttribs = manifest.getMainAttributes();
  	                    String version = mainAttribs.getValue("Internal-Channel");
  	                    if(version != null) {
  	                        return version;
  	                    }
  	                }
  	            }
  	            catch (Exception e) {
  	                // Silently ignore wrong manifests on classpath?
  	            }
  	        }
  	    } catch (IOException e1) {
  	        // Silently ignore wrong manifests on classpath?
  	    }
  	    return null; 
      }
  
      public static void decrypt(File infile) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
  		byte[] buf = new byte[1024];
  		try {
  			if (!infile.getName().endsWith(".enc")) throw new IOException("Bad filename");
  			RC4InputStream in = new RC4InputStream(new FileInputStream(infile));
  			File outfile = new File(infile.getAbsolutePath().substring(0, infile.getAbsolutePath().length()-4));
  	        OutputStream out = new FileOutputStream(outfile);
  	        int len;
  	        while((len = in.read(buf)) >= 0) {
  	            out.write(buf, 0, len);
  	        }
  	        out.flush();
  	        out.close();
  	        in.close();
  	      } catch(IOException e) {
  	    	  e.printStackTrace();
  	      }
  	}
  
  	  public static void encrypt(File infile) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
  		  byte[] buf = new byte[1024];
  	      try {
  	    	  String outname = infile.getAbsolutePath()+".enc";
  	    	  FileInputStream in = new FileInputStream(infile);
  	    	  RC4OutputStream out = new RC4OutputStream(new FileOutputStream(outname));
  	    	  int len;
  	    	  while((len = in.read(buf)) >= 0) {
  	    		  if (len > 0)
  	    			  out.write(buf, 0, len);
  	    	  }
  	    	  out.flush();
  	    	  out.close();
  	    	  in.close();
  	      } catch(IOException e) {
  	        e.printStackTrace();
  	      }
  	  }
  
  	  public static String getUserHome() {
  		  	return System.getProperty("user.home");
   	  }
  
  	  public static String getFolderCustom() {
  		  return getWorkDir()+File.separator+"custom";
  	  }
  
  	  public static String getFolderDevices() {
  			return OS.getFolderUserFlashtool()+File.separator+"devices";
  	  }
  
  	  public static String getFolderUserFlashtool() {
  		  String folder = GlobalConfig.getProperty("user.flashtool");
  		  if (folder==null) folder = getUserHome()+File.separator+".flashTool";
  		  new File(folder).mkdirs();
  		  GlobalConfig.setProperty("user.flashtool", folder);
  		  return folder;
  	  }
  
  	  public static String getFolderFirmwares() {
  		  new File(OS.getFolderUserFlashtool()+File.separator+"firmwares").mkdirs();
  		  return OS.getFolderUserFlashtool()+File.separator+"firmwares";
  	  }
  
  	  public static String getFolderFirmwaresPrepared() {
  		  new File(OS.getFolderFirmwares()+File.separator+"prepared").mkdirs();
  		  return OS.getFolderFirmwares()+File.separator+"prepared";
  	  }
  
  	  public static String getFolderFirmwaresDownloaded() {
  		  new File(OS.getFolderFirmwares()+File.separator+"Downloads").mkdirs();
  		  return OS.getFolderFirmwares()+File.separator+"Downloads";
  	  }
  
  	  public static String getFolderFirmwaresScript() {
  		  new File(OS.getFolderFirmwares()+File.separator+"Scripts").mkdirs();
  		  return OS.getFolderFirmwares()+File.separator+"Scripts";
  	  }
  
  	  public static String getFolderFirmwaresSinExtracted() {
  		  new File(OS.getFolderFirmwares()+File.separator+"sinExtracted").mkdirs();
  		  return OS.getFolderFirmwares()+File.separator+"sinExtracted";
  	  }
  
  	  public static String getFolderMyDevices() {
  		  new File(OS.getFolderUserFlashtool()+File.separator+"mydevices").mkdirs();
  		  return OS.getFolderUserFlashtool()+File.separator+"mydevices";		  
  	  }
  
  	  public static String getFolderRegisteredDevices() {
  		  new File(OS.getFolderUserFlashtool()+File.separator+"registeredDevices").mkdirs();
  		  return OS.getFolderUserFlashtool()+File.separator+"registeredDevices";
  	  }
  
  	  public static void unpackArchive(URL url, File targetDir) throws IOException {
  	      if (!targetDir.exists()) {
  	          targetDir.mkdirs();
  	      }
  	      OS.ZipExplodeTo(new BufferedInputStream(url.openStream()),new File(OS.getFolderUserFlashtool()));
  	  }
  	  
  	  public static void copyInputStream(InputStream in, OutputStream out) throws IOException {
  	      byte[] buffer = new byte[1024];
  	      int len = in.read(buffer);
  	      while (len >= 0) {
  	          out.write(buffer, 0, len);
  	          len = in.read(buffer);
  	      }
  	      in.close();
  	      out.close();
  	  }
  
  }