| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.internal.ccvs.core.connection; |
| |
| import java.io.*; |
| import java.net.Socket; |
| |
| import org.eclipse.core.net.proxy.IProxyData; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jsch.core.IJSchService; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.team.internal.ccvs.core.*; |
| import org.eclipse.team.internal.ccvs.core.util.Util; |
| import org.eclipse.team.internal.core.streams.*; |
| |
| import com.jcraft.jsch.Proxy; |
| |
| /** |
| * A connection used to talk to an cvs pserver. |
| */ |
| public class PServerConnection implements IServerConnection { |
| |
| public static final char NEWLINE= 0xA; |
| |
| /** default CVS pserver port */ |
| private static final int DEFAULT_PORT= 2401; |
| |
| /** error line indicators */ |
| private static final char ERROR_CHAR = 'E'; |
| private static final String ERROR_MESSAGE = "error 0";//$NON-NLS-1$ |
| private static final String NO_SUCH_USER = "no such user";//$NON-NLS-1$ |
| |
| private static final char[] SCRAMBLING_TABLE=new char[] { |
| 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, |
| 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, |
| 114,120,53,79,96,109,72,108,70,64,76,67,116,74,68,87, |
| 111,52,75,119,49,34,82,81,95,65,112,86,118,110,122,105, |
| 41,57,83,43,46,102,40,89,38,103,45,50,42,123,91,35, |
| 125,55,54,66,124,126,59,47,92,71,115,78,88,107,106,56, |
| 36,121,117,104,101,100,69,73,99,63,94,93,39,37,61,48, |
| 58,113,32,90,44,98,60,51,33,97,62,77,84,80,85,223, |
| 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190, |
| 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193, |
| 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212, |
| 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246, |
| 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176, |
| 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127, |
| 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195, |
| 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 |
| }; |
| |
| /** Communication strings */ |
| private static final String BEGIN= "BEGIN AUTH REQUEST";//$NON-NLS-1$ |
| private static final String END= "END AUTH REQUEST";//$NON-NLS-1$ |
| private static final String LOGIN_OK= "I LOVE YOU";//$NON-NLS-1$ |
| private static final String LOGIN_FAILED= "I HATE YOU";//$NON-NLS-1$ |
| |
| private String password; |
| private ICVSRepositoryLocation cvsroot; |
| |
| private Socket fSocket; |
| |
| private InputStream inputStream; |
| private OutputStream outputStream; |
| |
| @Override |
| public void close() throws IOException { |
| try { |
| if (inputStream != null) inputStream.close(); |
| } finally { |
| inputStream = null; |
| try { |
| if (outputStream != null) outputStream.close(); |
| } finally { |
| outputStream = null; |
| try { |
| if (fSocket != null) fSocket.close(); |
| } finally { |
| fSocket = null; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void open(IProgressMonitor monitor) throws IOException, CVSAuthenticationException { |
| |
| monitor.subTask(CVSMessages.PServerConnection_authenticating); |
| monitor.worked(1); |
| |
| InputStream is = null; |
| OutputStream os = null; |
| |
| Proxy proxy = getProxy(); |
| if (proxy!=null) { |
| String host = cvsroot.getHost(); |
| int port = cvsroot.getPort(); |
| if (port == ICVSRepositoryLocation.USE_DEFAULT_PORT) { |
| port = DEFAULT_PORT; |
| } |
| try { |
| int timeout = CVSProviderPlugin.getPlugin().getTimeout() * 1000; |
| IJSchService service = CVSProviderPlugin.getPlugin().getJSchService(); |
| service.connect(proxy, host, port, timeout, monitor); |
| } catch( Exception ex) { |
| ex.printStackTrace(); |
| throw new IOException(ex.getMessage()); |
| } |
| is = proxy.getInputStream(); |
| os = proxy.getOutputStream(); |
| |
| } else { |
| fSocket = createSocket(monitor); |
| is = fSocket.getInputStream(); |
| os = fSocket.getOutputStream(); |
| } |
| |
| boolean connected = false; |
| try { |
| this.inputStream = new BufferedInputStream(new PollingInputStream(is, |
| cvsroot.getTimeout(), monitor)); |
| this.outputStream = new PollingOutputStream(new TimeoutOutputStream( |
| os, 8192 /*bufferSize*/, 1000 /*writeTimeout*/, 1000 /*closeTimeout*/), |
| cvsroot.getTimeout(), monitor); |
| authenticate(); |
| connected = true; |
| } finally { |
| if (! connected) close(); |
| } |
| } |
| |
| private Proxy getProxy() { |
| IJSchService service = CVSProviderPlugin.getPlugin().getJSchService(); |
| if (service == null) |
| return null; |
| Proxy proxy = service.getProxyForHost(cvsroot.getHost(), IProxyData.SOCKS_PROXY_TYPE); |
| if (proxy == null) |
| proxy = service.getProxyForHost(cvsroot.getHost(), IProxyData.HTTPS_PROXY_TYPE); |
| return proxy; |
| } |
| |
| @Override |
| public InputStream getInputStream() { |
| return inputStream; |
| } |
| @Override |
| public OutputStream getOutputStream() { |
| return outputStream; |
| } |
| |
| /** |
| * Creates a new <code>PServerConnection</code> for the given |
| * cvs root. |
| */ |
| PServerConnection(ICVSRepositoryLocation cvsroot, String password) { |
| this.cvsroot = cvsroot; |
| this.password = password; |
| } |
| /** |
| * Does the actual authentication. |
| */ |
| private void authenticate() throws IOException, CVSAuthenticationException { |
| String scrambledPassword = scramblePassword(password); |
| |
| String user = cvsroot.getUsername(); |
| OutputStream out = getOutputStream(); |
| |
| StringBuffer request = new StringBuffer(); |
| request.append(BEGIN); |
| request.append(NEWLINE); |
| request.append(cvsroot.getRootDirectory()); |
| request.append(NEWLINE); |
| request.append(user); |
| request.append(NEWLINE); |
| request.append(scrambledPassword); |
| request.append(NEWLINE); |
| request.append(END); |
| request.append(NEWLINE); |
| out.write(request.toString().getBytes()); |
| out.flush(); |
| String line = Connection.readLine(cvsroot, getInputStream()).trim(); |
| |
| // Return if we succeeded |
| if (LOGIN_OK.equals(line)) |
| return; |
| |
| // Otherwise, determine the type of error |
| if (line.length() == 0) { |
| throw new IOException(CVSMessages.PServerConnection_noResponse); |
| } |
| |
| // Accumulate a message from the error (E) stream |
| String message = "";//$NON-NLS-1$ |
| String separator = ""; //$NON-NLS-1$ |
| |
| if(!CVSProviderPlugin.getPlugin().isUseProxy()) { |
| while (line.length() > 0 && line.charAt(0) == ERROR_CHAR) { |
| if (line.length() > 2) { |
| message += separator + line.substring(2); |
| separator = " "; //$NON-NLS-1$ |
| } |
| line = Connection.readLine(cvsroot, getInputStream()); |
| } |
| } else { |
| while (line.length() > 0) { |
| message += separator + line; |
| separator = "\n"; //$NON-NLS-1$ |
| line = Connection.readLine(cvsroot, getInputStream()); |
| } |
| } |
| |
| // If the last line is the login failed (I HATE YOU) message, return authentication failure |
| if (LOGIN_FAILED.equals(line)) { |
| if (message.length() == 0) { |
| throw new CVSAuthenticationException(CVSMessages.PServerConnection_loginRefused, CVSAuthenticationException.RETRY,cvsroot); |
| } else { |
| throw new CVSAuthenticationException(message, CVSAuthenticationException.RETRY,cvsroot); |
| } |
| } |
| |
| // Remove leading "error 0" |
| if (line.startsWith(ERROR_MESSAGE)) |
| message += separator + line.substring(ERROR_MESSAGE.length() + 1); |
| else |
| message += separator + line; |
| if (message.indexOf(NO_SUCH_USER) != -1) |
| throw new CVSAuthenticationException(NLS.bind(CVSMessages.PServerConnection_invalidUser, (new Object[] {message})), CVSAuthenticationException.RETRY,cvsroot); |
| throw new IOException(NLS.bind(CVSMessages.PServerConnection_connectionRefused, (new Object[] { message }))); |
| } |
| |
| /** |
| * Creates the actual socket |
| */ |
| protected Socket createSocket(IProgressMonitor monitor) throws IOException { |
| // Determine what port to use |
| int port = cvsroot.getPort(); |
| if (port == ICVSRepositoryLocation.USE_DEFAULT_PORT) |
| port = DEFAULT_PORT; |
| // Make the connection |
| Socket result; |
| try { |
| result= Util.createSocket(cvsroot.getHost(), port, monitor); |
| // Bug 36351: disable buffering and send bytes immediately |
| result.setTcpNoDelay(true); |
| } catch (InterruptedIOException e) { |
| // If we get this exception, chances are the host is not responding |
| throw new InterruptedIOException(NLS.bind(CVSMessages.PServerConnection_socket, (new Object[] {cvsroot.getHost()}))); |
| } |
| result.setSoTimeout(1000); // 1 second between timeouts |
| return result; |
| } |
| |
| private String scramblePassword(String password) throws CVSAuthenticationException { |
| int length = password.length(); |
| char[] out= new char[length]; |
| for (int i= 0; i < length; i++) { |
| char value = password.charAt(i); |
| if( value < 0 || value > 255 ) |
| throwInValidCharacter(); |
| out[i]= SCRAMBLING_TABLE[value]; |
| } |
| return "A" + new String(out);//$NON-NLS-1$ |
| } |
| |
| private void throwInValidCharacter() throws CVSAuthenticationException { |
| throw new CVSAuthenticationException(CVSMessages.PServerConnection_invalidChars, CVSAuthenticationException.RETRY, cvsroot); |
| } |
| |
| } |