1 /*******************************************************************************
2  * Copyright (c) 2000, 2014 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Brock Janiczak (brockj@tpg.com.au) - Bug 144419 Avoid calculating encoding for each line read/written
14  *******************************************************************************/
15 package org.eclipse.team.internal.ccvs.core.connection;
16 
17 
18 import java.io.*;
19 
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.osgi.util.NLS;
23 import org.eclipse.team.internal.ccvs.core.*;
24 
25 /**
26  * A connection to talk to a cvs server. The life cycle of a connection is
27  * as follows:
28  * <ul>
29  *	<li> constructor: creates a new connection object that wraps the given
30  *       repository location and connection method.
31  *	<li> open: opens a connection.
32  *	<li> send a request: use write* method or use the request stream directly.
33  *	     <code>GetRequestStream</code> returns an output stream to directly
34  *	     talk to the server.
35  *	<li> read responses: use read* methods or use the response stream directly.
36  *	     <code>GetResponseStream</code> returns an input stream to directly
37  *	     read output from the server.
38  *	<li> close: closes the connection. A closed connection can be reopened by
39  *	     calling open again.
40  * </ul>
41  */
42 public class Connection {
43 	private static final byte NEWLINE= 0xA;
44 
45 	private IServerConnection serverConnection;
46 	private ICVSRepositoryLocation fCVSRoot;
47 	private boolean fIsEstablished;
48 	private InputStream fResponseStream;
49 	private String fServerEncoding;
50 	private byte[] readLineBuffer = new byte[256];
51 
Connection(ICVSRepositoryLocation cvsroot, IServerConnection serverConnection)52 	public Connection(ICVSRepositoryLocation cvsroot, IServerConnection serverConnection) {
53 		fCVSRoot = cvsroot;
54 		this.serverConnection = serverConnection;
55 		fServerEncoding = getEncoding(fCVSRoot);
56 	}
57 
append(byte[] buffer, int index, byte b)58 	private static byte[] append(byte[] buffer, int index, byte b) {
59 		if (index >= buffer.length) {
60 			byte[] newBuffer= new byte[index * 2];
61 			System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
62 			buffer= newBuffer;
63 		}
64 		buffer[index]= b;
65 		return buffer;
66 	}
67 	/**
68 	 * Closes the connection.
69 	 */
close()70 	public void close() {
71 		if (!isEstablished())
72 			return;
73 		try {
74 			serverConnection.close();
75 		} catch (IOException ex) {
76 			// Generally, errors on close are of no interest.
77 			// However, log them if debugging is on
78 			if (Policy.DEBUG) {
79 				CVSProviderPlugin.log(new CVSCommunicationException(CVSMessages.Connection_cannotClose, fCVSRoot, ex));
80 			}
81 		} finally {
82 			fResponseStream = null;
83 			fIsEstablished = false;
84 		}
85 	}
86 	/**
87 	 * Flushes the request stream.
88 	 */
flush()89 	public void flush() throws CVSException {
90 		if (!isEstablished())
91 			return;
92 		try {
93 			getOutputStream().flush();
94 		} catch(IOException e) {
95 			throw new CVSCommunicationException(fCVSRoot,e);
96 		}
97 	}
98 
99 	/**
100 	 * Returns the <code>OutputStream</code> used to send requests
101 	 * to the server.
102 	 */
getOutputStream()103 	public OutputStream getOutputStream() {
104 		if (!isEstablished())
105 			return null;
106 		return serverConnection.getOutputStream();
107 	}
108 	/**
109 	 * Returns the <code>InputStream</code> used to read responses from
110 	 * the server.
111 	 */
getInputStream()112 	public InputStream getInputStream() {
113 		if (!isEstablished())
114 			return null;
115 		if (fResponseStream == null)
116 			fResponseStream = serverConnection.getInputStream();
117 		return fResponseStream;
118 	}
119 
120 	/**
121 	 * Returns <code>true</code> if the connection is established;
122 	 * otherwise <code>false</code>.
123 	 */
isEstablished()124 	public boolean isEstablished() {
125 		return fIsEstablished;
126 	}
127 
128 	/**
129 	 * Opens the connection.
130 	 */
open(IProgressMonitor monitor)131 	public void open(IProgressMonitor monitor) throws CVSException {
132 		if (isEstablished())
133 			return;
134 		try {
135 			serverConnection.open(monitor);
136 		} catch (IOException e) {
137 			throw new CVSCommunicationException(NLS.bind(CVSMessages.Connection_0, new String[] { fCVSRoot.getLocation(true), CVSCommunicationException.getMessageFor(e) }), fCVSRoot, e);
138 		}
139 		fIsEstablished= true;
140 	}
141 	/**
142 	 * Reads a line from the response stream.
143 	 */
readLine()144 	public String readLine() throws CVSException {
145 		if (!isEstablished())
146 			throw new CVSCommunicationException(CVSMessages.Connection_readUnestablishedConnection,fCVSRoot,null);
147 		try {
148 			InputStream in = getInputStream();
149 			int index = 0;
150 			int r;
151 			while ((r = in.read()) != -1) {
152 				if (r == NEWLINE) break;
153 				readLineBuffer = append(readLineBuffer, index++, (byte) r);
154 			}
155 
156 			String result = new String(readLineBuffer, 0, index, fServerEncoding);
157 			if (Policy.isDebugProtocol()) Policy.printProtocolLine(result);
158 			return result;
159 		} catch (IOException e) {
160 			throw new CVSCommunicationException(fCVSRoot,e);
161 		}
162 	}
163 
readLine(ICVSRepositoryLocation location, InputStream in)164 	static String readLine(ICVSRepositoryLocation location, InputStream in) throws IOException {
165 		byte[] buffer = new byte[256];
166 		int index = 0;
167 		int r;
168 		while ((r = in.read()) != -1) {
169 			if (r == NEWLINE)
170 				break;
171 			buffer = append(buffer, index++, (byte) r);
172 		}
173 
174 		String result = new String(buffer, 0, index, getEncoding(location));
175 		if (Policy.isDebugProtocol())
176 			Policy.printProtocolLine(result);
177 		return result;
178 	}
179 
180 	//---- Helper to send strings to the server ----------------------------
181 
182 	/**
183 	 * Sends the given string to the server.
184 	 */
write(String s)185 	public void write(String s) throws CVSException {
186 		try {
187 			write(s.getBytes(fServerEncoding), false);
188 		} catch (UnsupportedEncodingException e) {
189 			IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, e.getMessage(), e, fCVSRoot);
190 			throw new CVSException (status);
191 		}
192 	}
193 
194 	/**
195 	 * Return the encoding for the given repository location
196 	 * @return the encoding for the given repository location
197 	 */
getEncoding(ICVSRepositoryLocation location)198 	public static String getEncoding(ICVSRepositoryLocation location) {
199 		return location.getEncoding();
200 	}
201 
202 	/**
203 	 * Sends the given string and a newline to the server.
204 	 */
writeLine(String s)205 	public void writeLine(String s) throws CVSException {
206 		try {
207 			write(s.getBytes(fServerEncoding), true);
208 		} catch (UnsupportedEncodingException e) {
209 			IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.SERVER_ERROR, e.getMessage(), e, fCVSRoot);
210 			throw new CVSException (status);
211 		}
212 	}
213 
write(byte[] bytes, boolean newLine)214 	void write (byte[] bytes, boolean newLine) throws CVSException {
215 		write(bytes, 0, bytes.length, newLine);
216 	}
217 
218 	/**
219 	 * Low level method to write a string to the server. All write* methods are
220 	 * funneled through this method.
221 	 */
write(byte[] b, int off, int len, boolean newline)222 	void write(byte[] b, int off, int len, boolean newline) throws CVSException {
223 		if (!isEstablished())
224 			throw new CVSCommunicationException(CVSMessages.Connection_writeUnestablishedConnection,fCVSRoot,null);
225 
226 		if (Policy.isDebugProtocol())
227 			Policy.printProtocol(new String(b, off, len), newline);
228 
229 		try {
230 			OutputStream out= getOutputStream();
231 			out.write(b, off, len);
232 			if (newline)
233 				out.write(NEWLINE);
234 
235 		} catch (IOException e) {
236 			throw new CVSCommunicationException(fCVSRoot,e);
237 		}
238 	}
239 }
240