/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.buildServer.buildTriggers.url; import com.intellij.openapi.diagnostic.Logger; import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.util.ssl.SSLContextUtil; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPReply; import org.apache.commons.net.ftp.FTPSClient; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; /** * User: Victory.Bedrosova * Date: 10/1/12 * Time: 2:35 PM */ final public class FtpResourceHashProvider implements ResourceHashProvider { private static final Logger LOG = LoggerFactory.getLogger(UrlBuildTrigger.class); @Nullable private final KeyStore trustStore; public FtpResourceHashProvider(@Nullable final KeyStore trustStore) { this.trustStore = trustStore; } @NotNull public String getResourceHash(@NotNull TriggerParameters triggerParameters) throws ResourceHashProviderException { try { return getResourceHash(triggerParameters.getURL(), triggerParameters.getUsername(), triggerParameters.getPassword(), triggerParameters.getConnectionTimeout()); } catch (Throwable e) { throw new ResourceHashProviderException("URL " + triggerParameters.getURL() + ": " + e.getMessage(), e); } } @NotNull String getResourceHash( @NotNull String url, @Nullable String user, @Nullable String password ) throws IOException, ResourceHashProviderException, KeyStoreException, NoSuchAlgorithmException { return getResourceHash(url, user, password, UrlBuildTriggerUtil.DEFAULT_CONNECTION_TIMEOUT); } @NotNull String getResourceHash( @NotNull String urlStr, @Nullable String user, @Nullable String password, @NotNull Integer connectionTimeout ) throws IOException, ResourceHashProviderException, KeyStoreException, NoSuchAlgorithmException { boolean secure = false; if (urlStr.startsWith("ftps://")) { urlStr = urlStr.replace("ftps://", "ftp://"); secure = true; } final FTPClient ftp; if (secure) { ftp = new FTPSClient(); ((FTPSClient) ftp).setTrustManager(SSLContextUtil.createTrustManager(trustStore)); } else { ftp = new FTPClient(); } ftp.setConnectTimeout(connectionTimeout); ftp.setDataTimeout(2 * connectionTimeout); try { final URL url = new URL(urlStr); connectAndLogin(ftp, url, user, password); final FTPFile file = ftp.mlistFile(url.getFile()); // currently raw listing returns smth like ize=1;Modify=20121002101740.067;Type=file; file // ize=1 seems to stand instead of Size=1 // Modify seems to have some incorrect time zone return file == null ? UrlBuildTriggerUtil.UNEXITING_RESOURCE_HASH : file.getRawListing(); } finally { logoutAndDisconnect(ftp); } } private void connectAndLogin(@NotNull FTPClient ftp, @NotNull URL url, @Nullable String user, @Nullable String password) throws ResourceHashProviderException, IOException { connect(ftp, url); login(ftp, url, user, password); } private void connect(@NotNull FTPClient ftp, @NotNull URL url) throws ResourceHashProviderException, IOException { final String server = url.getHost(); final int port = url.getPort(); int reply; if (port > 0) { ftp.connect(server, port); } else { ftp.connect(server); } reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { throw new ResourceHashProviderException("FTP server " + server + " refused connection with " + reply + " code"); } LOG.debug("Connected to FTP server " + server + " on " + (port > 0 ? port : ftp.getDefaultPort())); } private void login(@NotNull FTPClient ftp, @NotNull URL url, @Nullable String user, @Nullable String password) throws ResourceHashProviderException, IOException { final String host = url.getHost(); String rUser; String rPassword; if (StringUtil.isNotEmpty(user)) { rUser = user; rPassword = password == null ? StringUtil.EMPTY : password; } else { final String userInfo = url.getUserInfo(); rUser = getUser(userInfo); rPassword = getPassword(userInfo); } if (!ftp.login(rUser, rPassword)) { throw new ResourceHashProviderException("FTP server " + host + " login failed for username " + rUser); } LOG.debug("Logged in to FTP server " + host + " for username " + rUser); } private void logoutAndDisconnect(@NotNull FTPClient ftp) { if (ftp.isConnected()) { try { ftp.logout(); ftp.disconnect(); } catch (IOException e) { LOG.warn(e.getMessage(), e); } } } @NotNull private String getUser(@Nullable String userInfo) throws ResourceHashProviderException { final String user = getUserInfoPart(userInfo, 0); return StringUtil.isNotEmpty(user) ? user : "anonymous"; } @NotNull private String getPassword(@Nullable String userInfo) throws ResourceHashProviderException { return getUserInfoPart(userInfo, 1); } @NotNull private String getUserInfoPart(@Nullable String userInfo, int index) throws ResourceHashProviderException { if (userInfo == null) return ""; final String[] parts = userInfo.split(":"); return parts.length > index ? parts[index] : StringUtil.EMPTY; } }