/** @throws IOException java.util.jar.JarFile#getJarEntry(java.lang.String) */
  public void test_getEntryLjava_lang_String() throws IOException {
    try {
      Support_Resources.copyFile(resources, null, jarName);
      JarFile jarFile = new JarFile(new File(resources, jarName));
      assertEquals("Error in returned entry", 311, jarFile.getEntry(entryName).getSize());
      jarFile.close();
    } catch (Exception e) {
      fail("Exception during test: " + e.toString());
    }

    Support_Resources.copyFile(resources, null, jarName);
    JarFile jarFile = new JarFile(new File(resources, jarName));
    Enumeration<JarEntry> enumeration = jarFile.entries();
    assertTrue(enumeration.hasMoreElements());
    while (enumeration.hasMoreElements()) {
      JarEntry je = enumeration.nextElement();
      jarFile.getEntry(je.getName());
    }

    enumeration = jarFile.entries();
    assertTrue(enumeration.hasMoreElements());
    JarEntry je = enumeration.nextElement();
    try {
      jarFile.close();
      jarFile.getEntry(je.getName());
      // fail("IllegalStateException expected.");
    } catch (IllegalStateException ee) { // Per documentation exception
      // may be thrown.
      // expected
    }
  }
  /**
   * @return an input stream for the this file in the jar. Closing this stream also closes the jar
   *     file this stream comes from.
   */
  public InputStream getResourceAsStream(String fileName) throws PluginParseException {
    Validate.notNull(fileName, "The file name must not be null");
    final JarFile jar;
    try {
      jar = new JarFile(jarFile);
    } catch (IOException e) {
      throw new PluginParseException("Cannot open JAR file for reading: " + jarFile, e);
    }

    ZipEntry entry = jar.getEntry(fileName);
    if (entry == null) {
      return null;
    }

    InputStream descriptorStream;
    try {
      descriptorStream =
          new BufferedInputStream(jar.getInputStream(entry)) {

            // because we do not expose a handle to the jar file this stream is associated with, we
            // need to make sure
            // we explicitly close the jar file when we're done with the stream (else we'll have a
            // file handle leak)
            public void close() throws IOException {
              super.close();
              jar.close();
            }
          };
    } catch (IOException e) {
      throw new PluginParseException(
          "Cannot retrieve " + fileName + " from plugin JAR [" + jarFile + "]", e);
    }
    return descriptorStream;
  }
示例#3
0
  public void connect() throws IOException {
    if (!connected) {
      String path = url.getPath();
      Matcher matcher = urlPattern.matcher(path);
      if (matcher.matches()) {
        path = matcher.group(1);
        String subPath = matcher.group(2);
        JarURLConnection jarURLConnection =
            (JarURLConnection) new URL("jar:" + path).openConnection();
        inputStream = jarURLConnection.getInputStream();
        if (subPath.isEmpty() == false) {
          JarFile jar = retrieve(new URL(path), inputStream);
          String[] nodes = nestingSeparatorPattern.split(subPath);
          int i;
          for (i = 0; i < nodes.length - 1; i++) {
            path += "!/" + nodes[i];
            jar = retrieve(new URL(path), inputStream);
          }
          ZipEntry entry = jar.getEntry(nodes[i]);
          entrySize = entry.getSize();
          inputStream = jar.getInputStream(entry);
        }
      } else {
        throw new MalformedURLException("Invalid JAP URL path: " + path);
      }

      connected = true;
    }
  }
示例#4
0
  protected boolean jarUpToDate(String source, String target, boolean verbose) {
    JarFile targetJar, sourceJar;

    try {
      targetJar = new JarFile(target);
    } catch (IOException e) {
      if (verbose) err.println(target + " does not exist yet");
      return false;
    }
    try {
      sourceJar = new JarFile(source);
    } catch (IOException e) {
      return true;
    }

    for (JarEntry entry : Collections.list(sourceJar.entries())) {
      JarEntry other = (JarEntry) targetJar.getEntry(entry.getName());
      if (other == null) {
        if (verbose) err.println(target + " lacks the file " + entry.getName());
        return false;
      }
      if (entry.getTime() > other.getTime()) {
        if (verbose) err.println(target + " is not " + "up-to-date because of " + entry.getName());
        return false;
      }
    }
    try {
      targetJar.close();
      sourceJar.close();
    } catch (IOException e) {
    }

    return true;
  }
