view mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java @ 320:568ce42b1e9d Eluru-6.5.x

TW-18262 escape ampersand in the password
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Thu, 15 Sep 2011 12:25:37 +0400
parents 77d3c69b8dfe
children 6667765025c6 d0edd172943f
line wrap: on
line source
/*
 * Copyright 2000-2011 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.vcs.mercurial.command;

import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.PathUtil;
import jetbrains.buildServer.log.Loggers;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.vcs.VcsRoot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

/**
 * Represents Mercurial repository settings
 */
public class Settings {
  private String myRepository;
  private String myHgCommandPath;
  private File myCustomWorkingDir;
  private String myUsername;
  private String myPassword;
  private String myBranchName;
  private boolean myUncompressedTransfer = false;
  private static final String DEFAULT_BRANCH_NAME = "default";
  private String myCustomClonePath;

  public Settings(@NotNull VcsRoot vcsRoot) {
    myRepository = vcsRoot.getProperty(Constants.REPOSITORY_PROP);
    myHgCommandPath = vcsRoot.getProperty(Constants.HG_COMMAND_PATH_PROP);
    myBranchName = vcsRoot.getProperty(Constants.BRANCH_NAME_PROP);
    myCustomClonePath = vcsRoot.getProperty(Constants.SERVER_CLONE_PATH_PROP);
    myUsername = vcsRoot.getProperty(Constants.USERNAME);
    myPassword = vcsRoot.getProperty(Constants.PASSWORD);
    myUncompressedTransfer = "true".equals(vcsRoot.getProperty(Constants.UNCOMPRESSED_TRANSFER));
  }

  public String getCustomClonePath() {
    return myCustomClonePath;
  }

  public String getRepository() {
    return myRepository;
  }

  /**
   * Returns name of the branch to use (returns 'default' if no branch specified)
   * @return see above
   */
  @NotNull
  public String getBranchName() {
    return StringUtil.isEmpty(myBranchName) ? DEFAULT_BRANCH_NAME : myBranchName;
  }

  /**
   * Returns true if current branch is default branch
   * @return see above
   */
  public boolean isDefaultBranch() {
    return getBranchName().equals(DEFAULT_BRANCH_NAME);
  }

  public boolean isUncompressedTransfer() {
    return myUncompressedTransfer;
  }

  /**
   * Returns path to hg command
   * @return path to hg command
   */
  @NotNull
  public String getHgCommandPath() {
    return myHgCommandPath;
  }

  public String getUsername() {
    return myUsername;
  }

  public String getPassword() {
    return myPassword;
  }

  private final static Set<String> AUTH_PROTOS = new HashSet<String>();
  static {
    AUTH_PROTOS.add("http");
    AUTH_PROTOS.add("https");
    AUTH_PROTOS.add("ssh");
  }

  /**
   * Returns URL to use for push command
   * @return URL to use for push command
   */
  public String getRepositoryUrl() {
    if (isRequireCredentials()) {
      if (containsCredentials(myRepository)) return myRepository;
      try {
        return createURLWithCredentials(myRepository);
      } catch (MalformedURLException e) {
        Loggers.VCS.warn("Error while parsing url " + myRepository, e);
      }
      return myRepository;
    } else {
      return myRepository;
    }
  }

  private boolean containsCredentials(final String repository) {
    try {
      URL url = new URL(repository);
      String userInfo = url.getUserInfo();
      return userInfo != null && userInfo.contains(":");
    } catch (MalformedURLException e) {
      return false;
    }
  }

  private String createURLWithCredentials(final String originalUrl) throws MalformedURLException {
    String userInfo = createUserInfo();
    if (!"".equals(userInfo)) {
      URL url = new URL(originalUrl);
      return url.getProtocol() + "://"
              + userInfo + "@"
              + url.getHost()
              + (url.getPort() != -1 ? ":" + url.getPort() : "")
              + url.getFile()
              + (url.getRef() != null ? url.getRef() : "");
    } else {
      return originalUrl;
    }
  }

  private boolean isRequireCredentials() {
    for (String scheme : AUTH_PROTOS) {
      if (myRepository.startsWith(scheme + ":")) {
        return true;
      }
    }
    return false;
  }

  private String createUserInfo() {
    String userInfo = "";
    if (!StringUtil.isEmpty(myUsername)) {
      userInfo += myUsername;
      if (!StringUtil.isEmpty(myPassword)) {
        userInfo += ":" + myPassword;
      }
    }
    return getEscapedUserInfo(userInfo);
  }

  private static String getEscapedUserInfo(String userInfo) {
    try {
      URI uri = new URI("http", userInfo, "somewhere.com", 80, "", "", "");
      String escapedURI = uri.toASCIIString();
      int from = "http://".length();
      int to = escapedURI.indexOf("somewhere.com") - 1;
      String escapedUserInfo = escapedURI.substring(from, to);
      escapedUserInfo = escapedUserInfo.replaceAll("&", "%26");
      return escapedUserInfo;
    } catch (URISyntaxException e) {
      assert false;
    }
    return userInfo;
  }

  /**
   * Set custom working dir for vcs root. This option make sence only for server-side checkout
   * @param customWorkingDir custom working dir
   */
  public void setCustomWorkingDir(@NotNull final File customWorkingDir) {
    myCustomWorkingDir = PathUtil.getCanonicalFile(customWorkingDir);
  }

  /**
   * Returns custom working dir for root or null if default working dir should be used.
   * This options make sence only with server-side checkout.
   * @return see above
   */
  @Nullable
  public File getCustomWorkingDir() {
    return myCustomWorkingDir;
  }

  public static boolean isValidRepository(File dir) {
    // need better way to check that repository copy is ok
    return dir.isDirectory() && new File(dir, ".hg").isDirectory();
  }
}