/******************************************************************************* * Copyright (c) 2011, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.server.git.jobs; import java.text.MessageFormat; import java.util.Locale; import java.util.regex.Pattern; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.orion.server.core.ServerStatus; import org.eclipse.orion.server.core.tasks.TaskJob; import org.eclipse.orion.server.git.GitActivator; import org.eclipse.orion.server.git.GitCredentialsProvider; import org.eclipse.orion.server.jsch.HostFingerprintException; import org.json.JSONException; import org.json.JSONObject; import com.jcraft.jsch.JSchException; /** * Base class for all Git jobs. * */ @SuppressWarnings("restriction") public abstract class GitJob extends TaskJob { public static final Object FAMILY = "org.eclipse.orion.server.git.jobs.GitJob"; @Override public boolean belongsTo(Object family) { return FAMILY.equals(family); } private static final String KEY_SCHEME = "Scheme"; //$NON-NLS-1$ private static final String KEY_PORT = "Port"; //$NON-NLS-1$ private static final String KEY_PASSWORD = "Password"; //$NON-NLS-1$ public static final String KEY_HUMANISH_NAME = "HumanishName"; //$NON-NLS-1$ public static final String KEY_URL = "Url"; //$NON-NLS-1$ public static final String KEY_USER = "User"; //$NON-NLS-1$ public static final String KEY_HOST = "Host"; //$NON-NLS-1$ protected GitCredentialsProvider credentials; protected Cookie cookie; protected static JSchException getJSchException(Throwable e) { if (e instanceof JSchException) { return (JSchException) e; } if (e.getCause() != null) { return getJSchException(e.getCause()); } return null; } private JSONObject addRepositoryInfo(JSONObject object) { try { if (credentials != null) { object.put(KEY_URL, credentials.getUri().toString()); if (credentials.getUri().getUser() != null) { object.put(KEY_USER, credentials.getUri().getUser()); } if (credentials.getUri().getHost() != null) { object.put(KEY_HOST, credentials.getUri().getHost()); } if (credentials.getUri().getHumanishName() != null) { object.put(KEY_HUMANISH_NAME, credentials.getUri().getHumanishName()); } if (credentials.getUri().getPass() != null) { object.put(KEY_PASSWORD, credentials.getUri().getPass()); } if (credentials.getUri().getPort() > 0) { object.put(KEY_PORT, credentials.getUri().getPort()); } if (credentials.getUri().getScheme() != null) { object.put(KEY_SCHEME, credentials.getUri().getScheme()); } } } catch (JSONException e) { // ignore, should always be able to put string } return object; } IStatus getExceptionStatus(Exception e, String message) { JSchException jschEx = getJSchException(e); if (jschEx != null && jschEx instanceof HostFingerprintException) { HostFingerprintException cause = (HostFingerprintException) jschEx; return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, cause.getMessage(), addRepositoryInfo(cause.formJson()), cause); } // JSch handles auth fail by exception message, another one handles only by exception message is "invalid privatekey: ..." if (jschEx != null && jschEx.getMessage() != null && (jschEx.getMessage().toLowerCase(Locale.ENGLISH).contains("auth fail") || jschEx.getMessage().toLowerCase(Locale.ENGLISH).contains("invalid privatekey"))) { //$NON-NLS-1$ return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_UNAUTHORIZED, jschEx.getMessage(), addRepositoryInfo(new JSONObject()), jschEx); } // Log connection problems directly if (e.getCause() instanceof TransportException) { TransportException cause = (TransportException) e.getCause(); if (matchMessage(JGitText.get().serviceNotPermitted, cause.getMessage())) { // HTTP connection problems are distinguished by exception message return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_FORBIDDEN, cause.getMessage(), addRepositoryInfo(new JSONObject()), cause); } else if (matchMessage(JGitText.get().notAuthorized, cause.getMessage())) { // HTTP connection problems are distinguished by exception message return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_UNAUTHORIZED, cause.getMessage(), addRepositoryInfo(new JSONObject()), cause); } else if (cause.getMessage() != null && cause.getMessage().endsWith("username must not be null.") || cause.getMessage().endsWith("host must not be null.")) { //$NON-NLS-1$ //$NON-NLS-2$ // see com.jcraft.jsch.JSch#getSession(String, String, int) return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, cause.getMessage(), addRepositoryInfo(new JSONObject()), cause); } else if (e instanceof GitAPIException) { // Other HTTP connection problems reported directly return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message, addRepositoryInfo(new JSONObject()), e); } else { // Other HTTP connection problems reported directly return new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message, addRepositoryInfo(new JSONObject()), cause); } } return new Status(IStatus.ERROR, GitActivator.PI_GIT, message, e.getCause() == null ? e : e.getCause()); } IStatus getGitAPIExceptionStatus(GitAPIException e, String message) { return getExceptionStatus(e, message); } IStatus getJGitInternalExceptionStatus(JGitInternalException e, String message) { IStatus status = getExceptionStatus(e, message); // TODO uncomment this when fix in jgit is merged // if (status instanceof ServerStatus) { // LogHelper.log(new Status(IStatus.WARNING, GitActivator.PI_GIT, // "JGitInternalException should not be thrown for authentication errors. See https://git.eclipse.org/r/#/c/6207/", e)); // } return status; } public GitJob(String userRunningTask, boolean keep, GitCredentialsProvider credentials) { super(userRunningTask, keep); this.credentials = credentials; } public GitJob(String userRunningTask, boolean keep) { this(userRunningTask, keep, null); } /** * Check if message matches or contains pattern in {@link MessageFormat} format. * * @param pattern * @param message * @return <code>true</code> if the messages match, and <code>false</code> otherwise * @see MessageFormat */ protected static boolean matchMessage(String pattern, String message) { if (message == null) { return false; } int argsNum = 0; for (int i = 0; i < pattern.length(); i++) { if (pattern.charAt(i) == '{') { argsNum++; } } Object[] args = new Object[argsNum]; for (int i = 0; i < args.length; i++) { args[i] = ".*"; //$NON-NLS-1$ } return Pattern.matches(".*" + MessageFormat.format(pattern, args) + ".*", message); //$NON-NLS-1$ //$NON-NLS-2$ } }