示例#5
0
 public byte[] read(String path) throws IOException {
   ZipEntry entry = m_source.getEntry(getInternalPath(path));
   if (entry == null) {
     throw new IOException("Jar Entry is not found for class " + path + ".");
   }
   return Streams.readBytes(m_source.getInputStream(entry));
 }
示例#6
0
 @SuppressWarnings("unchecked")
 private static void loadBots() {
   logger.info("Loading available bots");
   final Collection<File> botJars =
       FileUtils.listFiles(botsDirecotry, new String[] {"jar"}, false);
   for (File botJar : botJars) {
     try {
       JarFile jar = new JarFile(botJar);
       ZipEntry entry = jar.getEntry("bot.yml");
       if (entry == null) throw new RuntimeException("Bot has no bot.yml file!");
       InputStream is = jar.getInputStream(entry);
       YAMLNode n = new YAMLNode(new Yaml().loadAs(is, Map.class), true);
       String mainClass = n.getString("mainClass");
       String type = n.getString("type");
       URLClassLoader loader =
           new URLClassLoader(new URL[] {botJar.toURI().toURL()}, Chatty.class.getClassLoader());
       BOT_INFO.put(type, new BotClassInfo(loader.loadClass(mainClass).asSubclass(Bot.class), n));
       logger.info("Loaded bot '{}'", type);
     } catch (Exception e) {
       logger.error("Failed to load bot from {}", botJar.getName());
       e.printStackTrace();
     }
   }
   logger.info("Loaded {} bot(s)", BOT_INFO.size());
 }
示例#7
0
 @Test
 public void getEntryTime() throws Exception {
   java.util.jar.JarFile jdkJarFile = new java.util.jar.JarFile(this.rootJarFile);
   assertThat(this.jarFile.getEntry("META-INF/MANIFEST.MF").getTime())
       .isEqualTo(jdkJarFile.getEntry("META-INF/MANIFEST.MF").getTime());
   jdkJarFile.close();
 }
示例#8
0
 public static boolean isJarDirectory(String path) {
   if (isJarFile()) {
     try {
       JarFile jar = new JarFile(getJarFile());
       ZipEntry entry = jar.getEntry(path);
       return entry != null && entry.isDirectory();
     } catch (Exception e) {
       e.printStackTrace();
     }
   } else {
     try {
       URL url = FileUtilities.class.getResource("/" + path);
       if (url != null) {
         URI uri = url.toURI();
         File classpath = new File(uri);
         return classpath.isDirectory();
       } else {
         return false;
       }
     } catch (Exception e) {
       e.printStackTrace();
     }
   }
   return false;
 }
示例#9
0
 @SuppressWarnings("unchecked")
 private static void startPlugins() {
   logger.info("Loading available plugins");
   final Collection<File> botJars =
       FileUtils.listFiles(pluginsDirecotry, new String[] {"jar"}, false);
   for (File botJar : botJars) {
     try {
       JarFile jar = new JarFile(botJar);
       ZipEntry ze = jar.getEntry("plugin.yml");
       if (ze == null) throw new RuntimeException("Plugin has no plugin.yml file!");
       InputStream is = jar.getInputStream(ze);
       YAMLNode n = new YAMLNode(new Yaml().loadAs(is, Map.class), true);
       String mainClass = n.getString("mainClass");
       String name = n.getString("name");
       URLClassLoader loader =
           new URLClassLoader(new URL[] {botJar.toURI().toURL()}, Chatty.class.getClassLoader());
       Plugin plugin = loader.loadClass(mainClass).asSubclass(Plugin.class).newInstance();
       plugin.start();
       PLUGIN_INFO.put(name, new PluginInfo(name, plugin, n));
       logger.info("Loaded plugin '{}'", name);
     } catch (Exception e) {
       logger.error("Failed to load plugin from {}", botJar.getName());
       e.printStackTrace();
     }
   }
   logger.info("Loaded {} plugin(s)", PLUGIN_INFO.size());
 }
