package floobits.common;
import com.intellij.util.net.ssl.ConfirmingTrustManager;
import floobits.common.interfaces.IContext;
import floobits.common.interfaces.IFile;
import floobits.utilities.Flog;
import org.apache.commons.io.FilenameUtils;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.URI;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Pattern;
import static com.intellij.util.net.ssl.ConfirmingTrustManager.*;
public class Utils {
private static int requestId = 0;
public static int getRequestId() {
requestId += 1;
return requestId;
}
public static String stackToString(Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString(); // stack trace as a string
}
public static String absPath (String p1, String path) {
return FilenameUtils.concat(p1, path);
}
public static Boolean isShared (String path, String p1) {
return isChild(path, p1);
}
public static String toProjectRelPath (String path, String p1) {
try {
return getRelativePath(path, p1);
} catch (PathResolutionException e) {
return null;
}
}
// StartCom CA
private static final String cert =
"-----BEGIN CERTIFICATE-----\n" +
"MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW\n" +
"MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg\n" +
"Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh\n" +
"dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9\n" +
"MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi\n" +
"U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh\n" +
"cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA\n" +
"A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk\n" +
"pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf\n" +
"OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C\n" +
"Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT\n" +
"Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi\n" +
"HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM\n" +
"Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w\n" +
"+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+\n" +
"Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3\n" +
"Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B\n" +
"26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID\n" +
"AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE\n" +
"FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j\n" +
"ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js\n" +
"LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM\n" +
"BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0\n" +
"Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy\n" +
"dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh\n" +
"cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh\n" +
"YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg\n" +
"dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp\n" +
"bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ\n" +
"YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT\n" +
"TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ\n" +
"9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8\n" +
"jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW\n" +
"FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz\n" +
"ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1\n" +
"ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L\n" +
"EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu\n" +
"L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq\n" +
"yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC\n" +
"O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V\n" +
"um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh\n" +
"NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=\n" +
"-----END CERTIFICATE-----";
public static Boolean isSamePath (String p1, String p2) {
p1 = FilenameUtils.normalizeNoEndSeparator(p1);
p2 = FilenameUtils.normalizeNoEndSeparator(p2);
return FilenameUtils.equalsNormalizedOnSystem(p1, p2);
}
public static String fixPath(String path) {
return FilenameUtils.normalize(new File(path).getAbsolutePath());
}
public static boolean isChild (String path, String parent) {
try {
String relativePath = getRelativePath(fixPath(path), fixPath(parent));
return !relativePath.contains("..");
} catch (PathResolutionException e) {
return false;
} catch (StringIndexOutOfBoundsException e) {
return false;
}
}
/**
* see http://stackoverflow.com/questions/204784/how-to-construct-a-relative-file-in-java-from-two-absolute-paths-or-urls/3054692#3054692
* Get the relative file from one file to another, specifying the directory separator.
* If one of the provided resources does not exist, it is assumed to be a file unless it ends with '/' or
* '\'.
*
* @param targetPath targetPath is calculated to this file
* @param basePath basePath is calculated from this file
* @return String
*/
public static String getRelativePath (String targetPath, String basePath) throws PathResolutionException{
if (targetPath == null || basePath == null) {
return null;
}
// Normalize the paths
String pathSeparator = File.separator;
String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);
String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
// Undo the changes to the separators made by normalization
if (pathSeparator.equals("/")) {
normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);
} else if (pathSeparator.equals("\\")) {
normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);
} else {
throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'");
}
String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));
// First get all the common elements. Store them as a string,
// and also count how many of them there are.
StringBuilder common = new StringBuilder();
int commonIndex = 0;
while (commonIndex < target.length && commonIndex < base.length
&& target[commonIndex].equals(base[commonIndex])) {
common.append(target[commonIndex]).append(pathSeparator);
commonIndex++;
}
if (commonIndex == 0) {
// No single common file element. This most
// likely indicates differing drive letters, like C: and D:.
// These paths cannot be relativized.
throw new PathResolutionException("No common file element found for '" + normalizedTargetPath + "' and '" + normalizedBasePath
+ "'");
}
// The number of directories we have to backtrack depends on whether the base is a file or a dir
// For example, the relative file from
//
// /foo/bar/baz/gg/ff to /foo/bar/baz
//
// ".." if ff is a file
// "../.." if ff is a directory
//
// The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because
// the resource referred to by this file may not actually exist, but it's the best I can do
boolean baseIsFile = true;
File baseResource = new File(normalizedBasePath);
if (baseResource.exists()) {
baseIsFile = baseResource.isFile();
} else if (basePath.endsWith(pathSeparator)) {
baseIsFile = false;
}
StringBuilder relative = new StringBuilder();
if (base.length != commonIndex) {
int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex;
for (int i = 0; i < numDirsUp; i++) {
relative.append("..").append(pathSeparator);
}
}
String commonStr = common.toString();
// Handle missing trailing slash issues with base project directory:
if (normalizedTargetPath.equals(commonStr.substring(0, commonStr.length() - 1))) {
return "";
}
relative.append(normalizedTargetPath.substring(commonStr.length()));
return relative.toString();
}
static class PathResolutionException extends RuntimeException {
PathResolutionException (String msg) {
super(msg);
}
}
public interface FileProcessor<T> {
T call(IFile file);
}
static public SSLContext createSSLContext() {
X509TrustManager FloobitsSSLTrustManager = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {return null;}
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
try {
FloorcJson floorcJson = FloorcJson.getFloorcJsonFromSettings();
if (floorcJson.insecure) {
Flog.warn("Insecure mode enabled in ~/.floorc.json! Skipping certificate validation!");
return;
}
} catch (Exception e) {
Flog.error(e);
}
InputStream is = new ByteArrayInputStream(cert.getBytes());
CertificateFactory cf;
X509Certificate cert;
cf = CertificateFactory.getInstance("X.509");
cert = (X509Certificate) cf.generateCertificate(is);
cert.checkValidity();
ArrayList<X509Certificate> x509Certificates = new ArrayList<X509Certificate>(Arrays.asList(certs));
x509Certificates.add(cert);
X509Certificate a, b;
try {
a = x509Certificates.get(0);
for (int i = 1; i < x509Certificates.size(); i++) {
b = x509Certificates.get(i);
a.checkValidity();
a.verify(b.getPublicKey());
a = b;
}
} catch (Exception e) {
Flog.debug(e.toString());
String path = FilenameUtils.concat(FilenameUtils.concat(System.getProperty("user.home"), "floobits"), "trust-store");
X509TrustManager manager = ConfirmingTrustManager.createForStorage(path, "");
manager.checkServerTrusted(certs, authType);
}
}
};
SSLContext sc = null;
try {
sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{FloobitsSSLTrustManager}, new SecureRandom());
} catch (Exception e) {
Flog.error(e);
}
return sc;
}
static public String getDefaultHost() {
FloorcJson floorcJson;
try {
floorcJson = Settings.get();
} catch (Exception e) {
return Constants.defaultHost;
}
if (floorcJson != null && floorcJson.DEFAULT_HOST != null) {
return floorcJson.DEFAULT_HOST;
}
return Constants.defaultHost;
}
static public String getLinkHTML(String URL, String text) {
return String.format("<a href=\"%s\" style=\"color:#e78c0c;text-decoration:underline;\">%s</a>", URL, text);
}
static public boolean openInBrowser(URI uri, String defaultLinkText, IContext context) {
return BrowserOpener.getInstance().openInBrowser(uri, defaultLinkText, context);
}
}