diff --git a/app/lib/jsch-0.1.50.jar b/app/lib/jsch-0.1.50.jar new file mode 100644 index 000000000..33bbd370c Binary files /dev/null and b/app/lib/jsch-0.1.50.jar differ diff --git a/app/src/cc/arduino/packages/uploaders/HttpUploader.java b/app/src/cc/arduino/packages/uploaders/HttpUploader.java index f159f0349..ae17e9f67 100644 --- a/app/src/cc/arduino/packages/uploaders/HttpUploader.java +++ b/app/src/cc/arduino/packages/uploaders/HttpUploader.java @@ -51,7 +51,7 @@ public class HttpUploader extends Uploader { } public String getAuthorizationKey() { - return "pwd." + ipAddress; + return "runtime.pwd." + ipAddress; } @Override diff --git a/app/src/processing/app/AbstractMonitor.java b/app/src/processing/app/AbstractMonitor.java index 65719c04c..ddcc8b13d 100644 --- a/app/src/processing/app/AbstractMonitor.java +++ b/app/src/processing/app/AbstractMonitor.java @@ -11,7 +11,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.io.IOException; import static processing.app.I18n._; @@ -32,7 +31,7 @@ public abstract class AbstractMonitor extends JFrame implements MessageConsumer public void windowClosing(WindowEvent event) { try { close(); - } catch (IOException e) { + } catch (Exception e) { // ignore } } @@ -45,7 +44,7 @@ public abstract class AbstractMonitor extends JFrame implements MessageConsumer public void actionPerformed(ActionEvent event) { try { close(); - } catch (IOException e) { + } catch (Exception e) { // ignore } setVisible(false); @@ -174,7 +173,15 @@ public abstract class AbstractMonitor extends JFrame implements MessageConsumer }); } - public abstract void open() throws IOException; + public boolean requiresAuthorization() { + return false; + } - public abstract void close() throws IOException; + public String getAuthorizationKey() { + return null; + } + + public abstract void open() throws Exception; + + public abstract void close() throws Exception; } diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index cb2767534..bc70fd580 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -924,7 +924,7 @@ public class Base { editors.remove(editor); try { Editor.serialMonitor.close(); - } catch (IOException e) { + } catch (Exception e) { //ignore } storeSketches(); @@ -966,7 +966,7 @@ public class Base { storeSketches(); try { Editor.serialMonitor.close(); - } catch (IOException e) { + } catch (Exception e) { // ignore } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index d15fd31e8..17e63f4d8 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -22,7 +22,9 @@ package processing.app; +import com.jcraft.jsch.JSchException; import processing.app.debug.*; +import processing.app.forms.PasswordAuthorizationDialog; import processing.app.syntax.*; import processing.app.tools.*; import processing.core.*; @@ -704,7 +706,7 @@ public class Editor extends JFrame implements RunnerListener { JMenu boardsMenu = new JMenu(_("Board")); Editor.boardsMenus.add(boardsMenu); toolsMenu.add(boardsMenu); - + base.rebuildBoardsMenu(toolsMenu, this); //Debug: rebuild imports importMenu.removeAll(); @@ -969,7 +971,7 @@ public class Editor extends JFrame implements RunnerListener { Preferences.set("serial.port.file", name); try { serialMonitor.close(); - } catch (IOException e) { + } catch (Exception e) { // ignore } serialMonitor.setVisible(false); @@ -983,14 +985,14 @@ public class Editor extends JFrame implements RunnerListener { protected void populatePortMenu() { serialMenu.removeAll(); - + String selectedPort = Preferences.get("serial.port"); - + List ports = Base.getDiscoveryManager().discovery(); for (BoardPort port : ports) { String address = port.getAddress(); String label = port.getLabel(); - + JCheckBoxMenuItem item = new JCheckBoxMenuItem(label, address.equals(selectedPort)); item.addActionListener(new SerialMenuListener(address)); serialMenu.add(item); @@ -2490,14 +2492,39 @@ public class Editor extends JFrame implements RunnerListener { public void handleSerial() { if (uploading) return; - try { - serialMonitor.open(); - serialMonitor.setVisible(true); - } catch (ConnectException e) { - statusError(_("Unable to connect: is the sketch using the bridge?")); - } catch (IOException e) { - statusError(e); - } + boolean success = false; + do { + if (serialMonitor.requiresAuthorization() && !Preferences.has(serialMonitor.getAuthorizationKey())) { + PasswordAuthorizationDialog dialog = new PasswordAuthorizationDialog(this, _("Type board password to access its console")); + dialog.setLocationRelativeTo(this); + dialog.setVisible(true); + + if (dialog.isCancelled()) { + statusNotice(_("Unable to open serial monitor")); + return; + } + + Preferences.set(serialMonitor.getAuthorizationKey(), dialog.getPassword()); + } + + try { + serialMonitor.open(); + serialMonitor.setVisible(true); + success = true; + } catch (ConnectException e) { + statusError(_("Unable to connect: is the sketch using the bridge?")); + } catch (JSchException e) { + statusError(_("Unable to connect: wrong password?")); + } catch (Exception e) { + statusError(e); + } finally { + if (serialMonitor.requiresAuthorization() && !success) { + Preferences.remove(serialMonitor.getAuthorizationKey()); + } + } + + } while (serialMonitor.requiresAuthorization() && !success); + } @@ -2522,7 +2549,8 @@ public class Editor extends JFrame implements RunnerListener { statusError(_("Error while burning bootloader.")); e.printStackTrace(); } - }}); + } + }); } diff --git a/app/src/processing/app/NetworkMonitor.java b/app/src/processing/app/NetworkMonitor.java index 45ba8cf2c..d44ad3224 100644 --- a/app/src/processing/app/NetworkMonitor.java +++ b/app/src/processing/app/NetworkMonitor.java @@ -1,22 +1,26 @@ package processing.app; +import com.jcraft.jsch.*; import processing.app.debug.MessageSiphon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; import java.util.regex.Matcher; +import static processing.app.I18n._; + @SuppressWarnings("serial") public class NetworkMonitor extends AbstractMonitor { private final String ipAddress; - private Socket socket; - private MessageSiphon consumer; + private MessageSiphon inputConsumer; + private Session session; + private Channel channel; + private MessageSiphon errorConsumer; public NetworkMonitor(String port, Base base) { super(port); @@ -28,7 +32,7 @@ public class NetworkMonitor extends AbstractMonitor { onSendCommand(new ActionListener() { public void actionPerformed(ActionEvent event) { try { - OutputStream out = socket.getOutputStream(); + OutputStream out = channel.getOutputStream(); out.write(textField.getText().getBytes()); out.write('\n'); out.flush(); @@ -41,24 +45,81 @@ public class NetworkMonitor extends AbstractMonitor { } @Override - public void open() throws IOException { - try { - socket = new Socket(); - socket.connect(new InetSocketAddress(ipAddress, 6571), 5000); - consumer = new MessageSiphon(socket.getInputStream(), this); - return; - } catch (IOException e) { - socket = null; - throw e; - } + public boolean requiresAuthorization() { + return true; } @Override - public void close() throws IOException { - if (socket != null) { - consumer.stop(); - socket.close(); + public String getAuthorizationKey() { + return "runtime.pwd." + ipAddress; + } + + @Override + public void open() throws Exception { + JSch jSch = new JSch(); + session = jSch.getSession("root", ipAddress, 22); + session.setPassword(Preferences.get(getAuthorizationKey())); + + session.setUserInfo(new NoInteractionUserInfo()); + session.connect(30000); + + channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("telnet localhost 6571"); + + InputStream inputStream = channel.getInputStream(); + InputStream errStream = ((ChannelExec) channel).getErrStream(); + + channel.connect(); + + inputConsumer = new MessageSiphon(inputStream, this); + errorConsumer = new MessageSiphon(errStream, this); + } + + @Override + public void message(String s) { + if (s.contains("can't connect")) { + s = _("Unable to connect: is the sketch using the bridge?"); + } + super.message(s); //To change body of overridden methods use File | Settings | File Templates. + } + + @Override + public void close() throws Exception { + if (channel != null) { + inputConsumer.stop(); + channel.disconnect(); textArea.setText(""); } + + if (session != null) { + session.disconnect(); + } + } + + public static class NoInteractionUserInfo implements UserInfo { + + public String getPassword() { + return null; + } + + public boolean promptYesNo(String str) { + return true; + } + + public String getPassphrase() { + return null; + } + + public boolean promptPassphrase(String message) { + return false; + } + + public boolean promptPassword(String message) { + return false; + } + + public void showMessage(String message) { + } + } } diff --git a/app/src/processing/app/SerialMonitor.java b/app/src/processing/app/SerialMonitor.java index 21eec7785..ce3c4ab5a 100644 --- a/app/src/processing/app/SerialMonitor.java +++ b/app/src/processing/app/SerialMonitor.java @@ -22,7 +22,6 @@ import processing.core.PApplet; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.io.IOException; import static processing.app.I18n._; @@ -49,10 +48,10 @@ public class SerialMonitor extends AbstractMonitor { close(); Thread.sleep(100); // Wait for serial port to properly close open(); - } catch (IOException e) { - System.err.println(e); } catch (InterruptedException e) { - e.printStackTrace(); + // noop + } catch (Exception e) { + System.err.println(e); } } }); @@ -82,14 +81,14 @@ public class SerialMonitor extends AbstractMonitor { } } - public void open() throws IOException { + public void open() throws Exception { if (serial != null) return; serial = new Serial(port, serialRate); serial.addListener(this); } - public void close() throws IOException { + public void close() throws Exception { if (serial != null) { int[] location = getPlacement(); String locationStr = PApplet.join(PApplet.str(location), ","); diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index fc7910a6e..5c5a7117e 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1671,7 +1671,7 @@ public class Sketch { boolean success = false; do { if (uploader.requiresAuthorization() && !Preferences.has(uploader.getAuthorizationKey())) { - PasswordAuthorizationDialog dialog = new PasswordAuthorizationDialog(editor); + PasswordAuthorizationDialog dialog = new PasswordAuthorizationDialog(editor, _("Type board password to upload a new sketch")); dialog.setLocationRelativeTo(editor); dialog.setVisible(true); @@ -1680,7 +1680,7 @@ public class Sketch { return false; } - Preferences.set(uploader.getAuthorizationKey(), DigestUtils.sha256Hex(dialog.getPassword())); + Preferences.set(uploader.getAuthorizationKey(), dialog.getPassword()); } try { diff --git a/app/src/processing/app/forms/PasswordAuthorizationDialog.java b/app/src/processing/app/forms/PasswordAuthorizationDialog.java index 4902a3baa..e79c05426 100644 --- a/app/src/processing/app/forms/PasswordAuthorizationDialog.java +++ b/app/src/processing/app/forms/PasswordAuthorizationDialog.java @@ -22,7 +22,7 @@ public class PasswordAuthorizationDialog extends JDialog { protected boolean cancelled; protected String password; - public PasswordAuthorizationDialog(Frame parent) { + public PasswordAuthorizationDialog(Frame parent, String dialogText) { super(parent, true); this.cancelled = false; @@ -37,7 +37,7 @@ public class PasswordAuthorizationDialog extends JDialog { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - typePasswordLabel.setText(_("Type board password to upload a new sketch")); + typePasswordLabel.setText(dialogText); icon.setIcon(new ImageIcon(new File(Base.getContentFile("lib"), "theme/lock.png").getAbsolutePath())); diff --git a/build/build.xml b/build/build.xml index 85386efeb..3c02d8b22 100644 --- a/build/build.xml +++ b/build/build.xml @@ -24,13 +24,14 @@ - + + diff --git a/build/macosx/template.app/Contents/Info.plist b/build/macosx/template.app/Contents/Info.plist index 153525053..37d10f1d9 100755 --- a/build/macosx/template.app/Contents/Info.plist +++ b/build/macosx/template.app/Contents/Info.plist @@ -70,13 +70,13 @@ processing.app.Base JVMVersion - 1.5* + 1.6* ClassPath - $JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/registry.jar:$JAVAROOT/quaqua.jar:$JAVAROOT/RXTXcomm.jar:$JAVAROOT/commons-codec-1.7.jar:$JAVAROOT/commons-exec-1.1.jar:$JAVAROOT/commons-httpclient-3.1.jar:$JAVAROOT/commons-logging-1.0.4.jar:$JAVAROOT/jmdns-3.4.1.jar + $JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/registry.jar:$JAVAROOT/quaqua.jar:$JAVAROOT/RXTXcomm.jar:$JAVAROOT/commons-codec-1.7.jar:$JAVAROOT/commons-exec-1.1.jar:$JAVAROOT/commons-httpclient-3.1.jar:$JAVAROOT/commons-logging-1.0.4.jar:$JAVAROOT/jmdns-3.4.1.jar:$JAVAROOT/jsch-0.1.50.jar JVMArchs