示例#10
0
 @SuppressWarnings("resource")
 public InputStream getInputStream() throws IOException {
   // 注:JarFile与File的设计是不一样的,File相当于C#的FileInfo,只持有信息,
   // 而JarFile构造时即打开流,所以每次读取数据时,重新new新的实例,而不作为属性字段持有。
   JarFile jarFile = new JarFile(file);
   return jarFile.getInputStream(jarFile.getEntry(getName()));
 }
 private void determineNameMapping(JarFile paramJarFile, Set paramSet, Map paramMap)
     throws IOException {
   InputStream localInputStream =
       paramJarFile.getInputStream(paramJarFile.getEntry("META-INF/INDEX.JD"));
   if (localInputStream == null) handleException("jardiff.error.noindex", null);
   LineNumberReader localLineNumberReader =
       new LineNumberReader(new InputStreamReader(localInputStream, "UTF-8"));
   String str = localLineNumberReader.readLine();
   if ((str == null) || (!str.equals("version 1.0")))
     handleException("jardiff.error.badheader", str);
   while ((str = localLineNumberReader.readLine()) != null) {
     List localList;
     if (str.startsWith("remove")) {
       localList = getSubpaths(str.substring("remove".length()));
       if (localList.size() != 1) handleException("jardiff.error.badremove", str);
       paramSet.add(localList.get(0));
       continue;
     }
     if (str.startsWith("move")) {
       localList = getSubpaths(str.substring("move".length()));
       if (localList.size() != 2) handleException("jardiff.error.badmove", str);
       if (paramMap.put(localList.get(1), localList.get(0)) != null)
         handleException("jardiff.error.badmove", str);
       continue;
     }
     if (str.length() <= 0) continue;
     handleException("jardiff.error.badcommand", str);
   }
   localLineNumberReader.close();
   localInputStream.close();
 }
