diff --git a/.classpath b/.classpath index ce7ff7d9c..47e53ff2e 100644 --- a/.classpath +++ b/.classpath @@ -15,6 +15,7 @@ + diff --git a/app/.classpath b/app/.classpath index c18cf69d6..3ff43e484 100644 --- a/app/.classpath +++ b/app/.classpath @@ -12,6 +12,7 @@ + diff --git a/app/build.xml b/app/build.xml index 415a667be..30b2b1dcd 100644 --- a/app/build.xml +++ b/app/build.xml @@ -102,6 +102,7 @@ + diff --git a/app/lib/commons-net-3.3.jar b/app/lib/commons-net-3.3.jar new file mode 100644 index 000000000..f4f19a902 Binary files /dev/null and b/app/lib/commons-net-3.3.jar differ diff --git a/app/lib/commons-net.LICENSE.ASL-2.0.txt b/app/lib/commons-net.LICENSE.ASL-2.0.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/app/lib/commons-net.LICENSE.ASL-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/app/src/cc/arduino/view/preferences/Preferences.form b/app/src/cc/arduino/view/preferences/Preferences.form index b0105a781..32b80cbd0 100644 --- a/app/src/cc/arduino/view/preferences/Preferences.form +++ b/app/src/cc/arduino/view/preferences/Preferences.form @@ -1,10 +1,16 @@
+ + + + + + - + @@ -28,354 +34,23 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -385,55 +60,753 @@ - + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/cc/arduino/view/preferences/Preferences.java b/app/src/cc/arduino/view/preferences/Preferences.java index e5b19c332..0b2227711 100644 --- a/app/src/cc/arduino/view/preferences/Preferences.java +++ b/app/src/cc/arduino/view/preferences/Preferences.java @@ -29,6 +29,7 @@ package cc.arduino.view.preferences; +import cc.arduino.Constants; import processing.app.Base; import processing.app.BaseNoGui; import processing.app.I18n; @@ -203,6 +204,11 @@ public class Preferences extends javax.swing.JDialog { // //GEN-BEGIN:initComponents private void initComponents() { + proxyTypeButtonGroup = new javax.swing.ButtonGroup(); + manualProxyTypeButtonGroup = new javax.swing.ButtonGroup(); + javax.swing.JPanel jPanel2 = new javax.swing.JPanel(); + javax.swing.JTabbedPane jTabbedPane1 = new javax.swing.JTabbedPane(); + javax.swing.JPanel jPanel1 = new javax.swing.JPanel(); javax.swing.JLabel sketchbookLocationLabel = new javax.swing.JLabel(); sketchbookLocationField = new javax.swing.JTextField(); javax.swing.JButton browseButton = new javax.swing.JButton(); @@ -222,8 +228,6 @@ public class Preferences extends javax.swing.JDialog { javax.swing.JLabel morePreferencesLabel = new javax.swing.JLabel(); preferencesFileLabel = new javax.swing.JLabel(); javax.swing.JLabel arduinoNotRunningLabel = new javax.swing.JLabel(); - javax.swing.JButton okButton = new javax.swing.JButton(); - javax.swing.JButton cancelButton = new javax.swing.JButton(); javax.swing.JPanel checkboxesContainer = new javax.swing.JPanel(); displayLineNumbersBox = new javax.swing.JCheckBox(); enableCodeFoldingBox = new javax.swing.JCheckBox(); @@ -232,12 +236,40 @@ public class Preferences extends javax.swing.JDialog { checkUpdatesBox = new javax.swing.JCheckBox(); updateExtensionBox = new javax.swing.JCheckBox(); saveVerifyUploadBox = new javax.swing.JCheckBox(); + javax.swing.JPanel jPanel4 = new javax.swing.JPanel(); + noProxy = new javax.swing.JRadioButton(); + autoProxy = new javax.swing.JRadioButton(); + manualProxy = new javax.swing.JRadioButton(); + autoProxyUsePAC = new javax.swing.JCheckBox(); + autoProxyPACURL = new javax.swing.JTextField(); + manualProxyHTTP = new javax.swing.JRadioButton(); + manualProxySOCKS = new javax.swing.JRadioButton(); + manualProxyHostNameLabel = new javax.swing.JLabel(); + manualProxyPortLabel = new javax.swing.JLabel(); + manualProxyHostName = new javax.swing.JTextField(); + manualProxyPort = new javax.swing.JTextField(); + manualProxyUsernameLabel = new javax.swing.JLabel(); + manualProxyUsername = new javax.swing.JTextField(); + manualProxyPasswordLabel = new javax.swing.JLabel(); + manualProxyPassword = new javax.swing.JPasswordField(); + autoProxyUsernameLabel = new javax.swing.JLabel(); + autoProxyUsername = new javax.swing.JTextField(); + autoProxyPassword = new javax.swing.JPasswordField(); + autoProxyPasswordLabel = new javax.swing.JLabel(); + javax.swing.JPanel jPanel3 = new javax.swing.JPanel(); + javax.swing.JButton okButton = new javax.swing.JButton(); + javax.swing.JButton cancelButton = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle(tr("Preferences")); setModal(true); setResizable(false); + jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.Y_AXIS)); + + jTabbedPane1.setFocusable(false); + jTabbedPane1.setRequestFocusEnabled(false); + sketchbookLocationLabel.setText(tr("Sketchbook location:")); sketchbookLocationField.setColumns(40); @@ -300,20 +332,6 @@ public class Preferences extends javax.swing.JDialog { arduinoNotRunningLabel.setForeground(Color.GRAY); arduinoNotRunningLabel.setText(tr("(edit only when Arduino is not running)")); - okButton.setText(I18n.PROMPT_OK); - okButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - okButtonActionPerformed(evt); - } - }); - - cancelButton.setText(I18n.PROMPT_CANCEL); - cancelButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cancelButtonActionPerformed(evt); - } - }); - checkboxesContainer.setLayout(new javax.swing.BoxLayout(checkboxesContainer, javax.swing.BoxLayout.Y_AXIS)); displayLineNumbersBox.setText(tr("Display line numbers")); @@ -337,90 +355,86 @@ public class Preferences extends javax.swing.JDialog { saveVerifyUploadBox.setText(tr("Save when verifying or uploading")); checkboxesContainer.add(saveVerifyUploadBox); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(sketchbookLocationField) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(browseButton)) .addComponent(checkboxesContainer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(okButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(additionalBoardsManagerLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(additionalBoardsManagerField, javax.swing.GroupLayout.PREFERRED_SIZE, 500, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(extendedAdditionalUrlFieldWindow)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(sketchbookLocationLabel) - .addGroup(layout.createSequentialGroup() + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(comboWarningsLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(comboWarnings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() + .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(showVerboseLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(verboseCompilationBox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(verboseUploadBox)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(comboLanguageLabel) .addComponent(fontSizeLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(fontSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() + .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(comboLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(requiresRestartLabel)))) .addComponent(arduinoNotRunningLabel) .addComponent(morePreferencesLabel) - .addComponent(preferencesFileLabel)) + .addComponent(preferencesFileLabel) + .addComponent(sketchbookLocationLabel) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(additionalBoardsManagerLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(additionalBoardsManagerField, javax.swing.GroupLayout.PREFERRED_SIZE, 500, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(extendedAdditionalUrlFieldWindow))) .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addContainerGap() .addComponent(sketchbookLocationLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(sketchbookLocationField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(browseButton)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(comboLanguageLabel) .addComponent(comboLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(requiresRestartLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(fontSizeLabel) .addComponent(fontSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(showVerboseLabel) .addComponent(verboseCompilationBox) .addComponent(verboseUploadBox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(comboWarningsLabel) .addComponent(comboWarnings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(checkboxesContainer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(additionalBoardsManagerLabel) .addComponent(additionalBoardsManagerField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(extendedAdditionalUrlFieldWindow)) @@ -430,13 +444,216 @@ public class Preferences extends javax.swing.JDialog { .addComponent(preferencesFileLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(arduinoNotRunningLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(cancelButton) - .addComponent(okButton)) .addContainerGap()) ); + jTabbedPane1.addTab(tr("Settings"), jPanel1); + + proxyTypeButtonGroup.add(noProxy); + noProxy.setText(tr("No proxy")); + noProxy.setActionCommand(Constants.PROXY_TYPE_NONE); + + proxyTypeButtonGroup.add(autoProxy); + autoProxy.setText(tr("Auto-detect proxy settings")); + autoProxy.setActionCommand(Constants.PROXY_TYPE_AUTO); + autoProxy.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + autoProxyItemStateChanged(evt); + } + }); + + proxyTypeButtonGroup.add(manualProxy); + manualProxy.setText(tr("Manual proxy configuration")); + manualProxy.setActionCommand(Constants.PROXY_TYPE_MANUAL); + manualProxy.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + manualProxyItemStateChanged(evt); + } + }); + + autoProxyUsePAC.setText(tr("Automatic proxy configuration URL:")); + autoProxyUsePAC.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + autoProxyUsePACItemStateChanged(evt); + } + }); + + manualProxyTypeButtonGroup.add(manualProxyHTTP); + manualProxyHTTP.setText("HTTP"); + manualProxyHTTP.setActionCommand(Constants.PROXY_MANUAL_TYPE_HTTP); + + manualProxyTypeButtonGroup.add(manualProxySOCKS); + manualProxySOCKS.setText("SOCKS"); + manualProxySOCKS.setActionCommand(Constants.PROXY_MANUAL_TYPE_SOCKS); + + manualProxyHostNameLabel.setText(tr("Host name:")); + + manualProxyPortLabel.setText(tr("Port number:")); + + manualProxyUsernameLabel.setText(tr("Username:")); + + manualProxyPasswordLabel.setText(tr("Password:")); + + manualProxyPassword.setToolTipText(""); + + autoProxyUsernameLabel.setText(tr("Username:")); + + autoProxyPassword.setToolTipText(""); + + autoProxyPasswordLabel.setText(tr("Password:")); + + javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4); + jPanel4.setLayout(jPanel4Layout); + jPanel4Layout.setHorizontalGroup( + jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel4Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel4Layout.createSequentialGroup() + .addGap(12, 12, 12) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(autoProxyUsePAC) + .addGroup(jPanel4Layout.createSequentialGroup() + .addGap(12, 12, 12) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(autoProxyUsernameLabel) + .addComponent(autoProxyPasswordLabel)))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(autoProxyPACURL) + .addGroup(jPanel4Layout.createSequentialGroup() + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(autoProxyUsername, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(autoProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE)))) + .addGroup(jPanel4Layout.createSequentialGroup() + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(noProxy) + .addComponent(autoProxy) + .addComponent(manualProxy) + .addGroup(jPanel4Layout.createSequentialGroup() + .addGap(12, 12, 12) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel4Layout.createSequentialGroup() + .addComponent(manualProxyHTTP) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(manualProxySOCKS)) + .addGroup(jPanel4Layout.createSequentialGroup() + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(manualProxyHostNameLabel) + .addComponent(manualProxyPortLabel) + .addComponent(manualProxyUsernameLabel) + .addComponent(manualProxyPasswordLabel)) + .addGap(18, 18, 18) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(manualProxyHostName, javax.swing.GroupLayout.PREFERRED_SIZE, 541, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(manualProxyPort, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(manualProxyPassword, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(manualProxyUsername, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE))))))) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + jPanel4Layout.setVerticalGroup( + jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel4Layout.createSequentialGroup() + .addContainerGap() + .addComponent(noProxy) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(autoProxy) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(autoProxyUsePAC) + .addComponent(autoProxyPACURL, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(autoProxyUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(autoProxyUsernameLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(autoProxyPasswordLabel) + .addComponent(autoProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(manualProxy) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(manualProxyHTTP) + .addComponent(manualProxySOCKS)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(manualProxyHostNameLabel) + .addComponent(manualProxyHostName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(manualProxyPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(manualProxyPortLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(manualProxyUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(manualProxyUsernameLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(manualProxyPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(manualProxyPasswordLabel)) + .addContainerGap(163, Short.MAX_VALUE)) + ); + + jTabbedPane1.addTab(tr("Network"), jPanel4); + + jPanel2.add(jTabbedPane1); + + okButton.setText(I18n.PROMPT_OK); + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + cancelButton.setText(I18n.PROMPT_CANCEL); + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); + jPanel3.setLayout(jPanel3Layout); + jPanel3Layout.setHorizontalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton) + .addContainerGap()) + ); + jPanel3Layout.setVerticalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton)) + .addContainerGap()) + ); + + jPanel2.add(jPanel3); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 691, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 610, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + pack(); }// //GEN-END:initComponents @@ -490,8 +707,29 @@ public class Preferences extends javax.swing.JDialog { cancelButtonActionPerformed(evt); }//GEN-LAST:event_okButtonActionPerformed + private void autoProxyItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_autoProxyItemStateChanged + disableAllProxyFields(); + autoProxyFieldsSetEnabled(autoProxy.isSelected()); + }//GEN-LAST:event_autoProxyItemStateChanged + + private void manualProxyItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_manualProxyItemStateChanged + disableAllProxyFields(); + manualProxyFieldsSetEnabled(manualProxy.isSelected()); + }//GEN-LAST:event_manualProxyItemStateChanged + + private void autoProxyUsePACItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_autoProxyUsePACItemStateChanged + autoProxyPACFieldsSetEnabled(autoProxyUsePAC.isSelected()); + }//GEN-LAST:event_autoProxyUsePACItemStateChanged + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTextField additionalBoardsManagerField; + private javax.swing.JRadioButton autoProxy; + private javax.swing.JTextField autoProxyPACURL; + private javax.swing.JPasswordField autoProxyPassword; + private javax.swing.JLabel autoProxyPasswordLabel; + private javax.swing.JCheckBox autoProxyUsePAC; + private javax.swing.JTextField autoProxyUsername; + private javax.swing.JLabel autoProxyUsernameLabel; private javax.swing.JCheckBox checkUpdatesBox; private javax.swing.JComboBox comboLanguage; private javax.swing.JComboBox comboWarnings; @@ -499,7 +737,21 @@ public class Preferences extends javax.swing.JDialog { private javax.swing.JCheckBox enableCodeFoldingBox; private javax.swing.JCheckBox externalEditorBox; private javax.swing.JTextField fontSizeField; + private javax.swing.JRadioButton manualProxy; + private javax.swing.JRadioButton manualProxyHTTP; + private javax.swing.JTextField manualProxyHostName; + private javax.swing.JLabel manualProxyHostNameLabel; + private javax.swing.JPasswordField manualProxyPassword; + private javax.swing.JLabel manualProxyPasswordLabel; + private javax.swing.JTextField manualProxyPort; + private javax.swing.JLabel manualProxyPortLabel; + private javax.swing.JRadioButton manualProxySOCKS; + private javax.swing.ButtonGroup manualProxyTypeButtonGroup; + private javax.swing.JTextField manualProxyUsername; + private javax.swing.JLabel manualProxyUsernameLabel; + private javax.swing.JRadioButton noProxy; private javax.swing.JLabel preferencesFileLabel; + private javax.swing.ButtonGroup proxyTypeButtonGroup; private javax.swing.JCheckBox saveVerifyUploadBox; private javax.swing.JTextField sketchbookLocationField; private javax.swing.JCheckBox updateExtensionBox; @@ -570,7 +822,15 @@ public class Preferences extends javax.swing.JDialog { PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ",")); - //editor.applyPreferences(); + PreferencesData.set(Constants.PREF_PROXY_TYPE, proxyTypeButtonGroup.getSelection().getActionCommand()); + PreferencesData.set(Constants.PREF_PROXY_PAC_URL, autoProxyUsePAC.isSelected() ? autoProxyPACURL.getText() : ""); + PreferencesData.set(Constants.PREF_PROXY_MANUAL_TYPE, manualProxyTypeButtonGroup.getSelection().getActionCommand()); + PreferencesData.set(Constants.PREF_PROXY_MANUAL_HOSTNAME, manualProxyHostName.getText()); + PreferencesData.set(Constants.PREF_PROXY_MANUAL_PORT, manualProxyPort.getText()); + PreferencesData.set(Constants.PREF_PROXY_MANUAL_USERNAME, manualProxyUsername.getText()); + PreferencesData.set(Constants.PREF_PROXY_MANUAL_PASSWORD, String.valueOf(manualProxyPassword.getPassword())); + PreferencesData.set(Constants.PREF_PROXY_AUTO_USERNAME, autoProxyUsername.getText()); + PreferencesData.set(Constants.PREF_PROXY_AUTO_PASSWORD, String.valueOf(autoProxyPassword.getPassword())); } private void showPrerefencesData() { @@ -611,5 +871,63 @@ public class Preferences extends javax.swing.JDialog { saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify")); additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls")); + + disableAllProxyFields(); + String proxyType = PreferencesData.get(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + + if (Constants.PROXY_TYPE_NONE.equals(proxyType)) { + noProxy.setSelected(true); + } else if (Constants.PROXY_TYPE_AUTO.equals(proxyType)) { + autoProxy.setSelected(true); + autoProxyFieldsSetEnabled(true); + if (!PreferencesData.get(Constants.PREF_PROXY_PAC_URL, "").isEmpty()) { + autoProxyUsePAC.setSelected(true); + autoProxyPACURL.setText(PreferencesData.get(Constants.PREF_PROXY_PAC_URL)); + autoProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_AUTO_USERNAME)); + autoProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_AUTO_PASSWORD)); + } + } else { + manualProxy.setSelected(true); + manualProxyFieldsSetEnabled(true); + manualProxyHostName.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_HOSTNAME)); + manualProxyPort.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_PORT)); + manualProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_USERNAME)); + manualProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_PASSWORD)); + } + + String selectedManualProxyType = PreferencesData.get(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP); + manualProxyHTTP.setSelected(Constants.PROXY_MANUAL_TYPE_HTTP.equals(selectedManualProxyType)); + manualProxySOCKS.setSelected(Constants.PROXY_MANUAL_TYPE_SOCKS.equals(selectedManualProxyType)); + } + + private void manualProxyFieldsSetEnabled(boolean enabled) { + manualProxySOCKS.setEnabled(enabled); + manualProxyHTTP.setEnabled(enabled); + manualProxyHostNameLabel.setEnabled(enabled); + manualProxyHostName.setEnabled(enabled); + manualProxyPortLabel.setEnabled(enabled); + manualProxyPort.setEnabled(enabled); + manualProxyUsernameLabel.setEnabled(enabled); + manualProxyUsername.setEnabled(enabled); + manualProxyPasswordLabel.setEnabled(enabled); + manualProxyPassword.setEnabled(enabled); + } + + private void autoProxyFieldsSetEnabled(boolean enabled) { + autoProxyUsePAC.setEnabled(enabled); + autoProxyPACFieldsSetEnabled(enabled && autoProxyUsePAC.isSelected()); + } + + private void autoProxyPACFieldsSetEnabled(boolean enabled) { + autoProxyPACURL.setEnabled(enabled); + autoProxyUsername.setEnabled(enabled); + autoProxyUsernameLabel.setEnabled(enabled); + autoProxyPassword.setEnabled(enabled); + autoProxyPasswordLabel.setEnabled(enabled); + } + + private void disableAllProxyFields() { + autoProxyFieldsSetEnabled(false); + manualProxyFieldsSetEnabled(false); } } diff --git a/app/test/cc/arduino/net/CustomProxySelectorTest.java b/app/test/cc/arduino/net/CustomProxySelectorTest.java new file mode 100644 index 000000000..005f5cce8 --- /dev/null +++ b/app/test/cc/arduino/net/CustomProxySelectorTest.java @@ -0,0 +1,169 @@ +/* + * This file is part of Arduino. + * + * Arduino is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + */ + +package cc.arduino.net; + +import cc.arduino.Constants; +import org.junit.Before; +import org.junit.Test; + +import java.net.*; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class CustomProxySelectorTest { + + private Map preferences; + private URI uri; + + @Before + public void setUp() throws Exception { + System.setProperty("java.net.useSystemProxies", "true"); + uri = new URL("https://www.arduino.cc").toURI(); + preferences = new HashMap<>(); + } + + @Test + public void testNoProxy() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_NONE); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(Proxy.NO_PROXY, proxy); + } + + @Test + public void testSystemProxy() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(ProxySelector.getDefault().select(uri).get(0), proxy); + } + + @Test + public void testProxyPACHTTP() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_http.pac").toExternalForm()); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)), proxy); + } + + @Test + public void testProxyPACHTTPWithLogin() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_http.pac").toExternalForm()); + preferences.put(Constants.PREF_PROXY_AUTO_USERNAME, "auto"); + preferences.put(Constants.PREF_PROXY_AUTO_PASSWORD, "autopassword"); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)), proxy); + + PasswordAuthentication authentication = Authenticator.requestPasswordAuthentication(null, 8080, uri.toURL().getProtocol(), "ciao", ""); + assertEquals(authentication.getUserName(), "auto"); + assertEquals(String.valueOf(authentication.getPassword()), "autopassword"); + } + + @Test + public void testProxyPACSOCKS() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_socks.pac").toExternalForm()); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("proxy.example.com", 8080)), proxy); + } + + @Test + public void testProxyPACDirect() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_direct.pac").toExternalForm()); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(Proxy.NO_PROXY, proxy); + } + + @Test + public void testProxyPACComplex() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_complex.pac").toExternalForm()); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("4.5.6.7", 8080)), proxy); + } + + @Test + public void testProxyPACComplex2() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO); + preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_complex.pac").toExternalForm()); + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(new URL("http://www.intranet.domain.com/ciao").toURI()); + + assertEquals(Proxy.NO_PROXY, proxy); + } + + @Test + public void testManualProxy() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_MANUAL); + preferences.put(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP); + preferences.put(Constants.PREF_PROXY_MANUAL_HOSTNAME, "localhost"); + preferences.put(Constants.PREF_PROXY_MANUAL_PORT, "8080"); + + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080)), proxy); + } + + @Test + public void testManualProxyWithLogin() throws Exception { + preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_MANUAL); + preferences.put(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP); + preferences.put(Constants.PREF_PROXY_MANUAL_HOSTNAME, "localhost"); + preferences.put(Constants.PREF_PROXY_MANUAL_PORT, "8080"); + preferences.put(Constants.PREF_PROXY_MANUAL_USERNAME, "username"); + preferences.put(Constants.PREF_PROXY_MANUAL_PASSWORD, "pwd"); + + CustomProxySelector proxySelector = new CustomProxySelector(preferences); + Proxy proxy = proxySelector.getProxyFor(uri); + + assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080)), proxy); + + PasswordAuthentication authentication = Authenticator.requestPasswordAuthentication(null, 8080, uri.toURL().getProtocol(), "ciao", ""); + assertEquals(authentication.getUserName(), "username"); + assertEquals(String.valueOf(authentication.getPassword()), "pwd"); + } +} diff --git a/app/test/cc/arduino/net/PACSupportMethodsTest.java b/app/test/cc/arduino/net/PACSupportMethodsTest.java new file mode 100644 index 000000000..7029132ea --- /dev/null +++ b/app/test/cc/arduino/net/PACSupportMethodsTest.java @@ -0,0 +1,28 @@ +package cc.arduino.net; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PACSupportMethodsTest { + + private PACSupportMethods pacSupportMethods; + + @Before + public void setUp() throws Exception { + pacSupportMethods = new PACSupportMethods(); + } + + @Test + public void testIsInNet() throws Exception { + assertFalse(pacSupportMethods.isInNet("192.168.0.121", "198.95.249.79", "255.255.255.255")); + assertFalse(pacSupportMethods.isInNet("198.95.249.78", "198.95.249.79", "255.255.255.255")); + assertTrue(pacSupportMethods.isInNet("198.95.249.79", "198.95.249.79", "255.255.255.255")); + + assertFalse(pacSupportMethods.isInNet("192.168.0.121", "198.95.0.0", "255.255.0.0")); + assertTrue(pacSupportMethods.isInNet("198.95.249.78", "198.95.0.0", "255.255.0.0")); + assertTrue(pacSupportMethods.isInNet("198.95.249.79", "198.95.0.0", "255.255.0.0")); + } +} diff --git a/app/test/cc/arduino/net/proxy_complex.pac b/app/test/cc/arduino/net/proxy_complex.pac new file mode 100644 index 000000000..58267b38d --- /dev/null +++ b/app/test/cc/arduino/net/proxy_complex.pac @@ -0,0 +1,32 @@ +// example downloaded from http://findproxyforurl.com/example-pac-file/ + +function FindProxyForURL(url, host) { + +// If the hostname matches, send direct. + if (dnsDomainIs(host, ".intranet.domain.com") || + shExpMatch(host, "(*.abcdomain.com|abcdomain.com)")) + return "DIRECT"; + +// If the protocol or URL matches, send direct. + if (url.substring(0, 4)=="ftp:" || + shExpMatch(url, "http://abcdomain.com/folder/*")) + return "DIRECT"; + +// If the requested website is hosted within the internal network, send direct. + if (isPlainHostName(host) || + shExpMatch(host, "*.local") || + isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || + isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || + isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || + isInNet(dnsResolve(host), "127.0.0.0", "255.255.255.0")) + return "DIRECT"; + +// If the IP address of the local machine is within a defined +// subnet, send to a specific proxy. + if (isInNet(myIpAddress(), "10.10.5.0", "255.255.255.0")) + return "PROXY 1.2.3.4:8080"; + +// DEFAULT RULE: All other traffic, use below proxies, in fail-over order. + return "PROXY 4.5.6.7:8080; PROXY 7.8.9.10:8080"; + +} diff --git a/app/test/cc/arduino/net/proxy_direct.pac b/app/test/cc/arduino/net/proxy_direct.pac new file mode 100644 index 000000000..92643ea0f --- /dev/null +++ b/app/test/cc/arduino/net/proxy_direct.pac @@ -0,0 +1,4 @@ +function FindProxyForURL(url, host) +{ + return "DIRECT"; +} diff --git a/app/test/cc/arduino/net/proxy_http.pac b/app/test/cc/arduino/net/proxy_http.pac new file mode 100644 index 000000000..fa48ed4e5 --- /dev/null +++ b/app/test/cc/arduino/net/proxy_http.pac @@ -0,0 +1,4 @@ +function FindProxyForURL(url, host) +{ + return "PROXY proxy.example.com:8080; DIRECT"; +} diff --git a/app/test/cc/arduino/net/proxy_socks.pac b/app/test/cc/arduino/net/proxy_socks.pac new file mode 100644 index 000000000..cfea51dca --- /dev/null +++ b/app/test/cc/arduino/net/proxy_socks.pac @@ -0,0 +1,4 @@ +function FindProxyForURL(url, host) +{ + return "SOCKS proxy.example.com:8080; DIRECT"; +} diff --git a/arduino-core/.classpath b/arduino-core/.classpath index 183bd20e5..755346253 100644 --- a/arduino-core/.classpath +++ b/arduino-core/.classpath @@ -4,6 +4,7 @@ + diff --git a/arduino-core/lib/commons-net-3.3.jar b/arduino-core/lib/commons-net-3.3.jar new file mode 100644 index 000000000..f4f19a902 Binary files /dev/null and b/arduino-core/lib/commons-net-3.3.jar differ diff --git a/arduino-core/lib/commons-net.LICENSE.ASL-2.0.txt b/arduino-core/lib/commons-net.LICENSE.ASL-2.0.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/arduino-core/lib/commons-net.LICENSE.ASL-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/arduino-core/src/cc/arduino/Constants.java b/arduino-core/src/cc/arduino/Constants.java index 098caab97..d6bfe48d9 100644 --- a/arduino-core/src/cc/arduino/Constants.java +++ b/arduino-core/src/cc/arduino/Constants.java @@ -40,13 +40,28 @@ public class Constants { public static final String DEFAULT_INDEX_FILE_NAME = "package_index.json"; public static final List PROTECTED_PACKAGE_NAMES = Arrays.asList("arduino", "Intel"); - public static final String PACKAGE_INDEX_URL; public static final String LIBRARY_DEVELOPMENT_FLAG_FILE = ".development"; public static final long BOARDS_LIBS_UPDATABLE_CHECK_START_PERIOD = 60000; public static final int NOTIFICATION_POPUP_AUTOCLOSE_DELAY = 10000; + public static final String PROXY_TYPE_NONE = "none"; + public static final String PROXY_TYPE_AUTO = "auto"; + public static final String PROXY_TYPE_MANUAL = "manual"; + public static final String PROXY_MANUAL_TYPE_HTTP = "HTTP"; + public static final String PROXY_MANUAL_TYPE_SOCKS = "SOCKS"; + public static final String PREF_PROXY_MANUAL_TYPE = "proxy.manual.type"; + public static final String PREF_PROXY_TYPE = "proxy.type"; + public static final String PREF_PROXY_PAC_URL = "proxy.pac.url"; + public static final String PREF_PROXY_MANUAL_HOSTNAME = "proxy.manual.hostname"; + public static final String PREF_PROXY_MANUAL_PORT = "proxy.manual.port"; + public static final String PREF_PROXY_MANUAL_USERNAME = "proxy.manual.username"; + public static final String PREF_PROXY_MANUAL_PASSWORD = "proxy.manual.password"; + public static final String PREF_PROXY_AUTO_USERNAME = "proxy.manual.username"; + public static final String PREF_PROXY_AUTO_PASSWORD = "proxy.manual.password"; + + public static final String PACKAGE_INDEX_URL; public static final String LIBRARY_INDEX_URL; public static final String LIBRARY_INDEX_URL_GZ; diff --git a/arduino-core/src/cc/arduino/net/CustomProxySelector.java b/arduino-core/src/cc/arduino/net/CustomProxySelector.java new file mode 100644 index 000000000..a6606ac14 --- /dev/null +++ b/arduino-core/src/cc/arduino/net/CustomProxySelector.java @@ -0,0 +1,171 @@ +/* + * This file is part of Arduino. + * + * Arduino is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + */ + +package cc.arduino.net; + +import cc.arduino.Constants; +import org.apache.commons.compress.utils.IOUtils; + +import javax.script.*; +import java.io.IOException; +import java.net.*; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.stream.Stream; + +public class CustomProxySelector { + + private final Map preferences; + + public CustomProxySelector(Map preferences) { + this.preferences = preferences; + clearPreviousAuthenticator(); + } + + public Proxy getProxyFor(URI uri) throws IOException, ScriptException, NoSuchMethodException { + String proxyType = preferences.get(Constants.PREF_PROXY_TYPE); + if (proxyType == null || proxyType.isEmpty()) { + proxyType = Constants.PROXY_TYPE_AUTO; + } + + if (Constants.PROXY_TYPE_NONE.equals(proxyType)) { + return Proxy.NO_PROXY; + } + + if (Constants.PROXY_TYPE_AUTO.equals(proxyType)) { + String pac = preferences.get(Constants.PREF_PROXY_PAC_URL); + if (pac == null || pac.isEmpty()) { + return ProxySelector.getDefault().select(uri).get(0); + } + + return pacProxy(pac, uri); + } + + if (Constants.PROXY_TYPE_MANUAL.equals(proxyType)) { + return manualProxy(); + } + + throw new IllegalStateException("Unable to understand proxy settings"); + } + + private Proxy pacProxy(String pac, URI uri) throws IOException, ScriptException, NoSuchMethodException { + setAuthenticator(preferences.get(Constants.PREF_PROXY_AUTO_USERNAME), preferences.get(Constants.PREF_PROXY_AUTO_PASSWORD)); + + URLConnection urlConnection = new URL(pac).openConnection(); + urlConnection.connect(); + if (urlConnection instanceof HttpURLConnection) { + int responseCode = ((HttpURLConnection) urlConnection).getResponseCode(); + if (responseCode != 200) { + throw new IOException("Unable to fetch PAC file at " + pac + ". Response code is " + responseCode); + } + } + String pacScript = new String(IOUtils.toByteArray(urlConnection.getInputStream()), Charset.forName("ASCII")); + + ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn"); + nashorn.getBindings(ScriptContext.ENGINE_SCOPE).put("pac", new PACSupportMethods()); + Stream.of("isPlainHostName(host)", + "dnsDomainIs(host, domain)", + "localHostOrDomainIs(host, hostdom)", + "isResolvable(host)", + "isInNet(host, pattern, mask)", + "dnsResolve(host)", + "myIpAddress()", + "dnsDomainLevels(host)", + "shExpMatch(str, shexp)").forEach((fn) -> { + try { + nashorn.eval("function " + fn + " { return pac." + fn + "; }"); + } catch (ScriptException e) { + throw new RuntimeException(e); + } + }); + nashorn.eval(pacScript); + String proxyConfigs = callFindProxyForURL(uri, nashorn); + return makeProxyFrom(proxyConfigs); + } + + private String callFindProxyForURL(URI uri, ScriptEngine nashorn) throws ScriptException, NoSuchMethodException { + Invocable script = (Invocable) nashorn; + URL url = toUrl(uri); + return (String) script.invokeFunction("FindProxyForURL", url.toExternalForm(), url.getHost()); + } + + private Proxy makeProxyFrom(String proxyConfigs) { + String proxyConfig = proxyConfigs.split(";")[0]; + if (proxyConfig.startsWith("DIRECT")) { + return Proxy.NO_PROXY; + } + Proxy.Type type; + if (proxyConfig.startsWith("PROXY")) { + type = Proxy.Type.HTTP; + proxyConfig = proxyConfig.replace("PROXY ", ""); + } else { + type = Proxy.Type.SOCKS; + proxyConfig = proxyConfig.replace("SOCKS ", ""); + } + String[] hostPort = proxyConfig.split(":"); + return new Proxy(type, new InetSocketAddress(hostPort[0], Integer.valueOf(hostPort[1]))); + } + + private URL toUrl(URI uri) { + try { + return uri.toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + private Proxy manualProxy() { + setAuthenticator(preferences.get(Constants.PREF_PROXY_MANUAL_USERNAME), preferences.get(Constants.PREF_PROXY_MANUAL_PASSWORD)); + Proxy.Type type = Proxy.Type.valueOf(preferences.get(Constants.PREF_PROXY_MANUAL_TYPE)); + return new Proxy(type, new InetSocketAddress(preferences.get(Constants.PREF_PROXY_MANUAL_HOSTNAME), Integer.valueOf(preferences.get(Constants.PREF_PROXY_MANUAL_PORT)))); + } + + private void clearPreviousAuthenticator() { + Authenticator.setDefault(null); + } + + private void setAuthenticator(String username, String password) { + if (username == null || username.isEmpty()) { + return; + } + String actualPassword; + if (password == null) { + actualPassword = ""; + } else { + actualPassword = password; + } + Authenticator.setDefault( + new Authenticator() { + public PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, actualPassword.toCharArray()); + } + } + ); + } +} diff --git a/arduino-core/src/cc/arduino/net/PACSupportMethods.java b/arduino-core/src/cc/arduino/net/PACSupportMethods.java new file mode 100644 index 000000000..5ff9f53ae --- /dev/null +++ b/arduino-core/src/cc/arduino/net/PACSupportMethods.java @@ -0,0 +1,103 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + * + * Arduino is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package cc.arduino.net; + +import org.apache.commons.net.util.SubnetUtils; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.Optional; +import java.util.regex.Pattern; + +public class PACSupportMethods { + + public boolean isPlainHostName(String host) { + return !host.contains("."); + } + + public boolean dnsDomainIs(String host, String domain) { + return host.contains(domain); + } + + public boolean localHostOrDomainIs(String host, String hostdom) { + return hostdom.contains(host); + } + + public boolean isResolvable(String host) { + try { + dnsResolve(host); + return true; + } catch (UnknownHostException e) { + return false; + } + } + + public boolean isInNet(String host, String pattern, String mask) { + SubnetUtils subnetUtils = new SubnetUtils(pattern, mask); + subnetUtils.setInclusiveHostCount(true); + return subnetUtils.getInfo().isInRange(host); + } + + public String dnsResolve(String host) throws UnknownHostException { + return InetAddress.getByName(host).getHostAddress(); + } + + public String myIpAddress() throws SocketException { + Optional publicIface = Collections.list(NetworkInterface.getNetworkInterfaces()).stream() + .filter((iface) -> { + try { + return !iface.isLoopback(); + } catch (SocketException e) { + throw new RuntimeException(e); + } + }) + .filter((iface) -> iface.getInetAddresses().hasMoreElements()) + .findFirst(); + + if (publicIface.isPresent()) { + return publicIface.get().getInetAddresses().nextElement().getHostAddress(); + } + + return "127.0.0.1"; + } + + public int dnsDomainLevels(String host) { + return host.split("\\.").length - 1; + } + + public boolean shExpMatch(String str, String shexp) { + String exp = shexp.replace("*", ""); + return Pattern.compile(exp).matcher(str).find(); + } + +} diff --git a/arduino-core/src/cc/arduino/utils/network/FileDownloader.java b/arduino-core/src/cc/arduino/utils/network/FileDownloader.java index c874c556d..73e9ec5fe 100644 --- a/arduino-core/src/cc/arduino/utils/network/FileDownloader.java +++ b/arduino-core/src/cc/arduino/utils/network/FileDownloader.java @@ -29,8 +29,10 @@ package cc.arduino.utils.network; +import cc.arduino.net.CustomProxySelector; import org.apache.commons.codec.binary.Base64; import org.apache.commons.compress.utils.IOUtils; +import processing.app.PreferencesData; import java.io.File; import java.io.IOException; @@ -120,7 +122,10 @@ public class FileDownloader extends Observable { setStatus(Status.CONNECTING); - Proxy proxy = ProxySelector.getDefault().select(downloadUrl.toURI()).get(0); + Proxy proxy = new CustomProxySelector(PreferencesData.getMap()).getProxyFor(downloadUrl.toURI()); + if ("true".equals(System.getProperty("DEBUG"))) { + System.err.println("Using proxy " + proxy); + } HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(proxy); diff --git a/build/windows/launcher/config.xml b/build/windows/launcher/config.xml index 0e57f9295..25561c08c 100644 --- a/build/windows/launcher/config.xml +++ b/build/windows/launcher/config.xml @@ -17,6 +17,7 @@ %EXEDIR%/lib/commons-httpclient-3.1.jar %EXEDIR%/lib/commons-lang3-3.3.2.jar %EXEDIR%/lib/commons-logging-1.0.4.jar + %EXEDIR%/lib/commons-net-3.3.jar %EXEDIR%/lib/ecj.jar %EXEDIR%/lib/jackson-annotations-2.2.3.jar %EXEDIR%/lib/jackson-core-2.2.3.jar diff --git a/build/windows/launcher/config_debug.xml b/build/windows/launcher/config_debug.xml index 99bf33952..365d4c283 100644 --- a/build/windows/launcher/config_debug.xml +++ b/build/windows/launcher/config_debug.xml @@ -17,6 +17,7 @@ %EXEDIR%/lib/commons-httpclient-3.1.jar %EXEDIR%/lib/commons-lang3-3.3.2.jar %EXEDIR%/lib/commons-logging-1.0.4.jar + %EXEDIR%/lib/commons-net-3.3.jar %EXEDIR%/lib/ecj.jar %EXEDIR%/lib/jackson-annotations-2.2.3.jar %EXEDIR%/lib/jackson-core-2.2.3.jar @@ -36,6 +37,7 @@ 32 -Dsun.java2d.d3d=false -DAPP_DIR="%EXEDIR%" + -DDEBUG=true Arduino IDE