001package org.apache.maven.scm.provider.cvslib.cvsjava.util;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import ch.ethz.ssh2.Connection;
023import ch.ethz.ssh2.Session;
024import ch.ethz.ssh2.StreamGobbler;
025import org.netbeans.lib.cvsclient.CVSRoot;
026import org.netbeans.lib.cvsclient.command.CommandAbortedException;
027import org.netbeans.lib.cvsclient.connection.AbstractConnection;
028import org.netbeans.lib.cvsclient.connection.AuthenticationException;
029import org.netbeans.lib.cvsclient.connection.ConnectionModifier;
030import org.netbeans.lib.cvsclient.util.LoggedDataInputStream;
031import org.netbeans.lib.cvsclient.util.LoggedDataOutputStream;
032
033import java.io.BufferedReader;
034import java.io.File;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.InputStreamReader;
038
039/**
040 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
041 *
042 */
043public class ExtConnection
044    extends AbstractConnection
045{
046    private String host;
047
048    private int port;
049
050    private String userName;
051
052    private String password;
053
054    private Connection connection;
055
056    private Session session;
057
058    private BufferedReader stderrReader;
059
060    public ExtConnection( CVSRoot cvsRoot )
061    {
062        this( cvsRoot.getHostName(), cvsRoot.getPort(), cvsRoot.getUserName(), cvsRoot.getPassword(),
063              cvsRoot.getRepository() );
064    }
065
066    public ExtConnection( String host, int port, String username, String password, String repository )
067    {
068        this.userName = username;
069
070        if ( this.userName == null )
071        {
072            this.userName = System.getProperty( "user.name" );
073        }
074
075        this.password = password;
076
077        this.host = host;
078
079        setRepository( repository );
080
081        this.port = port;
082
083        if ( this.port == 0 )
084        {
085            this.port = 22;
086        }
087    }
088
089    /** {@inheritDoc} */
090    public void open()
091        throws AuthenticationException, CommandAbortedException
092    {
093        connection = new Connection( host, port );
094
095        /* TODO: add proxy support
096        ProxyData proxy = new HTTPProxyData( proxyHost, proxyPort, proxyUserName, proxyPassword );
097
098        connection.setProxyData( proxy );
099        */
100
101        try
102        {
103            // TODO: connection timeout?
104            connection.connect();
105        }
106        catch ( IOException e )
107        {
108            String message = "Cannot connect. Reason: " + e.getMessage();
109            throw new AuthenticationException( message, e, message );
110        }
111
112        File privateKey = getPrivateKey();
113
114        try
115        {
116            boolean authenticated;
117            if ( privateKey != null && privateKey.exists() )
118            {
119                authenticated = connection.authenticateWithPublicKey( userName, privateKey, getPassphrase() );
120            }
121            else
122            {
123                authenticated = connection.authenticateWithPassword( userName, password );
124            }
125
126            if ( !authenticated )
127            {
128                String message = "Authentication failed.";
129                throw new AuthenticationException( message, message );
130            }
131        }
132        catch ( IOException e )
133        {
134            closeConnection();
135            String message = "Cannot authenticate. Reason: " + e.getMessage();
136            throw new AuthenticationException( message, e, message );
137        }
138
139        try
140        {
141            session = connection.openSession();
142        }
143        catch ( IOException e )
144        {
145            String message = "Cannot open session. Reason: " + e.getMessage();
146            throw new CommandAbortedException( message, message );
147        }
148
149        String command = "cvs server";
150        try
151        {
152            session.execCommand( command );
153        }
154        catch ( IOException e )
155        {
156            String message = "Cannot execute remote command: " + command;
157            throw new CommandAbortedException( message, message );
158        }
159
160        InputStream stdout = new StreamGobbler( session.getStdout() );
161        InputStream stderr = new StreamGobbler( session.getStderr() );
162        stderrReader = new BufferedReader( new InputStreamReader( stderr ) );
163        setInputStream( new LoggedDataInputStream( stdout ) );
164        setOutputStream( new LoggedDataOutputStream( session.getStdin() ) );
165    }
166
167    /** {@inheritDoc} */
168    public void verify()
169        throws AuthenticationException
170    {
171        try
172        {
173            open();
174            verifyProtocol();
175            close();
176        }
177        catch ( Exception e )
178        {
179            String message = "Failed to verify the connection: " + e.getMessage();
180            throw new AuthenticationException( message, e, message );
181        }
182    }
183
184    private void closeConnection()
185    {
186        try
187        {
188            if ( stderrReader != null )
189            {
190                while ( true )
191                {
192                    String line = stderrReader.readLine();
193                    if ( line == null )
194                    {
195                        break;
196                    }
197
198                    System.err.println( line );
199                }
200            }
201        }
202        catch ( IOException e )
203        {
204            //nothing to do
205        }
206
207        if ( session != null )
208        {
209            System.out.println( "Exit code:" + session.getExitStatus().intValue() );
210            session.close();
211        }
212
213        if ( connection != null )
214        {
215            connection.close();
216        }
217
218        reset();
219    }
220
221    private void reset()
222    {
223        connection = null;
224        session = null;
225        stderrReader = null;
226        setInputStream( null );
227        setOutputStream( null );
228    }
229
230    /** {@inheritDoc} */
231    public void close()
232        throws IOException
233    {
234        closeConnection();
235    }
236
237    /** {@inheritDoc} */
238    public boolean isOpen()
239    {
240        return connection != null;
241    }
242
243    /** {@inheritDoc} */
244    public int getPort()
245    {
246        return port;
247    }
248
249    /** {@inheritDoc} */
250    public void modifyInputStream( ConnectionModifier modifier )
251        throws IOException
252    {
253        modifier.modifyInputStream( getInputStream() );
254    }
255
256    /** {@inheritDoc} */
257    public void modifyOutputStream( ConnectionModifier modifier )
258        throws IOException
259    {
260        modifier.modifyOutputStream( getOutputStream() );
261    }
262
263    private File getPrivateKey()
264    {
265        // If user don't define a password, he want to use a private key
266        File privateKey = null;
267        if ( password == null )
268        {
269            String pk = System.getProperty( "maven.scm.cvs.java.ssh.privateKey" );
270            if ( pk != null )
271            {
272                privateKey = new File( pk );
273            }
274            else
275            {
276                privateKey = findPrivateKey();
277            }
278        }
279        return privateKey;
280    }
281
282    private String getPassphrase()
283    {
284        String passphrase = System.getProperty( "maven.scm.cvs.java.ssh.passphrase" );
285
286        if ( passphrase == null )
287        {
288            passphrase = "";
289        }
290
291        return passphrase;
292    }
293
294    private File findPrivateKey()
295    {
296        String privateKeyDirectory = System.getProperty( "maven.scm.ssh.privateKeyDirectory" );
297
298        if ( privateKeyDirectory == null )
299        {
300            privateKeyDirectory = System.getProperty( "user.home" );
301        }
302
303        File privateKey = new File( privateKeyDirectory, ".ssh/id_dsa" );
304
305        if ( !privateKey.exists() )
306        {
307            privateKey = new File( privateKeyDirectory, ".ssh/id_rsa" );
308        }
309
310        return privateKey;
311    }
312}