示例#12
0
  /**
   * Take the name of a jar file and extract the plugin.xml file, if possible, to a temporary file.
   *
   * @param f The jar file to extract from.
   * @return a temporary file to which the plugin.xml file has been copied.
   */
  public static File unpackPluginXML(File f) {
    InputStream in = null;
    OutputStream out = null;

    try {
      JarFile jar = new JarFile(f);
      ZipEntry entry = jar.getEntry(PLUGIN_XML_FILE);
      if (entry == null) {
        return null;
      }
      File dest = File.createTempFile("jabref_plugin", ".xml");
      dest.deleteOnExit();

      in = new BufferedInputStream(jar.getInputStream(entry));
      out = new BufferedOutputStream(new FileOutputStream(dest));
      byte[] buffer = new byte[2048];
      for (; ; ) {
        int nBytes = in.read(buffer);
        if (nBytes <= 0) break;
        out.write(buffer, 0, nBytes);
      }
      out.flush();
      return dest;
    } catch (IOException ex) {
      ex.printStackTrace();
      return null;
    } finally {
      try {
        if (out != null) out.close();
        if (in != null) in.close();
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
  }
 /**
  * This is difficult to implement and close out resources underneath. Delaying until someone
  * actually requests this.
  *
  * <p>If we actually contain the entry throw UnsupportedOperationException, else return null in
  * case another classloader can handle this.
  */
 @Override
 public URL getResource(String name) {
   for (JarFile j : _jars) {
     ZipEntry entry = j.getEntry(name);
     if (entry != null) {
       throw new UnsupportedOperationException(
           ErrorMessages.GET_RESOURCE_NOT_IMPLEMENTED + name); // $NON-NLS-1$
     }
   }
   return null;
 }
示例#14
0
 public long getLength() {
   try {
     JarFile jarFile = new JarFile(file);
     try {
       return jarFile.getEntry(getName()).getSize();
     } finally {
       jarFile.close();
     }
   } catch (IOException e) {
     return super.getLength();
   }
 }
示例#15
0
  private String getSubsonicBuildNumber() {
    File war = new File(getWar());
    InputStream in = null;
    try {
      if (war.isFile()) {
        JarFile jar = new JarFile(war);
        ZipEntry entry = jar.getEntry("WEB-INF\\classes\\build_number.txt");
        if (entry == null) {
          entry = jar.getEntry("WEB-INF/classes/build_number.txt");
        }
        in = jar.getInputStream(entry);
      } else {
        in = new FileInputStream(war.getPath() + "/WEB-INF/classes/build_number.txt");
      }
      return IOUtils.toString(in);

    } catch (Exception x) {
      System.err.println("Failed to resolve build number from WAR " + war + ": " + x);
      return null;
    } finally {
      IOUtils.closeQuietly(in);
    }
  }
 private Path findJarWithSecureStore(SitePaths sitePaths, String secureStoreClass)
     throws IOException {
   List<Path> jars = SiteLibraryLoaderUtil.listJars(sitePaths.lib_dir);
   String secureStoreClassPath = secureStoreClass.replace('.', '/') + ".class";
   for (Path jar : jars) {
     try (JarFile jarFile = new JarFile(jar.toFile())) {
       ZipEntry entry = jarFile.getEntry(secureStoreClassPath);
       if (entry != null) {
         return jar;
       }
     } catch (IOException e) {
       log.error(e.getMessage(), e);
     }
   }
   return null;
 }
示例#17
0
 private boolean testExecutionMode() {
   executionMode = false;
   JarFile file = null;
   try {
     file = new JarFile(getTangaraPath());
     ZipEntry entry = file.getEntry(EXECUTION_PROPERTIES_FILENAME);
     if (entry != null) {
       executionMode = true;
       System.out.println("execution mode detected");
       Properties executionProperties = new Properties();
       InputStream ips = ClassLoader.getSystemResourceAsStream(EXECUTION_PROPERTIES_FILENAME);
       executionProperties.load(ips);
       if (executionProperties.containsKey("main-program")) {
         String mainTangaraFile = executionProperties.getProperty("main-program");
         System.out.println("main tangara file: " + mainTangaraFile);
         properties.setProperty("main-program", mainTangaraFile);
       } else {
         System.err.println("error : main program not specified");
       }
       if (executionProperties.containsKey("language")) {
         String language = executionProperties.getProperty("language");
         properties.setProperty("language", language);
         System.out.println("language: " + language);
       } else {
         System.err.println("error : language not specified");
       }
       if (executionProperties.containsKey("resources")) {
         String resources = executionProperties.getProperty("resources");
         properties.setProperty("program.resources", resources);
         System.out.println("resources: " + resources);
       } else {
         System.err.println("error : resources not specified");
       }
     }
   } catch (IOException e) {
     e.printStackTrace();
   } finally {
     if (file != null) {
       try {
         file.close();
       } catch (IOException e) {
         System.err.println("error while closing tangara JAR file");
       }
     }
   }
   return executionMode;
 }
示例#18
0
  private static Properties loadBuildProperties() {
    Properties properties = new Properties();

    Manifest manifest = null;
    JarFile jar = null;
    try {
      URL url = BuildInfo.class.getProtectionDomain().getCodeSource().getLocation();
      File file = new File(url.toURI());
      jar = new JarFile(file);
      ZipEntry entry = jar.getEntry("META-INF/build-stamp.properties");
      if (entry != null) {
        try (InputStream stream = jar.getInputStream(entry)) {
          properties.load(stream);
        }
      }

      manifest = jar.getManifest();
    } catch (IllegalArgumentException
        | IOException
        | NullPointerException
        | URISyntaxException ignored) {
    } finally {
      if (jar != null) {
        try {
          jar.close();
        } catch (IOException e) {
          // ignore
        }
      }
    }

    if (manifest == null) {
      return properties;
    }

    try {
      Attributes attributes = manifest.getAttributes("Build-Info");
      Set<Entry<Object, Object>> entries = attributes.entrySet();
      for (Entry<Object, Object> e : entries) {
        properties.put(String.valueOf(e.getKey()), String.valueOf(e.getValue()));
      }
    } catch (NullPointerException e) {
      // Fall through
    }

    return properties;
  }
示例#19
0
  public synchronized void connect() throws IOException {
    // Call is ignored if already connected.
    if (connected) return;

    jar_url = getJarFileURL();
    jar_file = JarFileCache.get(jar_url, useCaches);
    String entry_name = getEntryName();

    if (entry_name != null && !entry_name.equals("")) {
      jar_entry = (JarEntry) jar_file.getEntry(entry_name);

      if (jar_entry == null)
        throw new FileNotFoundException("No entry for " + entry_name + " exists.");
    }

    connected = true;
  }
示例#20
0
  /**
   * Create a default configuration file from the .jar.
   *
   * @param name
   */
  protected void createDefaultConfiguration(String name) {
    File actual = new File(getDataFolder(), name);
    if (!actual.exists()) {
      InputStream input = null;
      try {
        JarFile file = new JarFile(getFile());
        ZipEntry copy = file.getEntry("defaults/" + name);
        if (copy == null) throw new FileNotFoundException();
        input = file.getInputStream(copy);
      } catch (IOException e) {
        getLogger().severe("Unable to read default configuration: " + name);
      }
      if (input != null) {
        FileOutputStream output = null;

        try {
          output = new FileOutputStream(actual);
          byte[] buf = new byte[8192];
          int length = 0;
          while ((length = input.read(buf)) > 0) {
            output.write(buf, 0, length);
          }

          getLogger().info("Default configuration file written: " + name);
        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          try {
            if (input != null) {
              input.close();
            }
          } catch (IOException e) {
          }

          try {
            if (output != null) {
              output.close();
            }
          } catch (IOException e) {
          }
        }
      }
    }
  }
示例#21
0
  /**
   * Jarファイル内から直接 messages_en.yml を読み込み、 defaultMessagesとしてロードする。
   *
   * @param _jar jarファイル
   * @param _configFolder コンフィグフォルダ
   * @param lang デフォルト言語
   */
  protected static void initialize(File _jar, File _configFolder, String lang) {

    jar = _jar;
    configFolder = _configFolder;

    // コンフィグフォルダにメッセージファイルがまだ無いなら、コピーしておく
    for (String filename : new String[] {"messages_en.yml", "messages_ja.yml"}) {
      File file = new File(configFolder, filename);
      if (!file.exists()) {
        Utility.copyFileFromJar(jar, file, filename, false);
      }
    }

    // デフォルトメッセージを、jarファイル内からロードする
    defaultMessages = new YamlConfiguration();
    JarFile jarFile = null;
    try {
      jarFile = new JarFile(jar);
      ZipEntry zipEntry = jarFile.getEntry(String.format("messages_%s.yml", lang));
      InputStream inputStream = jarFile.getInputStream(zipEntry);
      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
      String line;
      while ((line = reader.readLine()) != null) {
        if (line.contains(":") && !line.startsWith("#")) {
          String key = line.substring(0, line.indexOf(":")).trim();
          String value = line.substring(line.indexOf(":") + 1).trim();
          if (value.startsWith("'") && value.endsWith("'"))
            value = value.substring(1, value.length() - 1);
          defaultMessages.set(key, value);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (jarFile != null) {
        try {
          jarFile.close();
        } catch (IOException e) {
          // do nothing.
        }
      }
    }
  }
  @Override
  public synchronized InputStream getResourceAsStream(String name) {
    InputStream input = getParent().getResourceAsStream(name);
    if (input != null) return input;

    if (!_open) return null;

    for (JarFile j : _jars) {
      try {
        ZipEntry entry = j.getEntry(name);
        if (entry != null) {
          InputStream zipInput = j.getInputStream(entry);
          return openInputStream(zipInput);
        }
      } catch (IOException ioe) {
        TapestryCore.logError(ErrorMessages.UNABLE_TO_GET_ENTRY_FROM_JAR + j, ioe); // $NON-NLS-1$
      }
    }
    return null;
  }
  private static void copyFileFromJar(String sourceFileName, File target) {
    try {
      JarFile jar = new JarFile(privateInstance.getHome());
      ZipEntry entry = jar.getEntry(sourceFileName);

      InputStream inputStream = new BufferedInputStream(jar.getInputStream(entry));
      OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(target));
      byte[] buffer = new byte[4096];
      for (; ; ) {
        int nBytes = inputStream.read(buffer);
        if (nBytes <= 0) break;
        outputStream.write(buffer, 0, nBytes);
      }
      outputStream.flush();
      outputStream.close();
      inputStream.close();
    } catch (FileNotFoundException e) {
      JOptionPane.showMessageDialog(null, e);
    } catch (IOException e) {
      JOptionPane.showMessageDialog(null, e);
    }
  }
  private static String getStringFromJar(String resourceIdentifier) {
    StringBuilder result = new StringBuilder();
    try {
      JarFile jar = new JarFile(privateInstance.getHome());
      ZipEntry entry = jar.getEntry(resourceIdentifier);

      BufferedReader inputReader =
          new BufferedReader(new InputStreamReader(jar.getInputStream(entry)));

      String newLine;
      while ((newLine = inputReader.readLine()) != null) {
        result.append(newLine);
      }
      inputReader.close();
    } catch (FileNotFoundException e) {
      JOptionPane.showMessageDialog(null, e);
    } catch (IOException e) {
      JOptionPane.showMessageDialog(null, e);
    }

    return result.toString();
  }
 /*
  * The content of Test.class is modified, jarFile.getInputStream will not
  * throw security Exception, but it will anytime before the inputStream got
  * from getInputStream method has been read to end.
  */
 public void test_JarFile_Modified_Class() throws IOException {
   String modifiedJarName = "Modified_Class.jar";
   Support_Resources.copyFile(resources, null, modifiedJarName);
   JarFile jarFile = new JarFile(new File(resources, modifiedJarName), true);
   Enumeration<JarEntry> entries = jarFile.entries();
   while (entries.hasMoreElements()) {
     ZipEntry zipEntry = entries.nextElement();
     jarFile.getInputStream(zipEntry);
   }
   /* The content of Test.class has been tampered. */
   ZipEntry zipEntry = jarFile.getEntry("Test.class");
   InputStream in = jarFile.getInputStream(zipEntry);
   byte[] buffer = new byte[1024];
   try {
     while (in.available() > 0) {
       in.read(buffer);
     }
     fail("SecurityException expected");
   } catch (SecurityException e) {
     // desired
   }
 }
示例#26
0
  public Image getImage(String sImage) {
    Image imReturn = null;
    try {
      if (jar == null) {
        imReturn = this.toolkit.createImage(this.getClass().getClassLoader().getResource(sImage));
      } else {
        //
        BufferedInputStream bis = new BufferedInputStream(jar.getInputStream(jar.getEntry(sImage)));
        ByteArrayOutputStream buffer = new ByteArrayOutputStream(4096);
        int b;
        while ((b = bis.read()) != -1) {
          buffer.write(b);
        }
        byte[] imageBuffer = buffer.toByteArray();
        imReturn = this.toolkit.createImage(imageBuffer);
        bis.close();
        buffer.close();
      }
    } catch (IOException ex) {

    }
    return imReturn;
  }
 private void verifyKeystoreFilePresence(JarFile outputWar) {
   assertNotNull(outputWar.getEntry(WEB_INF_CLASSES + KEYSTORE_FILE_NAMES_PARAM_VALUE));
 }
 private void verifyPresenceOfInternalKeyStore(JarFile outputWar) {
   assertNotNull(outputWar.getEntry(SOAP_KEYSTORE_JAR_ENTRY_NAME));
 }
示例#29
0
  public void enhance(@Observes final BeforeDeploymentEvent event) {
    if (enhancerMethod == null) {
      LOGGER.debug("OpenJPA is not available so no deploy-time enhancement will be done");
      return;
    }

    // find persistence.xml
    final Map<String, List<String>> classesByPXml = new HashMap<String, List<String>>();
    final List<URL> usedUrls = new ArrayList<URL>(); // for fake classloader
    for (URL url : event.getUrls()) {
      final File file = URLs.toFile(url);
      if (file.isDirectory()) {
        final String pXmls = getWarPersistenceXml(url);
        if (pXmls != null) {
          feed(classesByPXml, pXmls);
        }

        usedUrls.add(url);
      } else if (file.getName().endsWith(".jar")) {
        try {
          final JarFile jar = new JarFile(file);
          ZipEntry entry = jar.getEntry(META_INF_PERSISTENCE_XML);
          if (entry != null) {
            final String path = file.getAbsolutePath();
            final File unpacked =
                new File(path.substring(0, path.length() - 4) + TMP_ENHANCEMENT_SUFFIX);
            JarExtractor.extract(file, unpacked);

            // replace jar by folder url since otherwise enhancement doesn't work
            usedUrls.add(unpacked.toURI().toURL());

            feed(classesByPXml, new File(unpacked, META_INF_PERSISTENCE_XML).getAbsolutePath());
          }
        } catch (IOException e) {
          // ignored
        }
      } else {
        usedUrls.add(url);
      }
    }

    // enhancement

    final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
    final ClassLoader fakeClassLoader =
        new URLClassLoaderFirst(
            usedUrls.toArray(new URL[usedUrls.size()]), event.getParentClassLoader());

    Thread.currentThread().setContextClassLoader(fakeClassLoader);
    try {
      for (Map.Entry<String, List<String>> entry : classesByPXml.entrySet()) {
        final Properties opts = new Properties();
        opts.setProperty(PROPERTIES_FILE_PROP, entry.getKey());

        final Object optsArg;
        try {
          optsArg = optionsConstructor.newInstance(opts);
        } catch (Exception e) {
          LOGGER.debug("can't create options for enhancing");
          return;
        }

        LOGGER.info("enhancing url(s): " + Arrays.asList(event.getUrls()));
        try {
          enhancerMethod.invoke(null, toFilePaths(entry.getValue()), optsArg);
        } catch (Exception e) {
          LOGGER.warning("can't enhanced at deploy-time entities", e);
        }
      }
    } finally {
      Thread.currentThread().setContextClassLoader(tccl);
      usedUrls.clear();
    }

    // clean up extracted jars and replace jar to keep consistent classloading
    for (Map.Entry<String, List<String>> entry : classesByPXml.entrySet()) {
      final List<String> values = entry.getValue();
      for (String rawPath : values) {
        if (rawPath.endsWith(TMP_ENHANCEMENT_SUFFIX + "/")
            || rawPath.endsWith(TMP_ENHANCEMENT_SUFFIX)) {
          final File dir = new File(rawPath);
          final File file =
              new File(
                  rawPath.substring(0, rawPath.length() - TMP_ENHANCEMENT_SUFFIX.length() - 1)
                      + ".jar");
          if (file.exists()) {
            String name = dir.getName();
            name = name.substring(0, name.length() - TMP_ENHANCEMENT_SUFFIX.length()) + ".jar";

            final File target = new File(dir.getParentFile(), name);
            try { // override existing jar otherwise classloading is broken in tomee
              Files.delete(file);
              JarCreator.jarDir(dir, target);
            } catch (final IOException e) {
              LOGGER.error("can't repackage enhanced jar file " + file.getName());
            }
            Files.delete(dir);
          }
        }
      }
      values.clear();
    }

    classesByPXml.clear();
  }
  // Do an update from the given repo. All applications found, and their
  // APKs, are added to 'apps'. (If 'apps' already contains an app, its
  // APKs are merged into the existing one).
  // Returns null if successful, otherwise an error message to be displayed
  // to the user (if there is an interactive user!)
  // 'newetag' should be passed empty. On success, it may contain an etag
  // value for the index that was successfully processed, or it may contain
  // null if none was available.
  public static String doUpdate(
      Context ctx,
      DB.Repo repo,
      List<DB.App> apps,
      StringBuilder newetag,
      List<Integer> keeprepos,
      ProgressListener progressListener) {
    try {

      int code = 0;
      if (repo.pubkey != null) {

        // This is a signed repo - we download the jar file,
        // check the signature, and extract the index...
        Log.d(
            "FDroid",
            "Getting signed index from "
                + repo.address
                + " at "
                + logDateFormat.format(new Date(System.currentTimeMillis())));
        String address = repo.address + "/index.jar";
        PackageManager pm = ctx.getPackageManager();
        try {
          PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), 0);
          address += "?" + pi.versionName;
        } catch (Exception e) {
        }
        Bundle progressData = createProgressData(repo.address);
        ProgressListener.Event event =
            new ProgressListener.Event(RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, progressData);
        code =
            getRemoteFile(
                ctx, address, "tempindex.jar", repo.lastetag, newetag, progressListener, event);
        if (code == 200) {
          String jarpath = ctx.getFilesDir() + "/tempindex.jar";
          JarFile jar = null;
          JarEntry je;
          Certificate[] certs;
          try {
            jar = new JarFile(jarpath, true);
            je = (JarEntry) jar.getEntry("index.xml");
            File efile = new File(ctx.getFilesDir(), "/tempindex.xml");
            InputStream input = null;
            OutputStream output = null;
            try {
              input = jar.getInputStream(je);
              output = new FileOutputStream(efile);
              Utils.copy(input, output);
            } finally {
              Utils.closeQuietly(output);
              Utils.closeQuietly(input);
            }
            certs = je.getCertificates();
          } catch (SecurityException e) {
            Log.e("FDroid", "Invalid hash for index file");
            return "Invalid hash for index file";
          } finally {
            if (jar != null) {
              jar.close();
            }
          }
          if (certs == null) {
            Log.d("FDroid", "No signature found in index");
            return "No signature found in index";
          }
          Log.d(
              "FDroid",
              "Index has " + certs.length + " signature" + (certs.length > 1 ? "s." : "."));

          boolean match = false;
          for (Certificate cert : certs) {
            String certdata = Hasher.hex(cert.getEncoded());
            if (repo.pubkey.equals(certdata)) {
              match = true;
              break;
            }
          }
          if (!match) {
            Log.d("FDroid", "Index signature mismatch");
            return "Index signature mismatch";
          }
        }

      } else {

        // It's an old-fashioned unsigned repo...
        Log.d("FDroid", "Getting unsigned index from " + repo.address);
        Bundle eventData = createProgressData(repo.address);
        ProgressListener.Event event =
            new ProgressListener.Event(RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, eventData);
        code =
            getRemoteFile(
                ctx,
                repo.address + "/index.xml",
                "tempindex.xml",
                repo.lastetag,
                newetag,
                progressListener,
                event);
      }

      if (code == 200) {
        // Process the index...
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();
        RepoXMLHandler handler = new RepoXMLHandler(repo, apps, progressListener);
        xr.setContentHandler(handler);

        File tempIndex = new File(ctx.getFilesDir() + "/tempindex.xml");
        BufferedReader r = new BufferedReader(new FileReader(tempIndex));

        // A bit of a hack, this might return false positives if an apps description
        // or some other part of the XML file contains this, but it is a pretty good
        // estimate and makes the progress counter more informative.
        // As with asking the server about the size of the index before downloading,
        // this also has a time tradeoff. It takes about three seconds to iterate
        // through the file and count 600 apps on a slow emulator (v17), but if it is
        // taking two minutes to update, the three second wait may be worth it.
        final String APPLICATION = "<application";
        handler.setTotalAppCount(Utils.countSubstringOccurrence(tempIndex, APPLICATION));

        InputSource is = new InputSource(r);
        xr.parse(is);

        if (handler.pubkey != null && repo.pubkey == null) {
          // We read an unsigned index, but that indicates that
          // a signed version is now available...
          Log.d("FDroid", "Public key found - switching to signed repo for future updates");
          repo.pubkey = handler.pubkey;
          try {
            DB db = DB.getDB();
            db.updateRepoByAddress(repo);
          } finally {
            DB.releaseDB();
          }
        }

      } else if (code == 304) {
        // The index is unchanged since we last read it. We just mark
        // everything that came from this repo as being updated.
        Log.d("FDroid", "Repo index for " + repo.address + " is up to date (by etag)");
        keeprepos.add(repo.id);
        // Make sure we give back the same etag. (The 200 route will
        // have supplied a new one.
        newetag.append(repo.lastetag);

      } else {
        return "Failed to read index - HTTP response " + Integer.toString(code);
      }

    } catch (SSLHandshakeException sslex) {
      Log.e(
          "FDroid",
          "SSLHandShakeException updating from "
              + repo.address
              + ":\n"
              + Log.getStackTraceString(sslex));
      return "A problem occurred while establishing an SSL connection. If this problem persists, AND you have a very old device, you could try using http instead of https for the repo URL.";
    } catch (Exception e) {
      Log.e(
          "FDroid", "Exception updating from " + repo.address + ":\n" + Log.getStackTraceString(e));
      return "Failed to update - " + e.getMessage();
    } finally {
      ctx.deleteFile("tempindex.xml");
      ctx.deleteFile("tempindex.jar");
    }

    return null;
  }