blob: 4d628c7a66aaa1cd4a4fda71211df4557f05524c [file] [log] [blame]
/*******************************************************************************
* 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);
}
}