/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2014 Alex Buloichik Home page: http://www.omegat.org/ Support center: http://groups.yahoo.com/group/OmegaT/ This file is part of OmegaT. OmegaT is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OmegaT 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. **************************************************************************/ package org.omegat.core.team2.impl; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.MessageDigest; import java.util.Formatter; import java.util.Properties; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; import org.omegat.core.team2.IRemoteRepository2; import org.omegat.core.team2.ProjectTeamSettings; import org.omegat.util.Log; import gen.core.project.RepositoryDefinition; import gen.core.project.RepositoryMapping; /** * HTTP/HTTPS repository connection implementation. * * It can be used as read-only repository for retrieve sources, external TMX, glossaries, etc. Since HTTP * protocol doesn't support multiple files, each URL should be mapped to separate file, i.e. directory mapping * is not supported. * * @author Alex Buloichik (alex73mail@gmail.com) */ public class HTTPRemoteRepository implements IRemoteRepository2 { private static final Logger LOGGER = Logger.getLogger(HTTPRemoteRepository.class.getName()); private RepositoryDefinition config; private File baseDirectory; @Override public void init(RepositoryDefinition repo, File dir, ProjectTeamSettings teamSettings) throws Exception { Log.logDebug(LOGGER, "Initialize HTTP remote repository"); config = repo; baseDirectory = dir; } /** * Use SHA-1 as file version. */ @Override public String getFileVersion(String file) throws Exception { MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); sha1.reset(); // calculate SHA-1 byte[] buffer = new byte[8192]; InputStream in = new BufferedInputStream(new FileInputStream(file)); try { while (true) { int len = in.read(buffer); if (len < 0) { break; } sha1.update(buffer, 0, len); } } finally { in.close(); } // out as hex Formatter formatter = new Formatter(); try { for (byte b : sha1.digest()) { formatter.format("%02x", b); } return formatter.toString(); } finally { formatter.close(); } } @Override public void switchToVersion(String version) throws Exception { if (version != null) { throw new RuntimeException("Not supported"); } Log.logDebug(LOGGER, "Update to latest"); // retrieve all mapped files Properties etags = loadETags(); for (RepositoryMapping m : config.getMapping()) { String url = config.getUrl() + m.getRepository(); File out = new File(baseDirectory, m.getRepository()); retrieve(etags, m.getRepository(), url, out); } saveETags(etags); } @Override public void addForCommit(String path) throws Exception { throw new RuntimeException("Not supported"); } @Override public String commit(String[] onVersions, String comment) throws Exception { throw new RuntimeException("Not supported"); } /** * Load all ETags. */ protected Properties loadETags() throws Exception { Properties props = new Properties(); File f = new File(baseDirectory, ".etags"); if (f.exists()) { FileInputStream in = new FileInputStream(f); try { props.load(in); } finally { in.close(); } } return props; } /** * Save all ETags. */ protected void saveETags(Properties props) throws Exception { FileOutputStream out = new FileOutputStream(new File(baseDirectory, ".etags")); try { props.store(out, null); } finally { out.close(); } } /** * Retrieve remote URL with non-modified checking by ETag. If server doesn't support ETag, file will be * always retrieved. */ protected void retrieve(Properties etags, String file, String url, File out) throws Exception { String etag = etags.getProperty(file); Log.logDebug(LOGGER, "Retrieve " + url + " into " + out.getAbsolutePath() + " with ETag=" + etag); out.getParentFile().mkdirs(); HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); try { if (etag != null) { // use ETag if we know it conn.setRequestProperty("If-None-Match", etag); } switch (conn.getResponseCode()) { case HttpURLConnection.HTTP_OK: etag = conn.getHeaderField("ETag"); Log.logDebug(LOGGER, "Retrieve " + url + ": 200 with ETag=" + etag); break; case HttpURLConnection.HTTP_NOT_MODIFIED: // not modified - just return Log.logDebug(LOGGER, "Retrieve " + url + ": not modified"); return; default: throw new RuntimeException("HTTP response code: " + conn.getResponseCode()); } // load into .tmp file File temp = new File(out.getAbsolutePath() + ".tmp"); InputStream in = conn.getInputStream(); try { FileUtils.copyInputStreamToFile(in, temp); } finally { in.close(); } // rename into file if (out.exists()) { if (!out.delete()) { throw new IOException(); } } if (!temp.renameTo(out)) { throw new IOException(); } etags.setProperty(file, etag); } finally { conn.disconnect(); } Log.logDebug(LOGGER, "Retrieve " + url + " finished"); } }