1 package processing.app;
2 
3 import cc.arduino.packages.BoardPort;
4 import cc.arduino.packages.ssh.NoInteractionUserInfo;
5 import cc.arduino.packages.ssh.SSHClientSetupChainRing;
6 import cc.arduino.packages.ssh.SSHConfigFileSetup;
7 import cc.arduino.packages.ssh.SSHPwdSetup;
8 
9 import com.jcraft.jsch.*;
10 
11 import processing.app.debug.MessageConsumer;
12 import processing.app.debug.MessageSiphon;
13 
14 import javax.swing.*;
15 
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 
22 import static processing.app.I18n.tr;
23 
24 @SuppressWarnings("serial")
25 public class NetworkMonitor extends AbstractTextMonitor implements MessageConsumer {
26 
27   private static final int MAX_CONNECTION_ATTEMPTS = 5;
28 
29   private MessageSiphon inputConsumer;
30   private Session session;
31   private Channel channel;
32   private int connectionAttempts;
33 
NetworkMonitor(BoardPort port)34   public NetworkMonitor(BoardPort port) {
35     super(port);
36 
37     onSendCommand(new ActionListener() {
38       public void actionPerformed(ActionEvent event) {
39         try {
40           OutputStream out = channel.getOutputStream();
41           out.write(textField.getText().getBytes());
42           out.write('\n');
43           out.flush();
44         } catch (IOException e) {
45           e.printStackTrace();
46         }
47         textField.setText("");
48       }
49     });
50   }
51 
52   @Override
requiresAuthorization()53   public boolean requiresAuthorization() {
54     return true;
55   }
56 
57   @Override
getAuthorizationKey()58   public String getAuthorizationKey() {
59     return "runtime.pwd." + getBoardPort().getAddress();
60   }
61 
62   @Override
open()63   public void open() throws Exception {
64     super.open();
65     this.connectionAttempts = 0;
66 
67     JSch jSch = new JSch();
68     SSHClientSetupChainRing sshClientSetupChain = new SSHConfigFileSetup(new SSHPwdSetup());
69     session = sshClientSetupChain.setup(getBoardPort(), jSch);
70 
71     session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
72     session.setUserInfo(new NoInteractionUserInfo(PreferencesData.get(getAuthorizationKey())));
73     session.connect(30000);
74 
75     tryConnect();
76   }
77 
tryConnect()78   private void tryConnect() throws JSchException, IOException {
79     connectionAttempts++;
80 
81     if (connectionAttempts > 1) {
82       try {
83         Thread.sleep(2000);
84       } catch (InterruptedException e) {
85         // ignored
86       }
87     }
88 
89     if (!session.isConnected()) {
90       return;
91     }
92     channel = session.openChannel("exec");
93     ((ChannelExec) channel).setCommand("telnet localhost 6571");
94 
95     InputStream inputStream = channel.getInputStream();
96     InputStream errStream = ((ChannelExec) channel).getErrStream();
97 
98     channel.connect();
99 
100     inputConsumer = new MessageSiphon(inputStream, this);
101     new MessageSiphon(errStream, this);
102 
103     if (connectionAttempts > 1) {
104       SwingUtilities.invokeLater(new Runnable() {
105 
106         @Override
107         public void run() {
108           try {
109             Thread.sleep(1000);
110           } catch (InterruptedException e) {
111             // ignore
112           }
113           if (channel.isConnected()) {
114             NetworkMonitor.this.message(tr("connected!") + '\n');
115           }
116         }
117 
118       });
119     }
120   }
121 
122   @Override
message(String s)123   public synchronized void message(String s) {
124     if (s.contains("can't connect")) {
125       while (!channel.isClosed()) {
126         try {
127           Thread.sleep(100);
128         } catch (InterruptedException e) {
129           // ignore
130         }
131       }
132       if (connectionAttempts < MAX_CONNECTION_ATTEMPTS) {
133         s = "\n" + tr("Unable to connect: retrying") + " (" + connectionAttempts + ")... ";
134 
135         SwingUtilities.invokeLater(new Runnable() {
136           @Override
137           public void run() {
138             try {
139               NetworkMonitor.this.tryConnect();
140             } catch (JSchException e) {
141               e.printStackTrace();
142             } catch (IOException e) {
143               e.printStackTrace();
144             }
145           }
146         });
147       } else {
148         s = "\n" + tr("Unable to connect: is the sketch using the bridge?");
149       }
150     }
151     super.message(s);
152   }
153 
154   @Override
close()155   public void close() throws Exception {
156     super.close();
157 
158     if (channel != null) {
159       inputConsumer.stop();
160       channel.disconnect();
161       textArea.setText("");
162     }
163 
164     if (session != null) {
165       session.disconnect();
166     }
167   }
168 
169 }
170