2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org
.spearce
.jgit
.transport
;
41 import java
.awt
.Container
;
42 import java
.awt
.GridBagConstraints
;
43 import java
.awt
.GridBagLayout
;
44 import java
.awt
.Insets
;
46 import java
.io
.FileInputStream
;
47 import java
.io
.FileNotFoundException
;
48 import java
.io
.IOException
;
49 import java
.io
.OutputStream
;
50 import java
.security
.AccessController
;
51 import java
.security
.PrivilegedAction
;
52 import java
.util
.HashSet
;
55 import javax
.swing
.JLabel
;
56 import javax
.swing
.JOptionPane
;
57 import javax
.swing
.JPanel
;
58 import javax
.swing
.JPasswordField
;
59 import javax
.swing
.JTextField
;
61 import org
.spearce
.jgit
.util
.FS
;
63 import com
.jcraft
.jsch
.JSch
;
64 import com
.jcraft
.jsch
.JSchException
;
65 import com
.jcraft
.jsch
.Session
;
66 import com
.jcraft
.jsch
.UIKeyboardInteractive
;
67 import com
.jcraft
.jsch
.UserInfo
;
70 * Loads known hosts and private keys from <code>$HOME/.ssh</code>.
72 * This is the default implementation used by JGit and provides most of the
73 * compatibility necessary to match OpenSSH, a popular implementation of SSH
76 * If user interactivity is required by SSH (e.g. to obtain a password) AWT is
77 * used to display a password input field to the end-user.
79 class DefaultSshSessionFactory
extends SshSessionFactory
{
80 /** IANA assigned port number for SSH. */
81 static final int SSH_PORT
= 22;
83 private Set
<String
> loadedIdentities
;
85 private JSch userJSch
;
87 private OpenSshConfig config
;
90 public synchronized Session
getSession(String user
, String pass
,
91 String host
, int port
) throws JSchException
{
92 final OpenSshConfig
.Host hc
= getConfig().lookup(host
);
93 host
= hc
.getHostName();
99 final Session session
= getUserJSch().getSession(user
, host
, port
);
100 if (hc
.getIdentityFile() != null)
101 addIdentity(hc
.getIdentityFile());
103 session
.setPassword(pass
);
104 else if (!hc
.isBatchMode())
105 session
.setUserInfo(new AWT_UserInfo());
107 final String pauth
= hc
.getPreferredAuthentications();
109 session
.setConfig("PreferredAuthentications", pauth
);
113 static String
userName() {
114 return AccessController
.doPrivileged(new PrivilegedAction
<String
>() {
115 public String
run() {
116 return System
.getProperty("user.name");
121 private JSch
getUserJSch() throws JSchException
{
122 if (userJSch
== null) {
123 loadedIdentities
= new HashSet
<String
>();
124 userJSch
= new JSch();
125 knownHosts(userJSch
);
131 private OpenSshConfig
getConfig() {
133 config
= OpenSshConfig
.get();
137 private void knownHosts(final JSch sch
) throws JSchException
{
138 final File home
= FS
.userHome();
141 final File known_hosts
= new File(new File(home
, ".ssh"), "known_hosts");
143 final FileInputStream in
= new FileInputStream(known_hosts
);
145 sch
.setKnownHosts(in
);
149 } catch (FileNotFoundException none
) {
150 // Oh well. They don't have a known hosts in home.
151 } catch (IOException err
) {
152 // Oh well. They don't have a known hosts in home.
156 private void identities() throws JSchException
{
157 final File home
= FS
.userHome();
160 final File sshdir
= new File(home
, ".ssh");
161 final File
[] keys
= sshdir
.listFiles();
164 for (int i
= 0; i
< keys
.length
; i
++) {
165 final File pk
= keys
[i
];
166 final String n
= pk
.getName();
167 if (!n
.endsWith(".pub"))
169 final File k
= new File(sshdir
, n
.substring(0, n
.length() - 4));
175 } catch (JSchException e
) {
181 private void addIdentity(final File identityFile
) throws JSchException
{
182 final String path
= identityFile
.getAbsolutePath();
183 if (!loadedIdentities
.contains(path
)) {
184 userJSch
.addIdentity(path
);
185 loadedIdentities
.add(path
);
189 private static class AWT_UserInfo
implements UserInfo
,
190 UIKeyboardInteractive
{
191 private String passwd
;
193 private String passphrase
;
195 public void showMessage(final String msg
) {
196 JOptionPane
.showMessageDialog(null, msg
);
199 public boolean promptYesNo(final String msg
) {
200 return JOptionPane
.showConfirmDialog(null, msg
, "Warning",
201 JOptionPane
.YES_NO_OPTION
) == JOptionPane
.YES_OPTION
;
204 public boolean promptPassword(final String msg
) {
206 final JPasswordField passwordField
= new JPasswordField(20);
207 final int result
= JOptionPane
.showConfirmDialog(null,
208 new Object
[] { passwordField
}, msg
,
209 JOptionPane
.OK_CANCEL_OPTION
);
210 if (result
== JOptionPane
.OK_OPTION
) {
211 passwd
= new String(passwordField
.getPassword());
217 public boolean promptPassphrase(final String msg
) {
219 final JPasswordField passwordField
= new JPasswordField(20);
220 final int result
= JOptionPane
.showConfirmDialog(null,
221 new Object
[] { passwordField
}, msg
,
222 JOptionPane
.OK_CANCEL_OPTION
);
223 if (result
== JOptionPane
.OK_OPTION
) {
224 passphrase
= new String(passwordField
.getPassword());
230 public String
getPassword() {
234 public String
getPassphrase() {
238 public String
[] promptKeyboardInteractive(final String destination
,
239 final String name
, final String instruction
,
240 final String
[] prompt
, final boolean[] echo
) {
241 final GridBagConstraints gbc
= new GridBagConstraints(0, 0, 1, 1,
242 1, 1, GridBagConstraints
.NORTHWEST
,
243 GridBagConstraints
.NONE
, new Insets(0, 0, 0, 0), 0, 0);
244 final Container panel
= new JPanel();
245 panel
.setLayout(new GridBagLayout());
248 gbc
.gridwidth
= GridBagConstraints
.REMAINDER
;
250 panel
.add(new JLabel(instruction
), gbc
);
253 gbc
.gridwidth
= GridBagConstraints
.RELATIVE
;
255 final JTextField
[] texts
= new JTextField
[prompt
.length
];
256 for (int i
= 0; i
< prompt
.length
; i
++) {
257 gbc
.fill
= GridBagConstraints
.NONE
;
260 panel
.add(new JLabel(prompt
[i
]), gbc
);
263 gbc
.fill
= GridBagConstraints
.HORIZONTAL
;
266 texts
[i
] = new JTextField(20);
268 texts
[i
] = new JPasswordField(20);
270 panel
.add(texts
[i
], gbc
);
274 if (JOptionPane
.showConfirmDialog(null, panel
, destination
+ ": "
275 + name
, JOptionPane
.OK_CANCEL_OPTION
,
276 JOptionPane
.QUESTION_MESSAGE
) == JOptionPane
.OK_OPTION
) {
277 String
[] response
= new String
[prompt
.length
];
278 for (int i
= 0; i
< prompt
.length
; i
++) {
279 response
[i
] = texts
[i
].getText();
283 return null; // cancel
288 public OutputStream
getErrorStream() {
289 return new OutputStream() {
290 private StringBuilder all
= new StringBuilder();
292 private StringBuilder sb
= new StringBuilder();
294 public String
toString() {
295 String r
= all
.toString();
296 while (r
.endsWith("\n"))
297 r
= r
.substring(0, r
.length() - 1);
302 public void write(final int b
) throws IOException
{
304 System
.err
.print('\r');
311 final String line
= sb
.toString();
312 System
.err
.print(line
);
314 sb
= new StringBuilder();